From f51871df2b12d85dbf986da38c6e09cc4f4027d0 Mon Sep 17 00:00:00 2001 From: harris wong Date: Thu, 8 Jul 2010 15:01:18 +0000 Subject: [PATCH 1/1] ATutor 2.0 --- .htaccess | 119 + 404.php | 25 + about.php | 25 + acl.php | 16 + admin/config_edit.php | 423 + admin/config_template.php | 139 + admin/cron.php | 34 + admin/cron_config.php | 25 + admin/error_logging.php | 175 + admin/error_logging_bundle.php | 282 + admin/error_logging_details.php | 193 + admin/error_logging_reset.php | 49 + admin/error_logging_view.php | 72 + admin/fix_content.php | 56 + admin/index.php | 270 + bounce.php | 505 + browse.php | 21 + confirm.php | 187 + contact_instructor.php | 142 + content.php | 255 + documentation/add_note.php | 105 + documentation/admin/administrators.php | 23 + documentation/admin/auto_enroll.php | 15 + documentation/admin/backups.php | 20 + documentation/admin/categories.php | 6 + documentation/admin/configuration.php | 6 + documentation/admin/courses.php | 6 + documentation/admin/create_patches.php | 42 + documentation/admin/creating_courses.php | 17 + documentation/admin/creating_themes.php | 6 + documentation/admin/cron_setup.php | 32 + documentation/admin/default_preferences.php | 7 + documentation/admin/default_side_menu.php | 7 + documentation/admin/default_student_tools.php | 7 + documentation/admin/email_users.php | 6 + documentation/admin/en/index.php | 16 + documentation/admin/enrollment.php | 8 + documentation/admin/enrollment_privileges.php | 8 + documentation/admin/error_logging.php | 6 + documentation/admin/feeds.php | 6 + documentation/admin/forums.php | 6 + documentation/admin/fr/index.php | 21 + documentation/admin/google_key.php | 7 + documentation/admin/importing_themes.php | 8 + documentation/admin/index.php | 1 + documentation/admin/installation.php | 6 + documentation/admin/instructor_requests.php | 10 + documentation/admin/introduction.php | 6 + documentation/admin/languages.php | 58 + documentation/admin/link-out.gif | Bin 0 -> 52 bytes .../admin/managing_existing_themes.php | 28 + documentation/admin/master_student_list.php | 20 + documentation/admin/modules.php | 34 + documentation/admin/my_account.php | 6 + documentation/admin/new_installation.php | 46 + documentation/admin/pages.inc.php | 41 + documentation/admin/patcher.php | 22 + .../admin/requirements_recommendations.php | 81 + documentation/admin/styles.css | 131 + documentation/admin/system_preferences.php | 106 + documentation/admin/themes.php | 8 + documentation/admin/troubleshooting.php | 30 + documentation/admin/upgrading.php | 50 + documentation/admin/users.php | 28 + documentation/approve_note.php | 59 + documentation/common/body_footer.inc.php | 86 + documentation/common/body_header.inc.php | 89 + documentation/common/folder.gif | Bin 0 -> 65 bytes documentation/common/fr/text.php | 9 + documentation/common/frame_header.php | 80 + documentation/common/frame_toc.php | 227 + documentation/common/link-out.gif | Bin 0 -> 52 bytes documentation/common/paper.gif | Bin 0 -> 65 bytes documentation/common/print.php | 41 + documentation/common/search.php | 95 + documentation/common/styles.css | 161 + documentation/common/text.php | 44 + documentation/common/vitals.inc.php | 132 + documentation/config.inc.php | 43 + documentation/delete_note.php | 64 + documentation/developer/database.gif | Bin 0 -> 195789 bytes documentation/developer/guidelines.html | 1691 ++ documentation/developer/modules.html | 945 ++ documentation/developer/styles.css | 137 + documentation/developer/themes.html | 357 + documentation/general/browse_courses.php | 9 + documentation/general/create_course.php | 7 + documentation/general/en/index.php | 16 + documentation/general/export_content.php | 9 + documentation/general/file_storage.php | 28 + documentation/general/fr/index.php | 21 + documentation/general/inbox.php | 10 + documentation/general/index.php | 1 + documentation/general/inside_course.php | 9 + documentation/general/introduction.php | 7 + documentation/general/login.php | 7 + documentation/general/my_contacts.php | 9 + documentation/general/my_courses.php | 8 + documentation/general/my_gadgets.php | 20 + documentation/general/my_groups.php | 7 + documentation/general/my_network.php | 24 + documentation/general/my_profile.php | 11 + documentation/general/my_settings.php | 37 + documentation/general/my_start_page.php | 7 + documentation/general/pa_albums.php | 21 + documentation/general/pa_comments.php | 7 + documentation/general/pa_index.php | 27 + documentation/general/pa_photo.php | 9 + documentation/general/packages.php | 7 + documentation/general/pages.inc.php | 30 + documentation/general/password_reminder.php | 7 + documentation/general/preferences.php | 74 + documentation/general/profile.php | 8 + documentation/general/register.php | 9 + documentation/general/tile.php | 9 + documentation/index.php | 136 + documentation/index/en/index.php | 16 + documentation/index/fr/index.php | 21 + documentation/index/index.php | 164 + documentation/index_list.php | 4 + documentation/instructor/accessibility.php | 30 + documentation/instructor/add_questions.php | 9 + documentation/instructor/announcements.php | 8 + documentation/instructor/arrange_content.php | 8 + documentation/instructor/assignments.php | 14 + .../instructor/authenticated_access.php | 6 + documentation/instructor/backups.php | 10 + documentation/instructor/chat.php | 8 + documentation/instructor/content.html | 140 + documentation/instructor/content.php | 8 + .../instructor/content_alternatives.php | 30 + documentation/instructor/content_edit.php | 89 + documentation/instructor/content_packages.php | 29 + documentation/instructor/content_preview.php | 7 + .../instructor/content_properties.php | 21 + documentation/instructor/content_tests.php | 12 + documentation/instructor/content_usage.php | 9 + documentation/instructor/course_email.php | 13 + documentation/instructor/creating_courses.php | 38 + .../instructor/creating_editing_content.php | 7 + .../creating_editing_content_folder.php | 8 + .../instructor/creating_questions.php | 32 + .../instructor/creating_restoring.php | 8 + .../instructor/creating_tests_surveys.php | 63 + documentation/instructor/delete_course.php | 6 + .../instructor/downloading_uploading.php | 10 + .../instructor/edit_delete_tests.php | 9 + documentation/instructor/editing_deleting.php | 8 + documentation/instructor/en/index.php | 16 + documentation/instructor/enrollment.php | 8 + .../instructor/enrollment_alumni.php | 7 + .../instructor/enrollment_course_list.php | 20 + .../instructor/enrollment_privileges.php | 8 + .../instructor/extracting_zip_archives.php | 9 + documentation/instructor/faq.php | 8 + documentation/instructor/feeds.php | 6 + .../instructor/fha_student_tools.php | 8 + documentation/instructor/file_manager.php | 30 + documentation/instructor/forum_export.php | 7 + documentation/instructor/forums.php | 19 + documentation/instructor/fr/index.php | 21 + documentation/instructor/glossary.php | 8 + documentation/instructor/glossary_terms.php | 8 + documentation/instructor/gradebook.php | 20 + documentation/instructor/gradebook_add.php | 28 + .../instructor/gradebook_edit_marks.php | 20 + .../instructor/gradebook_external_marks.php | 28 + documentation/instructor/gradebook_scales.php | 20 + documentation/instructor/gradebook_update.php | 29 + documentation/instructor/groups.php | 25 + documentation/instructor/index.php | 16 + documentation/instructor/introduction.php | 9 + documentation/instructor/links.php | 14 + .../instructor/managing_files_folders.php | 13 + documentation/instructor/managing_posts.php | 29 + documentation/instructor/managing_threads.php | 41 + documentation/instructor/pages.inc.php | 71 + documentation/instructor/polls.php | 6 + documentation/instructor/preview.php | 7 + documentation/instructor/properties.php | 45 + .../instructor/question_categories.php | 6 + .../instructor/question_database.php | 12 + documentation/instructor/reading_list.php | 12 + documentation/instructor/scorm_packages.php | 24 + documentation/instructor/side_menu.php | 6 + documentation/instructor/statistics.php | 6 + .../instructor/student_submissions.php | 10 + documentation/instructor/student_tools.php | 19 + documentation/instructor/test_statistics.php | 9 + documentation/instructor/tests_surveys.php | 14 + documentation/instructor/tile_repository.php | 8 + documentation/instructor/web_search.php | 8 + documentation/link-out.gif | Bin 0 -> 52 bytes documentation/styles.css | 137 + enroll.php | 165 + exestyles.css | 163 + favicon.ico | Bin 0 -> 3249 bytes get.php | 131 + get_acheck.php | 43 + get_course_icon.php | 71 + get_noid.php | 133 + get_profile_img.php | 84 + get_rss.php | 101 + go.php | 115 + headstuff.php | 122 + help/accessibility.php | 25 + help/contact_support.php | 130 + help/index.php | 44 + images/AT_Logo_1.png | Bin 0 -> 14919 bytes images/AT_Logo_1.psd | Bin 0 -> 322752 bytes images/AT_Logo_1_sm.png | Bin 0 -> 10786 bytes images/AT_Logo_1_sm.psd | Bin 0 -> 98000 bytes images/achecker.png | Bin 0 -> 1630 bytes images/achecker_disabled.png | Bin 0 -> 878 bytes images/after.gif | Bin 0 -> 73 bytes images/application_get.png | Bin 0 -> 581 bytes images/archive.gif | Bin 0 -> 140 bytes images/arrow-mini-down.png | Bin 0 -> 408 bytes images/arrow-mini-left.png | Bin 0 -> 404 bytes images/arrow-mini-right.png | Bin 0 -> 410 bytes images/arrow-mini-up.png | Bin 0 -> 405 bytes images/arrow_ltr.gif | Bin 0 -> 88 bytes images/arrowicon.gif | Bin 0 -> 74 bytes images/at-logo.gif | Bin 0 -> 1627 bytes images/at-logo.v.3.gif | Bin 0 -> 1627 bytes images/atutor_ico.png | Bin 0 -> 3249 bytes images/atutor_ico.psd | Bin 0 -> 161084 bytes images/atutor_logo2.png | Bin 0 -> 3414 bytes images/audio_alternative.png | Bin 0 -> 3474 bytes images/bad.gif | Bin 0 -> 174 bytes images/before.gif | Bin 0 -> 71 bytes images/blue.gif | Bin 0 -> 802 bytes images/box.png | Bin 0 -> 1394 bytes images/calendar.gif | Bin 0 -> 392 bytes images/changes_bullet.gif | Bin 0 -> 211 bytes images/check.gif | Bin 0 -> 57 bytes images/checkbox_check.gif | Bin 0 -> 100 bytes images/checkbox_empty.gif | Bin 0 -> 861 bytes images/checkmark.gif | Bin 0 -> 212 bytes images/child_of.gif | Bin 0 -> 73 bytes images/clr.gif | Bin 0 -> 49 bytes images/content_pkg.gif | Bin 0 -> 1684 bytes images/courses/3dgraph.jpg | Bin 0 -> 1910 bytes images/courses/anotomy.jpg | Bin 0 -> 2008 bytes images/courses/art_supplies.jpg | Bin 0 -> 1969 bytes images/courses/astronaut.jpg | Bin 0 -> 1789 bytes images/courses/bar_graph.jpg | Bin 0 -> 1961 bytes images/courses/books.jpg | Bin 0 -> 1958 bytes images/courses/botany.jpg | Bin 0 -> 2048 bytes images/courses/brain2.jpg | Bin 0 -> 1544 bytes images/courses/business.jpg | Bin 0 -> 1428 bytes images/courses/business_service.jpg | Bin 0 -> 1477 bytes images/courses/caduceus.jpg | Bin 0 -> 1505 bytes images/courses/car_wireframe.jpg | Bin 0 -> 1235 bytes images/courses/cell-anatomy.jpg | Bin 0 -> 1667 bytes images/courses/chemistry.jpg | Bin 0 -> 1470 bytes images/courses/columbus.jpg | Bin 0 -> 1747 bytes images/courses/disabled_sign.gif | Bin 0 -> 870 bytes images/courses/drafting.jpg | Bin 0 -> 1729 bytes images/courses/engine.jpg | Bin 0 -> 1876 bytes images/courses/feather-pen.jpg | Bin 0 -> 1470 bytes images/courses/fire_helmut.jpg | Bin 0 -> 1274 bytes images/courses/fractal.jpg | Bin 0 -> 1496 bytes images/courses/head_wireframe.jpg | Bin 0 -> 1299 bytes images/courses/helix.jpg | Bin 0 -> 1297 bytes images/courses/helmet.jpg | Bin 0 -> 1722 bytes images/courses/light-bulb.jpg | Bin 0 -> 1556 bytes images/courses/map.jpg | Bin 0 -> 1064 bytes images/courses/microchip.jpg | Bin 0 -> 1611 bytes images/courses/microscope.jpg | Bin 0 -> 1778 bytes images/courses/molecule.jpg | Bin 0 -> 1708 bytes images/courses/nav_wheel.jpg | Bin 0 -> 1902 bytes images/courses/normal_surfaces.jpg | Bin 0 -> 1871 bytes images/courses/pedal.jpg | Bin 0 -> 1702 bytes images/courses/pharao.jpg | Bin 0 -> 1953 bytes images/courses/pharmacy.jpg | Bin 0 -> 1845 bytes images/courses/planet-earth.jpg | Bin 0 -> 1501 bytes images/courses/police_hat.jpg | Bin 0 -> 1337 bytes images/courses/recycling.jpg | Bin 0 -> 1874 bytes images/courses/skull.jpg | Bin 0 -> 2043 bytes images/courses/skull_wireframe.jpg | Bin 0 -> 1751 bytes images/courses/sl_logo.gif | Bin 0 -> 1504 bytes images/courses/snail.jpg | Bin 0 -> 1187 bytes images/courses/sphere.jpg | Bin 0 -> 1589 bytes images/courses/sphinx.jpg | Bin 0 -> 1994 bytes images/courses/toolbox.jpg | Bin 0 -> 1636 bytes images/courses/triangle.jpg | Bin 0 -> 1234 bytes images/courses/two_roads.jpg | Bin 0 -> 1461 bytes images/custom_head.png | Bin 0 -> 1542 bytes images/custom_head_disabled.png | Bin 0 -> 866 bytes images/detail_view.png | Bin 0 -> 565 bytes images/details_il.png | Bin 0 -> 282 bytes images/details_ir.png | Bin 0 -> 277 bytes images/details_l.png | Bin 0 -> 280 bytes images/details_r.png | Bin 0 -> 269 bytes images/donate.gif | Bin 0 -> 3813 bytes images/down.png | Bin 0 -> 3230 bytes images/download.png | Bin 0 -> 299 bytes images/edit.gif | Bin 0 -> 231 bytes images/eye-mini-close.png | Bin 0 -> 892 bytes images/eye-mini-open.png | Bin 0 -> 683 bytes images/feedback.gif | Bin 0 -> 156 bytes images/file-manager.png | Bin 0 -> 8220 bytes images/file-manager_disabled.png | Bin 0 -> 2593 bytes images/file.gif | Bin 0 -> 117 bytes images/file_types/audio.gif | Bin 0 -> 1010 bytes images/file_types/binary.gif | Bin 0 -> 1016 bytes images/file_types/csv.gif | Bin 0 -> 1050 bytes images/file_types/doc.gif | Bin 0 -> 1028 bytes images/file_types/dvi.gif | Bin 0 -> 972 bytes images/file_types/generic.gif | Bin 0 -> 373 bytes images/file_types/image.gif | Bin 0 -> 1025 bytes images/file_types/mdb.gif | Bin 0 -> 1032 bytes images/file_types/mpp.gif | Bin 0 -> 1035 bytes images/file_types/ood.gif | Bin 0 -> 1048 bytes images/file_types/oop.gif | Bin 0 -> 1044 bytes images/file_types/oos.gif | Bin 0 -> 1041 bytes images/file_types/oot.gif | Bin 0 -> 1031 bytes images/file_types/pdf.gif | Bin 0 -> 1023 bytes images/file_types/ppt.gif | Bin 0 -> 1040 bytes images/file_types/ps.gif | Bin 0 -> 990 bytes images/file_types/psd.gif | Bin 0 -> 951 bytes images/file_types/qt.gif | Bin 0 -> 603 bytes images/file_types/rtf.gif | Bin 0 -> 975 bytes images/file_types/sql.gif | Bin 0 -> 328 bytes images/file_types/sql2.gif | Bin 0 -> 610 bytes images/file_types/src.gif | Bin 0 -> 995 bytes images/file_types/swf.gif | Bin 0 -> 1010 bytes images/file_types/treeview.gif | Bin 0 -> 1008 bytes images/file_types/txt.gif | Bin 0 -> 972 bytes images/file_types/video.gif | Bin 0 -> 1041 bytes images/file_types/viewlet.gif | Bin 0 -> 1052 bytes images/file_types/vsd.gif | Bin 0 -> 1020 bytes images/file_types/xls.gif | Bin 0 -> 1028 bytes images/file_types/xml.gif | Bin 0 -> 970 bytes images/file_types/zip.gif | Bin 0 -> 1005 bytes images/flag_blue.png | Bin 0 -> 770 bytes images/folder.gif | Bin 0 -> 601 bytes images/folder_new.gif | Bin 0 -> 360 bytes images/folder_new_sibling.gif | Bin 0 -> 360 bytes images/folder_new_sub.gif | Bin 0 -> 387 bytes images/forum/19.gif | Bin 0 -> 456 bytes images/forum/27.gif | Bin 0 -> 702 bytes images/forum/3.gif | Bin 0 -> 112 bytes images/forum/30.gif | Bin 0 -> 379 bytes images/forum/51.gif | Bin 0 -> 880 bytes images/forum/52.gif | Bin 0 -> 879 bytes images/forum/54.gif | Bin 0 -> 158 bytes images/forum/55.gif | Bin 0 -> 542 bytes images/forum/56.gif | Bin 0 -> 341 bytes images/forum/57.gif | Bin 0 -> 303 bytes images/forum/58.gif | Bin 0 -> 400 bytes images/forum/frown.gif | Bin 0 -> 878 bytes images/forum/happy.gif | Bin 0 -> 880 bytes images/forum/index.html | 0 images/forum/move.gif | Bin 0 -> 267 bytes images/forum/ohwell.gif | Bin 0 -> 877 bytes images/forum/smile.gif | Bin 0 -> 877 bytes images/forum/sticky.gif | Bin 0 -> 385 bytes images/forum/tongue.gif | Bin 0 -> 881 bytes images/forum/topic_stick.gif | Bin 0 -> 442 bytes images/forum/wink.gif | Bin 0 -> 150 bytes images/glossary.gif | Bin 0 -> 214 bytes images/glossary_small.gif | Bin 0 -> 623 bytes images/graph.gif | Bin 0 -> 51 bytes images/grey.gif | Bin 0 -> 56 bytes images/help.png | Bin 0 -> 744 bytes images/help3.gif | Bin 0 -> 650 bytes images/help4.gif | Bin 0 -> 67 bytes images/hidemenu.gif | Bin 0 -> 270 bytes images/home-acollab.png | Bin 0 -> 4033 bytes images/home-blogs.png | Bin 0 -> 4948 bytes images/home-blogs_sm.png | Bin 0 -> 770 bytes images/home-chat.png | Bin 0 -> 3891 bytes images/home-chat_sm.png | Bin 0 -> 601 bytes images/home-directory.png | Bin 0 -> 4073 bytes images/home-directory_sm.png | Bin 0 -> 364 bytes images/home-export_content.png | Bin 0 -> 3484 bytes images/home-faq.png | Bin 0 -> 4355 bytes images/home-faq_sm.png | Bin 0 -> 893 bytes images/home-file_storage.png | Bin 0 -> 4581 bytes images/home-file_storage_sm.png | Bin 0 -> 688 bytes images/home-forums.png | Bin 0 -> 2513 bytes images/home-forums_sm.png | Bin 0 -> 618 bytes images/home-glossary.png | Bin 0 -> 4146 bytes images/home-glossary_sm.png | Bin 0 -> 801 bytes images/home-gradebook_sm.png | Bin 0 -> 788 bytes images/home-links.png | Bin 0 -> 5029 bytes images/home-links_sm.png | Bin 0 -> 944 bytes images/home-polls.png | Bin 0 -> 4381 bytes images/home-polls_sm.png | Bin 0 -> 918 bytes images/home-reading_list.png | Bin 0 -> 4825 bytes images/home-reading_list_sm.png | Bin 0 -> 593 bytes images/home-site_map.png | Bin 0 -> 4744 bytes images/home-tests.png | Bin 0 -> 2979 bytes images/home-tests_sm.png | Bin 0 -> 807 bytes images/home-tile_search.png | Bin 0 -> 3426 bytes images/home-tracker.png | Bin 0 -> 2155 bytes images/home-tracker_sm.png | Bin 0 -> 509 bytes images/icon-zip.gif | Bin 0 -> 164 bytes images/icon.png | Bin 0 -> 4355 bytes images/icon_delete.gif | Bin 0 -> 279 bytes images/icon_minipost.gif | Bin 0 -> 122 bytes images/icon_view.png | Bin 0 -> 546 bytes images/index.html | 1 + images/lock.gif | Bin 0 -> 266 bytes images/logo.gif | Bin 0 -> 2144 bytes images/medit.gif | Bin 0 -> 225 bytes images/mlock.gif | Bin 0 -> 252 bytes images/move-down.gif | Bin 0 -> 70 bytes images/move-up.gif | Bin 0 -> 70 bytes images/mswitch_minus.gif | Bin 0 -> 119 bytes images/mswitch_plus.gif | Bin 0 -> 124 bytes images/new.gif | Bin 0 -> 591 bytes images/page_add.gif | Bin 0 -> 377 bytes images/page_add_sibling.gif | Bin 0 -> 389 bytes images/page_add_sub.gif | Bin 0 -> 403 bytes images/page_delete.gif | Bin 0 -> 387 bytes images/paste.png | Bin 0 -> 1947 bytes images/paste_disabled.png | Bin 0 -> 1131 bytes images/pause.png | Bin 0 -> 807 bytes images/pen.gif | Bin 0 -> 84 bytes images/pen2.gif | Bin 0 -> 85 bytes images/pen3.gif | Bin 0 -> 146 bytes images/pin.png | Bin 0 -> 750 bytes images/preview.png | Bin 0 -> 2183 bytes images/pub_default.jpg | Bin 0 -> 20193 bytes images/question.gif | Bin 0 -> 136 bytes images/red.gif | Bin 0 -> 802 bytes images/rtl_tree/index.html | 0 images/rtl_tree/tree_collapse.gif | Bin 0 -> 115 bytes images/rtl_tree/tree_end.gif | Bin 0 -> 107 bytes images/rtl_tree/tree_expand.gif | Bin 0 -> 117 bytes images/rtl_tree/tree_horizontal.gif | Bin 0 -> 105 bytes images/rtl_tree/tree_split.gif | Bin 0 -> 110 bytes images/rtl_tree/tree_vertline.gif | Bin 0 -> 108 bytes images/search.gif | Bin 0 -> 315 bytes images/showmenu.gif | Bin 0 -> 284 bytes images/sign_lang_alternative.png | Bin 0 -> 799 bytes images/star.gif | Bin 0 -> 319 bytes images/subscribe-envelope.png | Bin 0 -> 487 bytes images/text_alternative.png | Bin 0 -> 861 bytes images/toc.gif | Bin 0 -> 860 bytes images/tool_go.jpg | Bin 0 -> 617 bytes images/tools.png | Bin 0 -> 879 bytes images/topic_lock.gif | Bin 0 -> 316 bytes images/transfer.gif | Bin 0 -> 468 bytes images/tree/index.html | 0 images/tree/tree_collapse.gif | Bin 0 -> 115 bytes images/tree/tree_disabled.gif | Bin 0 -> 116 bytes images/tree/tree_end.gif | Bin 0 -> 107 bytes images/tree/tree_expand.gif | Bin 0 -> 117 bytes images/tree/tree_horizontal.gif | Bin 0 -> 105 bytes images/tree/tree_space.gif | Bin 0 -> 88 bytes images/tree/tree_split.gif | Bin 0 -> 110 bytes images/tree/tree_vertline.gif | Bin 0 -> 108 bytes images/unlock.gif | Bin 0 -> 266 bytes images/unsubscribe-envelope.png | Bin 0 -> 402 bytes images/up.png | Bin 0 -> 3284 bytes images/visual_alternative.png | Bin 0 -> 3535 bytes images/wand.png | Bin 0 -> 570 bytes images/x.gif | Bin 0 -> 361 bytes inbox/export.php | 111 + inbox/index.php | 181 + inbox/send_message.php | 258 + inbox/sent_messages.php | 182 + include/classes/CSVExport.class.php | 79 + include/classes/CSVImport.class.php | 157 + include/classes/ContentManager.class.php | 1565 ++ include/classes/ContentOutputUtils.class.php | 50 + .../ErrorHandler/ErrorHandler.class.php | 529 + include/classes/Message/Message.class.php | 473 + include/classes/Savant2/Savant2.php | 1793 ++ include/classes/Savant2/Savant2/Compiler.php | 72 + include/classes/Savant2/Savant2/Error.php | 125 + include/classes/Savant2/Savant2/Filter.php | 76 + include/classes/Savant2/Savant2/Plugin.php | 74 + .../Savant2/Savant2_Error_exception.php | 53 + .../Savant2/Savant2/Savant2_Error_pear.php | 56 + .../Savant2/Savant2/Savant2_Error_stack.php | 60 + .../Savant2/Savant2/Savant2_Plugin_cycle.php | 97 + .../classes/UrlRewrite/ContentUrl.class.php | 114 + .../UrlRewrite/FileStorageUrl.class.php | 152 + .../classes/UrlRewrite/ForumsUrl.class.php | 105 + .../classes/UrlRewrite/GlossaryUrl.class.php | 115 + include/classes/UrlRewrite/TestsUrl.class.php | 111 + .../classes/UrlRewrite/UrlParser.class.php | 138 + .../classes/UrlRewrite/UrlRewrite.class.php | 381 + .../HTMLSax/XML_HTMLSax_Decorators.php | 306 + .../HTMLSax/XML_HTMLSax_States.php | 292 + include/classes/XML/XML_HTMLSax/OS/Guess.php | 343 + include/classes/XML/XML_HTMLSax/PEAR.php | 1095 ++ .../XML/XML_HTMLSax/PEAR/Autoloader.php | 223 + .../classes/XML/XML_HTMLSax/PEAR/Common.php | 1140 ++ .../classes/XML/XML_HTMLSax/PEAR/Config.php | 2054 +++ .../classes/XML/XML_HTMLSax/PEAR/Remote.php | 519 + include/classes/XML/XML_HTMLSax/System.php | 580 + .../classes/XML/XML_HTMLSax/XML_HTMLSax.php | 671 + include/classes/cssparser.php | 238 + include/classes/feedcreator.class.php | 1545 ++ include/classes/nusoap.php | 5467 ++++++ include/classes/pclzip.lib.php | 5694 +++++++ .../classes/phpmailer/atutormailer.class.php | 144 + include/classes/phpmailer/class.phpmailer.php | 1499 ++ include/classes/phpmailer/class.smtp.php | 1045 ++ .../classes/phpmailer/phpmailer.lang-en.php | 23 + include/classes/sqlutility.class.php | 229 + include/classes/subscribe.class.php | 187 + include/classes/vcard.php | 160 + include/classes/zipfile.class.php | 263 + include/classes/zipfile.class.php.bak | 368 + include/footer.inc.php | 101 + include/header.inc.php | 295 + include/html/auto_enroll_courses.inc.php | 101 + include/html/auto_enroll_list_courses.inc.php | 85 + include/html/browse.inc.php | 131 + include/html/code_picker.inc.php | 83 + include/html/copyright.inc.php | 40 + include/html/dropdowns/menu_menu.inc.php | 34 + include/html/dropdowns/posts.inc.php | 50 + include/html/dropdowns/related_topics.inc.php | 41 + include/html/dropdowns/search.inc.php | 57 + include/html/dropdowns/users_online.inc.php | 49 + include/html/frameset/footer.inc.php | 2 + include/html/frameset/header.inc.php | 34 + include/html/languages.inc.php | 33 + include/html/release_date.inc.php | 72 + include/html/search.inc.php | 340 + include/index.html | 1 + include/lib/constants.inc.php | 427 + include/lib/html_resource_parser.inc.php | 111 + include/lib/json.inc.php | 806 + include/lib/menu_pages.php | 504 + include/lib/mime.inc.php | 185 + include/lib/mysql_connect.inc.php | 42 + include/lib/output.inc.php | 1757 ++ include/lib/search.inc.php | 344 + include/lib/tinymce.inc.php | 123 + include/lib/tinymce_styles.css | 28 + include/lib/upload.php | 191 + include/lib/utf8.php | 1363 ++ include/phpCache/ChangeLog | 30 + include/phpCache/LICENSE | 227 + include/phpCache/README | 33 + include/phpCache/gc.php | 27 + include/phpCache/phpCache.inc.php | 535 + include/player.swf | Bin 0 -> 5260 bytes include/securimage/LICENSE.txt | 458 + include/securimage/README.txt | 57 + include/securimage/audio/0.wav | Bin 0 -> 22158 bytes include/securimage/audio/1.wav | Bin 0 -> 22158 bytes include/securimage/audio/2.wav | Bin 0 -> 22158 bytes include/securimage/audio/3.wav | Bin 0 -> 22158 bytes include/securimage/audio/4.wav | Bin 0 -> 22158 bytes include/securimage/audio/5.wav | Bin 0 -> 22158 bytes include/securimage/audio/6.wav | Bin 0 -> 22158 bytes include/securimage/audio/7.wav | Bin 0 -> 22158 bytes include/securimage/audio/8.wav | Bin 0 -> 22158 bytes include/securimage/audio/9.wav | Bin 0 -> 22158 bytes include/securimage/audio/A.wav | Bin 0 -> 22158 bytes include/securimage/audio/B.wav | Bin 0 -> 22158 bytes include/securimage/audio/C.wav | Bin 0 -> 22158 bytes include/securimage/audio/D.wav | Bin 0 -> 22158 bytes include/securimage/audio/E.wav | Bin 0 -> 22158 bytes include/securimage/audio/F.wav | Bin 0 -> 22158 bytes include/securimage/audio/G.wav | Bin 0 -> 22158 bytes include/securimage/audio/H.wav | Bin 0 -> 22158 bytes include/securimage/audio/I.wav | Bin 0 -> 22158 bytes include/securimage/audio/J.wav | Bin 0 -> 22158 bytes include/securimage/audio/K.wav | Bin 0 -> 22158 bytes include/securimage/audio/L.wav | Bin 0 -> 22158 bytes include/securimage/audio/M.wav | Bin 0 -> 22158 bytes include/securimage/audio/N.wav | Bin 0 -> 22158 bytes include/securimage/audio/O.wav | Bin 0 -> 22158 bytes include/securimage/audio/P.wav | Bin 0 -> 22158 bytes include/securimage/audio/Q.wav | Bin 0 -> 22158 bytes include/securimage/audio/R.wav | Bin 0 -> 22158 bytes include/securimage/audio/S.wav | Bin 0 -> 22158 bytes include/securimage/audio/T.wav | Bin 0 -> 22158 bytes include/securimage/audio/U.wav | Bin 0 -> 22158 bytes include/securimage/audio/V.wav | Bin 0 -> 22158 bytes include/securimage/audio/W.wav | Bin 0 -> 22158 bytes include/securimage/audio/X.wav | Bin 0 -> 22158 bytes include/securimage/audio/Y.wav | Bin 0 -> 22158 bytes include/securimage/audio/Z.wav | Bin 0 -> 22158 bytes include/securimage/elephant.ttf | Bin 0 -> 51652 bytes include/securimage/gdfonts/automatic.gdf | Bin 0 -> 61196 bytes include/securimage/gdfonts/bubblebath.gdf | Bin 0 -> 67516 bytes include/securimage/gdfonts/caveman.gdf | Bin 0 -> 160540 bytes include/securimage/gdfonts/crass.gdf | Bin 0 -> 39691 bytes include/securimage/images/audio_icon.gif | Bin 0 -> 621 bytes include/securimage/images/refresh.gif | Bin 0 -> 865 bytes include/securimage/securimage.php | 954 ++ include/securimage/securimage_play.php | 20 + include/securimage/securimage_show.php | 12 + include/side_menu.inc.php | 53 + include/style_popup.css | 172 + include/unit_tests/AllTests.php | 30 + include/unit_tests/ContentOutputUtilsTest.php | 49 + include/vitals.inc.php | 1423 ++ index.php | 165 + install/db/atutor_convert_db_to_utf8.sql | 196 + install/db/atutor_language_text.sql | 2356 +++ install/db/atutor_schema.sql | 1607 ++ install/db/atutor_upgrade_1.0_to_1.1.sql | 182 + install/db/atutor_upgrade_1.1_to_1.2.sql | 139 + install/db/atutor_upgrade_1.2_to_1.3.sql | 86 + install/db/atutor_upgrade_1.3.2_to_1.4.sql | 44 + install/db/atutor_upgrade_1.3_to_1.3.2.sql | 12 + install/db/atutor_upgrade_1.4.1_to_1.4.2.sql | 11 + install/db/atutor_upgrade_1.4.2_to_1.4.3.sql | 156 + install/db/atutor_upgrade_1.4.3_to_1.5.sql | 80 + install/db/atutor_upgrade_1.4_to_1.4.1.sql | 45 + install/db/atutor_upgrade_1.5.1_to_1.5.2.sql | 91 + install/db/atutor_upgrade_1.5.2_to_1.5.3.sql | 216 + .../db/atutor_upgrade_1.5.3.1_to_1.5.3.2.sql | 5 + .../db/atutor_upgrade_1.5.3.2_to_1.5.3.3.sql | 27 + .../db/atutor_upgrade_1.5.3.3_to_1.5.4.sql | 56 + .../db/atutor_upgrade_1.5.3_to_1.5.3.1.sql | 5 + install/db/atutor_upgrade_1.5.4_to_1.5.5.sql | 17 + install/db/atutor_upgrade_1.5.5_to_1.6.sql | 16 + install/db/atutor_upgrade_1.5_to_1.5.1.sql | 17 + install/db/atutor_upgrade_1.6.1_to_1.6.2.sql | 164 + install/db/atutor_upgrade_1.6.2_to_1.6.3.sql | 257 + install/db/atutor_upgrade_1.6.3_to_1.6.4.sql | 165 + install/db/atutor_upgrade_1.6.4_to_2.0.sql | 141 + install/db/atutor_upgrade_1.6_to_1.6.1.sql | 147 + install/images/bad.gif | Bin 0 -> 174 bytes install/images/feedback.gif | Bin 0 -> 543 bytes install/images/question.gif | Bin 0 -> 136 bytes install/images/warning.png | Bin 0 -> 470 bytes .../include/classes/TableConversion.class.php | 1981 +++ install/include/classes/sqlutility.php | 155 + install/include/common.inc.php | 263 + install/include/config_template.php | 132 + install/include/footer.php | 17 + install/include/header.php | 46 + install/include/step1.php | 38 + install/include/step2.php | 144 + install/include/step3.php | 277 + install/include/step4.php | 126 + install/include/step5.php | 273 + install/include/step6.php | 111 + install/include/step7.php | 185 + install/include/step8.php | 33 + install/include/upgrade_header.php | 55 + install/include/ustep1.php | 269 + install/include/ustep2.php | 167 + install/include/ustep3.php | 303 + install/include/ustep4.php | 113 + install/include/ustep5.php | 102 + install/include/ustep6.php | 30 + install/include/ustep7.php | 566 + install/include/ustep_content_conversion.php | 130 + install/include/ustep_pwd_encryt.php | 32 + install/index.php | 412 + install/install.php | 73 + install/not_installed.php | 30 + install/stylesheet.css | 482 + install/update_config.php | 58 + install/upgrade.php | 85 + jscripts/ATutor.js | 479 + jscripts/ATutorCourse.js | 155 + jscripts/ATutor_js.php | 53 + .../insert_tag/editor_plugin.js | 1 + .../insert_tag/editor_plugin_src.js | 126 + .../insert_tag/img/code.png | Bin 0 -> 359 bytes .../insert_tag/img/media.png | Bin 0 -> 653 bytes .../insert_tag/img/term.png | Bin 0 -> 294 bytes .../insert_tag/img/tex.png | Bin 0 -> 333 bytes .../insert_tag/langs/en.js | 6 + .../swap_toolbar/editor_plugin.js | 1 + .../swap_toolbar/editor_plugin_src.js | 76 + .../swap_toolbar/img/bullet_arrow_down.png | Bin 0 -> 201 bytes .../swap_toolbar/img/bullet_arrow_up.png | Bin 0 -> 201 bytes .../swap_toolbar/langs/en.js | 7 + jscripts/TILE.js | 275 + jscripts/calendar.js | 1750 ++ jscripts/help.js | 25 + jscripts/infusion/InfusionAll.js | 33 + jscripts/infusion/README.txt | 247 + .../components/inlineEdit/css/InlineEdit.css | 3 + .../components/inlineEdit/js/InlineEdit.js | 1 + .../inlineEdit/js/InlineEditIntegrations.js | 1 + .../inlineEdit/js/jquery.tinymce.js | 1 + .../infusion/components/pager/css/Pager.css | 11 + .../components/pager/images/arrow-dn.png | Bin 0 -> 116 bytes .../components/pager/images/arrow-up.png | Bin 0 -> 116 bytes .../infusion/components/pager/js/Pager.js | 1 + .../components/progress/js/Progress.js | 1 + .../reorderer/css/ImageReorderer.css | 16 + .../components/reorderer/css/Reorderer.css | 8 + .../reorderer/html/ImageReordererRSF.html | 285 + .../components/reorderer/images/Banana.jpg | Bin 0 -> 1708 bytes .../reorderer/images/Blackberry.jpg | Bin 0 -> 4241 bytes .../components/reorderer/images/Cherry.jpg | Bin 0 -> 3359 bytes .../reorderer/images/Dragonfruit.jpg | Bin 0 -> 4353 bytes .../components/reorderer/images/Fig.jpg | Bin 0 -> 4568 bytes .../components/reorderer/images/Grapes.jpg | Bin 0 -> 5185 bytes .../components/reorderer/images/Kiwano.jpg | Bin 0 -> 2611 bytes .../components/reorderer/images/Kiwi.jpg | Bin 0 -> 3216 bytes .../components/reorderer/images/Kumquat.jpg | Bin 0 -> 3428 bytes .../components/reorderer/images/Lemon.jpg | Bin 0 -> 2330 bytes .../reorderer/images/Mangosteen.jpg | Bin 0 -> 3637 bytes .../components/reorderer/images/Orange.jpg | Bin 0 -> 4029 bytes .../components/reorderer/images/RedApple.jpg | Bin 0 -> 2482 bytes .../components/reorderer/images/Tamarillo.jpg | Bin 0 -> 2535 bytes .../reorderer/js/GeometricManager.js | 1 + .../components/reorderer/js/ImageReorderer.js | 1 + .../reorderer/js/LayoutReorderer.js | 1 + .../components/reorderer/js/ModuleLayout.js | 1 + .../components/reorderer/js/Reorderer.js | 1 + .../tableOfContents/html/TableOfContents.html | 33 + .../tableOfContents/js/TableOfContents.js | 1 + .../components/uiOptions/css/Slider.css | 14 + .../components/uiOptions/css/UIOptions.css | 36 + .../uiOptions/css/UIOptionsPreview.css | 5 + .../components/uiOptions/html/UIOptions.html | 178 + .../uiOptions/html/UIOptionsPreview.html | 85 + .../uiOptions/images/500x327_mint_truffle.jpg | Bin 0 -> 57689 bytes .../components/uiOptions/images/border_1.png | Bin 0 -> 1214 bytes .../uiOptions/images/expand_collapse.png | Bin 0 -> 252 bytes .../components/uiOptions/images/h1.png | Bin 0 -> 899 bytes .../components/uiOptions/images/main_bg.png | Bin 0 -> 10485 bytes .../components/uiOptions/images/mintleaf.png | Bin 0 -> 23238 bytes .../components/uiOptions/js/UIEnhancer.js | 1 + .../components/uiOptions/js/UIOptions.js | 1 + jscripts/infusion/components/undo/js/Undo.js | 1 + .../infusion/components/uploader/ReadMe.txt | 103 + .../components/uploader/css/Uploader.css | 76 + .../components/uploader/html/Uploader.html | 127 + .../components/uploader/images/add.png | Bin 0 -> 733 bytes .../components/uploader/images/browse.png | Bin 0 -> 5993 bytes .../components/uploader/images/error.png | Bin 0 -> 666 bytes .../uploader/images/gradient-file-green.png | Bin 0 -> 1262 bytes .../uploader/images/gradient-file-grey.png | Bin 0 -> 47113 bytes .../uploader/images/gradient-total-green.png | Bin 0 -> 1379 bytes .../uploader/images/gradient-total-grey.png | Bin 0 -> 49062 bytes .../uploader/images/gradient-total-yellow.png | Bin 0 -> 1374 bytes .../components/uploader/images/remove.png | Bin 0 -> 801 bytes .../components/uploader/images/tick.png | Bin 0 -> 537 bytes .../uploader/js/DemoUploadManager.js | 1 + .../components/uploader/js/FileQueue.js | 1 + .../uploader/js/SWFUploadManager.js | 1 + .../components/uploader/js/Scroller.js | 1 + .../components/uploader/js/Uploader.js | 1 + .../infusion/framework/core/js/DataBinding.js | 1 + jscripts/infusion/framework/core/js/Fluid.js | 1 + .../framework/core/js/FluidDOMUtilities.js | 1 + .../core/js/ProgressiveEnhancement.js | 1 + .../framework/core/js/jquery.keyboard-a11y.js | 1 + .../framework/fss/css/fss-JSR168Bridge.css | 0 .../infusion/framework/fss/css/fss-layout.css | 115 + .../framework/fss/css/fss-mobile-layout.css | 539 + .../fss/css/fss-mobile-theme-android.css | 279 + .../fss/css/fss-mobile-theme-iphone.css | 264 + .../infusion/framework/fss/css/fss-reset.css | 37 + .../infusion/framework/fss/css/fss-text.css | 61 + .../framework/fss/css/fss-theme-coal.css | 57 + .../framework/fss/css/fss-theme-debug.css | 18 + .../framework/fss/css/fss-theme-hc.css | 35 + .../framework/fss/css/fss-theme-hci.css | 35 + .../framework/fss/css/fss-theme-mist.css | 50 + .../framework/fss/css/fss-theme-rust.css | 30 + .../framework/fss/css/fss-theme-slate.css | 51 + .../framework/fss/images/exclamation.png | Bin 0 -> 622 bytes .../infusion/framework/fss/images/gripper.png | Bin 0 -> 172 bytes .../fss/images/themes/_common/exclamation.png | Bin 0 -> 622 bytes .../themes/_common/gloss_25_repeater.png | Bin 0 -> 156 bytes .../fss/images/themes/_common/gripper.png | Bin 0 -> 172 bytes .../images/themes/android/backbutton_mask.png | Bin 0 -> 429 bytes .../themes/android/button_bg_insetShadow.png | Bin 0 -> 1262 bytes .../images/themes/android/listmenu_arrow.png | Bin 0 -> 440 bytes .../images/themes/android/listmenu_loader.gif | Bin 0 -> 673 bytes .../images/themes/android/listmenu_loader.png | Bin 0 -> 455 bytes .../navbar_back_button_insetShadow.png | Bin 0 -> 820 bytes .../navbar_normal_button_insetShadow.png | Bin 0 -> 399 bytes .../images/themes/coal/buttons-light-bg.png | Bin 0 -> 457 bytes .../images/themes/coal/buttons-light-cap.png | Bin 0 -> 442 bytes .../fss/images/themes/coal/buttons-med-bg.png | Bin 0 -> 453 bytes .../images/themes/coal/buttons-med-cap.png | Bin 0 -> 435 bytes .../themes/coal/buttons-titlebar-bg.png | Bin 0 -> 328 bytes .../themes/coal/buttons-titlebar-cap.png | Bin 0 -> 311 bytes .../fss/images/themes/coal/coal-icons.psd | Bin 0 -> 443048 bytes .../images/themes/coal/icon-menu-Delete.png | Bin 0 -> 3770 bytes .../images/themes/coal/icon-widget-Close.png | Bin 0 -> 460 bytes .../images/themes/coal/icon-widget-Less.png | Bin 0 -> 438 bytes .../images/themes/coal/icon-widget-More.png | Bin 0 -> 405 bytes .../themes/coal/icon-widget-ShowSettings.png | Bin 0 -> 321 bytes .../themes/coal/icon-widget-gripper.png | Bin 0 -> 249 bytes .../themes/coal/tabs-light-active-bg.png | Bin 0 -> 397 bytes .../themes/coal/tabs-light-active-cap.png | Bin 0 -> 347 bytes .../fss/images/themes/coal/tabs-light-bg.png | Bin 0 -> 351 bytes .../fss/images/themes/coal/tabs-light-cap.png | Bin 0 -> 345 bytes .../themes/coal/tabs-light-container-bg.png | Bin 0 -> 157 bytes .../themes/coal/tabs-light-content-bg.png | Bin 0 -> 169 bytes .../images/themes/coal/tabs-med-active-bg.png | Bin 0 -> 343 bytes .../themes/coal/tabs-med-active-cap.png | Bin 0 -> 332 bytes .../fss/images/themes/coal/tabs-med-bg.png | Bin 0 -> 343 bytes .../fss/images/themes/coal/tabs-med-cap.png | Bin 0 -> 333 bytes .../themes/coal/tabs-med-container-bg.png | Bin 0 -> 157 bytes .../themes/coal/tabs-med-content-bg.png | Bin 0 -> 169 bytes .../fss/images/themes/coal/widget-bg.png | Bin 0 -> 427 bytes .../images/themes/iphone/backbutton_mask.png | Bin 0 -> 429 bytes .../themes/iphone/button_bg_insetShadow.png | Bin 0 -> 1262 bytes .../images/themes/iphone/listmenu_arrow.png | Bin 0 -> 440 bytes .../images/themes/iphone/listmenu_loader.gif | Bin 0 -> 2991 bytes .../images/themes/iphone/listmenu_loader.png | Bin 0 -> 455 bytes .../iphone/navbar_back_button_insetShadow.png | Bin 0 -> 820 bytes .../navbar_normal_button_insetShadow.png | Bin 0 -> 399 bytes .../images/themes/mist/buttons-light-bg.png | Bin 0 -> 456 bytes .../images/themes/mist/buttons-light-cap.png | Bin 0 -> 451 bytes .../fss/images/themes/mist/buttons-med-bg.png | Bin 0 -> 455 bytes .../images/themes/mist/buttons-med-cap.png | Bin 0 -> 444 bytes .../themes/mist/buttons-titlebar-bg.png | Bin 0 -> 323 bytes .../themes/mist/buttons-titlebar-cap.png | Bin 0 -> 321 bytes .../images/themes/mist/icon-menu-Delete.png | Bin 0 -> 3770 bytes .../themes/mist/icon-options-ListOrGrid.png | Bin 0 -> 213 bytes .../images/themes/mist/icon-widget-Close.png | Bin 0 -> 520 bytes .../images/themes/mist/icon-widget-Less.png | Bin 0 -> 467 bytes .../images/themes/mist/icon-widget-More.png | Bin 0 -> 465 bytes .../themes/mist/icon-widget-ShowSettings.png | Bin 0 -> 321 bytes .../themes/mist/icon-widget-gripper.png | Bin 0 -> 195 bytes .../fss/images/themes/mist/mist-icons.psd | Bin 0 -> 439723 bytes .../themes/mist/tabs-light-active-bg.png | Bin 0 -> 304 bytes .../themes/mist/tabs-light-active-cap.png | Bin 0 -> 310 bytes .../fss/images/themes/mist/tabs-light-bg.png | Bin 0 -> 336 bytes .../fss/images/themes/mist/tabs-light-cap.png | Bin 0 -> 329 bytes .../themes/mist/tabs-light-container-bg.png | Bin 0 -> 161 bytes .../images/themes/mist/tabs-med-active-bg.png | Bin 0 -> 303 bytes .../themes/mist/tabs-med-active-cap.png | Bin 0 -> 324 bytes .../fss/images/themes/mist/tabs-med-bg.png | Bin 0 -> 345 bytes .../fss/images/themes/mist/tabs-med-cap.png | Bin 0 -> 339 bytes .../themes/mist/tabs-med-container-bg.png | Bin 0 -> 156 bytes .../fss/images/themes/mist/widget-bg.png | Bin 0 -> 224 bytes .../fss/images/themes/rust/gripper.png | Bin 0 -> 3607 bytes .../images/themes/rust/icon-menu-Delete.png | Bin 0 -> 674 bytes .../themes/rust/icon-options-ListOrGrid.png | Bin 0 -> 213 bytes .../images/themes/rust/icon-widget-Close.png | Bin 0 -> 689 bytes .../images/themes/rust/icon-widget-More.png | Bin 0 -> 450 bytes .../themes/rust/icon-widget-Settings.png | Bin 0 -> 555 bytes .../fss/images/themes/rust/menu-hover.png | Bin 0 -> 224 bytes .../fss/images/themes/rust/widget-earmark.png | Bin 0 -> 1860 bytes .../images/themes/slate/buttons-light-bg.png | Bin 0 -> 692 bytes .../images/themes/slate/buttons-light-cap.png | Bin 0 -> 604 bytes .../images/themes/slate/buttons-med-bg.png | Bin 0 -> 710 bytes .../images/themes/slate/buttons-med-cap.png | Bin 0 -> 597 bytes .../themes/slate/buttons-titlebar-bg.png | Bin 0 -> 453 bytes .../themes/slate/buttons-titlebar-cap.png | Bin 0 -> 412 bytes .../images/themes/slate/icon-widget-Close.png | Bin 0 -> 483 bytes .../images/themes/slate/icon-widget-Less.png | Bin 0 -> 334 bytes .../images/themes/slate/icon-widget-More.png | Bin 0 -> 358 bytes .../themes/slate/icon-widget-gripper.png | Bin 0 -> 248 bytes .../fss/images/themes/slate/slate-icons.psd | Bin 0 -> 394447 bytes .../fss/images/themes/slate/sprites.png | Bin 0 -> 7954 bytes .../themes/slate/tabs-light-active-bg.png | Bin 0 -> 370 bytes .../themes/slate/tabs-light-active-cap.png | Bin 0 -> 415 bytes .../fss/images/themes/slate/tabs-light-bg.png | Bin 0 -> 319 bytes .../images/themes/slate/tabs-light-cap.png | Bin 0 -> 339 bytes .../themes/slate/tabs-light-container-bg.png | Bin 0 -> 166 bytes .../themes/slate/tabs-med-active-bg.png | Bin 0 -> 405 bytes .../themes/slate/tabs-med-active-cap.png | Bin 0 -> 466 bytes .../fss/images/themes/slate/tabs-med-bg.png | Bin 0 -> 374 bytes .../fss/images/themes/slate/tabs-med-cap.png | Bin 0 -> 400 bytes .../themes/slate/tabs-med-container-bg.png | Bin 0 -> 186 bytes .../fss/images/themes/slate/widget-bg.png | Bin 0 -> 663 bytes .../images/themes/slate/widget-earmark.png | Bin 0 -> 1982 bytes .../framework/renderer/js/fluidParser.js | 1 + .../framework/renderer/js/fluidRenderer.js | 1 + jscripts/infusion/jquery.autoHeight.js | 34 + .../lib/fastXmlPull/js/fastXmlPull.js | 1 + .../infusion/lib/jquery/core/js/jquery.js | 19 + .../plugins/bgiframe/js/jquery.bgiframe.js | 1 + .../plugins/delegate/js/jquery.delegate.js | 1 + .../plugins/tooltip/css/jquery.tooltip.css | 2 + .../plugins/tooltip/js/jquery.tooltip.js | 1 + .../lib/jquery/ui/css/fl-theme-coal/coal.css | 319 + .../images/ui-bg_flat_0_000000_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_flat_75_cccccc_40x100.png | Bin 0 -> 180 bytes .../ui-bg_highlight-soft_25_575757_1x100.png | Bin 0 -> 145 bytes .../ui-bg_highlight-soft_55_cccccc_1x100.png | Bin 0 -> 100 bytes .../ui-bg_highlight-soft_65_000000_1x100.png | Bin 0 -> 125 bytes .../ui-bg_highlight-soft_75_333333_1x100.png | Bin 0 -> 124 bytes .../ui-bg_highlight-soft_75_666666_1x100.png | Bin 0 -> 115 bytes .../ui-bg_inset-soft_95_fef1ec_1x100.png | Bin 0 -> 123 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_333333_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_a3a3a3_256x240.png | Bin 0 -> 5399 bytes .../images/ui-icons_cccccc_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 4379 bytes .../lib/jquery/ui/css/fl-theme-hc/hc.css | 319 + .../images/ui-bg_flat_0_000000_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_flat_0_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-icons_000000_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 4379 bytes .../lib/jquery/ui/css/fl-theme-hci/hci.css | 319 + .../images/ui-bg_flat_0_000000_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_flat_0_999999_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_0_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-icons_000000_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_fffff_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 4379 bytes .../images/ui-bg_flat_0_000000_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_flat_0_2e83ff_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_9dcaf6_1x400.png | Bin 0 -> 175 bytes .../images/ui-bg_glass_75_d9e8f7_1x400.png | Bin 0 -> 156 bytes .../ui-bg_highlight-soft_55_9dcaf6_1x100.png | Bin 0 -> 162 bytes .../ui-bg_inset-soft_95_fef1ec_1x100.png | Bin 0 -> 123 bytes .../images/ui-icons_000000_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4379 bytes .../lib/jquery/ui/css/fl-theme-mist/mist.css | 319 + .../images/ui-bg_flat_0_666666_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_0_999999_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_0_cccccc_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_0_ebebeb_40x100.png | Bin 0 -> 211 bytes .../images/ui-bg_flat_0_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_75_666666_1x400.png | Bin 0 -> 127 bytes .../ui-bg_highlight-hard_100_ebebeb_1x100.png | Bin 0 -> 102 bytes .../ui-bg_highlight-soft_75_999999_1x100.png | Bin 0 -> 114 bytes .../ui-bg_inset-hard_100_ebebeb_1x100.png | Bin 0 -> 133 bytes .../images/ui-icons_000000_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_666666_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_ebebeb_256x240.png | Bin 0 -> 4379 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 4379 bytes .../jquery/ui/css/fl-theme-slate/slate.css | 319 + .../lib/jquery/ui/js/ui-FLUID-readme.txt | 1 + .../infusion/lib/jquery/ui/js/ui.accordion.js | 1 + jscripts/infusion/lib/jquery/ui/js/ui.core.js | 1 + .../infusion/lib/jquery/ui/js/ui.dialog.js | 1 + .../infusion/lib/jquery/ui/js/ui.draggable.js | 1 + .../infusion/lib/jquery/ui/js/ui.slider.js | 1 + jscripts/infusion/lib/json/js/json2.js | 1 + .../infusion/lib/swfobject/js/swfobject.js | 14 + .../lib/swfupload/flash/swfupload.swf | Bin 0 -> 12787 bytes .../infusion/lib/swfupload/js/swfupload.js | 1 + .../infusion/licenses/Infusion-LICENSE.txt | 227 + .../infusion/licenses/fastXmlPull-LICENSE.txt | 525 + .../infusion/licenses/fckeditor-LICENSE.txt | 1246 ++ jscripts/infusion/licenses/jQuery-LICENSE.txt | 299 + .../infusion/licenses/swfobject-LICENSE.txt | 4 + .../infusion/licenses/swfupload-LICENSE.txt | 22 + jscripts/interface.js | 8 + jscripts/jquery.js | 11 + jscripts/opensocial/activity.js | 342 + jscripts/opensocial/address.js | 145 + jscripts/opensocial/all_opensocial.php | 27 + jscripts/opensocial/bodyType.js | 101 + jscripts/opensocial/collection.js | 88 + jscripts/opensocial/datarequest.js | 365 + jscripts/opensocial/dataresponse.js | 77 + jscripts/opensocial/email.js | 75 + jscripts/opensocial/enum.js | 318 + jscripts/opensocial/environment.js | 152 + jscripts/opensocial/message.js | 164 + jscripts/opensocial/name.js | 102 + jscripts/opensocial/opensocial.js | 406 + jscripts/opensocial/organization.js | 149 + jscripts/opensocial/person.js | 551 + jscripts/opensocial/phone.js | 75 + jscripts/opensocial/responseitem.js | 142 + jscripts/opensocial/url.js | 82 + jscripts/tiny_mce/changelog.txt | 984 ++ jscripts/tiny_mce/langs/en.js | 169 + jscripts/tiny_mce/license.txt | 504 + .../tiny_mce/plugins/acheck/editor_plugin.js | 1 + .../plugins/acheck/editor_plugin_src.js | 83 + .../tiny_mce/plugins/acheck/img/acheck.gif | Bin 0 -> 1087 bytes jscripts/tiny_mce/plugins/acheck/js/dialog.js | 19 + jscripts/tiny_mce/plugins/acheck/langs/en.js | 3 + .../tiny_mce/plugins/acheck/langs/en_dlg.js | 3 + jscripts/tiny_mce/plugins/advhr/css/advhr.css | 5 + .../tiny_mce/plugins/advhr/editor_plugin.js | 1 + .../plugins/advhr/editor_plugin_src.js | 57 + jscripts/tiny_mce/plugins/advhr/js/rule.js | 43 + .../tiny_mce/plugins/advhr/langs/en_dlg.js | 5 + jscripts/tiny_mce/plugins/advhr/rule.htm | 57 + .../plugins/advimage/css/advimage.css | 13 + .../plugins/advimage/editor_plugin.js | 1 + .../plugins/advimage/editor_plugin_src.js | 50 + jscripts/tiny_mce/plugins/advimage/image.htm | 232 + .../tiny_mce/plugins/advimage/img/sample.gif | Bin 0 -> 1624 bytes .../tiny_mce/plugins/advimage/js/image.js | 443 + .../tiny_mce/plugins/advimage/langs/en_dlg.js | 43 + .../tiny_mce/plugins/advlink/css/advlink.css | 8 + .../tiny_mce/plugins/advlink/editor_plugin.js | 1 + .../plugins/advlink/editor_plugin_src.js | 61 + .../tiny_mce/plugins/advlink/js/advlink.js | 528 + .../tiny_mce/plugins/advlink/langs/en_dlg.js | 52 + jscripts/tiny_mce/plugins/advlink/link.htm | 333 + .../tiny_mce/plugins/advlist/editor_plugin.js | 1 + .../plugins/advlist/editor_plugin_src.js | 154 + .../plugins/autoresize/editor_plugin.js | 1 + .../plugins/autoresize/editor_plugin_src.js | 117 + .../plugins/autosave/editor_plugin.js | 1 + .../plugins/autosave/editor_plugin_src.js | 422 + .../tiny_mce/plugins/autosave/langs/en.js | 4 + .../tiny_mce/plugins/bbcode/editor_plugin.js | 1 + .../plugins/bbcode/editor_plugin_src.js | 120 + .../plugins/compat2x/editor_plugin.js | 1 + .../plugins/compat2x/editor_plugin_src.js | 616 + .../plugins/contextmenu/editor_plugin.js | 1 + .../plugins/contextmenu/editor_plugin_src.js | 127 + .../plugins/directionality/editor_plugin.js | 1 + .../directionality/editor_plugin_src.js | 82 + .../plugins/emotions/editor_plugin.js | 1 + .../plugins/emotions/editor_plugin_src.js | 43 + .../tiny_mce/plugins/emotions/emotions.htm | 40 + .../plugins/emotions/img/smiley-cool.gif | Bin 0 -> 354 bytes .../plugins/emotions/img/smiley-cry.gif | Bin 0 -> 329 bytes .../emotions/img/smiley-embarassed.gif | Bin 0 -> 331 bytes .../emotions/img/smiley-foot-in-mouth.gif | Bin 0 -> 344 bytes .../plugins/emotions/img/smiley-frown.gif | Bin 0 -> 340 bytes .../plugins/emotions/img/smiley-innocent.gif | Bin 0 -> 336 bytes .../plugins/emotions/img/smiley-kiss.gif | Bin 0 -> 338 bytes .../plugins/emotions/img/smiley-laughing.gif | Bin 0 -> 344 bytes .../emotions/img/smiley-money-mouth.gif | Bin 0 -> 321 bytes .../plugins/emotions/img/smiley-sealed.gif | Bin 0 -> 325 bytes .../plugins/emotions/img/smiley-smile.gif | Bin 0 -> 345 bytes .../plugins/emotions/img/smiley-surprised.gif | Bin 0 -> 342 bytes .../emotions/img/smiley-tongue-out.gif | Bin 0 -> 328 bytes .../plugins/emotions/img/smiley-undecided.gif | Bin 0 -> 337 bytes .../plugins/emotions/img/smiley-wink.gif | Bin 0 -> 351 bytes .../plugins/emotions/img/smiley-yell.gif | Bin 0 -> 336 bytes .../tiny_mce/plugins/emotions/js/emotions.js | 22 + .../tiny_mce/plugins/emotions/langs/en_dlg.js | 20 + jscripts/tiny_mce/plugins/example/dialog.htm | 22 + .../tiny_mce/plugins/example/editor_plugin.js | 1 + .../plugins/example/editor_plugin_src.js | 84 + .../tiny_mce/plugins/example/img/example.gif | Bin 0 -> 87 bytes .../tiny_mce/plugins/example/js/dialog.js | 19 + jscripts/tiny_mce/plugins/example/langs/en.js | 3 + .../tiny_mce/plugins/example/langs/en_dlg.js | 3 + .../plugins/fullpage/css/fullpage.css | 182 + .../plugins/fullpage/editor_plugin.js | 1 + .../plugins/fullpage/editor_plugin_src.js | 149 + .../tiny_mce/plugins/fullpage/fullpage.htm | 571 + .../tiny_mce/plugins/fullpage/js/fullpage.js | 471 + .../tiny_mce/plugins/fullpage/langs/en_dlg.js | 85 + .../plugins/fullscreen/editor_plugin.js | 1 + .../plugins/fullscreen/editor_plugin_src.js | 148 + .../plugins/fullscreen/fullscreen.htm | 109 + .../tiny_mce/plugins/iespell/editor_plugin.js | 1 + .../plugins/iespell/editor_plugin_src.js | 54 + .../plugins/inlinepopups/editor_plugin.js | 1 + .../plugins/inlinepopups/editor_plugin_src.js | 635 + .../skins/clearlooks2/img/alert.gif | Bin 0 -> 818 bytes .../skins/clearlooks2/img/button.gif | Bin 0 -> 280 bytes .../skins/clearlooks2/img/buttons.gif | Bin 0 -> 1195 bytes .../skins/clearlooks2/img/confirm.gif | Bin 0 -> 915 bytes .../skins/clearlooks2/img/corners.gif | Bin 0 -> 911 bytes .../skins/clearlooks2/img/horizontal.gif | Bin 0 -> 769 bytes .../skins/clearlooks2/img/vertical.gif | Bin 0 -> 92 bytes .../inlinepopups/skins/clearlooks2/window.css | 90 + .../plugins/inlinepopups/template.htm | 387 + .../plugins/insertdatetime/editor_plugin.js | 1 + .../insertdatetime/editor_plugin_src.js | 83 + .../tiny_mce/plugins/layer/editor_plugin.js | 1 + .../plugins/layer/editor_plugin_src.js | 212 + .../plugins/legacyoutput/editor_plugin.js | 1 + .../plugins/legacyoutput/editor_plugin_src.js | 136 + .../tiny_mce/plugins/media/css/content.css | 6 + jscripts/tiny_mce/plugins/media/css/media.css | 16 + .../tiny_mce/plugins/media/editor_plugin.js | 1 + .../plugins/media/editor_plugin_src.js | 414 + jscripts/tiny_mce/plugins/media/img/flash.gif | Bin 0 -> 241 bytes .../tiny_mce/plugins/media/img/flv_player.swf | Bin 0 -> 11668 bytes .../tiny_mce/plugins/media/img/quicktime.gif | Bin 0 -> 303 bytes .../tiny_mce/plugins/media/img/realmedia.gif | Bin 0 -> 439 bytes .../tiny_mce/plugins/media/img/shockwave.gif | Bin 0 -> 387 bytes jscripts/tiny_mce/plugins/media/img/trans.gif | Bin 0 -> 43 bytes .../plugins/media/img/windowsmedia.gif | Bin 0 -> 415 bytes jscripts/tiny_mce/plugins/media/js/embed.js | 73 + jscripts/tiny_mce/plugins/media/js/media.js | 630 + .../tiny_mce/plugins/media/langs/en_dlg.js | 103 + jscripts/tiny_mce/plugins/media/media.htm | 817 + .../plugins/nonbreaking/editor_plugin.js | 1 + .../plugins/nonbreaking/editor_plugin_src.js | 53 + .../plugins/noneditable/editor_plugin.js | 1 + .../plugins/noneditable/editor_plugin_src.js | 90 + .../plugins/pagebreak/css/content.css | 1 + .../plugins/pagebreak/editor_plugin.js | 1 + .../plugins/pagebreak/editor_plugin_src.js | 77 + .../plugins/pagebreak/img/pagebreak.gif | Bin 0 -> 325 bytes .../tiny_mce/plugins/pagebreak/img/trans.gif | Bin 0 -> 43 bytes .../tiny_mce/plugins/paste/editor_plugin.js | 1 + .../plugins/paste/editor_plugin_src.js | 929 ++ .../tiny_mce/plugins/paste/js/pastetext.js | 36 + .../tiny_mce/plugins/paste/js/pasteword.js | 51 + .../tiny_mce/plugins/paste/langs/en_dlg.js | 5 + jscripts/tiny_mce/plugins/paste/pastetext.htm | 27 + jscripts/tiny_mce/plugins/paste/pasteword.htm | 21 + .../tiny_mce/plugins/preview/editor_plugin.js | 1 + .../plugins/preview/editor_plugin_src.js | 53 + .../tiny_mce/plugins/preview/example.html | 28 + .../plugins/preview/jscripts/embed.js | 73 + .../tiny_mce/plugins/preview/preview.html | 17 + .../tiny_mce/plugins/print/editor_plugin.js | 1 + .../plugins/print/editor_plugin_src.js | 34 + jscripts/tiny_mce/plugins/safari/blank.htm | 1 + .../tiny_mce/plugins/safari/editor_plugin.js | 1 + .../plugins/safari/editor_plugin_src.js | 438 + .../tiny_mce/plugins/save/editor_plugin.js | 1 + .../plugins/save/editor_plugin_src.js | 101 + .../searchreplace/css/searchreplace.css | 6 + .../plugins/searchreplace/editor_plugin.js | 1 + .../searchreplace/editor_plugin_src.js | 57 + .../plugins/searchreplace/js/searchreplace.js | 130 + .../plugins/searchreplace/langs/en_dlg.js | 16 + .../plugins/searchreplace/searchreplace.htm | 99 + .../plugins/spellchecker/css/content.css | 1 + .../plugins/spellchecker/editor_plugin.js | 1 + .../plugins/spellchecker/editor_plugin_src.js | 341 + .../plugins/spellchecker/img/wline.gif | Bin 0 -> 46 bytes jscripts/tiny_mce/plugins/style/css/props.css | 13 + .../tiny_mce/plugins/style/editor_plugin.js | 1 + .../plugins/style/editor_plugin_src.js | 55 + jscripts/tiny_mce/plugins/style/js/props.js | 641 + .../tiny_mce/plugins/style/langs/en_dlg.js | 63 + jscripts/tiny_mce/plugins/style/props.htm | 726 + .../plugins/tabfocus/editor_plugin.js | 1 + .../plugins/tabfocus/editor_plugin_src.js | 112 + jscripts/tiny_mce/plugins/table/cell.htm | 178 + jscripts/tiny_mce/plugins/table/css/cell.css | 17 + jscripts/tiny_mce/plugins/table/css/row.css | 25 + jscripts/tiny_mce/plugins/table/css/table.css | 13 + .../tiny_mce/plugins/table/editor_plugin.js | 1 + .../plugins/table/editor_plugin_src.js | 1118 ++ jscripts/tiny_mce/plugins/table/js/cell.js | 286 + .../tiny_mce/plugins/table/js/merge_cells.js | 27 + jscripts/tiny_mce/plugins/table/js/row.js | 237 + jscripts/tiny_mce/plugins/table/js/table.js | 449 + .../tiny_mce/plugins/table/langs/en_dlg.js | 74 + .../tiny_mce/plugins/table/merge_cells.htm | 32 + jscripts/tiny_mce/plugins/table/row.htm | 155 + jscripts/tiny_mce/plugins/table/table.htm | 187 + jscripts/tiny_mce/plugins/template/blank.htm | 12 + .../plugins/template/css/template.css | 23 + .../plugins/template/editor_plugin.js | 1 + .../plugins/template/editor_plugin_src.js | 159 + .../tiny_mce/plugins/template/js/template.js | 106 + .../tiny_mce/plugins/template/langs/en_dlg.js | 15 + .../tiny_mce/plugins/template/template.htm | 32 + .../plugins/visualchars/editor_plugin.js | 1 + .../plugins/visualchars/editor_plugin_src.js | 76 + .../plugins/wordcount/editor_plugin.js | 1 + .../plugins/wordcount/editor_plugin_src.js | 98 + jscripts/tiny_mce/plugins/xhtmlxtras/abbr.htm | 141 + .../tiny_mce/plugins/xhtmlxtras/acronym.htm | 141 + .../plugins/xhtmlxtras/attributes.htm | 148 + jscripts/tiny_mce/plugins/xhtmlxtras/cite.htm | 141 + .../plugins/xhtmlxtras/css/attributes.css | 11 + .../tiny_mce/plugins/xhtmlxtras/css/popup.css | 9 + jscripts/tiny_mce/plugins/xhtmlxtras/del.htm | 161 + .../plugins/xhtmlxtras/editor_plugin.js | 1 + .../plugins/xhtmlxtras/editor_plugin_src.js | 144 + jscripts/tiny_mce/plugins/xhtmlxtras/ins.htm | 161 + .../tiny_mce/plugins/xhtmlxtras/js/abbr.js | 28 + .../tiny_mce/plugins/xhtmlxtras/js/acronym.js | 28 + .../plugins/xhtmlxtras/js/attributes.js | 126 + .../tiny_mce/plugins/xhtmlxtras/js/cite.js | 28 + .../tiny_mce/plugins/xhtmlxtras/js/del.js | 63 + .../plugins/xhtmlxtras/js/element_common.js | 231 + .../tiny_mce/plugins/xhtmlxtras/js/ins.js | 62 + .../plugins/xhtmlxtras/langs/en_dlg.js | 32 + jscripts/tiny_mce/themes/advanced/about.htm | 54 + jscripts/tiny_mce/themes/advanced/anchor.htm | 26 + jscripts/tiny_mce/themes/advanced/charmap.htm | 53 + .../tiny_mce/themes/advanced/color_picker.htm | 73 + .../themes/advanced/editor_template.js | 1 + .../themes/advanced/editor_template_src.js | 1204 ++ jscripts/tiny_mce/themes/advanced/image.htm | 80 + .../themes/advanced/img/colorpicker.jpg | Bin 0 -> 3189 bytes .../tiny_mce/themes/advanced/img/icons.gif | Bin 0 -> 11794 bytes jscripts/tiny_mce/themes/advanced/js/about.js | 72 + .../tiny_mce/themes/advanced/js/anchor.js | 37 + .../tiny_mce/themes/advanced/js/charmap.js | 335 + .../themes/advanced/js/color_picker.js | 253 + jscripts/tiny_mce/themes/advanced/js/image.js | 245 + jscripts/tiny_mce/themes/advanced/js/link.js | 156 + .../themes/advanced/js/source_editor.js | 62 + jscripts/tiny_mce/themes/advanced/langs/en.js | 62 + .../tiny_mce/themes/advanced/langs/en_dlg.js | 51 + jscripts/tiny_mce/themes/advanced/link.htm | 58 + .../themes/advanced/skins/default/content.css | 35 + .../themes/advanced/skins/default/dialog.css | 117 + .../advanced/skins/default/img/buttons.png | Bin 0 -> 3274 bytes .../advanced/skins/default/img/items.gif | Bin 0 -> 70 bytes .../advanced/skins/default/img/menu_arrow.gif | Bin 0 -> 68 bytes .../advanced/skins/default/img/menu_check.gif | Bin 0 -> 70 bytes .../advanced/skins/default/img/progress.gif | Bin 0 -> 1787 bytes .../advanced/skins/default/img/tabs.gif | Bin 0 -> 1326 bytes .../themes/advanced/skins/default/ui.css | 214 + .../themes/advanced/skins/o2k7/content.css | 35 + .../themes/advanced/skins/o2k7/dialog.css | 116 + .../advanced/skins/o2k7/img/button_bg.png | Bin 0 -> 5859 bytes .../skins/o2k7/img/button_bg_black.png | Bin 0 -> 3736 bytes .../skins/o2k7/img/button_bg_silver.png | Bin 0 -> 5358 bytes .../themes/advanced/skins/o2k7/ui.css | 216 + .../themes/advanced/skins/o2k7/ui_black.css | 8 + .../themes/advanced/skins/o2k7/ui_silver.css | 5 + .../themes/advanced/source_editor.htm | 26 + .../tiny_mce/themes/simple/editor_template.js | 1 + .../themes/simple/editor_template_src.js | 85 + jscripts/tiny_mce/themes/simple/img/icons.gif | Bin 0 -> 1440 bytes jscripts/tiny_mce/themes/simple/langs/en.js | 11 + .../themes/simple/skins/default/content.css | 25 + .../themes/simple/skins/default/ui.css | 32 + .../themes/simple/skins/o2k7/content.css | 17 + .../simple/skins/o2k7/img/button_bg.png | Bin 0 -> 5102 bytes .../tiny_mce/themes/simple/skins/o2k7/ui.css | 35 + jscripts/tiny_mce/tiny_mce.js | 1 + jscripts/tiny_mce/tiny_mce_popup.js | 5 + jscripts/tiny_mce/tiny_mce_src.js | 13821 ++++++++++++++++ jscripts/tiny_mce/utils/editable_selects.js | 70 + jscripts/tiny_mce/utils/form_utils.js | 200 + jscripts/tiny_mce/utils/mctabs.js | 77 + jscripts/tiny_mce/utils/validate.js | 220 + jscripts/wz_jsgraphics.js | 943 ++ license/gpl_licence.txt | 280 + login.php | 249 + logout.php | 35 + mods/_core/backups/admin/create.php | 85 + mods/_core/backups/admin/delete.php | 45 + mods/_core/backups/admin/edit.php | 68 + mods/_core/backups/admin/index.php | 120 + mods/_core/backups/admin/restore.php | 124 + mods/_core/backups/classes/Backup.class.php | 464 + .../backups/classes/TableBackup.class.php | 1153 ++ mods/_core/backups/create.php | 64 + mods/_core/backups/delete.php | 51 + mods/_core/backups/edit.php | 59 + mods/_core/backups/index.php | 93 + mods/_core/backups/module.php | 62 + mods/_core/backups/module.xml | 23 + mods/_core/backups/module_delete.php | 14 + mods/_core/backups/restore.php | 108 + mods/_core/backups/upload.php | 85 + .../admin/course_categories.php | 91 + .../cats_categories/admin/create_category.php | 138 + .../cats_categories/admin/delete_category.php | 67 + .../cats_categories/admin/edit_category.php | 146 + .../lib/admin_categories.inc.php | 150 + mods/_core/cats_categories/module.php | 30 + mods/_core/cats_categories/module.xml | 23 + mods/_core/content/index.php | 161 + .../content/menu_inline_editor_submit.php | 32 + mods/_core/content/module.php | 65 + mods/_core/content/module.xml | 23 + mods/_core/content/module_backup.php | 99 + mods/_core/content/module_delete.php | 35 + mods/_core/content/refresh_content_nav.php | 21 + mods/_core/courses/admin/auto_enroll.php | 91 + .../courses/admin/auto_enroll_delete.php | 66 + mods/_core/courses/admin/auto_enroll_edit.php | 252 + .../admin/auto_enroll_filter_courses.php | 216 + mods/_core/courses/admin/courses.php | 209 + mods/_core/courses/admin/create_course.php | 48 + mods/_core/courses/admin/default_mods.php | 256 + mods/_core/courses/admin/default_side.php | 99 + mods/_core/courses/admin/instructor_login.php | 63 + mods/_core/courses/html/course_icon.inc.php | 52 + .../courses/html/course_properties.inc.php | 678 + mods/_core/courses/lib/course.inc.php | 430 + mods/_core/courses/module.php | 56 + mods/_core/courses/module.xml | 23 + mods/_core/courses/users/create_course.php | 86 + .../courses/users/request_instructor.php | 107 + mods/_core/editor/accessibility.php | 122 + mods/_core/editor/add_content.php | 17 + mods/_core/editor/arrange_content.php | 35 + mods/_core/editor/delete_content.php | 68 + mods/_core/editor/edit_content.php | 425 + mods/_core/editor/edit_content_folder.php | 301 + .../_core/editor/editor_tab_functions.inc.php | 572 + .../editor/editor_tabs/alternatives.inc.php | 267 + mods/_core/editor/editor_tabs/edit.inc.php | 149 + .../_core/editor/editor_tabs/glossary.inc.php | 98 + .../editor/editor_tabs/pastefromfile.php | 166 + .../editor/editor_tabs/properties.inc.php | 54 + mods/_core/editor/editor_tabs/tests.inc.php | 207 + mods/_core/editor/index.php | 26 + mods/_core/editor/js/edit.js | 242 + mods/_core/editor/preview.php | 71 + mods/_core/editor/remove_alternative.php | 56 + mods/_core/editor/save_alternative.php | 68 + mods/_core/enrolment/admin/enroll_edit.php | 23 + mods/_core/enrolment/admin/index.php | 40 + mods/_core/enrolment/admin/privileges.php | 23 + mods/_core/enrolment/create_course_list.php | 70 + mods/_core/enrolment/enroll_edit.php | 23 + mods/_core/enrolment/export_course_list.php | 124 + mods/_core/enrolment/html/enroll_edit.inc.php | 371 + .../html/enroll_tab_functions.inc.php | 132 + mods/_core/enrolment/html/enrollment.inc.php | 430 + mods/_core/enrolment/html/privileges.inc.php | 159 + mods/_core/enrolment/import_course_list.php | 50 + mods/_core/enrolment/index.php | 28 + mods/_core/enrolment/lib/enroll.inc.php | 261 + mods/_core/enrolment/module.php | 54 + mods/_core/enrolment/module.xml | 23 + mods/_core/enrolment/module_delete.php | 13 + mods/_core/enrolment/privileges.php | 27 + mods/_core/enrolment/verify_list.php | 223 + mods/_core/file_manager/delete.php | 185 + mods/_core/file_manager/edit.php | 146 + mods/_core/file_manager/filemanager.inc.php | 425 + .../file_manager/filemanager_display.inc.php | 620 + mods/_core/file_manager/index.php | 126 + mods/_core/file_manager/module.php | 30 + mods/_core/file_manager/module.xml | 23 + mods/_core/file_manager/module_backup.php | 6 + mods/_core/file_manager/module_delete.php | 9 + mods/_core/file_manager/move.php | 208 + mods/_core/file_manager/new.php | 249 + mods/_core/file_manager/preview.php | 46 + mods/_core/file_manager/preview_top.php | 44 + mods/_core/file_manager/rename.php | 100 + mods/_core/file_manager/top.php | 186 + mods/_core/file_manager/upload.php | 173 + mods/_core/file_manager/zip.php | 291 + mods/_core/glossary/dropdown/glossary.inc.php | 81 + mods/_core/glossary/index.php | 146 + mods/_core/glossary/module.php | 46 + mods/_core/glossary/module.xml | 23 + mods/_core/glossary/module_backup.php | 22 + mods/_core/glossary/module_delete.php | 11 + mods/_core/glossary/module_news.php | 30 + mods/_core/glossary/sublinks.php | 35 + mods/_core/glossary/tools/add.php | 158 + mods/_core/glossary/tools/delete.php | 70 + mods/_core/glossary/tools/edit.php | 150 + mods/_core/glossary/tools/index.php | 122 + mods/_core/groups/create.php | 46 + mods/_core/groups/create_automatic.php | 230 + mods/_core/groups/create_manual.php | 151 + mods/_core/groups/delete_group.php | 90 + mods/_core/groups/delete_type.php | 71 + mods/_core/groups/edit_group.php | 141 + mods/_core/groups/edit_type.php | 76 + mods/_core/groups/groups.php | 53 + mods/_core/groups/index.php | 113 + mods/_core/groups/members.php | 221 + mods/_core/groups/module.php | 43 + mods/_core/groups/module.xml | 23 + mods/_core/groups/module_backup.php | 33 + mods/_core/groups/module_delete.php | 26 + mods/_core/imsafa/classes/A4a.class.php | 243 + mods/_core/imsafa/classes/A4a.tmpl.php | 56 + mods/_core/imsafa/classes/A4aExport.class.php | 181 + mods/_core/imsafa/classes/A4aImport.class.php | 158 + .../imsafa/html/resources_parser.inc.php | 148 + .../imscc/classes/DiscussionTools.class.php | 43 + .../classes/DiscussionToolsImport.class.php | 79 + .../classes/DiscussionToolsParser.class.php | 97 + mods/_core/imscc/classes/Weblinks.class.php | 65 + mods/_core/imscc/classes/Weblinks.tmpl.php | 7 + .../imscc/classes/WeblinksExport.class.php | 56 + .../imscc/classes/WeblinksParser.class.php | 102 + mods/_core/imscc/ims_export.php | 347 + mods/_core/imscc/ims_import.php | 17 + mods/_core/imscc/include/ims_template.inc.php | 692 + .../imscp/domainProfile_0/imsccauth_v1p0.xsd | 45 + .../imsccauth_v1p0_localised.xsd | 55 + .../imscp/domainProfile_1/anyElement.xsd | 36 + .../domainProfile_1/anyElement_localised.xsd | 44 + .../domainProfile_1/common/anyElement.xsd | 39 + .../domainProfile_1/common/dataTypes.xsd | 118 + .../domainProfile_1/common/elementNames.xsd | 783 + .../domainProfile_1/common/elementTypes.xsd | 779 + .../domainProfile_1/common/rootElement.xsd | 43 + .../domainProfile_1/common/vocabTypes.xsd | 355 + .../domainProfile_1/common/vocabValues.xsd | 266 + .../domainProfile_1/dataTypes_localised.xsd | 130 + .../elementNames_localised.xsd | 787 + .../elementTypes_localised.xsd | 905 + .../imscp/domainProfile_1/extend/custom.xsd | 52 + .../domainProfile_1/imscc_m_definition.xsd | 14 + mods/_core/imscp/domainProfile_1/lomLoose.xsd | 71 + .../domainProfile_1/lomLoose_localised.xsd | 96 + mods/_core/imscp/domainProfile_1/loose.xsd | 292 + .../domainProfile_1/rootElement_localised.xsd | 47 + .../imscp/domainProfile_1/unique/loose.xsd | 295 + .../imscp/domainProfile_1/vocab/loose.xsd | 147 + .../domainProfile_1/vocabTypes_localised.xsd | 379 + .../domainProfile_1/vocabValues_localised.xsd | 270 + .../imscp/domainProfile_2/anyElement.xsd | 36 + .../domainProfile_2/anyElement_localised.xsd | 44 + .../domainProfile_2/common/anyElement.xsd | 39 + .../domainProfile_2/common/dataTypes.xsd | 118 + .../domainProfile_2/common/elementNames.xsd | 783 + .../domainProfile_2/common/elementTypes.xsd | 779 + .../domainProfile_2/common/rootElement.xsd | 43 + .../domainProfile_2/common/vocabTypes.xsd | 355 + .../domainProfile_2/common/vocabValues.xsd | 266 + .../domainProfile_2/dataTypes_localised.xsd | 127 + .../elementNames_localised.xsd | 787 + .../elementTypes_localised.xsd | 806 + .../imscp/domainProfile_2/extend/custom.xsd | 52 + .../domainProfile_2/imscc_mR_definition.xsd | 34 + mods/_core/imscp/domainProfile_2/lomLoose.xsd | 71 + .../domainProfile_2/lomLoose_localised.xsd | 79 + mods/_core/imscp/domainProfile_2/loose.xsd | 292 + .../domainProfile_2/rootElement_localised.xsd | 47 + .../imscp/domainProfile_2/unique/loose.xsd | 295 + .../imscp/domainProfile_2/vocab/loose.xsd | 147 + .../domainProfile_2/vocabTypes_localised.xsd | 408 + .../domainProfile_2/vocabValues_localised.xsd | 270 + .../domainProfile_3/imscp_extensionv1p2.xsd | 179 + .../imscp_extensionv1p2_localised.xsd | 187 + .../imscp/domainProfile_4/ims_qtiasiv1p2.xsd | 2203 +++ .../ims_qtiasiv1p2_def_copy.xsd | 2218 +++ .../ims_qtiasiv1p2_localised.xsd | 2148 +++ .../domainProfile_4/imscc_q_definition.xsd | 150 + mods/_core/imscp/domainProfile_4/xml.xsd | 145 + .../imscp/domainProfile_5/imswl_v1p0.xsd | 16 + .../domainProfile_5/imswl_v1p0_localised.xsd | 23 + .../imscp/domainProfile_6/imsdt_v1p0.xsd | 36 + .../domainProfile_6/imsdt_v1p0_localised.xsd | 43 + mods/_core/imscp/export.php | 95 + mods/_core/imscp/glossary.xsd | 14 + mods/_core/imscp/ims_export.php | 388 + mods/_core/imscp/ims_import.php | 1394 ++ .../imscp/imscc_c1p2maeV0p15_definition.xsd | 80 + mods/_core/imscp/imscp_v1p2.xsd | 329 + .../imscp/imscp_v1p2_constraintsDocument.scmt | 96 + mods/_core/imscp/imscp_v1p2_localised.xsd | 401 + mods/_core/imscp/imsdt_v1p0_localised.xsd | 74 + mods/_core/imscp/imsmanifest.xml | 56 + mods/_core/imscp/imswl_v1p0_localised.xsd | 61 + mods/_core/imscp/include/adlcp_rootv1p2.xsd | 110 + mods/_core/imscp/include/footer.html | 17 + mods/_core/imscp/include/ims.css | 40 + mods/_core/imscp/include/ims_template.inc.php | 565 + mods/_core/imscp/include/ims_xml.xsd | 20 + mods/_core/imscp/include/imscp_rootv1p1p2.xsd | 345 + mods/_core/imscp/include/imsmd_rootv1p2p1.xsd | 573 + mods/_core/imscp/index.php | 196 + mods/_core/imscp/main.xsd | 27 + mods/_core/imscp/module.php | 21 + mods/_core/imscp/module.xml | 20 + mods/_core/imscp/ns.inc.php | 67 + mods/_core/imscp/oauth/OAuth.php | 798 + mods/_core/imscp/oauth/OAuthUtility.class.php | 78 + mods/_core/imscp/oauth/oauth_authenticate.php | 223 + mods/_core/imsqti/classes/QTIImport.class.php | 417 + mods/_core/imsqti/classes/QTIParser.class.php | 466 + mods/_core/imsqti/lib/qti.inc.php | 80 + .../languages/classes/Language.class.php | 220 + .../classes/LanguageEditor.class.php | 435 + .../classes/LanguageManager.class.php | 396 + .../classes/LanguageParser.class.php | 114 + .../classes/LanguagesParser.class.php | 37 + .../classes/RemoteLanguageManager.class.php | 76 + mods/_core/languages/language.php | 98 + mods/_core/languages/language_add.php | 95 + mods/_core/languages/language_delete.php | 56 + mods/_core/languages/language_edit.php | 121 + mods/_core/languages/language_editor.php | 166 + mods/_core/languages/language_import.php | 103 + mods/_core/languages/language_term.php | 102 + mods/_core/languages/language_translate.php | 72 + mods/_core/languages/missing_language.php | 38 + mods/_core/languages/module.php | 41 + mods/_core/languages/module.xml | 23 + mods/_core/languages/module_cron.php | 28 + mods/_core/languages/translate.php | 44 + mods/_core/languages/translate_atutor.php | 83 + mods/_core/languages/translator.php | 605 + mods/_core/modules/add_new.php | 80 + mods/_core/modules/classes/Module.class.php | 835 + .../classes/ModuleListParser.class.php | 140 + .../modules/classes/ModuleParser.class.php | 130 + mods/_core/modules/create.php | 137 + mods/_core/modules/default_mods.php | 256 + mods/_core/modules/default_side.php | 99 + mods/_core/modules/details.php | 185 + mods/_core/modules/index.php | 270 + mods/_core/modules/install_modules.php | 370 + mods/_core/modules/module.php | 48 + mods/_core/modules/module.template.php | 24 + mods/_core/modules/module.xml | 19 + mods/_core/modules/module_install_step_1.php | 53 + mods/_core/modules/module_install_step_2.php | 232 + mods/_core/modules/module_install_step_3.php | 60 + .../_core/modules/module_uninstall_step_1.php | 53 + .../_core/modules/module_uninstall_step_2.php | 76 + .../_core/modules/module_uninstall_step_3.php | 70 + mods/_core/modules/version_history.php | 173 + mods/_core/properties/access.php | 162 + mods/_core/properties/admin/delete_course.php | 51 + mods/_core/properties/admin/edit_course.php | 46 + mods/_core/properties/course_properties.php | 70 + mods/_core/properties/delete_course.php | 49 + mods/_core/properties/lib/course.inc.php | 430 + .../properties/lib/delete_course.inc.php | 70 + mods/_core/properties/module.php | 46 + mods/_core/properties/module.xml | 23 + mods/_core/properties/module_delete.php | 11 + .../themes/classes/ThemeListParser.class.php | 148 + .../themes/classes/ThemeParser.class.php | 87 + mods/_core/themes/delete.php | 40 + mods/_core/themes/import.php | 204 + mods/_core/themes/index.php | 178 + mods/_core/themes/install_themes.php | 332 + mods/_core/themes/lib/theme_template.inc.php | 16 + mods/_core/themes/lib/themes.inc.php | 307 + mods/_core/themes/module.php | 42 + mods/_core/themes/module.xml | 23 + mods/_core/themes/theme_install_step_1.php | 53 + mods/_core/themes/theme_install_step_2.php | 99 + mods/_core/themes/theme_install_step_3.php | 60 + mods/_core/themes/version_history.php | 175 + mods/_core/tool_manager/forums_tool.php | 55 + mods/_core/tool_manager/index.php | 79 + mods/_core/tool_manager/module.php | 10 + mods/_core/tool_manager/module.xml | 23 + mods/_core/users/admin_delete.php | 164 + mods/_core/users/admin_deny.php | 150 + mods/_core/users/admin_email.php | 131 + mods/_core/users/admins/create.php | 228 + mods/_core/users/admins/delete.php | 59 + mods/_core/users/admins/detail_log.php | 86 + mods/_core/users/admins/edit.php | 155 + mods/_core/users/admins/index.php | 138 + mods/_core/users/admins/log.php | 99 + mods/_core/users/admins/my_edit.php | 94 + mods/_core/users/admins/my_password.php | 77 + mods/_core/users/admins/password.php | 126 + mods/_core/users/admins/reset_log.php | 46 + mods/_core/users/create_user.php | 234 + mods/_core/users/default_preferences.php | 146 + mods/_core/users/edit_user.php | 263 + mods/_core/users/index.php | 19 + mods/_core/users/instructor_requests.php | 128 + mods/_core/users/lib/pref_functions.inc.php | 392 + .../users/lib/pref_tab_functions.inc.php | 267 + mods/_core/users/master_list.php | 340 + mods/_core/users/master_list_delete.php | 49 + mods/_core/users/master_list_edit.php | 74 + mods/_core/users/module.php | 90 + mods/_core/users/module.xml | 23 + mods/_core/users/password_user.php | 145 + mods/_core/users/user_enrollment.php | 210 + mods/_core/users/user_status.php | 63 + mods/_core/users/users.php | 411 + mods/_standard/announcements/add_news.php | 148 + mods/_standard/announcements/delete_news.php | 67 + mods/_standard/announcements/edit_news.php | 156 + mods/_standard/announcements/index.php | 95 + mods/_standard/announcements/module.php | 21 + mods/_standard/announcements/module.xml | 23 + .../_standard/announcements/module_backup.php | 33 + .../_standard/announcements/module_delete.php | 20 + mods/_standard/announcements/module_news.php | 41 + mods/_standard/assignments/add_assignment.php | 374 + .../assignments/delete_assignment.php | 58 + .../_standard/assignments/edit_assignment.php | 16 + mods/_standard/assignments/index.php | 17 + .../assignments/index_instructor.php | 134 + mods/_standard/assignments/module.php | 35 + mods/_standard/assignments/module.sql | 33 + mods/_standard/assignments/module.xml | 19 + mods/_standard/assignments/module_backup.php | 19 + mods/_standard/assignments/module_delete.php | 20 + mods/_standard/assignments/module_groups.php | 12 + mods/_standard/assignments/module_news.php | 41 + mods/_standard/blogs/add_post.php | 102 + mods/_standard/blogs/delete_comment.php | 68 + mods/_standard/blogs/delete_post.php | 71 + mods/_standard/blogs/edit_post.php | 106 + mods/_standard/blogs/index.php | 69 + mods/_standard/blogs/module.php | 92 + mods/_standard/blogs/module.xml | 20 + mods/_standard/blogs/module_delete.php | 7 + mods/_standard/blogs/module_groups.php | 29 + mods/_standard/blogs/module_news.php | 56 + mods/_standard/blogs/post.php | 143 + mods/_standard/blogs/sublinks.php | 45 + mods/_standard/blogs/view.php | 94 + mods/_standard/calendar/module.php | 30 + mods/_standard/calendar/module.xml | 23 + mods/_standard/calendar/module_backup.php | 9 + mods/_standard/calendar/module_delete.php | 19 + mods/_standard/calendar/module_groups.php | 15 + mods/_standard/chat/MachineThatGoesBing.class | Bin 0 -> 2452 bytes mods/_standard/chat/admin.settings.default | 10 + mods/_standard/chat/atrc.gif | Bin 0 -> 2353 bytes mods/_standard/chat/bing.php | 30 + mods/_standard/chat/bings/.html | 37 + mods/_standard/chat/bings/chime.au | Bin 0 -> 23846 bytes mods/_standard/chat/bings/chime.wav | Bin 0 -> 47672 bytes mods/_standard/chat/bings/taras.html | 39 + mods/_standard/chat/bings/taras.php | 37 + mods/_standard/chat/chat.php | 83 + mods/_standard/chat/display.php | 130 + mods/_standard/chat/filterHistory.php | 61 + mods/_standard/chat/help.php | 76 + mods/_standard/chat/history.php | 128 + .../chat/include/html/chat_footer.inc.php | 2 + .../chat/include/html/chat_header.inc.php | 16 + .../chat/include/html/login_footer.inc.php | 5 + .../chat/include/html/login_header.inc.php | 11 + mods/_standard/chat/index.php | 134 + mods/_standard/chat/index.php.old | 134 + mods/_standard/chat/lib/chat.inc.php | 555 + mods/_standard/chat/lib/chat_defaults.inc.php | 169 + mods/_standard/chat/logout.php | 57 + .../chat/manage/delete_transcript.php | 58 + mods/_standard/chat/manage/index.php | 152 + .../chat/manage/start_transcript.php | 248 + .../_standard/chat/manage/view_transcript.php | 27 + mods/_standard/chat/module.php | 52 + mods/_standard/chat/module.xml | 23 + mods/_standard/chat/module_delete.php | 13 + mods/_standard/chat/options.php | 88 + mods/_standard/chat/poster.php | 46 + mods/_standard/chat/prefs.php | 89 + mods/_standard/chat/prefs2.php | 79 + mods/_standard/chat/sublinks.php | 41 + mods/_standard/chat/view_transcript.php | 33 + mods/_standard/course_email/course_email.php | 231 + mods/_standard/course_email/module.php | 12 + mods/_standard/course_email/module.xml | 23 + mods/_standard/course_tools/module.php | 16 + mods/_standard/course_tools/module.xml | 23 + mods/_standard/course_tools/modules.php | 195 + mods/_standard/course_tools/side_menu.php | 88 + mods/_standard/directory/directory.php | 109 + mods/_standard/directory/module.php | 15 + mods/_standard/directory/module.xml | 23 + mods/_standard/directory/sublinks.php | 35 + mods/_standard/faq/add_question.php | 111 + mods/_standard/faq/add_topic.php | 65 + mods/_standard/faq/delete_question.php | 61 + mods/_standard/faq/delete_topic.php | 58 + mods/_standard/faq/edit_question.php | 133 + mods/_standard/faq/edit_topic.php | 89 + mods/_standard/faq/icon.gif | Bin 0 -> 502 bytes mods/_standard/faq/index.php | 52 + mods/_standard/faq/index_instructor.php | 94 + mods/_standard/faq/module.php | 43 + mods/_standard/faq/module.xml | 19 + mods/_standard/faq/module_backup.php | 29 + mods/_standard/faq/module_delete.php | 17 + mods/_standard/faq/module_news.php | 42 + mods/_standard/faq/sublinks.php | 24 + mods/_standard/farchive/forum_post.php | 94 + mods/_standard/farchive/index.php | 11 + mods/_standard/farchive/index_instructor.php | 124 + mods/_standard/farchive/module.php | 28 + mods/_standard/farchive/module.xml | 19 + mods/_standard/farchive/send_zip_archive.php | 35 + mods/_standard/farchive/styles.css | 88 + mods/_standard/file_storage/assignment.php | 151 + mods/_standard/file_storage/comments.php | 198 + .../_standard/file_storage/delete_comment.php | 62 + .../file_storage/delete_revision.php | 91 + mods/_standard/file_storage/edit.php | 222 + mods/_standard/file_storage/edit_folder.php | 86 + .../file_storage/file_storage.inc.php | 577 + mods/_standard/file_storage/index.php | 742 + mods/_standard/file_storage/module.php | 58 + mods/_standard/file_storage/module.xml | 23 + mods/_standard/file_storage/module_delete.php | 23 + mods/_standard/file_storage/module_groups.php | 24 + mods/_standard/file_storage/module_news.php | 49 + mods/_standard/file_storage/move.php | 129 + mods/_standard/file_storage/new.php | 160 + mods/_standard/file_storage/revisions.php | 142 + mods/_standard/file_storage/sublinks.php | 29 + mods/_standard/flowplayer/LICENSE.txt | 721 + mods/_standard/flowplayer/README.txt | 222 + .../flowplayer/flowplayer-3.1.2.min.js | 24 + .../_standard/flowplayer/flowplayer-3.1.2.swf | Bin 0 -> 107779 bytes .../flowplayer/flowplayer.controls-3.1.2.swf | Bin 0 -> 26349 bytes mods/_standard/flowplayer/module.php | 20 + mods/_standard/flowplayer/module.xml | 19 + .../flowplayer/module_format_content.php | 67 + mods/_standard/forums/add_forum.php | 73 + mods/_standard/forums/admin/forum_add.php | 111 + mods/_standard/forums/admin/forum_delete.php | 77 + mods/_standard/forums/admin/forum_edit.php | 166 + mods/_standard/forums/admin/forums.php | 109 + mods/_standard/forums/delete_forum.php | 73 + mods/_standard/forums/dropdown/posts.inc.php | 50 + mods/_standard/forums/edit_forum.php | 98 + mods/_standard/forums/edit_post.php | 149 + mods/_standard/forums/forum/delete_thread.php | 138 + mods/_standard/forums/forum/index.php | 60 + mods/_standard/forums/forum/list.php | 95 + mods/_standard/forums/forum/lock_thread.php | 98 + mods/_standard/forums/forum/move_thread.php | 108 + mods/_standard/forums/forum/new_thread.php | 196 + mods/_standard/forums/forum/stick.php | 30 + mods/_standard/forums/forum/subscribe.php | 82 + .../forums/forum/subscribe_forum.php | 52 + mods/_standard/forums/forum/view.php | 220 + mods/_standard/forums/html/forum.inc.php | 195 + mods/_standard/forums/html/new_thread.inc.php | 102 + mods/_standard/forums/index.php | 78 + mods/_standard/forums/lib/forums.inc.php | 287 + mods/_standard/forums/module.php | 77 + mods/_standard/forums/module.xml | 23 + mods/_standard/forums/module_delete.php | 74 + .../forums/module_format_content.php | 20 + mods/_standard/forums/module_groups.php | 31 + mods/_standard/forums/module_news.php | 56 + mods/_standard/forums/sublinks.php | 35 + mods/_standard/forums/view_item.php | 21 + mods/_standard/google_search/SOAP_Google.php | 114 + .../google_search/admin/module_prefs.php | 127 + mods/_standard/google_search/google.gif | Bin 0 -> 1446 bytes mods/_standard/google_search/gsearch.php | 317 + mods/_standard/google_search/index.php | 109 + mods/_standard/google_search/module.css | 55 + mods/_standard/google_search/module.php | 25 + mods/_standard/google_search/module.xml | 23 + .../_standard/google_search/side_menu.inc.php | 31 + mods/_standard/gradebook/README | 51 + mods/_standard/gradebook/edit_marks.php | 423 + mods/_standard/gradebook/grade_scale.php | 185 + mods/_standard/gradebook/grade_scale_add.php | 17 + .../gradebook/grade_scale_delete.php | 66 + mods/_standard/gradebook/grade_scale_edit.php | 17 + mods/_standard/gradebook/gradebook.png | Bin 0 -> 1839 bytes .../gradebook/gradebook_add_tests.php | 304 + .../gradebook/gradebook_delete_tests.php | 77 + .../gradebook/gradebook_edit_tests.php | 195 + mods/_standard/gradebook/gradebook_tests.php | 129 + .../html/grade_scale_add_edit.inc.php | 221 + .../import_export_external_marks.php | 277 + .../_standard/gradebook/lib/gradebook.inc.php | 488 + mods/_standard/gradebook/module.php | 102 + mods/_standard/gradebook/module.xml | 19 + mods/_standard/gradebook/my_gradebook.php | 130 + mods/_standard/gradebook/update_gradebook.php | 292 + mods/_standard/gradebook/verify_list.php | 201 + mods/_standard/gradebook/verify_tests.php | 225 + mods/_standard/links/add.php | 140 + mods/_standard/links/index.php | 200 + mods/_standard/links/lib/links.inc.php | 230 + mods/_standard/links/module.php | 91 + mods/_standard/links/module.xml | 23 + mods/_standard/links/module_backup.php | 39 + mods/_standard/links/module_delete.php | 16 + mods/_standard/links/module_groups.php | 27 + mods/_standard/links/module_news.php | 44 + mods/_standard/links/sublinks.php | 22 + mods/_standard/links/tools/add.php | 173 + mods/_standard/links/tools/categories.php | 110 + .../links/tools/categories_create.php | 124 + .../links/tools/categories_delete.php | 78 + .../_standard/links/tools/categories_edit.php | 125 + mods/_standard/links/tools/delete.php | 74 + mods/_standard/links/tools/edit.php | 158 + mods/_standard/links/tools/index.php | 174 + .../_standard/patcher/classes/Patch.class.php | 832 + .../patcher/classes/PatchCreator.class.php | 386 + .../patcher/classes/PatchListParser.class.php | 132 + .../patcher/classes/PatchParser.class.php | 139 + mods/_standard/patcher/include/common.inc.php | 145 + mods/_standard/patcher/include/json.inc.php | 84 + .../include/patch_xml_template.inc.php | 56 + mods/_standard/patcher/index_admin.php | 465 + mods/_standard/patcher/module.php | 43 + mods/_standard/patcher/module.xml | 19 + mods/_standard/patcher/myown_patches.php | 99 + mods/_standard/patcher/patch_create.php | 23 + mods/_standard/patcher/patch_creator.php | 134 + mods/_standard/patcher/patch_delete.php | 71 + mods/_standard/patcher/patch_edit.php | 42 + .../patcher/patch_edit_interface.tmpl.php | 349 + mods/_standard/patcher/sample_patch.xml | 51 + mods/_standard/patcher/sample_patch_list.xml | 25 + mods/_standard/patcher/xml_special_chars.txt | 5 + mods/_standard/photos/addComment.php | 54 + mods/_standard/photos/admin/edit_album.php | 29 + mods/_standard/photos/admin/edit_photos.php | 29 + mods/_standard/photos/admin/preferences.php | 39 + mods/_standard/photos/albums.php | 193 + mods/_standard/photos/course_albums.php | 47 + mods/_standard/photos/create_album.php | 74 + mods/_standard/photos/delete_album.php | 63 + mods/_standard/photos/delete_comment.php | 88 + mods/_standard/photos/delete_photo.php | 72 + mods/_standard/photos/edit_album.php | 93 + mods/_standard/photos/edit_comment.php | 46 + mods/_standard/photos/edit_photos.php | 97 + mods/_standard/photos/get_photo.php | 103 + mods/_standard/photos/images/loading.gif | Bin 0 -> 1849 bytes mods/_standard/photos/images/next.png | Bin 0 -> 5946 bytes mods/_standard/photos/images/no.png | Bin 0 -> 465 bytes .../_standard/photos/images/photo_gallery.png | Bin 0 -> 7430 bytes mods/_standard/photos/images/prev.png | Bin 0 -> 5895 bytes mods/_standard/photos/images/search_icon.png | Bin 0 -> 3443 bytes mods/_standard/photos/include/ajaxupload.js | 711 + .../include/classes/AjaxMessage.class.php | 87 + .../include/classes/PhotoAlbum.class.php | 747 + .../include/classes/SimpleImage.class.php | 86 + .../photos/include/constants.inc.php | 48 + .../photos/include/edit_album.inc.php | 90 + .../photos/include/edit_photos.inc.php | 118 + .../photos/include/imageReorderer.js | 57 + mods/_standard/photos/include/lib.inc.php | 158 + .../photos/include/profile_album.inc.php | 74 + mods/_standard/photos/include/upload.js | 46 + mods/_standard/photos/index.php | 49 + mods/_standard/photos/index_admin.php | 75 + mods/_standard/photos/module.css | 425 + mods/_standard/photos/module.php | 153 + mods/_standard/photos/module.xml | 19 + mods/_standard/photos/photo.php | 89 + mods/_standard/photos/profile_album.php | 19 + .../photos/remove_uploaded_photo.php | 44 + mods/_standard/photos/search.php | 51 + mods/_standard/photos/set_profile_picture.php | 27 + mods/_standard/photos/shared_albums.php | 46 + mods/_standard/photos/sublinks.php | 84 + mods/_standard/polls/dropdown/poll.inc.php | 105 + mods/_standard/polls/index.php | 92 + mods/_standard/polls/module.php | 38 + mods/_standard/polls/module.xml | 23 + mods/_standard/polls/module_backup.php | 32 + mods/_standard/polls/module_delete.php | 19 + mods/_standard/polls/module_news.php | 43 + mods/_standard/polls/sublinks.php | 23 + mods/_standard/polls/tools/add.php | 94 + mods/_standard/polls/tools/delete.php | 63 + mods/_standard/polls/tools/edit.php | 113 + mods/_standard/polls/tools/index.php | 103 + .../admin/profile_picture.php | 21 + .../html/profile_picture.inc.php | 39 + mods/_standard/profile_pictures/module.php | 16 + mods/_standard/profile_pictures/module.xml | 19 + mods/_standard/profile_pictures/profile.php | 152 + .../profile_pictures/profile_picture.php | 25 + .../profile_pictures/save_profile_picture.php | 267 + .../reading_list/add_resource_av.php | 165 + .../reading_list/add_resource_book.php | 174 + .../reading_list/add_resource_file.php | 173 + .../reading_list/add_resource_handout.php | 156 + .../reading_list/add_resource_url.php | 157 + .../_standard/reading_list/delete_reading.php | 61 + .../reading_list/delete_resource.php | 60 + .../reading_list/display_resource.php | 152 + .../reading_list/display_resources.php | 110 + .../reading_list/edit_reading_av.php | 180 + .../reading_list/edit_reading_book.php | 185 + .../reading_list/edit_reading_file.php | 181 + .../reading_list/edit_reading_handout.php | 180 + .../reading_list/edit_reading_url.php | 180 + mods/_standard/reading_list/index.php | 81 + .../reading_list/index_instructor.php | 162 + mods/_standard/reading_list/module.php | 104 + mods/_standard/reading_list/module.xml | 19 + mods/_standard/reading_list/module_backup.php | 36 + mods/_standard/reading_list/module_delete.php | 13 + mods/_standard/reading_list/module_news.php | 38 + .../_standard/reading_list/new_reading_av.php | 181 + .../reading_list/new_reading_book.php | 178 + .../reading_list/new_reading_file.php | 183 + .../reading_list/new_reading_handout.php | 183 + .../reading_list/new_reading_url.php | 184 + .../reading_list/reading_details.php | 99 + mods/_standard/reading_list/sublinks.php | 23 + mods/_standard/rss_feeds/add_feed.php | 136 + mods/_standard/rss_feeds/classes/lastRSS.php | 226 + mods/_standard/rss_feeds/delete_feed.php | 65 + mods/_standard/rss_feeds/edit_feed.php | 103 + mods/_standard/rss_feeds/index.php | 82 + mods/_standard/rss_feeds/load_file.php | 7 + mods/_standard/rss_feeds/module.php | 112 + mods/_standard/rss_feeds/module.xml | 19 + mods/_standard/rss_feeds/preview.php | 58 + mods/_standard/sitemap/module.php | 13 + mods/_standard/sitemap/module.xml | 23 + mods/_standard/sitemap/sitemap.php | 74 + mods/_standard/social/README | 97 + mods/_standard/social/activities.php | 31 + .../social/admin/delete_applications.php | 45 + mods/_standard/social/applications.php | 141 + mods/_standard/social/basic_profile.php | 148 + mods/_standard/social/connections.php | 172 + mods/_standard/social/edit_profile.php | 336 + mods/_standard/social/groups/create.php | 177 + mods/_standard/social/groups/delete.php | 53 + .../social/groups/delete_message.php | 56 + mods/_standard/social/groups/edit.php | 198 + .../social/groups/get_sgroup_logo.php | 70 + mods/_standard/social/groups/index.php | 63 + .../social/groups/invitation_handler.php | 132 + mods/_standard/social/groups/invite.php | 68 + mods/_standard/social/groups/join.php | 80 + mods/_standard/social/groups/list.php | 91 + mods/_standard/social/groups/search.php | 107 + mods/_standard/social/groups/view.php | 76 + mods/_standard/social/images/b_drop.png | Bin 0 -> 311 bytes mods/_standard/social/images/check_icon.gif | Bin 0 -> 564 bytes mods/_standard/social/images/check_icon.jpg | Bin 0 -> 4147 bytes mods/_standard/social/images/check_icon.png | Bin 0 -> 4349 bytes mods/_standard/social/images/edit_profile.gif | Bin 0 -> 232 bytes .../_standard/social/images/icon-settings.png | Bin 0 -> 326 bytes mods/_standard/social/images/nophoto.gif | Bin 0 -> 193 bytes mods/_standard/social/images/placelogo.gif | Bin 0 -> 1035 bytes mods/_standard/social/images/placelogo.png | Bin 0 -> 3854 bytes mods/_standard/social/images/plus_icon.gif | Bin 0 -> 997 bytes mods/_standard/social/images/plus_icon.jpg | Bin 0 -> 458 bytes mods/_standard/social/images/social.gif | Bin 0 -> 2156 bytes mods/_standard/social/index.php | 168 + mods/_standard/social/index_admin.php | 50 + mods/_standard/social/index_instructor.php | 23 + mods/_standard/social/index_mystart.php | 19 + mods/_standard/social/index_public.php | 127 + .../_standard/social/lib/BasicBlobCrypter.php | 130 + .../social/lib/BasicSecurityToken.php | 160 + mods/_standard/social/lib/BlobCrypter.php | 53 + mods/_standard/social/lib/Crypto.php | 123 + mods/_standard/social/lib/OAuth/OAuth.php | 747 + .../social/lib/OAuth/access_token.php | 35 + .../lib/OAuth/approve_authorization.php | 41 + mods/_standard/social/lib/OAuth/authorize.php | 60 + .../_standard/social/lib/OAuth/common.inc.php | 34 + .../social/lib/OAuth/request_token.php | 32 + mods/_standard/social/lib/SecurityToken.php | 70 + .../lib/Shindig/ATutorActivityService.php | 102 + .../lib/Shindig/ATutorAppDataService.php | 74 + .../social/lib/Shindig/ATutorDbFetcher.php | 871 + .../lib/Shindig/ATutorMessagesService.php | 26 + .../lib/Shindig/ATutorOAuthDataStore.php | 201 + .../lib/Shindig/ATutorOAuthLookupService.php | 111 + .../lib/Shindig/ATutorPersonService.php | 78 + .../social/lib/Shindig/ATutorService.php | 114 + mods/_standard/social/lib/Shindig/README | 11 + .../social/lib/classes/Activity.class.php | 151 + .../social/lib/classes/Application.class.php | 442 + .../social/lib/classes/Applications.class.php | 128 + .../social/lib/classes/Member.class.php | 716 + .../PrivacyController.class.php | 162 + .../PrivacyControl/PrivacyObject.class.php | 92 + .../PrivacyControl/SetPrivacyControl.php | 60 + .../SocialGroups/SocialGroup.class.php | 486 + .../SocialGroups/SocialGroups.class.php | 267 + mods/_standard/social/lib/constants.inc.php | 75 + mods/_standard/social/lib/friends.inc.php | 645 + mods/_standard/social/lib/js/container.js | 89 + mods/_standard/social/lib/js/livesearch.js | 40 + mods/_standard/social/lib/js/prototype.js | 4221 +++++ .../_standard/social/lib/profile_menu.inc.php | 6 + mods/_standard/social/module.php | 173 + mods/_standard/social/module.sql | 421 + mods/_standard/social/module.xml | 19 + mods/_standard/social/module_backup.php | 33 + mods/_standard/social/module_news.php | 37 + mods/_standard/social/privacy_settings.php | 46 + mods/_standard/social/profile_picture.php | 20 + mods/_standard/social/set_prefs.php | 47 + mods/_standard/social/settings.php | 92 + mods/_standard/social/side_menu.inc.php | 75 + mods/_standard/social/sprofile.php | 113 + mods/_standard/social/sublinks.php | 76 + mods/_standard/statistics/course_stats.php | 204 + mods/_standard/statistics/module.php | 9 + mods/_standard/statistics/module.xml | 23 + mods/_standard/statistics/module_backup.php | 17 + mods/_standard/statistics/module_delete.php | 11 + mods/_standard/student_tools/icon.gif | Bin 0 -> 2935 bytes mods/_standard/student_tools/index.php | 104 + .../student_tools/instructor_index.php | 149 + mods/_standard/student_tools/module.php | 24 + mods/_standard/student_tools/module.xml | 19 + mods/_standard/support_tools/module.php | 31 + mods/_standard/support_tools/module.xml | 23 + mods/_standard/support_tools/scaffolds.php | 74 + .../_standard/support_tools/side_menu.inc.php | 50 + mods/_standard/tests/add_test_questions.php | 39 + .../tests/add_test_questions_confirm.php | 95 + .../tests/classes/testQuestions.class.php | 1571 ++ .../tests/create_question_likert.php | 203 + mods/_standard/tests/create_question_long.php | 109 + .../tests/create_question_matching.php | 175 + .../tests/create_question_matchingdd.php | 177 + .../_standard/tests/create_question_multi.php | 139 + .../tests/create_question_multianswer.php | 174 + .../tests/create_question_multichoice.php | 16 + .../tests/create_question_ordering.php | 157 + mods/_standard/tests/create_question_tf.php | 105 + .../tests/create_question_truefalse.php | 16 + mods/_standard/tests/create_test.php | 495 + mods/_standard/tests/dd.php | 220 + mods/_standard/tests/delete_question.php | 63 + mods/_standard/tests/delete_result.php | 68 + mods/_standard/tests/delete_test.php | 83 + mods/_standard/tests/edit_question_likert.php | 232 + mods/_standard/tests/edit_question_long.php | 127 + .../tests/edit_question_matching.php | 212 + .../tests/edit_question_matchingdd.php | 212 + mods/_standard/tests/edit_question_multi.php | 170 + .../tests/edit_question_multianswer.php | 189 + .../tests/edit_question_multichoice.php | 16 + .../tests/edit_question_ordering.php | 188 + mods/_standard/tests/edit_question_tf.php | 150 + .../tests/edit_question_truefalse.php | 16 + mods/_standard/tests/edit_test.php | 536 + mods/_standard/tests/export_test.php | 32 + mods/_standard/tests/form_editor.php | 128 + .../tests/html/tests_questions.inc.php | 168 + mods/_standard/tests/import_test.php | 339 + mods/_standard/tests/index.php | 177 + .../tests/lib/likert_presets.inc.php | 28 + mods/_standard/tests/lib/take_test.js | 23 + .../tests/lib/test_question_queries.inc.php | 91 + .../tests/lib/test_result_functions.inc.php | 216 + mods/_standard/tests/module.php | 150 + mods/_standard/tests/module.xml | 23 + mods/_standard/tests/module_backup.php | 147 + mods/_standard/tests/module_delete.php | 33 + mods/_standard/tests/module_groups.php | 15 + mods/_standard/tests/module_news.php | 50 + mods/_standard/tests/my_tests.php | 175 + mods/_standard/tests/preview.php | 141 + mods/_standard/tests/preview_question.php | 57 + mods/_standard/tests/question_cats.php | 65 + mods/_standard/tests/question_cats_delete.php | 62 + mods/_standard/tests/question_cats_manage.php | 82 + mods/_standard/tests/question_db.php | 123 + mods/_standard/tests/question_import.php | 316 + mods/_standard/tests/question_remove.php | 58 + mods/_standard/tests/questions.php | 281 + mods/_standard/tests/results.php | 295 + mods/_standard/tests/results_all.php | 404 + mods/_standard/tests/results_all_quest.php | 118 + mods/_standard/tests/results_quest_long.php | 75 + mods/_standard/tests/sublinks.php | 32 + mods/_standard/tests/take_test.php | 246 + mods/_standard/tests/take_test_q.php | 281 + mods/_standard/tests/test_intro.php | 240 + mods/_standard/tests/view_results.php | 196 + mods/_standard/tests/view_results_manage.php | 141 + mods/_standard/tile_search/TILE.css | 353 + .../tile_search/admin/module_setup.php | 103 + .../classes/ResultParser.class.php | 143 + mods/_standard/tile_search/import.php | 89 + mods/_standard/tile_search/index.php | 116 + mods/_standard/tile_search/module.php | 59 + mods/_standard/tile_search/module.xml | 23 + mods/_standard/tile_search/tile.php | 112 + mods/_standard/tracker/course_tracker.php | 315 + mods/_standard/tracker/lib/tracker.inc.php | 274 + mods/_standard/tracker/lib/tracker2.inc.php | 131 + .../tracker/lib/tracker_stats.inc.php | 487 + .../tracker/lib/tracker_stats2.inc.php | 87 + mods/_standard/tracker/module.php | 27 + mods/_standard/tracker/module.xml | 20 + mods/_standard/tracker/my_stats.php | 61 + mods/_standard/tracker/sublinks.php | 25 + mods/_standard/tracker/tools/export.php | 61 + mods/_standard/tracker/tools/index.php | 129 + .../tracker/tools/page_student_stats.php | 59 + mods/_standard/tracker/tools/reset.php | 46 + .../_standard/tracker/tools/student_usage.php | 84 + mods/_standard/tracker/tracker.php | 63 + mods/index.html | 1 + move_module.php | 78 + password_reminder.php | 176 + popuphelp.php | 39 + profile.php | 58 + readme | 17 + registration.php | 358 + search.php | 24 + sha-1factory.js | 229 + svn.php | 27 + switch_view.php | 35 + themes/blumin/content.tmpl.php | 36 + themes/blumin/forms.css | 73 + themes/blumin/ie_styles.css | 26 + themes/blumin/images/arrow_ltr.gif | Bin 0 -> 88 bytes themes/blumin/images/back.gif | Bin 0 -> 331 bytes themes/blumin/images/continue.gif | Bin 0 -> 410 bytes themes/blumin/images/linkOpaque.gif | Bin 0 -> 101 bytes themes/blumin/images/linkTransparent.gif | Bin 0 -> 102 bytes themes/blumin/images/newsitem_icon.gif | Bin 0 -> 951 bytes themes/blumin/images/next.gif | Bin 0 -> 904 bytes themes/blumin/images/previous.gif | Bin 0 -> 901 bytes themes/blumin/images/resume.gif | Bin 0 -> 919 bytes themes/blumin/images/side_arrow.gif | Bin 0 -> 58 bytes themes/blumin/images/sort.gif | Bin 0 -> 64 bytes themes/blumin/images/user-star.gif | Bin 0 -> 319 bytes themes/blumin/images/user.gif | Bin 0 -> 882 bytes themes/blumin/include/box.tmpl.php | 12 + themes/blumin/include/footer.tmpl.php | 23 + themes/blumin/include/header.tmpl.php | 233 + themes/blumin/include/side_menu.tmpl.php | 8 + themes/blumin/print.css | 13 + themes/blumin/readme.txt | 9 + themes/blumin/screenshot.gif | Bin 0 -> 1393 bytes themes/blumin/styles.css | 1926 +++ themes/blumin/theme.cfg.php | 43 + themes/blumin/theme_info.xml | 12 + themes/default/confirmmessage.tmpl.php | 34 + themes/default/content.tmpl.php | 102 + themes/default/directory.tmpl.php | 95 + .../default/editor/arrange_content.tmpl.php | 49 + .../editor/edit_content_folder.tmpl.php | 108 + themes/default/errormessage.tmpl.php | 21 + themes/default/feedbackmessage.tmpl.php | 20 + themes/default/forms.css | 131 + themes/default/ie_styles.css | 122 + themes/default/images/Thumbs.db | Bin 0 -> 70144 bytes themes/default/images/arrow_ltr.gif | Bin 0 -> 917 bytes themes/default/images/atutor_head.jpg | Bin 0 -> 52371 bytes themes/default/images/atutor_head.psd | Bin 0 -> 244025 bytes themes/default/images/atutor_logo.png | Bin 0 -> 5073 bytes themes/default/images/atutor_logo.psd | Bin 0 -> 43826 bytes themes/default/images/atutor_logo2.png | Bin 0 -> 4035 bytes themes/default/images/continue.gif | Bin 0 -> 191 bytes themes/default/images/edit.gif | Bin 0 -> 231 bytes themes/default/images/help.png | Bin 0 -> 744 bytes themes/default/images/hidemenu.gif | Bin 0 -> 270 bytes themes/default/images/linkOpaque.gif | Bin 0 -> 81 bytes themes/default/images/linkTransparent.gif | Bin 0 -> 81 bytes themes/default/images/mswitch_minus.gif | Bin 0 -> 119 bytes themes/default/images/mswitch_plus.gif | Bin 0 -> 124 bytes themes/default/images/newsitem_icon.gif | Bin 0 -> 97 bytes themes/default/images/next.png | Bin 0 -> 399 bytes themes/default/images/pencil_bottom.gif | Bin 0 -> 8874 bytes themes/default/images/pencil_bottom_ds.gif | Bin 0 -> 8877 bytes themes/default/images/pencil_bottom_ds.png | Bin 0 -> 21411 bytes themes/default/images/pencil_top.gif | Bin 0 -> 16447 bytes themes/default/images/pencil_top_ds.png | Bin 0 -> 45230 bytes themes/default/images/pencils.gif | Bin 0 -> 6661 bytes themes/default/images/pencils_up.jpg | Bin 0 -> 33135 bytes themes/default/images/pencils_up_bottom.jpg | Bin 0 -> 11533 bytes themes/default/images/pencils_up_top.jpg | Bin 0 -> 25269 bytes themes/default/images/photos_arrange.png | Bin 0 -> 647 bytes themes/default/images/previous.gif | Bin 0 -> 150 bytes themes/default/images/previous.png | Bin 0 -> 402 bytes themes/default/images/profile.gif | Bin 0 -> 287 bytes themes/default/images/resume.png | Bin 0 -> 445 bytes themes/default/images/resume2.png | Bin 0 -> 445 bytes themes/default/images/showmenu.gif | Bin 0 -> 284 bytes themes/default/images/side_arrow.gif | Bin 0 -> 117 bytes themes/default/images/sort.png | Bin 0 -> 472 bytes themes/default/images/texture_bg.png | Bin 0 -> 7622 bytes themes/default/images/tl_corner.gif | Bin 0 -> 107 bytes themes/default/images/top.gif | Bin 0 -> 176 bytes themes/default/images/tree/index.html | 0 themes/default/images/tree/tree_collapse.gif | Bin 0 -> 115 bytes themes/default/images/tree/tree_disabled.gif | Bin 0 -> 116 bytes themes/default/images/tree/tree_end.gif | Bin 0 -> 107 bytes themes/default/images/tree/tree_expand.gif | Bin 0 -> 117 bytes .../default/images/tree/tree_horizontal.gif | Bin 0 -> 105 bytes themes/default/images/tree/tree_space.gif | Bin 0 -> 88 bytes themes/default/images/tree/tree_split.gif | Bin 0 -> 110 bytes themes/default/images/tree/tree_vertline.gif | Bin 0 -> 108 bytes themes/default/images/up.png | Bin 0 -> 397 bytes themes/default/images/user-star.gif | Bin 0 -> 155 bytes themes/default/images/user.gif | Bin 0 -> 115 bytes themes/default/images/x.gif | Bin 0 -> 361 bytes themes/default/include/box.tmpl.php | 12 + themes/default/include/fm_footer.tmpl.php | 9 + themes/default/include/fm_header.tmpl.php | 51 + themes/default/include/footer.tmpl.php | 47 + themes/default/include/forms.css | 75 + themes/default/include/header.tmpl.php | 326 + themes/default/include/side_menu.tmpl.php | 8 + themes/default/include/tm_footer.tmpl.php | 7 + themes/default/include/tm_header.tmpl.php | 51 + themes/default/index.tmpl.php | 170 + themes/default/infomessage.tmpl.php | 18 + themes/default/login.tmpl.php | 73 + themes/default/password_change.tmpl.php | 62 + themes/default/password_reminder.tmpl.php | 28 + .../password_reminder_feedback.tmpl.php | 6 + themes/default/photos/admin/pa_index.tmpl.php | 49 + .../photos/admin/pa_preferences.tmpl.php | 12 + themes/default/photos/pa_albums.tmpl.php | 337 + .../default/photos/pa_create_album.tmpl.php | 34 + themes/default/photos/pa_edit_album.tmpl.php | 35 + themes/default/photos/pa_edit_photos.tmpl.php | 34 + themes/default/photos/pa_index.tmpl.php | 76 + .../photos/pa_organize_photos.tmpl.php | 28 + themes/default/photos/pa_photo.tmpl.php | 181 + .../default/photos/pa_profile_albums.tmpl.php | 179 + themes/default/photos/pa_search.tmpl.php | 167 + themes/default/print.css | 13 + themes/default/profile.tmpl.php | 81 + themes/default/readme.txt | 9 + themes/default/registration.tmpl.php | 237 + themes/default/rtl.css | 16 + themes/default/screenshot.gif | Bin 0 -> 11157 bytes themes/default/social/activities.tmpl.php | 34 + .../social/admin/delete_applications.tmpl.php | 42 + .../social/application_settings.tmpl.php | 66 + themes/default/social/applications.tmpl.php | 108 + themes/default/social/basic_profile.tmpl.php | 135 + themes/default/social/connections.tmpl.php | 109 + themes/default/social/edit_profile.tmpl.php | 166 + .../edit_profile/edit_additional.tmpl.php | 29 + .../social/edit_profile/edit_contact.tmpl.php | 39 + .../edit_profile/edit_education.tmpl.php | 63 + .../edit_profile/edit_personal.tmpl.php | 53 + .../edit_profile/edit_position.tmpl.php | 51 + .../edit_profile/edit_representation.tmpl.php | 43 + .../edit_profile/edit_websites.tmpl.php | 29 + themes/default/social/friend_list.tmpl.php | 35 + themes/default/social/index_public.tmpl.php | 73 + .../social/individual_application.tmpl.php | 20 + themes/default/social/notifications.tmpl.php | 72 + .../default/social/oauth/authorize.tmpl.php | 15 + themes/default/social/oauth/footer.tmpl.php | 10 + themes/default/social/oauth/header.tmpl.php | 70 + .../default/social/profile_picture.html.php | 216 + .../social/settings/account_settings.tmpl.php | 10 + .../settings/application_settings.tmpl.php | 27 + .../social/settings/privacy_settings.tmpl.php | 144 + .../social/settings/settings_menu.tmpl.php | 6 + themes/default/social/sgroup_create.tmpl.php | 38 + themes/default/social/sgroup_edit.tmpl.php | 93 + themes/default/social/sgroup_invite.tmpl.php | 46 + themes/default/social/sgroup_list.tmpl.php | 61 + themes/default/social/sgroup_search.tmpl.php | 42 + themes/default/social/sgroup_view.tmpl.php | 135 + themes/default/social/sgroups.tmpl.php | 40 + themes/default/social/sprofile.tmpl.php | 299 + .../default/social/tiny_applications.tmpl.php | 37 + themes/default/social/tiny_sgroups.tmpl.php | 36 + themes/default/styles.css | 2448 +++ themes/default/test_questions/footer.tmpl.php | 1 + themes/default/test_questions/header.tmpl.php | 18 + themes/default/test_questions/likert.tmpl.php | 12 + .../test_questions/likert_qti_1p2.tmpl.php | 48 + .../test_questions/likert_qti_2p1.tmpl.php | 24 + .../test_questions/likert_result.tmpl.php | 16 + .../test_questions/likert_stats.tmpl.php | 28 + themes/default/test_questions/long.tmpl.php | 15 + .../test_questions/long_qti_1p2.tmpl.php | 43 + .../test_questions/long_qti_2p1.tmpl.php | 31 + .../test_questions/long_result.tmpl.php | 3 + .../test_questions/long_stats.tmpl.php | 21 + .../test_questions/manifest_qti_1p2.tmpl.php | 28 + .../test_questions/manifest_qti_2p1.tmpl.php | 29 + .../default/test_questions/matching.tmpl.php | 28 + .../test_questions/matching_qti_1p2.tmpl.php | 71 + .../test_questions/matching_qti_2p1.tmpl.php | 54 + .../test_questions/matching_result.tmpl.php | 37 + .../test_questions/matching_stats.tmpl.php | 23 + .../test_questions/matchingdd.tmpl.php | 9 + .../matchingdd_qti_1p2.tmpl.php | 1 + .../matchingdd_qti_2p1.tmpl.php | 1 + .../test_questions/matchingdd_result.tmpl.php | 1 + .../test_questions/matchingdd_stats.tmpl.php | 1 + .../test_questions/multianswer.tmpl.php | 10 + .../multianswer_qti_1p2.tmpl.php | 65 + .../multianswer_qti_2p1.tmpl.php | 46 + .../multianswer_result.tmpl.php | 1 + .../test_questions/multianswer_stats.tmpl.php | 1 + .../test_questions/multichoice.tmpl.php | 13 + .../multichoice_qti_1p2.tmpl.php | 65 + .../multichoice_qti_2p1.tmpl.php | 39 + .../multichoice_result.tmpl.php | 23 + .../test_questions/multichoice_stats.tmpl.php | 30 + .../default/test_questions/ordering.tmpl.php | 14 + .../test_questions/ordering_qti_1p2.tmpl.php | 57 + .../test_questions/ordering_qti_2p1.tmpl.php | 37 + .../test_questions/ordering_result.tmpl.php | 14 + .../test_questions/ordering_stats.tmpl.php | 23 + .../default/test_questions/truefalse.tmpl.php | 7 + .../test_questions/truefalse_qti_1p2.tmpl.php | 74 + .../test_questions/truefalse_qti_2p1.tmpl.php | 38 + .../test_questions/truefalse_result.tmpl.php | 36 + .../test_questions/truefalse_stats.tmpl.php | 31 + .../default/test_questions/wrapper.tmpl.php | 15 + themes/default/theme.cfg.php | 44 + themes/default/theme_info.xml | 12 + themes/default/tile_search/index.tmpl.php | 118 + themes/default/users/browse.tmpl.php | 124 + themes/default/users/email_change.tmpl.php | 39 + themes/default/users/index.tmpl.php | 143 + themes/default/users/password_change.tmpl.php | 66 + .../default/users/pref_wizard/index.tmpl.php | 73 + .../users/pref_wizard/initialize.tmpl.php | 47 + themes/default/users/preferences.tmpl.php | 279 + themes/default/users/profile.tmpl.php | 132 + themes/default/warningmessage.tmpl.php | 20 + themes/default15/confirmmessage.tmpl.php | 32 + themes/default15/content.tmpl.php | 37 + themes/default15/errormessage.tmpl.php | 21 + themes/default15/feedbackmessage.tmpl.php | 20 + themes/default15/forms.css | 124 + themes/default15/ie_styles.css | 26 + themes/default15/images/arrow_ltr.gif | Bin 0 -> 88 bytes themes/default15/images/back.gif | Bin 0 -> 331 bytes themes/default15/images/error-large.gif | Bin 0 -> 234 bytes themes/default15/images/guide.gif | Bin 0 -> 122 bytes themes/default15/images/home-acollab.png | Bin 0 -> 1911 bytes themes/default15/images/home-blogs.png | Bin 0 -> 1790 bytes themes/default15/images/home-chat.png | Bin 0 -> 1363 bytes themes/default15/images/home-directory.png | Bin 0 -> 1061 bytes .../default15/images/home-export_content.png | Bin 0 -> 2762 bytes themes/default15/images/home-faq.png | Bin 0 -> 727 bytes themes/default15/images/home-file_storage.png | Bin 0 -> 730 bytes themes/default15/images/home-forums.png | Bin 0 -> 2086 bytes themes/default15/images/home-glossary.png | Bin 0 -> 2502 bytes themes/default15/images/home-links.png | Bin 0 -> 2504 bytes themes/default15/images/home-polls.png | Bin 0 -> 904 bytes themes/default15/images/home-reading_list.png | Bin 0 -> 1606 bytes themes/default15/images/home-site_map.png | Bin 0 -> 1598 bytes themes/default15/images/home-tests.png | Bin 0 -> 2307 bytes themes/default15/images/home-tile_search.png | Bin 0 -> 2079 bytes themes/default15/images/home-tracker.png | Bin 0 -> 1154 bytes themes/default15/images/instructor.gif | Bin 0 -> 886 bytes themes/default15/images/mswitch_minus.gif | Bin 0 -> 119 bytes themes/default15/images/mswitch_plus.gif | Bin 0 -> 124 bytes themes/default15/images/next.gif | Bin 0 -> 904 bytes themes/default15/images/pen.gif | Bin 0 -> 84 bytes themes/default15/images/pen2.gif | Bin 0 -> 85 bytes themes/default15/images/pencils.jpg | Bin 0 -> 14565 bytes themes/default15/images/previous.gif | Bin 0 -> 901 bytes themes/default15/images/resume.gif | Bin 0 -> 919 bytes themes/default15/images/side_arrow.gif | Bin 0 -> 58 bytes themes/default15/images/sort.gif | Bin 0 -> 64 bytes themes/default15/images/tree/index.html | 0 .../default15/images/tree/tree_collapse.gif | Bin 0 -> 115 bytes .../default15/images/tree/tree_disabled.gif | Bin 0 -> 116 bytes themes/default15/images/tree/tree_end.gif | Bin 0 -> 107 bytes themes/default15/images/tree/tree_expand.gif | Bin 0 -> 117 bytes .../default15/images/tree/tree_horizontal.gif | Bin 0 -> 105 bytes themes/default15/images/tree/tree_space.gif | Bin 0 -> 88 bytes themes/default15/images/tree/tree_split.gif | Bin 0 -> 110 bytes .../default15/images/tree/tree_vertline.gif | Bin 0 -> 108 bytes themes/default15/images/user-star.gif | Bin 0 -> 319 bytes themes/default15/images/user.gif | Bin 0 -> 918 bytes themes/default15/include/box.tmpl.php | 7 + themes/default15/include/fm_footer.tmpl.php | 7 + themes/default15/include/fm_header.tmpl.php | 50 + themes/default15/include/footer.tmpl.php | 39 + themes/default15/include/header.tmpl.php | 231 + themes/default15/index.tmpl.php | 45 + themes/default15/infomessage.tmpl.php | 18 + themes/default15/password_change.tmpl.php | 62 + .../password_reminder_feedback.tmpl.php | 6 + themes/default15/print.css | 13 + themes/default15/profile.tmpl.php | 65 + themes/default15/readme.txt | 6 + themes/default15/rtl.css | 14 + themes/default15/screenshot.gif | Bin 0 -> 8496 bytes themes/default15/styles.css | 1722 ++ themes/default15/theme.cfg.php | 42 + themes/default15/warningmessage.tmpl.php | 20 + themes/default16/forms.css | 66 + themes/default16/ie_styles.css | 31 + themes/default16/images/Thumbs.db | Bin 0 -> 6656 bytes themes/default16/images/arrow_ltr.gif | Bin 0 -> 120 bytes themes/default16/images/back.gif | Bin 0 -> 160 bytes themes/default16/images/continue.gif | Bin 0 -> 191 bytes themes/default16/images/guide.gif | Bin 0 -> 122 bytes themes/default16/images/linkOpaque.gif | Bin 0 -> 81 bytes themes/default16/images/linkTransparent.gif | Bin 0 -> 81 bytes themes/default16/images/mswitch_minus.gif | Bin 0 -> 119 bytes themes/default16/images/mswitch_plus.gif | Bin 0 -> 124 bytes themes/default16/images/newsitem_icon.gif | Bin 0 -> 97 bytes themes/default16/images/next.gif | Bin 0 -> 152 bytes themes/default16/images/pencil_bottom.gif | Bin 0 -> 2307 bytes themes/default16/images/pencil_top.gif | Bin 0 -> 26834 bytes themes/default16/images/pencils.gif | Bin 0 -> 6661 bytes themes/default16/images/previous.gif | Bin 0 -> 150 bytes themes/default16/images/resume.gif | Bin 0 -> 171 bytes themes/default16/images/side_arrow.gif | Bin 0 -> 117 bytes themes/default16/images/sort.gif | Bin 0 -> 80 bytes themes/default16/images/tl_corner.gif | Bin 0 -> 107 bytes themes/default16/images/top.gif | Bin 0 -> 176 bytes themes/default16/images/tree/index.html | 0 .../default16/images/tree/tree_collapse.gif | Bin 0 -> 115 bytes .../default16/images/tree/tree_disabled.gif | Bin 0 -> 116 bytes themes/default16/images/tree/tree_end.gif | Bin 0 -> 107 bytes themes/default16/images/tree/tree_expand.gif | Bin 0 -> 117 bytes .../default16/images/tree/tree_horizontal.gif | Bin 0 -> 105 bytes themes/default16/images/tree/tree_space.gif | Bin 0 -> 88 bytes themes/default16/images/tree/tree_split.gif | Bin 0 -> 110 bytes .../default16/images/tree/tree_vertline.gif | Bin 0 -> 108 bytes themes/default16/images/user-star.gif | Bin 0 -> 155 bytes themes/default16/images/user.gif | Bin 0 -> 115 bytes themes/default16/include/fm_footer.tmpl.php | 2 + themes/default16/include/fm_header.tmpl.php | 41 + themes/default16/include/footer.tmpl.php | 33 + themes/default16/include/forms.css | 75 + themes/default16/include/header.tmpl.php | 264 + themes/default16/print.css | 13 + themes/default16/readme.txt | 9 + themes/default16/rtl.css | 16 + themes/default16/screenshot.gif | Bin 0 -> 11157 bytes themes/default16/styles.css | 2057 +++ themes/default16/theme.cfg.php | 43 + themes/default16/theme_info.xml | 12 + themes/default_classic/ie_styles.css | 23 + themes/default_classic/images/guide.gif | Bin 0 -> 122 bytes .../default_classic/images/mswitch_minus.gif | Bin 0 -> 119 bytes .../default_classic/images/mswitch_plus.gif | Bin 0 -> 124 bytes themes/default_classic/images/tree/index.html | 0 .../images/tree/tree_collapse.gif | Bin 0 -> 115 bytes .../images/tree/tree_disabled.gif | Bin 0 -> 116 bytes .../default_classic/images/tree/tree_end.gif | Bin 0 -> 107 bytes .../images/tree/tree_expand.gif | Bin 0 -> 117 bytes .../images/tree/tree_horizontal.gif | Bin 0 -> 105 bytes .../images/tree/tree_space.gif | Bin 0 -> 88 bytes .../images/tree/tree_split.gif | Bin 0 -> 110 bytes .../images/tree/tree_vertline.gif | Bin 0 -> 108 bytes themes/default_classic/images/user-star.gif | Bin 0 -> 319 bytes .../default_classic/include/footer.tmpl.php | 34 + .../default_classic/include/header.tmpl.php | 206 + themes/default_classic/print.css | 13 + themes/default_classic/rtl.css | 14 + themes/default_classic/screenshot.gif | Bin 0 -> 9001 bytes themes/default_classic/styles.css | 1640 ++ themes/default_classic/theme.cfg.php | 41 + themes/fluid/forms.css | 66 + themes/fluid/ie_styles.css | 54 + themes/fluid/images/arrow_left.png | Bin 0 -> 345 bytes themes/fluid/images/continue.gif | Bin 0 -> 191 bytes themes/fluid/images/layers.png | Bin 0 -> 597 bytes themes/fluid/images/mswitch_minus.gif | Bin 0 -> 119 bytes themes/fluid/images/mswitch_plus.gif | Bin 0 -> 124 bytes themes/fluid/images/next.gif | Bin 0 -> 152 bytes themes/fluid/images/previous.gif | Bin 0 -> 150 bytes themes/fluid/images/resume.gif | Bin 0 -> 171 bytes themes/fluid/images/top.gif | Bin 0 -> 176 bytes themes/fluid/images/tree/index.html | 0 themes/fluid/images/tree/tree_collapse.gif | Bin 0 -> 115 bytes themes/fluid/images/tree/tree_disabled.gif | Bin 0 -> 116 bytes themes/fluid/images/tree/tree_end.gif | Bin 0 -> 107 bytes themes/fluid/images/tree/tree_expand.gif | Bin 0 -> 117 bytes themes/fluid/images/tree/tree_horizontal.gif | Bin 0 -> 105 bytes themes/fluid/images/tree/tree_space.gif | Bin 0 -> 88 bytes themes/fluid/images/tree/tree_split.gif | Bin 0 -> 110 bytes themes/fluid/images/tree/tree_vertline.gif | Bin 0 -> 108 bytes themes/fluid/include/footer.tmpl.php | 35 + themes/fluid/include/header.tmpl.php | 284 + themes/fluid/print.css | 13 + themes/fluid/save_state.php | 14 + themes/fluid/screenshot.gif | Bin 0 -> 7350 bytes themes/fluid/styles.css | 1922 +++ themes/fluid/theme.cfg.php | 43 + themes/fluid/theme_info.xml | 10 + themes/greenmin/content.tmpl.php | 36 + themes/greenmin/forms.css | 73 + themes/greenmin/ie_styles.css | 27 + themes/greenmin/images/Thumbs.db | Bin 0 -> 6656 bytes themes/greenmin/images/arrow_ltr.gif | Bin 0 -> 88 bytes themes/greenmin/images/back.gif | Bin 0 -> 331 bytes themes/greenmin/images/continue.gif | Bin 0 -> 410 bytes themes/greenmin/images/linkOpaque.gif | Bin 0 -> 101 bytes themes/greenmin/images/linkTransparent.gif | Bin 0 -> 102 bytes themes/greenmin/images/mswitch_minus.gif | Bin 0 -> 119 bytes themes/greenmin/images/mswitch_plus.gif | Bin 0 -> 124 bytes themes/greenmin/images/newsitem_icon.gif | Bin 0 -> 951 bytes themes/greenmin/images/next.gif | Bin 0 -> 904 bytes themes/greenmin/images/previous.gif | Bin 0 -> 901 bytes themes/greenmin/images/resume.gif | Bin 0 -> 919 bytes themes/greenmin/images/side_arrow.gif | Bin 0 -> 58 bytes themes/greenmin/images/sort.gif | Bin 0 -> 64 bytes themes/greenmin/images/tree/index.html | 0 themes/greenmin/images/tree/tree_collapse.gif | Bin 0 -> 115 bytes themes/greenmin/images/tree/tree_disabled.gif | Bin 0 -> 116 bytes themes/greenmin/images/tree/tree_end.gif | Bin 0 -> 107 bytes themes/greenmin/images/tree/tree_expand.gif | Bin 0 -> 117 bytes .../greenmin/images/tree/tree_horizontal.gif | Bin 0 -> 105 bytes themes/greenmin/images/tree/tree_space.gif | Bin 0 -> 88 bytes themes/greenmin/images/tree/tree_split.gif | Bin 0 -> 110 bytes themes/greenmin/images/tree/tree_vertline.gif | Bin 0 -> 108 bytes themes/greenmin/images/user-star.gif | Bin 0 -> 319 bytes themes/greenmin/images/user.gif | Bin 0 -> 882 bytes themes/greenmin/include/box.tmpl.php | 12 + themes/greenmin/include/footer.tmpl.php | 26 + themes/greenmin/include/header.tmpl.php | 236 + themes/greenmin/include/side_menu.tmpl.php | 8 + themes/greenmin/print.css | 13 + themes/greenmin/readme.txt | 9 + themes/greenmin/screenshot.gif | Bin 0 -> 4157 bytes themes/greenmin/styles.css | 1804 ++ themes/greenmin/theme.cfg.php | 43 + themes/greenmin/theme_info.xml | 12 + themes/mobile/forms.css | 95 + themes/mobile/ie_styles.css | 84 + themes/mobile/include/header.tmpl.php | 321 + themes/mobile/print.css | 13 + themes/mobile/readme.txt | 9 + themes/mobile/rtl.css | 16 + themes/mobile/screenshot.gif | Bin 0 -> 11157 bytes themes/mobile/styles.css | 1834 ++ themes/mobile/theme.cfg.php | 44 + themes/mobile/theme_info.xml | 12 + themes/themes_readme.txt | 87 + tools/index.php | 41 + tools/prog.php | 102 + users/alt_to_audio.inc.php | 70 + users/alt_to_text.inc.php | 69 + users/alt_to_visual.inc.php | 72 + users/atutor_settings.inc.php | 204 + users/browse.php | 24 + users/contact_instructor.php | 174 + users/content_settings.inc.php | 290 + users/control_settings.inc.php | 63 + users/display_settings.inc.php | 137 + users/email_change.php | 129 + users/index.php | 116 + users/password_change.php | 88 + users/pref_wizard/index.php | 140 + users/preferences.php | 80 + users/private_enroll.php | 126 + users/profile.php | 151 + users/prog_unused.php | 100 + users/remove_course.php | 56 + users/search.php | 28 + users/tool_settings.inc.php | 96 + 2521 files changed, 272383 insertions(+) create mode 100644 .htaccess create mode 100644 404.php create mode 100644 about.php create mode 100644 acl.php create mode 100644 admin/config_edit.php create mode 100644 admin/config_template.php create mode 100644 admin/cron.php create mode 100644 admin/cron_config.php create mode 100644 admin/error_logging.php create mode 100644 admin/error_logging_bundle.php create mode 100644 admin/error_logging_details.php create mode 100644 admin/error_logging_reset.php create mode 100644 admin/error_logging_view.php create mode 100644 admin/fix_content.php create mode 100644 admin/index.php create mode 100644 bounce.php create mode 100644 browse.php create mode 100644 confirm.php create mode 100644 contact_instructor.php create mode 100644 content.php create mode 100644 documentation/add_note.php create mode 100644 documentation/admin/administrators.php create mode 100644 documentation/admin/auto_enroll.php create mode 100644 documentation/admin/backups.php create mode 100644 documentation/admin/categories.php create mode 100644 documentation/admin/configuration.php create mode 100644 documentation/admin/courses.php create mode 100644 documentation/admin/create_patches.php create mode 100644 documentation/admin/creating_courses.php create mode 100644 documentation/admin/creating_themes.php create mode 100644 documentation/admin/cron_setup.php create mode 100644 documentation/admin/default_preferences.php create mode 100644 documentation/admin/default_side_menu.php create mode 100644 documentation/admin/default_student_tools.php create mode 100644 documentation/admin/email_users.php create mode 100644 documentation/admin/en/index.php create mode 100644 documentation/admin/enrollment.php create mode 100644 documentation/admin/enrollment_privileges.php create mode 100644 documentation/admin/error_logging.php create mode 100644 documentation/admin/feeds.php create mode 100644 documentation/admin/forums.php create mode 100644 documentation/admin/fr/index.php create mode 100644 documentation/admin/google_key.php create mode 100644 documentation/admin/importing_themes.php create mode 100644 documentation/admin/index.php create mode 100644 documentation/admin/installation.php create mode 100644 documentation/admin/instructor_requests.php create mode 100644 documentation/admin/introduction.php create mode 100644 documentation/admin/languages.php create mode 100644 documentation/admin/link-out.gif create mode 100644 documentation/admin/managing_existing_themes.php create mode 100644 documentation/admin/master_student_list.php create mode 100644 documentation/admin/modules.php create mode 100644 documentation/admin/my_account.php create mode 100644 documentation/admin/new_installation.php create mode 100644 documentation/admin/pages.inc.php create mode 100644 documentation/admin/patcher.php create mode 100644 documentation/admin/requirements_recommendations.php create mode 100644 documentation/admin/styles.css create mode 100644 documentation/admin/system_preferences.php create mode 100644 documentation/admin/themes.php create mode 100644 documentation/admin/troubleshooting.php create mode 100644 documentation/admin/upgrading.php create mode 100644 documentation/admin/users.php create mode 100644 documentation/approve_note.php create mode 100644 documentation/common/body_footer.inc.php create mode 100644 documentation/common/body_header.inc.php create mode 100644 documentation/common/folder.gif create mode 100644 documentation/common/fr/text.php create mode 100644 documentation/common/frame_header.php create mode 100644 documentation/common/frame_toc.php create mode 100644 documentation/common/link-out.gif create mode 100644 documentation/common/paper.gif create mode 100644 documentation/common/print.php create mode 100644 documentation/common/search.php create mode 100644 documentation/common/styles.css create mode 100644 documentation/common/text.php create mode 100644 documentation/common/vitals.inc.php create mode 100644 documentation/config.inc.php create mode 100644 documentation/delete_note.php create mode 100644 documentation/developer/database.gif create mode 100644 documentation/developer/guidelines.html create mode 100644 documentation/developer/modules.html create mode 100644 documentation/developer/styles.css create mode 100644 documentation/developer/themes.html create mode 100644 documentation/general/browse_courses.php create mode 100644 documentation/general/create_course.php create mode 100644 documentation/general/en/index.php create mode 100644 documentation/general/export_content.php create mode 100644 documentation/general/file_storage.php create mode 100644 documentation/general/fr/index.php create mode 100644 documentation/general/inbox.php create mode 100644 documentation/general/index.php create mode 100644 documentation/general/inside_course.php create mode 100644 documentation/general/introduction.php create mode 100644 documentation/general/login.php create mode 100644 documentation/general/my_contacts.php create mode 100644 documentation/general/my_courses.php create mode 100644 documentation/general/my_gadgets.php create mode 100644 documentation/general/my_groups.php create mode 100644 documentation/general/my_network.php create mode 100644 documentation/general/my_profile.php create mode 100644 documentation/general/my_settings.php create mode 100644 documentation/general/my_start_page.php create mode 100644 documentation/general/pa_albums.php create mode 100644 documentation/general/pa_comments.php create mode 100644 documentation/general/pa_index.php create mode 100644 documentation/general/pa_photo.php create mode 100644 documentation/general/packages.php create mode 100644 documentation/general/pages.inc.php create mode 100644 documentation/general/password_reminder.php create mode 100644 documentation/general/preferences.php create mode 100644 documentation/general/profile.php create mode 100644 documentation/general/register.php create mode 100644 documentation/general/tile.php create mode 100644 documentation/index.php create mode 100644 documentation/index/en/index.php create mode 100644 documentation/index/fr/index.php create mode 100644 documentation/index/index.php create mode 100644 documentation/index_list.php create mode 100644 documentation/instructor/accessibility.php create mode 100644 documentation/instructor/add_questions.php create mode 100644 documentation/instructor/announcements.php create mode 100644 documentation/instructor/arrange_content.php create mode 100644 documentation/instructor/assignments.php create mode 100644 documentation/instructor/authenticated_access.php create mode 100644 documentation/instructor/backups.php create mode 100644 documentation/instructor/chat.php create mode 100644 documentation/instructor/content.html create mode 100644 documentation/instructor/content.php create mode 100644 documentation/instructor/content_alternatives.php create mode 100644 documentation/instructor/content_edit.php create mode 100644 documentation/instructor/content_packages.php create mode 100644 documentation/instructor/content_preview.php create mode 100644 documentation/instructor/content_properties.php create mode 100644 documentation/instructor/content_tests.php create mode 100644 documentation/instructor/content_usage.php create mode 100644 documentation/instructor/course_email.php create mode 100644 documentation/instructor/creating_courses.php create mode 100644 documentation/instructor/creating_editing_content.php create mode 100644 documentation/instructor/creating_editing_content_folder.php create mode 100644 documentation/instructor/creating_questions.php create mode 100644 documentation/instructor/creating_restoring.php create mode 100644 documentation/instructor/creating_tests_surveys.php create mode 100644 documentation/instructor/delete_course.php create mode 100644 documentation/instructor/downloading_uploading.php create mode 100644 documentation/instructor/edit_delete_tests.php create mode 100644 documentation/instructor/editing_deleting.php create mode 100644 documentation/instructor/en/index.php create mode 100644 documentation/instructor/enrollment.php create mode 100644 documentation/instructor/enrollment_alumni.php create mode 100644 documentation/instructor/enrollment_course_list.php create mode 100644 documentation/instructor/enrollment_privileges.php create mode 100644 documentation/instructor/extracting_zip_archives.php create mode 100644 documentation/instructor/faq.php create mode 100644 documentation/instructor/feeds.php create mode 100644 documentation/instructor/fha_student_tools.php create mode 100644 documentation/instructor/file_manager.php create mode 100644 documentation/instructor/forum_export.php create mode 100644 documentation/instructor/forums.php create mode 100644 documentation/instructor/fr/index.php create mode 100644 documentation/instructor/glossary.php create mode 100644 documentation/instructor/glossary_terms.php create mode 100644 documentation/instructor/gradebook.php create mode 100644 documentation/instructor/gradebook_add.php create mode 100644 documentation/instructor/gradebook_edit_marks.php create mode 100644 documentation/instructor/gradebook_external_marks.php create mode 100644 documentation/instructor/gradebook_scales.php create mode 100644 documentation/instructor/gradebook_update.php create mode 100644 documentation/instructor/groups.php create mode 100644 documentation/instructor/index.php create mode 100644 documentation/instructor/introduction.php create mode 100644 documentation/instructor/links.php create mode 100644 documentation/instructor/managing_files_folders.php create mode 100644 documentation/instructor/managing_posts.php create mode 100644 documentation/instructor/managing_threads.php create mode 100644 documentation/instructor/pages.inc.php create mode 100644 documentation/instructor/polls.php create mode 100644 documentation/instructor/preview.php create mode 100644 documentation/instructor/properties.php create mode 100644 documentation/instructor/question_categories.php create mode 100644 documentation/instructor/question_database.php create mode 100644 documentation/instructor/reading_list.php create mode 100644 documentation/instructor/scorm_packages.php create mode 100644 documentation/instructor/side_menu.php create mode 100644 documentation/instructor/statistics.php create mode 100644 documentation/instructor/student_submissions.php create mode 100644 documentation/instructor/student_tools.php create mode 100644 documentation/instructor/test_statistics.php create mode 100644 documentation/instructor/tests_surveys.php create mode 100644 documentation/instructor/tile_repository.php create mode 100644 documentation/instructor/web_search.php create mode 100644 documentation/link-out.gif create mode 100644 documentation/styles.css create mode 100644 enroll.php create mode 100644 exestyles.css create mode 100644 favicon.ico create mode 100644 get.php create mode 100644 get_acheck.php create mode 100644 get_course_icon.php create mode 100644 get_noid.php create mode 100644 get_profile_img.php create mode 100644 get_rss.php create mode 100644 go.php create mode 100644 headstuff.php create mode 100644 help/accessibility.php create mode 100644 help/contact_support.php create mode 100644 help/index.php create mode 100644 images/AT_Logo_1.png create mode 100644 images/AT_Logo_1.psd create mode 100644 images/AT_Logo_1_sm.png create mode 100644 images/AT_Logo_1_sm.psd create mode 100644 images/achecker.png create mode 100644 images/achecker_disabled.png create mode 100644 images/after.gif create mode 100644 images/application_get.png create mode 100644 images/archive.gif create mode 100644 images/arrow-mini-down.png create mode 100644 images/arrow-mini-left.png create mode 100644 images/arrow-mini-right.png create mode 100644 images/arrow-mini-up.png create mode 100644 images/arrow_ltr.gif create mode 100644 images/arrowicon.gif create mode 100644 images/at-logo.gif create mode 100644 images/at-logo.v.3.gif create mode 100644 images/atutor_ico.png create mode 100644 images/atutor_ico.psd create mode 100644 images/atutor_logo2.png create mode 100644 images/audio_alternative.png create mode 100644 images/bad.gif create mode 100644 images/before.gif create mode 100644 images/blue.gif create mode 100644 images/box.png create mode 100644 images/calendar.gif create mode 100644 images/changes_bullet.gif create mode 100644 images/check.gif create mode 100644 images/checkbox_check.gif create mode 100644 images/checkbox_empty.gif create mode 100644 images/checkmark.gif create mode 100644 images/child_of.gif create mode 100644 images/clr.gif create mode 100644 images/content_pkg.gif create mode 100644 images/courses/3dgraph.jpg create mode 100644 images/courses/anotomy.jpg create mode 100644 images/courses/art_supplies.jpg create mode 100644 images/courses/astronaut.jpg create mode 100644 images/courses/bar_graph.jpg create mode 100644 images/courses/books.jpg create mode 100644 images/courses/botany.jpg create mode 100644 images/courses/brain2.jpg create mode 100644 images/courses/business.jpg create mode 100644 images/courses/business_service.jpg create mode 100644 images/courses/caduceus.jpg create mode 100644 images/courses/car_wireframe.jpg create mode 100644 images/courses/cell-anatomy.jpg create mode 100644 images/courses/chemistry.jpg create mode 100644 images/courses/columbus.jpg create mode 100644 images/courses/disabled_sign.gif create mode 100644 images/courses/drafting.jpg create mode 100644 images/courses/engine.jpg create mode 100644 images/courses/feather-pen.jpg create mode 100644 images/courses/fire_helmut.jpg create mode 100644 images/courses/fractal.jpg create mode 100644 images/courses/head_wireframe.jpg create mode 100644 images/courses/helix.jpg create mode 100644 images/courses/helmet.jpg create mode 100644 images/courses/light-bulb.jpg create mode 100644 images/courses/map.jpg create mode 100644 images/courses/microchip.jpg create mode 100644 images/courses/microscope.jpg create mode 100644 images/courses/molecule.jpg create mode 100644 images/courses/nav_wheel.jpg create mode 100644 images/courses/normal_surfaces.jpg create mode 100644 images/courses/pedal.jpg create mode 100644 images/courses/pharao.jpg create mode 100644 images/courses/pharmacy.jpg create mode 100644 images/courses/planet-earth.jpg create mode 100644 images/courses/police_hat.jpg create mode 100644 images/courses/recycling.jpg create mode 100644 images/courses/skull.jpg create mode 100644 images/courses/skull_wireframe.jpg create mode 100644 images/courses/sl_logo.gif create mode 100644 images/courses/snail.jpg create mode 100644 images/courses/sphere.jpg create mode 100644 images/courses/sphinx.jpg create mode 100644 images/courses/toolbox.jpg create mode 100644 images/courses/triangle.jpg create mode 100644 images/courses/two_roads.jpg create mode 100644 images/custom_head.png create mode 100644 images/custom_head_disabled.png create mode 100644 images/detail_view.png create mode 100644 images/details_il.png create mode 100644 images/details_ir.png create mode 100644 images/details_l.png create mode 100644 images/details_r.png create mode 100644 images/donate.gif create mode 100644 images/down.png create mode 100644 images/download.png create mode 100644 images/edit.gif create mode 100644 images/eye-mini-close.png create mode 100644 images/eye-mini-open.png create mode 100644 images/feedback.gif create mode 100644 images/file-manager.png create mode 100644 images/file-manager_disabled.png create mode 100644 images/file.gif create mode 100644 images/file_types/audio.gif create mode 100644 images/file_types/binary.gif create mode 100644 images/file_types/csv.gif create mode 100644 images/file_types/doc.gif create mode 100644 images/file_types/dvi.gif create mode 100644 images/file_types/generic.gif create mode 100644 images/file_types/image.gif create mode 100644 images/file_types/mdb.gif create mode 100644 images/file_types/mpp.gif create mode 100644 images/file_types/ood.gif create mode 100644 images/file_types/oop.gif create mode 100644 images/file_types/oos.gif create mode 100644 images/file_types/oot.gif create mode 100644 images/file_types/pdf.gif create mode 100644 images/file_types/ppt.gif create mode 100644 images/file_types/ps.gif create mode 100644 images/file_types/psd.gif create mode 100644 images/file_types/qt.gif create mode 100644 images/file_types/rtf.gif create mode 100644 images/file_types/sql.gif create mode 100644 images/file_types/sql2.gif create mode 100644 images/file_types/src.gif create mode 100644 images/file_types/swf.gif create mode 100644 images/file_types/treeview.gif create mode 100644 images/file_types/txt.gif create mode 100644 images/file_types/video.gif create mode 100644 images/file_types/viewlet.gif create mode 100644 images/file_types/vsd.gif create mode 100644 images/file_types/xls.gif create mode 100644 images/file_types/xml.gif create mode 100644 images/file_types/zip.gif create mode 100644 images/flag_blue.png create mode 100644 images/folder.gif create mode 100644 images/folder_new.gif create mode 100644 images/folder_new_sibling.gif create mode 100644 images/folder_new_sub.gif create mode 100644 images/forum/19.gif create mode 100644 images/forum/27.gif create mode 100644 images/forum/3.gif create mode 100644 images/forum/30.gif create mode 100644 images/forum/51.gif create mode 100644 images/forum/52.gif create mode 100644 images/forum/54.gif create mode 100644 images/forum/55.gif create mode 100644 images/forum/56.gif create mode 100644 images/forum/57.gif create mode 100644 images/forum/58.gif create mode 100644 images/forum/frown.gif create mode 100644 images/forum/happy.gif create mode 100644 images/forum/index.html create mode 100644 images/forum/move.gif create mode 100644 images/forum/ohwell.gif create mode 100644 images/forum/smile.gif create mode 100644 images/forum/sticky.gif create mode 100644 images/forum/tongue.gif create mode 100644 images/forum/topic_stick.gif create mode 100644 images/forum/wink.gif create mode 100644 images/glossary.gif create mode 100644 images/glossary_small.gif create mode 100644 images/graph.gif create mode 100644 images/grey.gif create mode 100644 images/help.png create mode 100644 images/help3.gif create mode 100644 images/help4.gif create mode 100644 images/hidemenu.gif create mode 100644 images/home-acollab.png create mode 100644 images/home-blogs.png create mode 100644 images/home-blogs_sm.png create mode 100644 images/home-chat.png create mode 100644 images/home-chat_sm.png create mode 100644 images/home-directory.png create mode 100644 images/home-directory_sm.png create mode 100644 images/home-export_content.png create mode 100644 images/home-faq.png create mode 100644 images/home-faq_sm.png create mode 100644 images/home-file_storage.png create mode 100644 images/home-file_storage_sm.png create mode 100644 images/home-forums.png create mode 100644 images/home-forums_sm.png create mode 100644 images/home-glossary.png create mode 100644 images/home-glossary_sm.png create mode 100644 images/home-gradebook_sm.png create mode 100644 images/home-links.png create mode 100644 images/home-links_sm.png create mode 100644 images/home-polls.png create mode 100644 images/home-polls_sm.png create mode 100644 images/home-reading_list.png create mode 100644 images/home-reading_list_sm.png create mode 100644 images/home-site_map.png create mode 100644 images/home-tests.png create mode 100644 images/home-tests_sm.png create mode 100644 images/home-tile_search.png create mode 100644 images/home-tracker.png create mode 100644 images/home-tracker_sm.png create mode 100644 images/icon-zip.gif create mode 100644 images/icon.png create mode 100644 images/icon_delete.gif create mode 100644 images/icon_minipost.gif create mode 100644 images/icon_view.png create mode 100644 images/index.html create mode 100644 images/lock.gif create mode 100644 images/logo.gif create mode 100644 images/medit.gif create mode 100644 images/mlock.gif create mode 100644 images/move-down.gif create mode 100644 images/move-up.gif create mode 100644 images/mswitch_minus.gif create mode 100644 images/mswitch_plus.gif create mode 100644 images/new.gif create mode 100644 images/page_add.gif create mode 100644 images/page_add_sibling.gif create mode 100644 images/page_add_sub.gif create mode 100644 images/page_delete.gif create mode 100644 images/paste.png create mode 100644 images/paste_disabled.png create mode 100644 images/pause.png create mode 100644 images/pen.gif create mode 100644 images/pen2.gif create mode 100644 images/pen3.gif create mode 100644 images/pin.png create mode 100644 images/preview.png create mode 100644 images/pub_default.jpg create mode 100644 images/question.gif create mode 100644 images/red.gif create mode 100644 images/rtl_tree/index.html create mode 100644 images/rtl_tree/tree_collapse.gif create mode 100644 images/rtl_tree/tree_end.gif create mode 100644 images/rtl_tree/tree_expand.gif create mode 100644 images/rtl_tree/tree_horizontal.gif create mode 100644 images/rtl_tree/tree_split.gif create mode 100644 images/rtl_tree/tree_vertline.gif create mode 100644 images/search.gif create mode 100644 images/showmenu.gif create mode 100644 images/sign_lang_alternative.png create mode 100644 images/star.gif create mode 100644 images/subscribe-envelope.png create mode 100644 images/text_alternative.png create mode 100644 images/toc.gif create mode 100644 images/tool_go.jpg create mode 100644 images/tools.png create mode 100644 images/topic_lock.gif create mode 100644 images/transfer.gif create mode 100644 images/tree/index.html create mode 100644 images/tree/tree_collapse.gif create mode 100644 images/tree/tree_disabled.gif create mode 100644 images/tree/tree_end.gif create mode 100644 images/tree/tree_expand.gif create mode 100644 images/tree/tree_horizontal.gif create mode 100644 images/tree/tree_space.gif create mode 100644 images/tree/tree_split.gif create mode 100644 images/tree/tree_vertline.gif create mode 100644 images/unlock.gif create mode 100644 images/unsubscribe-envelope.png create mode 100644 images/up.png create mode 100644 images/visual_alternative.png create mode 100644 images/wand.png create mode 100644 images/x.gif create mode 100644 inbox/export.php create mode 100644 inbox/index.php create mode 100644 inbox/send_message.php create mode 100644 inbox/sent_messages.php create mode 100644 include/classes/CSVExport.class.php create mode 100644 include/classes/CSVImport.class.php create mode 100644 include/classes/ContentManager.class.php create mode 100644 include/classes/ContentOutputUtils.class.php create mode 100644 include/classes/ErrorHandler/ErrorHandler.class.php create mode 100644 include/classes/Message/Message.class.php create mode 100644 include/classes/Savant2/Savant2.php create mode 100644 include/classes/Savant2/Savant2/Compiler.php create mode 100644 include/classes/Savant2/Savant2/Error.php create mode 100644 include/classes/Savant2/Savant2/Filter.php create mode 100644 include/classes/Savant2/Savant2/Plugin.php create mode 100644 include/classes/Savant2/Savant2/Savant2_Error_exception.php create mode 100644 include/classes/Savant2/Savant2/Savant2_Error_pear.php create mode 100644 include/classes/Savant2/Savant2/Savant2_Error_stack.php create mode 100644 include/classes/Savant2/Savant2/Savant2_Plugin_cycle.php create mode 100644 include/classes/UrlRewrite/ContentUrl.class.php create mode 100644 include/classes/UrlRewrite/FileStorageUrl.class.php create mode 100644 include/classes/UrlRewrite/ForumsUrl.class.php create mode 100644 include/classes/UrlRewrite/GlossaryUrl.class.php create mode 100644 include/classes/UrlRewrite/TestsUrl.class.php create mode 100644 include/classes/UrlRewrite/UrlParser.class.php create mode 100644 include/classes/UrlRewrite/UrlRewrite.class.php create mode 100644 include/classes/XML/XML_HTMLSax/HTMLSax/XML_HTMLSax_Decorators.php create mode 100644 include/classes/XML/XML_HTMLSax/HTMLSax/XML_HTMLSax_States.php create mode 100644 include/classes/XML/XML_HTMLSax/OS/Guess.php create mode 100644 include/classes/XML/XML_HTMLSax/PEAR.php create mode 100644 include/classes/XML/XML_HTMLSax/PEAR/Autoloader.php create mode 100644 include/classes/XML/XML_HTMLSax/PEAR/Common.php create mode 100644 include/classes/XML/XML_HTMLSax/PEAR/Config.php create mode 100644 include/classes/XML/XML_HTMLSax/PEAR/Remote.php create mode 100644 include/classes/XML/XML_HTMLSax/System.php create mode 100644 include/classes/XML/XML_HTMLSax/XML_HTMLSax.php create mode 100644 include/classes/cssparser.php create mode 100644 include/classes/feedcreator.class.php create mode 100644 include/classes/nusoap.php create mode 100644 include/classes/pclzip.lib.php create mode 100644 include/classes/phpmailer/atutormailer.class.php create mode 100644 include/classes/phpmailer/class.phpmailer.php create mode 100644 include/classes/phpmailer/class.smtp.php create mode 100644 include/classes/phpmailer/phpmailer.lang-en.php create mode 100644 include/classes/sqlutility.class.php create mode 100644 include/classes/subscribe.class.php create mode 100644 include/classes/vcard.php create mode 100644 include/classes/zipfile.class.php create mode 100644 include/classes/zipfile.class.php.bak create mode 100644 include/footer.inc.php create mode 100644 include/header.inc.php create mode 100644 include/html/auto_enroll_courses.inc.php create mode 100644 include/html/auto_enroll_list_courses.inc.php create mode 100644 include/html/browse.inc.php create mode 100644 include/html/code_picker.inc.php create mode 100644 include/html/copyright.inc.php create mode 100644 include/html/dropdowns/menu_menu.inc.php create mode 100644 include/html/dropdowns/posts.inc.php create mode 100644 include/html/dropdowns/related_topics.inc.php create mode 100644 include/html/dropdowns/search.inc.php create mode 100644 include/html/dropdowns/users_online.inc.php create mode 100644 include/html/frameset/footer.inc.php create mode 100644 include/html/frameset/header.inc.php create mode 100644 include/html/languages.inc.php create mode 100644 include/html/release_date.inc.php create mode 100644 include/html/search.inc.php create mode 100644 include/index.html create mode 100644 include/lib/constants.inc.php create mode 100644 include/lib/html_resource_parser.inc.php create mode 100644 include/lib/json.inc.php create mode 100644 include/lib/menu_pages.php create mode 100644 include/lib/mime.inc.php create mode 100644 include/lib/mysql_connect.inc.php create mode 100644 include/lib/output.inc.php create mode 100644 include/lib/search.inc.php create mode 100644 include/lib/tinymce.inc.php create mode 100644 include/lib/tinymce_styles.css create mode 100644 include/lib/upload.php create mode 100644 include/lib/utf8.php create mode 100644 include/phpCache/ChangeLog create mode 100644 include/phpCache/LICENSE create mode 100644 include/phpCache/README create mode 100644 include/phpCache/gc.php create mode 100644 include/phpCache/phpCache.inc.php create mode 100644 include/player.swf create mode 100644 include/securimage/LICENSE.txt create mode 100644 include/securimage/README.txt create mode 100644 include/securimage/audio/0.wav create mode 100644 include/securimage/audio/1.wav create mode 100644 include/securimage/audio/2.wav create mode 100644 include/securimage/audio/3.wav create mode 100644 include/securimage/audio/4.wav create mode 100644 include/securimage/audio/5.wav create mode 100644 include/securimage/audio/6.wav create mode 100644 include/securimage/audio/7.wav create mode 100644 include/securimage/audio/8.wav create mode 100644 include/securimage/audio/9.wav create mode 100644 include/securimage/audio/A.wav create mode 100644 include/securimage/audio/B.wav create mode 100644 include/securimage/audio/C.wav create mode 100644 include/securimage/audio/D.wav create mode 100644 include/securimage/audio/E.wav create mode 100644 include/securimage/audio/F.wav create mode 100644 include/securimage/audio/G.wav create mode 100644 include/securimage/audio/H.wav create mode 100644 include/securimage/audio/I.wav create mode 100644 include/securimage/audio/J.wav create mode 100644 include/securimage/audio/K.wav create mode 100644 include/securimage/audio/L.wav create mode 100644 include/securimage/audio/M.wav create mode 100644 include/securimage/audio/N.wav create mode 100644 include/securimage/audio/O.wav create mode 100644 include/securimage/audio/P.wav create mode 100644 include/securimage/audio/Q.wav create mode 100644 include/securimage/audio/R.wav create mode 100644 include/securimage/audio/S.wav create mode 100644 include/securimage/audio/T.wav create mode 100644 include/securimage/audio/U.wav create mode 100644 include/securimage/audio/V.wav create mode 100644 include/securimage/audio/W.wav create mode 100644 include/securimage/audio/X.wav create mode 100644 include/securimage/audio/Y.wav create mode 100644 include/securimage/audio/Z.wav create mode 100644 include/securimage/elephant.ttf create mode 100644 include/securimage/gdfonts/automatic.gdf create mode 100644 include/securimage/gdfonts/bubblebath.gdf create mode 100644 include/securimage/gdfonts/caveman.gdf create mode 100644 include/securimage/gdfonts/crass.gdf create mode 100644 include/securimage/images/audio_icon.gif create mode 100644 include/securimage/images/refresh.gif create mode 100644 include/securimage/securimage.php create mode 100644 include/securimage/securimage_play.php create mode 100644 include/securimage/securimage_show.php create mode 100644 include/side_menu.inc.php create mode 100644 include/style_popup.css create mode 100644 include/unit_tests/AllTests.php create mode 100644 include/unit_tests/ContentOutputUtilsTest.php create mode 100644 include/vitals.inc.php create mode 100644 index.php create mode 100644 install/db/atutor_convert_db_to_utf8.sql create mode 100644 install/db/atutor_language_text.sql create mode 100644 install/db/atutor_schema.sql create mode 100644 install/db/atutor_upgrade_1.0_to_1.1.sql create mode 100644 install/db/atutor_upgrade_1.1_to_1.2.sql create mode 100644 install/db/atutor_upgrade_1.2_to_1.3.sql create mode 100644 install/db/atutor_upgrade_1.3.2_to_1.4.sql create mode 100644 install/db/atutor_upgrade_1.3_to_1.3.2.sql create mode 100644 install/db/atutor_upgrade_1.4.1_to_1.4.2.sql create mode 100644 install/db/atutor_upgrade_1.4.2_to_1.4.3.sql create mode 100644 install/db/atutor_upgrade_1.4.3_to_1.5.sql create mode 100644 install/db/atutor_upgrade_1.4_to_1.4.1.sql create mode 100644 install/db/atutor_upgrade_1.5.1_to_1.5.2.sql create mode 100644 install/db/atutor_upgrade_1.5.2_to_1.5.3.sql create mode 100644 install/db/atutor_upgrade_1.5.3.1_to_1.5.3.2.sql create mode 100644 install/db/atutor_upgrade_1.5.3.2_to_1.5.3.3.sql create mode 100644 install/db/atutor_upgrade_1.5.3.3_to_1.5.4.sql create mode 100644 install/db/atutor_upgrade_1.5.3_to_1.5.3.1.sql create mode 100644 install/db/atutor_upgrade_1.5.4_to_1.5.5.sql create mode 100644 install/db/atutor_upgrade_1.5.5_to_1.6.sql create mode 100644 install/db/atutor_upgrade_1.5_to_1.5.1.sql create mode 100644 install/db/atutor_upgrade_1.6.1_to_1.6.2.sql create mode 100644 install/db/atutor_upgrade_1.6.2_to_1.6.3.sql create mode 100644 install/db/atutor_upgrade_1.6.3_to_1.6.4.sql create mode 100644 install/db/atutor_upgrade_1.6.4_to_2.0.sql create mode 100644 install/db/atutor_upgrade_1.6_to_1.6.1.sql create mode 100644 install/images/bad.gif create mode 100644 install/images/feedback.gif create mode 100644 install/images/question.gif create mode 100644 install/images/warning.png create mode 100644 install/include/classes/TableConversion.class.php create mode 100644 install/include/classes/sqlutility.php create mode 100644 install/include/common.inc.php create mode 100644 install/include/config_template.php create mode 100644 install/include/footer.php create mode 100644 install/include/header.php create mode 100644 install/include/step1.php create mode 100644 install/include/step2.php create mode 100644 install/include/step3.php create mode 100644 install/include/step4.php create mode 100644 install/include/step5.php create mode 100644 install/include/step6.php create mode 100644 install/include/step7.php create mode 100644 install/include/step8.php create mode 100644 install/include/upgrade_header.php create mode 100644 install/include/ustep1.php create mode 100644 install/include/ustep2.php create mode 100644 install/include/ustep3.php create mode 100644 install/include/ustep4.php create mode 100644 install/include/ustep5.php create mode 100644 install/include/ustep6.php create mode 100644 install/include/ustep7.php create mode 100644 install/include/ustep_content_conversion.php create mode 100644 install/include/ustep_pwd_encryt.php create mode 100644 install/index.php create mode 100644 install/install.php create mode 100644 install/not_installed.php create mode 100644 install/stylesheet.css create mode 100644 install/update_config.php create mode 100644 install/upgrade.php create mode 100644 jscripts/ATutor.js create mode 100644 jscripts/ATutorCourse.js create mode 100644 jscripts/ATutor_js.php create mode 100644 jscripts/ATutor_tiny_mce_plugins/insert_tag/editor_plugin.js create mode 100644 jscripts/ATutor_tiny_mce_plugins/insert_tag/editor_plugin_src.js create mode 100644 jscripts/ATutor_tiny_mce_plugins/insert_tag/img/code.png create mode 100644 jscripts/ATutor_tiny_mce_plugins/insert_tag/img/media.png create mode 100644 jscripts/ATutor_tiny_mce_plugins/insert_tag/img/term.png create mode 100644 jscripts/ATutor_tiny_mce_plugins/insert_tag/img/tex.png create mode 100644 jscripts/ATutor_tiny_mce_plugins/insert_tag/langs/en.js create mode 100644 jscripts/ATutor_tiny_mce_plugins/swap_toolbar/editor_plugin.js create mode 100644 jscripts/ATutor_tiny_mce_plugins/swap_toolbar/editor_plugin_src.js create mode 100644 jscripts/ATutor_tiny_mce_plugins/swap_toolbar/img/bullet_arrow_down.png create mode 100644 jscripts/ATutor_tiny_mce_plugins/swap_toolbar/img/bullet_arrow_up.png create mode 100644 jscripts/ATutor_tiny_mce_plugins/swap_toolbar/langs/en.js create mode 100644 jscripts/TILE.js create mode 100644 jscripts/calendar.js create mode 100644 jscripts/help.js create mode 100644 jscripts/infusion/InfusionAll.js create mode 100644 jscripts/infusion/README.txt create mode 100644 jscripts/infusion/components/inlineEdit/css/InlineEdit.css create mode 100644 jscripts/infusion/components/inlineEdit/js/InlineEdit.js create mode 100644 jscripts/infusion/components/inlineEdit/js/InlineEditIntegrations.js create mode 100644 jscripts/infusion/components/inlineEdit/js/jquery.tinymce.js create mode 100644 jscripts/infusion/components/pager/css/Pager.css create mode 100644 jscripts/infusion/components/pager/images/arrow-dn.png create mode 100644 jscripts/infusion/components/pager/images/arrow-up.png create mode 100644 jscripts/infusion/components/pager/js/Pager.js create mode 100644 jscripts/infusion/components/progress/js/Progress.js create mode 100644 jscripts/infusion/components/reorderer/css/ImageReorderer.css create mode 100644 jscripts/infusion/components/reorderer/css/Reorderer.css create mode 100644 jscripts/infusion/components/reorderer/html/ImageReordererRSF.html create mode 100644 jscripts/infusion/components/reorderer/images/Banana.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Blackberry.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Cherry.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Dragonfruit.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Fig.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Grapes.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Kiwano.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Kiwi.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Kumquat.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Lemon.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Mangosteen.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Orange.jpg create mode 100644 jscripts/infusion/components/reorderer/images/RedApple.jpg create mode 100644 jscripts/infusion/components/reorderer/images/Tamarillo.jpg create mode 100644 jscripts/infusion/components/reorderer/js/GeometricManager.js create mode 100644 jscripts/infusion/components/reorderer/js/ImageReorderer.js create mode 100644 jscripts/infusion/components/reorderer/js/LayoutReorderer.js create mode 100644 jscripts/infusion/components/reorderer/js/ModuleLayout.js create mode 100644 jscripts/infusion/components/reorderer/js/Reorderer.js create mode 100644 jscripts/infusion/components/tableOfContents/html/TableOfContents.html create mode 100644 jscripts/infusion/components/tableOfContents/js/TableOfContents.js create mode 100644 jscripts/infusion/components/uiOptions/css/Slider.css create mode 100644 jscripts/infusion/components/uiOptions/css/UIOptions.css create mode 100644 jscripts/infusion/components/uiOptions/css/UIOptionsPreview.css create mode 100644 jscripts/infusion/components/uiOptions/html/UIOptions.html create mode 100644 jscripts/infusion/components/uiOptions/html/UIOptionsPreview.html create mode 100644 jscripts/infusion/components/uiOptions/images/500x327_mint_truffle.jpg create mode 100644 jscripts/infusion/components/uiOptions/images/border_1.png create mode 100644 jscripts/infusion/components/uiOptions/images/expand_collapse.png create mode 100644 jscripts/infusion/components/uiOptions/images/h1.png create mode 100644 jscripts/infusion/components/uiOptions/images/main_bg.png create mode 100644 jscripts/infusion/components/uiOptions/images/mintleaf.png create mode 100644 jscripts/infusion/components/uiOptions/js/UIEnhancer.js create mode 100644 jscripts/infusion/components/uiOptions/js/UIOptions.js create mode 100644 jscripts/infusion/components/undo/js/Undo.js create mode 100644 jscripts/infusion/components/uploader/ReadMe.txt create mode 100644 jscripts/infusion/components/uploader/css/Uploader.css create mode 100644 jscripts/infusion/components/uploader/html/Uploader.html create mode 100644 jscripts/infusion/components/uploader/images/add.png create mode 100644 jscripts/infusion/components/uploader/images/browse.png create mode 100644 jscripts/infusion/components/uploader/images/error.png create mode 100644 jscripts/infusion/components/uploader/images/gradient-file-green.png create mode 100644 jscripts/infusion/components/uploader/images/gradient-file-grey.png create mode 100644 jscripts/infusion/components/uploader/images/gradient-total-green.png create mode 100644 jscripts/infusion/components/uploader/images/gradient-total-grey.png create mode 100644 jscripts/infusion/components/uploader/images/gradient-total-yellow.png create mode 100644 jscripts/infusion/components/uploader/images/remove.png create mode 100644 jscripts/infusion/components/uploader/images/tick.png create mode 100644 jscripts/infusion/components/uploader/js/DemoUploadManager.js create mode 100644 jscripts/infusion/components/uploader/js/FileQueue.js create mode 100644 jscripts/infusion/components/uploader/js/SWFUploadManager.js create mode 100644 jscripts/infusion/components/uploader/js/Scroller.js create mode 100644 jscripts/infusion/components/uploader/js/Uploader.js create mode 100644 jscripts/infusion/framework/core/js/DataBinding.js create mode 100644 jscripts/infusion/framework/core/js/Fluid.js create mode 100644 jscripts/infusion/framework/core/js/FluidDOMUtilities.js create mode 100644 jscripts/infusion/framework/core/js/ProgressiveEnhancement.js create mode 100644 jscripts/infusion/framework/core/js/jquery.keyboard-a11y.js create mode 100644 jscripts/infusion/framework/fss/css/fss-JSR168Bridge.css create mode 100644 jscripts/infusion/framework/fss/css/fss-layout.css create mode 100644 jscripts/infusion/framework/fss/css/fss-mobile-layout.css create mode 100644 jscripts/infusion/framework/fss/css/fss-mobile-theme-android.css create mode 100644 jscripts/infusion/framework/fss/css/fss-mobile-theme-iphone.css create mode 100644 jscripts/infusion/framework/fss/css/fss-reset.css create mode 100644 jscripts/infusion/framework/fss/css/fss-text.css create mode 100644 jscripts/infusion/framework/fss/css/fss-theme-coal.css create mode 100644 jscripts/infusion/framework/fss/css/fss-theme-debug.css create mode 100644 jscripts/infusion/framework/fss/css/fss-theme-hc.css create mode 100644 jscripts/infusion/framework/fss/css/fss-theme-hci.css create mode 100644 jscripts/infusion/framework/fss/css/fss-theme-mist.css create mode 100644 jscripts/infusion/framework/fss/css/fss-theme-rust.css create mode 100644 jscripts/infusion/framework/fss/css/fss-theme-slate.css create mode 100644 jscripts/infusion/framework/fss/images/exclamation.png create mode 100644 jscripts/infusion/framework/fss/images/gripper.png create mode 100644 jscripts/infusion/framework/fss/images/themes/_common/exclamation.png create mode 100644 jscripts/infusion/framework/fss/images/themes/_common/gloss_25_repeater.png create mode 100644 jscripts/infusion/framework/fss/images/themes/_common/gripper.png create mode 100644 jscripts/infusion/framework/fss/images/themes/android/backbutton_mask.png create mode 100644 jscripts/infusion/framework/fss/images/themes/android/button_bg_insetShadow.png create mode 100644 jscripts/infusion/framework/fss/images/themes/android/listmenu_arrow.png create mode 100644 jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.gif create mode 100644 jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.png create mode 100644 jscripts/infusion/framework/fss/images/themes/android/navbar_back_button_insetShadow.png create mode 100644 jscripts/infusion/framework/fss/images/themes/android/navbar_normal_button_insetShadow.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/buttons-light-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/buttons-light-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/buttons-med-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/buttons-med-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/coal-icons.psd create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/icon-menu-Delete.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Close.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Less.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/icon-widget-More.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/icon-widget-ShowSettings.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/icon-widget-gripper.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-light-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-light-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-light-container-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-light-content-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-med-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-med-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-med-container-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/tabs-med-content-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/coal/widget-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/iphone/backbutton_mask.png create mode 100644 jscripts/infusion/framework/fss/images/themes/iphone/button_bg_insetShadow.png create mode 100644 jscripts/infusion/framework/fss/images/themes/iphone/listmenu_arrow.png create mode 100644 jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.gif create mode 100644 jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.png create mode 100644 jscripts/infusion/framework/fss/images/themes/iphone/navbar_back_button_insetShadow.png create mode 100644 jscripts/infusion/framework/fss/images/themes/iphone/navbar_normal_button_insetShadow.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/buttons-light-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/buttons-light-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/buttons-med-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/buttons-med-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/icon-menu-Delete.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/icon-options-ListOrGrid.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Close.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Less.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/icon-widget-More.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/icon-widget-ShowSettings.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/icon-widget-gripper.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/mist-icons.psd create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-light-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-light-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-light-container-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-med-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-med-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/tabs-med-container-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/mist/widget-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/rust/gripper.png create mode 100644 jscripts/infusion/framework/fss/images/themes/rust/icon-menu-Delete.png create mode 100644 jscripts/infusion/framework/fss/images/themes/rust/icon-options-ListOrGrid.png create mode 100644 jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Close.png create mode 100644 jscripts/infusion/framework/fss/images/themes/rust/icon-widget-More.png create mode 100644 jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Settings.png create mode 100644 jscripts/infusion/framework/fss/images/themes/rust/menu-hover.png create mode 100644 jscripts/infusion/framework/fss/images/themes/rust/widget-earmark.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/buttons-light-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/buttons-light-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/buttons-med-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/buttons-med-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Close.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Less.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/icon-widget-More.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/icon-widget-gripper.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/slate-icons.psd create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/sprites.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-light-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-light-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-light-container-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-med-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-med-cap.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/tabs-med-container-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/widget-bg.png create mode 100644 jscripts/infusion/framework/fss/images/themes/slate/widget-earmark.png create mode 100644 jscripts/infusion/framework/renderer/js/fluidParser.js create mode 100644 jscripts/infusion/framework/renderer/js/fluidRenderer.js create mode 100644 jscripts/infusion/jquery.autoHeight.js create mode 100644 jscripts/infusion/lib/fastXmlPull/js/fastXmlPull.js create mode 100644 jscripts/infusion/lib/jquery/core/js/jquery.js create mode 100644 jscripts/infusion/lib/jquery/plugins/bgiframe/js/jquery.bgiframe.js create mode 100644 jscripts/infusion/lib/jquery/plugins/delegate/js/jquery.delegate.js create mode 100644 jscripts/infusion/lib/jquery/plugins/tooltip/css/jquery.tooltip.css create mode 100644 jscripts/infusion/lib/jquery/plugins/tooltip/js/jquery.tooltip.js create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/coal.css create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_0_000000_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_75_cccccc_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_25_575757_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_55_cccccc_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_65_000000_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_333333_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_666666_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_inset-soft_95_fef1ec_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_222222_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_333333_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_a3a3a3_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cccccc_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cd0a0a_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_ffffff_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/hc.css create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_000000_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_ffffff_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_000000_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_ffffff_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/hci.css create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_000000_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_999999_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_ffffff_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_000000_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_fffff_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_ffffff_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_000000_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_2e83ff_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_9dcaf6_1x400.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_d9e8f7_1x400.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_highlight-soft_55_9dcaf6_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_inset-soft_95_fef1ec_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_000000_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_222222_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_2e83ff_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_454545_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_888888_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_cd0a0a_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/mist.css create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_666666_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_999999_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_cccccc_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ebebeb_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ffffff_40x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_glass_75_666666_1x400.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-hard_100_ebebeb_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-soft_75_999999_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_inset-hard_100_ebebeb_1x100.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_000000_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_666666_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ebebeb_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ffffff_256x240.png create mode 100644 jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/slate.css create mode 100644 jscripts/infusion/lib/jquery/ui/js/ui-FLUID-readme.txt create mode 100644 jscripts/infusion/lib/jquery/ui/js/ui.accordion.js create mode 100644 jscripts/infusion/lib/jquery/ui/js/ui.core.js create mode 100644 jscripts/infusion/lib/jquery/ui/js/ui.dialog.js create mode 100644 jscripts/infusion/lib/jquery/ui/js/ui.draggable.js create mode 100644 jscripts/infusion/lib/jquery/ui/js/ui.slider.js create mode 100644 jscripts/infusion/lib/json/js/json2.js create mode 100644 jscripts/infusion/lib/swfobject/js/swfobject.js create mode 100644 jscripts/infusion/lib/swfupload/flash/swfupload.swf create mode 100644 jscripts/infusion/lib/swfupload/js/swfupload.js create mode 100644 jscripts/infusion/licenses/Infusion-LICENSE.txt create mode 100644 jscripts/infusion/licenses/fastXmlPull-LICENSE.txt create mode 100644 jscripts/infusion/licenses/fckeditor-LICENSE.txt create mode 100644 jscripts/infusion/licenses/jQuery-LICENSE.txt create mode 100644 jscripts/infusion/licenses/swfobject-LICENSE.txt create mode 100644 jscripts/infusion/licenses/swfupload-LICENSE.txt create mode 100644 jscripts/interface.js create mode 100644 jscripts/jquery.js create mode 100644 jscripts/opensocial/activity.js create mode 100644 jscripts/opensocial/address.js create mode 100644 jscripts/opensocial/all_opensocial.php create mode 100644 jscripts/opensocial/bodyType.js create mode 100644 jscripts/opensocial/collection.js create mode 100644 jscripts/opensocial/datarequest.js create mode 100644 jscripts/opensocial/dataresponse.js create mode 100644 jscripts/opensocial/email.js create mode 100644 jscripts/opensocial/enum.js create mode 100644 jscripts/opensocial/environment.js create mode 100644 jscripts/opensocial/message.js create mode 100644 jscripts/opensocial/name.js create mode 100644 jscripts/opensocial/opensocial.js create mode 100644 jscripts/opensocial/organization.js create mode 100644 jscripts/opensocial/person.js create mode 100644 jscripts/opensocial/phone.js create mode 100644 jscripts/opensocial/responseitem.js create mode 100644 jscripts/opensocial/url.js create mode 100644 jscripts/tiny_mce/changelog.txt create mode 100644 jscripts/tiny_mce/langs/en.js create mode 100644 jscripts/tiny_mce/license.txt create mode 100644 jscripts/tiny_mce/plugins/acheck/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/acheck/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/acheck/img/acheck.gif create mode 100644 jscripts/tiny_mce/plugins/acheck/js/dialog.js create mode 100644 jscripts/tiny_mce/plugins/acheck/langs/en.js create mode 100644 jscripts/tiny_mce/plugins/acheck/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/advhr/css/advhr.css create mode 100644 jscripts/tiny_mce/plugins/advhr/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/advhr/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/advhr/js/rule.js create mode 100644 jscripts/tiny_mce/plugins/advhr/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/advhr/rule.htm create mode 100644 jscripts/tiny_mce/plugins/advimage/css/advimage.css create mode 100644 jscripts/tiny_mce/plugins/advimage/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/advimage/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/advimage/image.htm create mode 100644 jscripts/tiny_mce/plugins/advimage/img/sample.gif create mode 100644 jscripts/tiny_mce/plugins/advimage/js/image.js create mode 100644 jscripts/tiny_mce/plugins/advimage/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/advlink/css/advlink.css create mode 100644 jscripts/tiny_mce/plugins/advlink/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/advlink/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/advlink/js/advlink.js create mode 100644 jscripts/tiny_mce/plugins/advlink/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/advlink/link.htm create mode 100644 jscripts/tiny_mce/plugins/advlist/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/advlist/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/autoresize/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/autoresize/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/autosave/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/autosave/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/autosave/langs/en.js create mode 100644 jscripts/tiny_mce/plugins/bbcode/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/bbcode/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/compat2x/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/compat2x/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/contextmenu/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/contextmenu/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/directionality/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/directionality/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/emotions/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/emotions/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/emotions/emotions.htm create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-cool.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-cry.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-embarassed.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-foot-in-mouth.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-frown.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-innocent.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-kiss.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-laughing.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-money-mouth.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-sealed.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-smile.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-surprised.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-tongue-out.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-undecided.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-wink.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/img/smiley-yell.gif create mode 100644 jscripts/tiny_mce/plugins/emotions/js/emotions.js create mode 100644 jscripts/tiny_mce/plugins/emotions/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/example/dialog.htm create mode 100644 jscripts/tiny_mce/plugins/example/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/example/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/example/img/example.gif create mode 100644 jscripts/tiny_mce/plugins/example/js/dialog.js create mode 100644 jscripts/tiny_mce/plugins/example/langs/en.js create mode 100644 jscripts/tiny_mce/plugins/example/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/fullpage/css/fullpage.css create mode 100644 jscripts/tiny_mce/plugins/fullpage/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/fullpage/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/fullpage/fullpage.htm create mode 100644 jscripts/tiny_mce/plugins/fullpage/js/fullpage.js create mode 100644 jscripts/tiny_mce/plugins/fullpage/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/fullscreen/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/fullscreen/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/fullscreen/fullscreen.htm create mode 100644 jscripts/tiny_mce/plugins/iespell/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/iespell/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/alert.gif create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/button.gif create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/corners.gif create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/skins/clearlooks2/window.css create mode 100644 jscripts/tiny_mce/plugins/inlinepopups/template.htm create mode 100644 jscripts/tiny_mce/plugins/insertdatetime/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/insertdatetime/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/layer/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/layer/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/legacyoutput/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/legacyoutput/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/media/css/content.css create mode 100644 jscripts/tiny_mce/plugins/media/css/media.css create mode 100644 jscripts/tiny_mce/plugins/media/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/media/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/media/img/flash.gif create mode 100644 jscripts/tiny_mce/plugins/media/img/flv_player.swf create mode 100644 jscripts/tiny_mce/plugins/media/img/quicktime.gif create mode 100644 jscripts/tiny_mce/plugins/media/img/realmedia.gif create mode 100644 jscripts/tiny_mce/plugins/media/img/shockwave.gif create mode 100644 jscripts/tiny_mce/plugins/media/img/trans.gif create mode 100644 jscripts/tiny_mce/plugins/media/img/windowsmedia.gif create mode 100644 jscripts/tiny_mce/plugins/media/js/embed.js create mode 100644 jscripts/tiny_mce/plugins/media/js/media.js create mode 100644 jscripts/tiny_mce/plugins/media/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/media/media.htm create mode 100644 jscripts/tiny_mce/plugins/nonbreaking/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/nonbreaking/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/noneditable/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/noneditable/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/pagebreak/css/content.css create mode 100644 jscripts/tiny_mce/plugins/pagebreak/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/pagebreak/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/pagebreak/img/pagebreak.gif create mode 100644 jscripts/tiny_mce/plugins/pagebreak/img/trans.gif create mode 100644 jscripts/tiny_mce/plugins/paste/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/paste/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/paste/js/pastetext.js create mode 100644 jscripts/tiny_mce/plugins/paste/js/pasteword.js create mode 100644 jscripts/tiny_mce/plugins/paste/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/paste/pastetext.htm create mode 100644 jscripts/tiny_mce/plugins/paste/pasteword.htm create mode 100644 jscripts/tiny_mce/plugins/preview/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/preview/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/preview/example.html create mode 100644 jscripts/tiny_mce/plugins/preview/jscripts/embed.js create mode 100644 jscripts/tiny_mce/plugins/preview/preview.html create mode 100644 jscripts/tiny_mce/plugins/print/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/print/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/safari/blank.htm create mode 100644 jscripts/tiny_mce/plugins/safari/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/safari/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/save/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/save/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/searchreplace/css/searchreplace.css create mode 100644 jscripts/tiny_mce/plugins/searchreplace/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/searchreplace/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/searchreplace/js/searchreplace.js create mode 100644 jscripts/tiny_mce/plugins/searchreplace/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/searchreplace/searchreplace.htm create mode 100644 jscripts/tiny_mce/plugins/spellchecker/css/content.css create mode 100644 jscripts/tiny_mce/plugins/spellchecker/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/spellchecker/img/wline.gif create mode 100644 jscripts/tiny_mce/plugins/style/css/props.css create mode 100644 jscripts/tiny_mce/plugins/style/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/style/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/style/js/props.js create mode 100644 jscripts/tiny_mce/plugins/style/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/style/props.htm create mode 100644 jscripts/tiny_mce/plugins/tabfocus/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/tabfocus/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/table/cell.htm create mode 100644 jscripts/tiny_mce/plugins/table/css/cell.css create mode 100644 jscripts/tiny_mce/plugins/table/css/row.css create mode 100644 jscripts/tiny_mce/plugins/table/css/table.css create mode 100644 jscripts/tiny_mce/plugins/table/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/table/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/table/js/cell.js create mode 100644 jscripts/tiny_mce/plugins/table/js/merge_cells.js create mode 100644 jscripts/tiny_mce/plugins/table/js/row.js create mode 100644 jscripts/tiny_mce/plugins/table/js/table.js create mode 100644 jscripts/tiny_mce/plugins/table/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/table/merge_cells.htm create mode 100644 jscripts/tiny_mce/plugins/table/row.htm create mode 100644 jscripts/tiny_mce/plugins/table/table.htm create mode 100644 jscripts/tiny_mce/plugins/template/blank.htm create mode 100644 jscripts/tiny_mce/plugins/template/css/template.css create mode 100644 jscripts/tiny_mce/plugins/template/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/template/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/template/js/template.js create mode 100644 jscripts/tiny_mce/plugins/template/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/plugins/template/template.htm create mode 100644 jscripts/tiny_mce/plugins/visualchars/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/visualchars/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/wordcount/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/wordcount/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/abbr.htm create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/acronym.htm create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/attributes.htm create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/cite.htm create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/css/attributes.css create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/css/popup.css create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/del.htm create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/editor_plugin.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/editor_plugin_src.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/ins.htm create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/js/abbr.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/js/acronym.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/js/attributes.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/js/cite.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/js/del.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/js/element_common.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/js/ins.js create mode 100644 jscripts/tiny_mce/plugins/xhtmlxtras/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/themes/advanced/about.htm create mode 100644 jscripts/tiny_mce/themes/advanced/anchor.htm create mode 100644 jscripts/tiny_mce/themes/advanced/charmap.htm create mode 100644 jscripts/tiny_mce/themes/advanced/color_picker.htm create mode 100644 jscripts/tiny_mce/themes/advanced/editor_template.js create mode 100644 jscripts/tiny_mce/themes/advanced/editor_template_src.js create mode 100644 jscripts/tiny_mce/themes/advanced/image.htm create mode 100644 jscripts/tiny_mce/themes/advanced/img/colorpicker.jpg create mode 100644 jscripts/tiny_mce/themes/advanced/img/icons.gif create mode 100644 jscripts/tiny_mce/themes/advanced/js/about.js create mode 100644 jscripts/tiny_mce/themes/advanced/js/anchor.js create mode 100644 jscripts/tiny_mce/themes/advanced/js/charmap.js create mode 100644 jscripts/tiny_mce/themes/advanced/js/color_picker.js create mode 100644 jscripts/tiny_mce/themes/advanced/js/image.js create mode 100644 jscripts/tiny_mce/themes/advanced/js/link.js create mode 100644 jscripts/tiny_mce/themes/advanced/js/source_editor.js create mode 100644 jscripts/tiny_mce/themes/advanced/langs/en.js create mode 100644 jscripts/tiny_mce/themes/advanced/langs/en_dlg.js create mode 100644 jscripts/tiny_mce/themes/advanced/link.htm create mode 100644 jscripts/tiny_mce/themes/advanced/skins/default/content.css create mode 100644 jscripts/tiny_mce/themes/advanced/skins/default/dialog.css create mode 100644 jscripts/tiny_mce/themes/advanced/skins/default/img/buttons.png create mode 100644 jscripts/tiny_mce/themes/advanced/skins/default/img/items.gif create mode 100644 jscripts/tiny_mce/themes/advanced/skins/default/img/menu_arrow.gif create mode 100644 jscripts/tiny_mce/themes/advanced/skins/default/img/menu_check.gif create mode 100644 jscripts/tiny_mce/themes/advanced/skins/default/img/progress.gif create mode 100644 jscripts/tiny_mce/themes/advanced/skins/default/img/tabs.gif create mode 100644 jscripts/tiny_mce/themes/advanced/skins/default/ui.css create mode 100644 jscripts/tiny_mce/themes/advanced/skins/o2k7/content.css create mode 100644 jscripts/tiny_mce/themes/advanced/skins/o2k7/dialog.css create mode 100644 jscripts/tiny_mce/themes/advanced/skins/o2k7/img/button_bg.png create mode 100644 jscripts/tiny_mce/themes/advanced/skins/o2k7/img/button_bg_black.png create mode 100644 jscripts/tiny_mce/themes/advanced/skins/o2k7/img/button_bg_silver.png create mode 100644 jscripts/tiny_mce/themes/advanced/skins/o2k7/ui.css create mode 100644 jscripts/tiny_mce/themes/advanced/skins/o2k7/ui_black.css create mode 100644 jscripts/tiny_mce/themes/advanced/skins/o2k7/ui_silver.css create mode 100644 jscripts/tiny_mce/themes/advanced/source_editor.htm create mode 100644 jscripts/tiny_mce/themes/simple/editor_template.js create mode 100644 jscripts/tiny_mce/themes/simple/editor_template_src.js create mode 100644 jscripts/tiny_mce/themes/simple/img/icons.gif create mode 100644 jscripts/tiny_mce/themes/simple/langs/en.js create mode 100644 jscripts/tiny_mce/themes/simple/skins/default/content.css create mode 100644 jscripts/tiny_mce/themes/simple/skins/default/ui.css create mode 100644 jscripts/tiny_mce/themes/simple/skins/o2k7/content.css create mode 100644 jscripts/tiny_mce/themes/simple/skins/o2k7/img/button_bg.png create mode 100644 jscripts/tiny_mce/themes/simple/skins/o2k7/ui.css create mode 100644 jscripts/tiny_mce/tiny_mce.js create mode 100644 jscripts/tiny_mce/tiny_mce_popup.js create mode 100644 jscripts/tiny_mce/tiny_mce_src.js create mode 100644 jscripts/tiny_mce/utils/editable_selects.js create mode 100644 jscripts/tiny_mce/utils/form_utils.js create mode 100644 jscripts/tiny_mce/utils/mctabs.js create mode 100644 jscripts/tiny_mce/utils/validate.js create mode 100644 jscripts/wz_jsgraphics.js create mode 100644 license/gpl_licence.txt create mode 100644 login.php create mode 100644 logout.php create mode 100644 mods/_core/backups/admin/create.php create mode 100644 mods/_core/backups/admin/delete.php create mode 100644 mods/_core/backups/admin/edit.php create mode 100644 mods/_core/backups/admin/index.php create mode 100644 mods/_core/backups/admin/restore.php create mode 100644 mods/_core/backups/classes/Backup.class.php create mode 100644 mods/_core/backups/classes/TableBackup.class.php create mode 100644 mods/_core/backups/create.php create mode 100644 mods/_core/backups/delete.php create mode 100644 mods/_core/backups/edit.php create mode 100644 mods/_core/backups/index.php create mode 100644 mods/_core/backups/module.php create mode 100644 mods/_core/backups/module.xml create mode 100644 mods/_core/backups/module_delete.php create mode 100644 mods/_core/backups/restore.php create mode 100644 mods/_core/backups/upload.php create mode 100644 mods/_core/cats_categories/admin/course_categories.php create mode 100644 mods/_core/cats_categories/admin/create_category.php create mode 100644 mods/_core/cats_categories/admin/delete_category.php create mode 100644 mods/_core/cats_categories/admin/edit_category.php create mode 100644 mods/_core/cats_categories/lib/admin_categories.inc.php create mode 100644 mods/_core/cats_categories/module.php create mode 100644 mods/_core/cats_categories/module.xml create mode 100644 mods/_core/content/index.php create mode 100644 mods/_core/content/menu_inline_editor_submit.php create mode 100644 mods/_core/content/module.php create mode 100644 mods/_core/content/module.xml create mode 100644 mods/_core/content/module_backup.php create mode 100644 mods/_core/content/module_delete.php create mode 100644 mods/_core/content/refresh_content_nav.php create mode 100644 mods/_core/courses/admin/auto_enroll.php create mode 100644 mods/_core/courses/admin/auto_enroll_delete.php create mode 100644 mods/_core/courses/admin/auto_enroll_edit.php create mode 100644 mods/_core/courses/admin/auto_enroll_filter_courses.php create mode 100644 mods/_core/courses/admin/courses.php create mode 100644 mods/_core/courses/admin/create_course.php create mode 100644 mods/_core/courses/admin/default_mods.php create mode 100644 mods/_core/courses/admin/default_side.php create mode 100644 mods/_core/courses/admin/instructor_login.php create mode 100644 mods/_core/courses/html/course_icon.inc.php create mode 100644 mods/_core/courses/html/course_properties.inc.php create mode 100644 mods/_core/courses/lib/course.inc.php create mode 100644 mods/_core/courses/module.php create mode 100644 mods/_core/courses/module.xml create mode 100644 mods/_core/courses/users/create_course.php create mode 100644 mods/_core/courses/users/request_instructor.php create mode 100644 mods/_core/editor/accessibility.php create mode 100644 mods/_core/editor/add_content.php create mode 100644 mods/_core/editor/arrange_content.php create mode 100644 mods/_core/editor/delete_content.php create mode 100644 mods/_core/editor/edit_content.php create mode 100644 mods/_core/editor/edit_content_folder.php create mode 100644 mods/_core/editor/editor_tab_functions.inc.php create mode 100644 mods/_core/editor/editor_tabs/alternatives.inc.php create mode 100644 mods/_core/editor/editor_tabs/edit.inc.php create mode 100644 mods/_core/editor/editor_tabs/glossary.inc.php create mode 100644 mods/_core/editor/editor_tabs/pastefromfile.php create mode 100644 mods/_core/editor/editor_tabs/properties.inc.php create mode 100644 mods/_core/editor/editor_tabs/tests.inc.php create mode 100644 mods/_core/editor/index.php create mode 100644 mods/_core/editor/js/edit.js create mode 100644 mods/_core/editor/preview.php create mode 100644 mods/_core/editor/remove_alternative.php create mode 100644 mods/_core/editor/save_alternative.php create mode 100644 mods/_core/enrolment/admin/enroll_edit.php create mode 100644 mods/_core/enrolment/admin/index.php create mode 100644 mods/_core/enrolment/admin/privileges.php create mode 100644 mods/_core/enrolment/create_course_list.php create mode 100644 mods/_core/enrolment/enroll_edit.php create mode 100644 mods/_core/enrolment/export_course_list.php create mode 100644 mods/_core/enrolment/html/enroll_edit.inc.php create mode 100644 mods/_core/enrolment/html/enroll_tab_functions.inc.php create mode 100644 mods/_core/enrolment/html/enrollment.inc.php create mode 100644 mods/_core/enrolment/html/privileges.inc.php create mode 100644 mods/_core/enrolment/import_course_list.php create mode 100644 mods/_core/enrolment/index.php create mode 100644 mods/_core/enrolment/lib/enroll.inc.php create mode 100644 mods/_core/enrolment/module.php create mode 100644 mods/_core/enrolment/module.xml create mode 100644 mods/_core/enrolment/module_delete.php create mode 100644 mods/_core/enrolment/privileges.php create mode 100644 mods/_core/enrolment/verify_list.php create mode 100644 mods/_core/file_manager/delete.php create mode 100644 mods/_core/file_manager/edit.php create mode 100644 mods/_core/file_manager/filemanager.inc.php create mode 100644 mods/_core/file_manager/filemanager_display.inc.php create mode 100644 mods/_core/file_manager/index.php create mode 100644 mods/_core/file_manager/module.php create mode 100644 mods/_core/file_manager/module.xml create mode 100644 mods/_core/file_manager/module_backup.php create mode 100644 mods/_core/file_manager/module_delete.php create mode 100644 mods/_core/file_manager/move.php create mode 100644 mods/_core/file_manager/new.php create mode 100644 mods/_core/file_manager/preview.php create mode 100644 mods/_core/file_manager/preview_top.php create mode 100644 mods/_core/file_manager/rename.php create mode 100644 mods/_core/file_manager/top.php create mode 100644 mods/_core/file_manager/upload.php create mode 100644 mods/_core/file_manager/zip.php create mode 100644 mods/_core/glossary/dropdown/glossary.inc.php create mode 100644 mods/_core/glossary/index.php create mode 100644 mods/_core/glossary/module.php create mode 100644 mods/_core/glossary/module.xml create mode 100644 mods/_core/glossary/module_backup.php create mode 100644 mods/_core/glossary/module_delete.php create mode 100644 mods/_core/glossary/module_news.php create mode 100644 mods/_core/glossary/sublinks.php create mode 100644 mods/_core/glossary/tools/add.php create mode 100644 mods/_core/glossary/tools/delete.php create mode 100644 mods/_core/glossary/tools/edit.php create mode 100644 mods/_core/glossary/tools/index.php create mode 100644 mods/_core/groups/create.php create mode 100644 mods/_core/groups/create_automatic.php create mode 100644 mods/_core/groups/create_manual.php create mode 100644 mods/_core/groups/delete_group.php create mode 100644 mods/_core/groups/delete_type.php create mode 100644 mods/_core/groups/edit_group.php create mode 100644 mods/_core/groups/edit_type.php create mode 100644 mods/_core/groups/groups.php create mode 100644 mods/_core/groups/index.php create mode 100644 mods/_core/groups/members.php create mode 100644 mods/_core/groups/module.php create mode 100644 mods/_core/groups/module.xml create mode 100644 mods/_core/groups/module_backup.php create mode 100644 mods/_core/groups/module_delete.php create mode 100644 mods/_core/imsafa/classes/A4a.class.php create mode 100644 mods/_core/imsafa/classes/A4a.tmpl.php create mode 100644 mods/_core/imsafa/classes/A4aExport.class.php create mode 100644 mods/_core/imsafa/classes/A4aImport.class.php create mode 100644 mods/_core/imsafa/html/resources_parser.inc.php create mode 100644 mods/_core/imscc/classes/DiscussionTools.class.php create mode 100644 mods/_core/imscc/classes/DiscussionToolsImport.class.php create mode 100644 mods/_core/imscc/classes/DiscussionToolsParser.class.php create mode 100644 mods/_core/imscc/classes/Weblinks.class.php create mode 100644 mods/_core/imscc/classes/Weblinks.tmpl.php create mode 100644 mods/_core/imscc/classes/WeblinksExport.class.php create mode 100644 mods/_core/imscc/classes/WeblinksParser.class.php create mode 100644 mods/_core/imscc/ims_export.php create mode 100644 mods/_core/imscc/ims_import.php create mode 100644 mods/_core/imscc/include/ims_template.inc.php create mode 100644 mods/_core/imscp/domainProfile_0/imsccauth_v1p0.xsd create mode 100644 mods/_core/imscp/domainProfile_0/imsccauth_v1p0_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_1/anyElement.xsd create mode 100644 mods/_core/imscp/domainProfile_1/anyElement_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_1/common/anyElement.xsd create mode 100644 mods/_core/imscp/domainProfile_1/common/dataTypes.xsd create mode 100644 mods/_core/imscp/domainProfile_1/common/elementNames.xsd create mode 100644 mods/_core/imscp/domainProfile_1/common/elementTypes.xsd create mode 100644 mods/_core/imscp/domainProfile_1/common/rootElement.xsd create mode 100644 mods/_core/imscp/domainProfile_1/common/vocabTypes.xsd create mode 100644 mods/_core/imscp/domainProfile_1/common/vocabValues.xsd create mode 100644 mods/_core/imscp/domainProfile_1/dataTypes_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_1/elementNames_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_1/elementTypes_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_1/extend/custom.xsd create mode 100644 mods/_core/imscp/domainProfile_1/imscc_m_definition.xsd create mode 100644 mods/_core/imscp/domainProfile_1/lomLoose.xsd create mode 100644 mods/_core/imscp/domainProfile_1/lomLoose_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_1/loose.xsd create mode 100644 mods/_core/imscp/domainProfile_1/rootElement_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_1/unique/loose.xsd create mode 100644 mods/_core/imscp/domainProfile_1/vocab/loose.xsd create mode 100644 mods/_core/imscp/domainProfile_1/vocabTypes_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_1/vocabValues_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_2/anyElement.xsd create mode 100644 mods/_core/imscp/domainProfile_2/anyElement_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_2/common/anyElement.xsd create mode 100644 mods/_core/imscp/domainProfile_2/common/dataTypes.xsd create mode 100644 mods/_core/imscp/domainProfile_2/common/elementNames.xsd create mode 100644 mods/_core/imscp/domainProfile_2/common/elementTypes.xsd create mode 100644 mods/_core/imscp/domainProfile_2/common/rootElement.xsd create mode 100644 mods/_core/imscp/domainProfile_2/common/vocabTypes.xsd create mode 100644 mods/_core/imscp/domainProfile_2/common/vocabValues.xsd create mode 100644 mods/_core/imscp/domainProfile_2/dataTypes_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_2/elementNames_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_2/elementTypes_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_2/extend/custom.xsd create mode 100644 mods/_core/imscp/domainProfile_2/imscc_mR_definition.xsd create mode 100644 mods/_core/imscp/domainProfile_2/lomLoose.xsd create mode 100644 mods/_core/imscp/domainProfile_2/lomLoose_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_2/loose.xsd create mode 100644 mods/_core/imscp/domainProfile_2/rootElement_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_2/unique/loose.xsd create mode 100644 mods/_core/imscp/domainProfile_2/vocab/loose.xsd create mode 100644 mods/_core/imscp/domainProfile_2/vocabTypes_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_2/vocabValues_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_3/imscp_extensionv1p2.xsd create mode 100644 mods/_core/imscp/domainProfile_3/imscp_extensionv1p2_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2.xsd create mode 100644 mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2_def_copy.xsd create mode 100644 mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_4/imscc_q_definition.xsd create mode 100644 mods/_core/imscp/domainProfile_4/xml.xsd create mode 100644 mods/_core/imscp/domainProfile_5/imswl_v1p0.xsd create mode 100644 mods/_core/imscp/domainProfile_5/imswl_v1p0_localised.xsd create mode 100644 mods/_core/imscp/domainProfile_6/imsdt_v1p0.xsd create mode 100644 mods/_core/imscp/domainProfile_6/imsdt_v1p0_localised.xsd create mode 100644 mods/_core/imscp/export.php create mode 100644 mods/_core/imscp/glossary.xsd create mode 100644 mods/_core/imscp/ims_export.php create mode 100644 mods/_core/imscp/ims_import.php create mode 100644 mods/_core/imscp/imscc_c1p2maeV0p15_definition.xsd create mode 100644 mods/_core/imscp/imscp_v1p2.xsd create mode 100644 mods/_core/imscp/imscp_v1p2_constraintsDocument.scmt create mode 100644 mods/_core/imscp/imscp_v1p2_localised.xsd create mode 100644 mods/_core/imscp/imsdt_v1p0_localised.xsd create mode 100644 mods/_core/imscp/imsmanifest.xml create mode 100644 mods/_core/imscp/imswl_v1p0_localised.xsd create mode 100644 mods/_core/imscp/include/adlcp_rootv1p2.xsd create mode 100644 mods/_core/imscp/include/footer.html create mode 100644 mods/_core/imscp/include/ims.css create mode 100644 mods/_core/imscp/include/ims_template.inc.php create mode 100644 mods/_core/imscp/include/ims_xml.xsd create mode 100644 mods/_core/imscp/include/imscp_rootv1p1p2.xsd create mode 100644 mods/_core/imscp/include/imsmd_rootv1p2p1.xsd create mode 100644 mods/_core/imscp/index.php create mode 100644 mods/_core/imscp/main.xsd create mode 100644 mods/_core/imscp/module.php create mode 100644 mods/_core/imscp/module.xml create mode 100644 mods/_core/imscp/ns.inc.php create mode 100644 mods/_core/imscp/oauth/OAuth.php create mode 100644 mods/_core/imscp/oauth/OAuthUtility.class.php create mode 100644 mods/_core/imscp/oauth/oauth_authenticate.php create mode 100644 mods/_core/imsqti/classes/QTIImport.class.php create mode 100644 mods/_core/imsqti/classes/QTIParser.class.php create mode 100644 mods/_core/imsqti/lib/qti.inc.php create mode 100644 mods/_core/languages/classes/Language.class.php create mode 100644 mods/_core/languages/classes/LanguageEditor.class.php create mode 100644 mods/_core/languages/classes/LanguageManager.class.php create mode 100644 mods/_core/languages/classes/LanguageParser.class.php create mode 100644 mods/_core/languages/classes/LanguagesParser.class.php create mode 100644 mods/_core/languages/classes/RemoteLanguageManager.class.php create mode 100644 mods/_core/languages/language.php create mode 100644 mods/_core/languages/language_add.php create mode 100644 mods/_core/languages/language_delete.php create mode 100644 mods/_core/languages/language_edit.php create mode 100644 mods/_core/languages/language_editor.php create mode 100644 mods/_core/languages/language_import.php create mode 100644 mods/_core/languages/language_term.php create mode 100644 mods/_core/languages/language_translate.php create mode 100644 mods/_core/languages/missing_language.php create mode 100644 mods/_core/languages/module.php create mode 100644 mods/_core/languages/module.xml create mode 100644 mods/_core/languages/module_cron.php create mode 100644 mods/_core/languages/translate.php create mode 100644 mods/_core/languages/translate_atutor.php create mode 100644 mods/_core/languages/translator.php create mode 100644 mods/_core/modules/add_new.php create mode 100644 mods/_core/modules/classes/Module.class.php create mode 100644 mods/_core/modules/classes/ModuleListParser.class.php create mode 100644 mods/_core/modules/classes/ModuleParser.class.php create mode 100644 mods/_core/modules/create.php create mode 100644 mods/_core/modules/default_mods.php create mode 100644 mods/_core/modules/default_side.php create mode 100644 mods/_core/modules/details.php create mode 100644 mods/_core/modules/index.php create mode 100644 mods/_core/modules/install_modules.php create mode 100644 mods/_core/modules/module.php create mode 100644 mods/_core/modules/module.template.php create mode 100644 mods/_core/modules/module.xml create mode 100644 mods/_core/modules/module_install_step_1.php create mode 100644 mods/_core/modules/module_install_step_2.php create mode 100644 mods/_core/modules/module_install_step_3.php create mode 100644 mods/_core/modules/module_uninstall_step_1.php create mode 100644 mods/_core/modules/module_uninstall_step_2.php create mode 100644 mods/_core/modules/module_uninstall_step_3.php create mode 100644 mods/_core/modules/version_history.php create mode 100644 mods/_core/properties/access.php create mode 100644 mods/_core/properties/admin/delete_course.php create mode 100644 mods/_core/properties/admin/edit_course.php create mode 100644 mods/_core/properties/course_properties.php create mode 100644 mods/_core/properties/delete_course.php create mode 100644 mods/_core/properties/lib/course.inc.php create mode 100644 mods/_core/properties/lib/delete_course.inc.php create mode 100644 mods/_core/properties/module.php create mode 100644 mods/_core/properties/module.xml create mode 100644 mods/_core/properties/module_delete.php create mode 100644 mods/_core/themes/classes/ThemeListParser.class.php create mode 100644 mods/_core/themes/classes/ThemeParser.class.php create mode 100644 mods/_core/themes/delete.php create mode 100644 mods/_core/themes/import.php create mode 100644 mods/_core/themes/index.php create mode 100644 mods/_core/themes/install_themes.php create mode 100644 mods/_core/themes/lib/theme_template.inc.php create mode 100644 mods/_core/themes/lib/themes.inc.php create mode 100644 mods/_core/themes/module.php create mode 100644 mods/_core/themes/module.xml create mode 100644 mods/_core/themes/theme_install_step_1.php create mode 100644 mods/_core/themes/theme_install_step_2.php create mode 100644 mods/_core/themes/theme_install_step_3.php create mode 100644 mods/_core/themes/version_history.php create mode 100644 mods/_core/tool_manager/forums_tool.php create mode 100644 mods/_core/tool_manager/index.php create mode 100644 mods/_core/tool_manager/module.php create mode 100644 mods/_core/tool_manager/module.xml create mode 100644 mods/_core/users/admin_delete.php create mode 100644 mods/_core/users/admin_deny.php create mode 100644 mods/_core/users/admin_email.php create mode 100644 mods/_core/users/admins/create.php create mode 100644 mods/_core/users/admins/delete.php create mode 100644 mods/_core/users/admins/detail_log.php create mode 100644 mods/_core/users/admins/edit.php create mode 100644 mods/_core/users/admins/index.php create mode 100644 mods/_core/users/admins/log.php create mode 100644 mods/_core/users/admins/my_edit.php create mode 100644 mods/_core/users/admins/my_password.php create mode 100644 mods/_core/users/admins/password.php create mode 100644 mods/_core/users/admins/reset_log.php create mode 100644 mods/_core/users/create_user.php create mode 100644 mods/_core/users/default_preferences.php create mode 100644 mods/_core/users/edit_user.php create mode 100644 mods/_core/users/index.php create mode 100644 mods/_core/users/instructor_requests.php create mode 100644 mods/_core/users/lib/pref_functions.inc.php create mode 100644 mods/_core/users/lib/pref_tab_functions.inc.php create mode 100644 mods/_core/users/master_list.php create mode 100644 mods/_core/users/master_list_delete.php create mode 100644 mods/_core/users/master_list_edit.php create mode 100644 mods/_core/users/module.php create mode 100644 mods/_core/users/module.xml create mode 100644 mods/_core/users/password_user.php create mode 100644 mods/_core/users/user_enrollment.php create mode 100644 mods/_core/users/user_status.php create mode 100644 mods/_core/users/users.php create mode 100644 mods/_standard/announcements/add_news.php create mode 100644 mods/_standard/announcements/delete_news.php create mode 100644 mods/_standard/announcements/edit_news.php create mode 100644 mods/_standard/announcements/index.php create mode 100644 mods/_standard/announcements/module.php create mode 100644 mods/_standard/announcements/module.xml create mode 100644 mods/_standard/announcements/module_backup.php create mode 100644 mods/_standard/announcements/module_delete.php create mode 100644 mods/_standard/announcements/module_news.php create mode 100644 mods/_standard/assignments/add_assignment.php create mode 100644 mods/_standard/assignments/delete_assignment.php create mode 100644 mods/_standard/assignments/edit_assignment.php create mode 100644 mods/_standard/assignments/index.php create mode 100644 mods/_standard/assignments/index_instructor.php create mode 100644 mods/_standard/assignments/module.php create mode 100644 mods/_standard/assignments/module.sql create mode 100644 mods/_standard/assignments/module.xml create mode 100644 mods/_standard/assignments/module_backup.php create mode 100644 mods/_standard/assignments/module_delete.php create mode 100644 mods/_standard/assignments/module_groups.php create mode 100644 mods/_standard/assignments/module_news.php create mode 100644 mods/_standard/blogs/add_post.php create mode 100644 mods/_standard/blogs/delete_comment.php create mode 100644 mods/_standard/blogs/delete_post.php create mode 100644 mods/_standard/blogs/edit_post.php create mode 100644 mods/_standard/blogs/index.php create mode 100644 mods/_standard/blogs/module.php create mode 100644 mods/_standard/blogs/module.xml create mode 100644 mods/_standard/blogs/module_delete.php create mode 100644 mods/_standard/blogs/module_groups.php create mode 100644 mods/_standard/blogs/module_news.php create mode 100644 mods/_standard/blogs/post.php create mode 100644 mods/_standard/blogs/sublinks.php create mode 100644 mods/_standard/blogs/view.php create mode 100644 mods/_standard/calendar/module.php create mode 100644 mods/_standard/calendar/module.xml create mode 100644 mods/_standard/calendar/module_backup.php create mode 100644 mods/_standard/calendar/module_delete.php create mode 100644 mods/_standard/calendar/module_groups.php create mode 100644 mods/_standard/chat/MachineThatGoesBing.class create mode 100644 mods/_standard/chat/admin.settings.default create mode 100644 mods/_standard/chat/atrc.gif create mode 100644 mods/_standard/chat/bing.php create mode 100644 mods/_standard/chat/bings/.html create mode 100644 mods/_standard/chat/bings/chime.au create mode 100644 mods/_standard/chat/bings/chime.wav create mode 100644 mods/_standard/chat/bings/taras.html create mode 100644 mods/_standard/chat/bings/taras.php create mode 100644 mods/_standard/chat/chat.php create mode 100644 mods/_standard/chat/display.php create mode 100644 mods/_standard/chat/filterHistory.php create mode 100644 mods/_standard/chat/help.php create mode 100644 mods/_standard/chat/history.php create mode 100644 mods/_standard/chat/include/html/chat_footer.inc.php create mode 100644 mods/_standard/chat/include/html/chat_header.inc.php create mode 100644 mods/_standard/chat/include/html/login_footer.inc.php create mode 100644 mods/_standard/chat/include/html/login_header.inc.php create mode 100644 mods/_standard/chat/index.php create mode 100644 mods/_standard/chat/index.php.old create mode 100644 mods/_standard/chat/lib/chat.inc.php create mode 100644 mods/_standard/chat/lib/chat_defaults.inc.php create mode 100644 mods/_standard/chat/logout.php create mode 100644 mods/_standard/chat/manage/delete_transcript.php create mode 100644 mods/_standard/chat/manage/index.php create mode 100644 mods/_standard/chat/manage/start_transcript.php create mode 100644 mods/_standard/chat/manage/view_transcript.php create mode 100644 mods/_standard/chat/module.php create mode 100644 mods/_standard/chat/module.xml create mode 100644 mods/_standard/chat/module_delete.php create mode 100644 mods/_standard/chat/options.php create mode 100644 mods/_standard/chat/poster.php create mode 100644 mods/_standard/chat/prefs.php create mode 100644 mods/_standard/chat/prefs2.php create mode 100644 mods/_standard/chat/sublinks.php create mode 100644 mods/_standard/chat/view_transcript.php create mode 100644 mods/_standard/course_email/course_email.php create mode 100644 mods/_standard/course_email/module.php create mode 100644 mods/_standard/course_email/module.xml create mode 100644 mods/_standard/course_tools/module.php create mode 100644 mods/_standard/course_tools/module.xml create mode 100644 mods/_standard/course_tools/modules.php create mode 100644 mods/_standard/course_tools/side_menu.php create mode 100644 mods/_standard/directory/directory.php create mode 100644 mods/_standard/directory/module.php create mode 100644 mods/_standard/directory/module.xml create mode 100644 mods/_standard/directory/sublinks.php create mode 100644 mods/_standard/faq/add_question.php create mode 100644 mods/_standard/faq/add_topic.php create mode 100644 mods/_standard/faq/delete_question.php create mode 100644 mods/_standard/faq/delete_topic.php create mode 100644 mods/_standard/faq/edit_question.php create mode 100644 mods/_standard/faq/edit_topic.php create mode 100644 mods/_standard/faq/icon.gif create mode 100644 mods/_standard/faq/index.php create mode 100644 mods/_standard/faq/index_instructor.php create mode 100644 mods/_standard/faq/module.php create mode 100644 mods/_standard/faq/module.xml create mode 100644 mods/_standard/faq/module_backup.php create mode 100644 mods/_standard/faq/module_delete.php create mode 100644 mods/_standard/faq/module_news.php create mode 100644 mods/_standard/faq/sublinks.php create mode 100755 mods/_standard/farchive/forum_post.php create mode 100755 mods/_standard/farchive/index.php create mode 100755 mods/_standard/farchive/index_instructor.php create mode 100755 mods/_standard/farchive/module.php create mode 100755 mods/_standard/farchive/module.xml create mode 100755 mods/_standard/farchive/send_zip_archive.php create mode 100755 mods/_standard/farchive/styles.css create mode 100644 mods/_standard/file_storage/assignment.php create mode 100644 mods/_standard/file_storage/comments.php create mode 100644 mods/_standard/file_storage/delete_comment.php create mode 100644 mods/_standard/file_storage/delete_revision.php create mode 100644 mods/_standard/file_storage/edit.php create mode 100644 mods/_standard/file_storage/edit_folder.php create mode 100644 mods/_standard/file_storage/file_storage.inc.php create mode 100644 mods/_standard/file_storage/index.php create mode 100644 mods/_standard/file_storage/module.php create mode 100644 mods/_standard/file_storage/module.xml create mode 100644 mods/_standard/file_storage/module_delete.php create mode 100644 mods/_standard/file_storage/module_groups.php create mode 100644 mods/_standard/file_storage/module_news.php create mode 100644 mods/_standard/file_storage/move.php create mode 100644 mods/_standard/file_storage/new.php create mode 100644 mods/_standard/file_storage/revisions.php create mode 100644 mods/_standard/file_storage/sublinks.php create mode 100644 mods/_standard/flowplayer/LICENSE.txt create mode 100644 mods/_standard/flowplayer/README.txt create mode 100644 mods/_standard/flowplayer/flowplayer-3.1.2.min.js create mode 100644 mods/_standard/flowplayer/flowplayer-3.1.2.swf create mode 100644 mods/_standard/flowplayer/flowplayer.controls-3.1.2.swf create mode 100644 mods/_standard/flowplayer/module.php create mode 100644 mods/_standard/flowplayer/module.xml create mode 100644 mods/_standard/flowplayer/module_format_content.php create mode 100644 mods/_standard/forums/add_forum.php create mode 100644 mods/_standard/forums/admin/forum_add.php create mode 100644 mods/_standard/forums/admin/forum_delete.php create mode 100644 mods/_standard/forums/admin/forum_edit.php create mode 100644 mods/_standard/forums/admin/forums.php create mode 100644 mods/_standard/forums/delete_forum.php create mode 100644 mods/_standard/forums/dropdown/posts.inc.php create mode 100644 mods/_standard/forums/edit_forum.php create mode 100644 mods/_standard/forums/edit_post.php create mode 100644 mods/_standard/forums/forum/delete_thread.php create mode 100644 mods/_standard/forums/forum/index.php create mode 100644 mods/_standard/forums/forum/list.php create mode 100644 mods/_standard/forums/forum/lock_thread.php create mode 100644 mods/_standard/forums/forum/move_thread.php create mode 100644 mods/_standard/forums/forum/new_thread.php create mode 100644 mods/_standard/forums/forum/stick.php create mode 100644 mods/_standard/forums/forum/subscribe.php create mode 100644 mods/_standard/forums/forum/subscribe_forum.php create mode 100644 mods/_standard/forums/forum/view.php create mode 100644 mods/_standard/forums/html/forum.inc.php create mode 100644 mods/_standard/forums/html/new_thread.inc.php create mode 100644 mods/_standard/forums/index.php create mode 100644 mods/_standard/forums/lib/forums.inc.php create mode 100644 mods/_standard/forums/module.php create mode 100644 mods/_standard/forums/module.xml create mode 100644 mods/_standard/forums/module_delete.php create mode 100644 mods/_standard/forums/module_format_content.php create mode 100644 mods/_standard/forums/module_groups.php create mode 100644 mods/_standard/forums/module_news.php create mode 100644 mods/_standard/forums/sublinks.php create mode 100644 mods/_standard/forums/view_item.php create mode 100644 mods/_standard/google_search/SOAP_Google.php create mode 100644 mods/_standard/google_search/admin/module_prefs.php create mode 100644 mods/_standard/google_search/google.gif create mode 100644 mods/_standard/google_search/gsearch.php create mode 100644 mods/_standard/google_search/index.php create mode 100644 mods/_standard/google_search/module.css create mode 100644 mods/_standard/google_search/module.php create mode 100644 mods/_standard/google_search/module.xml create mode 100644 mods/_standard/google_search/side_menu.inc.php create mode 100644 mods/_standard/gradebook/README create mode 100644 mods/_standard/gradebook/edit_marks.php create mode 100644 mods/_standard/gradebook/grade_scale.php create mode 100644 mods/_standard/gradebook/grade_scale_add.php create mode 100644 mods/_standard/gradebook/grade_scale_delete.php create mode 100644 mods/_standard/gradebook/grade_scale_edit.php create mode 100644 mods/_standard/gradebook/gradebook.png create mode 100644 mods/_standard/gradebook/gradebook_add_tests.php create mode 100644 mods/_standard/gradebook/gradebook_delete_tests.php create mode 100644 mods/_standard/gradebook/gradebook_edit_tests.php create mode 100644 mods/_standard/gradebook/gradebook_tests.php create mode 100644 mods/_standard/gradebook/html/grade_scale_add_edit.inc.php create mode 100644 mods/_standard/gradebook/import_export_external_marks.php create mode 100644 mods/_standard/gradebook/lib/gradebook.inc.php create mode 100644 mods/_standard/gradebook/module.php create mode 100644 mods/_standard/gradebook/module.xml create mode 100644 mods/_standard/gradebook/my_gradebook.php create mode 100644 mods/_standard/gradebook/update_gradebook.php create mode 100644 mods/_standard/gradebook/verify_list.php create mode 100644 mods/_standard/gradebook/verify_tests.php create mode 100644 mods/_standard/links/add.php create mode 100644 mods/_standard/links/index.php create mode 100644 mods/_standard/links/lib/links.inc.php create mode 100644 mods/_standard/links/module.php create mode 100644 mods/_standard/links/module.xml create mode 100644 mods/_standard/links/module_backup.php create mode 100644 mods/_standard/links/module_delete.php create mode 100644 mods/_standard/links/module_groups.php create mode 100644 mods/_standard/links/module_news.php create mode 100644 mods/_standard/links/sublinks.php create mode 100644 mods/_standard/links/tools/add.php create mode 100644 mods/_standard/links/tools/categories.php create mode 100644 mods/_standard/links/tools/categories_create.php create mode 100644 mods/_standard/links/tools/categories_delete.php create mode 100644 mods/_standard/links/tools/categories_edit.php create mode 100644 mods/_standard/links/tools/delete.php create mode 100644 mods/_standard/links/tools/edit.php create mode 100644 mods/_standard/links/tools/index.php create mode 100644 mods/_standard/patcher/classes/Patch.class.php create mode 100644 mods/_standard/patcher/classes/PatchCreator.class.php create mode 100644 mods/_standard/patcher/classes/PatchListParser.class.php create mode 100644 mods/_standard/patcher/classes/PatchParser.class.php create mode 100644 mods/_standard/patcher/include/common.inc.php create mode 100644 mods/_standard/patcher/include/json.inc.php create mode 100644 mods/_standard/patcher/include/patch_xml_template.inc.php create mode 100644 mods/_standard/patcher/index_admin.php create mode 100644 mods/_standard/patcher/module.php create mode 100644 mods/_standard/patcher/module.xml create mode 100644 mods/_standard/patcher/myown_patches.php create mode 100644 mods/_standard/patcher/patch_create.php create mode 100644 mods/_standard/patcher/patch_creator.php create mode 100644 mods/_standard/patcher/patch_delete.php create mode 100644 mods/_standard/patcher/patch_edit.php create mode 100644 mods/_standard/patcher/patch_edit_interface.tmpl.php create mode 100644 mods/_standard/patcher/sample_patch.xml create mode 100644 mods/_standard/patcher/sample_patch_list.xml create mode 100644 mods/_standard/patcher/xml_special_chars.txt create mode 100644 mods/_standard/photos/addComment.php create mode 100644 mods/_standard/photos/admin/edit_album.php create mode 100644 mods/_standard/photos/admin/edit_photos.php create mode 100644 mods/_standard/photos/admin/preferences.php create mode 100644 mods/_standard/photos/albums.php create mode 100644 mods/_standard/photos/course_albums.php create mode 100644 mods/_standard/photos/create_album.php create mode 100644 mods/_standard/photos/delete_album.php create mode 100644 mods/_standard/photos/delete_comment.php create mode 100644 mods/_standard/photos/delete_photo.php create mode 100644 mods/_standard/photos/edit_album.php create mode 100644 mods/_standard/photos/edit_comment.php create mode 100644 mods/_standard/photos/edit_photos.php create mode 100644 mods/_standard/photos/get_photo.php create mode 100644 mods/_standard/photos/images/loading.gif create mode 100644 mods/_standard/photos/images/next.png create mode 100644 mods/_standard/photos/images/no.png create mode 100644 mods/_standard/photos/images/photo_gallery.png create mode 100644 mods/_standard/photos/images/prev.png create mode 100644 mods/_standard/photos/images/search_icon.png create mode 100644 mods/_standard/photos/include/ajaxupload.js create mode 100644 mods/_standard/photos/include/classes/AjaxMessage.class.php create mode 100644 mods/_standard/photos/include/classes/PhotoAlbum.class.php create mode 100644 mods/_standard/photos/include/classes/SimpleImage.class.php create mode 100644 mods/_standard/photos/include/constants.inc.php create mode 100644 mods/_standard/photos/include/edit_album.inc.php create mode 100644 mods/_standard/photos/include/edit_photos.inc.php create mode 100644 mods/_standard/photos/include/imageReorderer.js create mode 100644 mods/_standard/photos/include/lib.inc.php create mode 100644 mods/_standard/photos/include/profile_album.inc.php create mode 100644 mods/_standard/photos/include/upload.js create mode 100644 mods/_standard/photos/index.php create mode 100644 mods/_standard/photos/index_admin.php create mode 100644 mods/_standard/photos/module.css create mode 100644 mods/_standard/photos/module.php create mode 100644 mods/_standard/photos/module.xml create mode 100644 mods/_standard/photos/photo.php create mode 100644 mods/_standard/photos/profile_album.php create mode 100644 mods/_standard/photos/remove_uploaded_photo.php create mode 100644 mods/_standard/photos/search.php create mode 100644 mods/_standard/photos/set_profile_picture.php create mode 100644 mods/_standard/photos/shared_albums.php create mode 100644 mods/_standard/photos/sublinks.php create mode 100644 mods/_standard/polls/dropdown/poll.inc.php create mode 100644 mods/_standard/polls/index.php create mode 100644 mods/_standard/polls/module.php create mode 100644 mods/_standard/polls/module.xml create mode 100644 mods/_standard/polls/module_backup.php create mode 100644 mods/_standard/polls/module_delete.php create mode 100644 mods/_standard/polls/module_news.php create mode 100644 mods/_standard/polls/sublinks.php create mode 100644 mods/_standard/polls/tools/add.php create mode 100644 mods/_standard/polls/tools/delete.php create mode 100644 mods/_standard/polls/tools/edit.php create mode 100644 mods/_standard/polls/tools/index.php create mode 100644 mods/_standard/profile_pictures/admin/profile_picture.php create mode 100644 mods/_standard/profile_pictures/html/profile_picture.inc.php create mode 100644 mods/_standard/profile_pictures/module.php create mode 100644 mods/_standard/profile_pictures/module.xml create mode 100644 mods/_standard/profile_pictures/profile.php create mode 100644 mods/_standard/profile_pictures/profile_picture.php create mode 100644 mods/_standard/profile_pictures/save_profile_picture.php create mode 100644 mods/_standard/reading_list/add_resource_av.php create mode 100644 mods/_standard/reading_list/add_resource_book.php create mode 100644 mods/_standard/reading_list/add_resource_file.php create mode 100644 mods/_standard/reading_list/add_resource_handout.php create mode 100644 mods/_standard/reading_list/add_resource_url.php create mode 100644 mods/_standard/reading_list/delete_reading.php create mode 100644 mods/_standard/reading_list/delete_resource.php create mode 100644 mods/_standard/reading_list/display_resource.php create mode 100644 mods/_standard/reading_list/display_resources.php create mode 100644 mods/_standard/reading_list/edit_reading_av.php create mode 100644 mods/_standard/reading_list/edit_reading_book.php create mode 100644 mods/_standard/reading_list/edit_reading_file.php create mode 100644 mods/_standard/reading_list/edit_reading_handout.php create mode 100644 mods/_standard/reading_list/edit_reading_url.php create mode 100644 mods/_standard/reading_list/index.php create mode 100644 mods/_standard/reading_list/index_instructor.php create mode 100644 mods/_standard/reading_list/module.php create mode 100644 mods/_standard/reading_list/module.xml create mode 100644 mods/_standard/reading_list/module_backup.php create mode 100644 mods/_standard/reading_list/module_delete.php create mode 100644 mods/_standard/reading_list/module_news.php create mode 100644 mods/_standard/reading_list/new_reading_av.php create mode 100644 mods/_standard/reading_list/new_reading_book.php create mode 100644 mods/_standard/reading_list/new_reading_file.php create mode 100644 mods/_standard/reading_list/new_reading_handout.php create mode 100644 mods/_standard/reading_list/new_reading_url.php create mode 100644 mods/_standard/reading_list/reading_details.php create mode 100644 mods/_standard/reading_list/sublinks.php create mode 100644 mods/_standard/rss_feeds/add_feed.php create mode 100644 mods/_standard/rss_feeds/classes/lastRSS.php create mode 100644 mods/_standard/rss_feeds/delete_feed.php create mode 100644 mods/_standard/rss_feeds/edit_feed.php create mode 100644 mods/_standard/rss_feeds/index.php create mode 100644 mods/_standard/rss_feeds/load_file.php create mode 100644 mods/_standard/rss_feeds/module.php create mode 100644 mods/_standard/rss_feeds/module.xml create mode 100644 mods/_standard/rss_feeds/preview.php create mode 100644 mods/_standard/sitemap/module.php create mode 100644 mods/_standard/sitemap/module.xml create mode 100644 mods/_standard/sitemap/sitemap.php create mode 100644 mods/_standard/social/README create mode 100644 mods/_standard/social/activities.php create mode 100644 mods/_standard/social/admin/delete_applications.php create mode 100644 mods/_standard/social/applications.php create mode 100644 mods/_standard/social/basic_profile.php create mode 100644 mods/_standard/social/connections.php create mode 100644 mods/_standard/social/edit_profile.php create mode 100644 mods/_standard/social/groups/create.php create mode 100644 mods/_standard/social/groups/delete.php create mode 100644 mods/_standard/social/groups/delete_message.php create mode 100644 mods/_standard/social/groups/edit.php create mode 100644 mods/_standard/social/groups/get_sgroup_logo.php create mode 100644 mods/_standard/social/groups/index.php create mode 100644 mods/_standard/social/groups/invitation_handler.php create mode 100644 mods/_standard/social/groups/invite.php create mode 100644 mods/_standard/social/groups/join.php create mode 100644 mods/_standard/social/groups/list.php create mode 100644 mods/_standard/social/groups/search.php create mode 100644 mods/_standard/social/groups/view.php create mode 100644 mods/_standard/social/images/b_drop.png create mode 100644 mods/_standard/social/images/check_icon.gif create mode 100644 mods/_standard/social/images/check_icon.jpg create mode 100644 mods/_standard/social/images/check_icon.png create mode 100644 mods/_standard/social/images/edit_profile.gif create mode 100644 mods/_standard/social/images/icon-settings.png create mode 100644 mods/_standard/social/images/nophoto.gif create mode 100644 mods/_standard/social/images/placelogo.gif create mode 100644 mods/_standard/social/images/placelogo.png create mode 100644 mods/_standard/social/images/plus_icon.gif create mode 100644 mods/_standard/social/images/plus_icon.jpg create mode 100644 mods/_standard/social/images/social.gif create mode 100644 mods/_standard/social/index.php create mode 100644 mods/_standard/social/index_admin.php create mode 100644 mods/_standard/social/index_instructor.php create mode 100644 mods/_standard/social/index_mystart.php create mode 100644 mods/_standard/social/index_public.php create mode 100644 mods/_standard/social/lib/BasicBlobCrypter.php create mode 100644 mods/_standard/social/lib/BasicSecurityToken.php create mode 100644 mods/_standard/social/lib/BlobCrypter.php create mode 100644 mods/_standard/social/lib/Crypto.php create mode 100644 mods/_standard/social/lib/OAuth/OAuth.php create mode 100644 mods/_standard/social/lib/OAuth/access_token.php create mode 100644 mods/_standard/social/lib/OAuth/approve_authorization.php create mode 100644 mods/_standard/social/lib/OAuth/authorize.php create mode 100644 mods/_standard/social/lib/OAuth/common.inc.php create mode 100644 mods/_standard/social/lib/OAuth/request_token.php create mode 100644 mods/_standard/social/lib/SecurityToken.php create mode 100644 mods/_standard/social/lib/Shindig/ATutorActivityService.php create mode 100644 mods/_standard/social/lib/Shindig/ATutorAppDataService.php create mode 100644 mods/_standard/social/lib/Shindig/ATutorDbFetcher.php create mode 100644 mods/_standard/social/lib/Shindig/ATutorMessagesService.php create mode 100644 mods/_standard/social/lib/Shindig/ATutorOAuthDataStore.php create mode 100644 mods/_standard/social/lib/Shindig/ATutorOAuthLookupService.php create mode 100644 mods/_standard/social/lib/Shindig/ATutorPersonService.php create mode 100644 mods/_standard/social/lib/Shindig/ATutorService.php create mode 100644 mods/_standard/social/lib/Shindig/README create mode 100644 mods/_standard/social/lib/classes/Activity.class.php create mode 100644 mods/_standard/social/lib/classes/Application.class.php create mode 100644 mods/_standard/social/lib/classes/Applications.class.php create mode 100644 mods/_standard/social/lib/classes/Member.class.php create mode 100644 mods/_standard/social/lib/classes/PrivacyControl/PrivacyController.class.php create mode 100644 mods/_standard/social/lib/classes/PrivacyControl/PrivacyObject.class.php create mode 100644 mods/_standard/social/lib/classes/PrivacyControl/SetPrivacyControl.php create mode 100644 mods/_standard/social/lib/classes/SocialGroups/SocialGroup.class.php create mode 100644 mods/_standard/social/lib/classes/SocialGroups/SocialGroups.class.php create mode 100644 mods/_standard/social/lib/constants.inc.php create mode 100644 mods/_standard/social/lib/friends.inc.php create mode 100644 mods/_standard/social/lib/js/container.js create mode 100644 mods/_standard/social/lib/js/livesearch.js create mode 100644 mods/_standard/social/lib/js/prototype.js create mode 100644 mods/_standard/social/lib/profile_menu.inc.php create mode 100644 mods/_standard/social/module.php create mode 100644 mods/_standard/social/module.sql create mode 100644 mods/_standard/social/module.xml create mode 100644 mods/_standard/social/module_backup.php create mode 100644 mods/_standard/social/module_news.php create mode 100644 mods/_standard/social/privacy_settings.php create mode 100644 mods/_standard/social/profile_picture.php create mode 100644 mods/_standard/social/set_prefs.php create mode 100644 mods/_standard/social/settings.php create mode 100644 mods/_standard/social/side_menu.inc.php create mode 100644 mods/_standard/social/sprofile.php create mode 100644 mods/_standard/social/sublinks.php create mode 100644 mods/_standard/statistics/course_stats.php create mode 100644 mods/_standard/statistics/module.php create mode 100644 mods/_standard/statistics/module.xml create mode 100644 mods/_standard/statistics/module_backup.php create mode 100644 mods/_standard/statistics/module_delete.php create mode 100644 mods/_standard/student_tools/icon.gif create mode 100644 mods/_standard/student_tools/index.php create mode 100644 mods/_standard/student_tools/instructor_index.php create mode 100644 mods/_standard/student_tools/module.php create mode 100644 mods/_standard/student_tools/module.xml create mode 100644 mods/_standard/support_tools/module.php create mode 100644 mods/_standard/support_tools/module.xml create mode 100644 mods/_standard/support_tools/scaffolds.php create mode 100644 mods/_standard/support_tools/side_menu.inc.php create mode 100644 mods/_standard/tests/add_test_questions.php create mode 100644 mods/_standard/tests/add_test_questions_confirm.php create mode 100644 mods/_standard/tests/classes/testQuestions.class.php create mode 100644 mods/_standard/tests/create_question_likert.php create mode 100644 mods/_standard/tests/create_question_long.php create mode 100644 mods/_standard/tests/create_question_matching.php create mode 100644 mods/_standard/tests/create_question_matchingdd.php create mode 100644 mods/_standard/tests/create_question_multi.php create mode 100644 mods/_standard/tests/create_question_multianswer.php create mode 100644 mods/_standard/tests/create_question_multichoice.php create mode 100644 mods/_standard/tests/create_question_ordering.php create mode 100644 mods/_standard/tests/create_question_tf.php create mode 100644 mods/_standard/tests/create_question_truefalse.php create mode 100644 mods/_standard/tests/create_test.php create mode 100644 mods/_standard/tests/dd.php create mode 100644 mods/_standard/tests/delete_question.php create mode 100644 mods/_standard/tests/delete_result.php create mode 100644 mods/_standard/tests/delete_test.php create mode 100644 mods/_standard/tests/edit_question_likert.php create mode 100644 mods/_standard/tests/edit_question_long.php create mode 100644 mods/_standard/tests/edit_question_matching.php create mode 100644 mods/_standard/tests/edit_question_matchingdd.php create mode 100644 mods/_standard/tests/edit_question_multi.php create mode 100644 mods/_standard/tests/edit_question_multianswer.php create mode 100644 mods/_standard/tests/edit_question_multichoice.php create mode 100644 mods/_standard/tests/edit_question_ordering.php create mode 100644 mods/_standard/tests/edit_question_tf.php create mode 100644 mods/_standard/tests/edit_question_truefalse.php create mode 100644 mods/_standard/tests/edit_test.php create mode 100644 mods/_standard/tests/export_test.php create mode 100644 mods/_standard/tests/form_editor.php create mode 100644 mods/_standard/tests/html/tests_questions.inc.php create mode 100644 mods/_standard/tests/import_test.php create mode 100644 mods/_standard/tests/index.php create mode 100644 mods/_standard/tests/lib/likert_presets.inc.php create mode 100644 mods/_standard/tests/lib/take_test.js create mode 100644 mods/_standard/tests/lib/test_question_queries.inc.php create mode 100644 mods/_standard/tests/lib/test_result_functions.inc.php create mode 100644 mods/_standard/tests/module.php create mode 100644 mods/_standard/tests/module.xml create mode 100644 mods/_standard/tests/module_backup.php create mode 100644 mods/_standard/tests/module_delete.php create mode 100644 mods/_standard/tests/module_groups.php create mode 100644 mods/_standard/tests/module_news.php create mode 100644 mods/_standard/tests/my_tests.php create mode 100644 mods/_standard/tests/preview.php create mode 100644 mods/_standard/tests/preview_question.php create mode 100644 mods/_standard/tests/question_cats.php create mode 100644 mods/_standard/tests/question_cats_delete.php create mode 100644 mods/_standard/tests/question_cats_manage.php create mode 100644 mods/_standard/tests/question_db.php create mode 100644 mods/_standard/tests/question_import.php create mode 100644 mods/_standard/tests/question_remove.php create mode 100644 mods/_standard/tests/questions.php create mode 100644 mods/_standard/tests/results.php create mode 100644 mods/_standard/tests/results_all.php create mode 100644 mods/_standard/tests/results_all_quest.php create mode 100644 mods/_standard/tests/results_quest_long.php create mode 100644 mods/_standard/tests/sublinks.php create mode 100644 mods/_standard/tests/take_test.php create mode 100644 mods/_standard/tests/take_test_q.php create mode 100644 mods/_standard/tests/test_intro.php create mode 100644 mods/_standard/tests/view_results.php create mode 100644 mods/_standard/tests/view_results_manage.php create mode 100644 mods/_standard/tile_search/TILE.css create mode 100644 mods/_standard/tile_search/admin/module_setup.php create mode 100644 mods/_standard/tile_search/classes/ResultParser.class.php create mode 100644 mods/_standard/tile_search/import.php create mode 100644 mods/_standard/tile_search/index.php create mode 100644 mods/_standard/tile_search/module.php create mode 100644 mods/_standard/tile_search/module.xml create mode 100644 mods/_standard/tile_search/tile.php create mode 100644 mods/_standard/tracker/course_tracker.php create mode 100644 mods/_standard/tracker/lib/tracker.inc.php create mode 100644 mods/_standard/tracker/lib/tracker2.inc.php create mode 100644 mods/_standard/tracker/lib/tracker_stats.inc.php create mode 100644 mods/_standard/tracker/lib/tracker_stats2.inc.php create mode 100644 mods/_standard/tracker/module.php create mode 100644 mods/_standard/tracker/module.xml create mode 100644 mods/_standard/tracker/my_stats.php create mode 100644 mods/_standard/tracker/sublinks.php create mode 100644 mods/_standard/tracker/tools/export.php create mode 100644 mods/_standard/tracker/tools/index.php create mode 100644 mods/_standard/tracker/tools/page_student_stats.php create mode 100644 mods/_standard/tracker/tools/reset.php create mode 100644 mods/_standard/tracker/tools/student_usage.php create mode 100644 mods/_standard/tracker/tracker.php create mode 100644 mods/index.html create mode 100644 move_module.php create mode 100644 password_reminder.php create mode 100644 popuphelp.php create mode 100644 profile.php create mode 100644 readme create mode 100644 registration.php create mode 100644 search.php create mode 100644 sha-1factory.js create mode 100644 svn.php create mode 100644 switch_view.php create mode 100644 themes/blumin/content.tmpl.php create mode 100644 themes/blumin/forms.css create mode 100644 themes/blumin/ie_styles.css create mode 100644 themes/blumin/images/arrow_ltr.gif create mode 100644 themes/blumin/images/back.gif create mode 100644 themes/blumin/images/continue.gif create mode 100644 themes/blumin/images/linkOpaque.gif create mode 100644 themes/blumin/images/linkTransparent.gif create mode 100644 themes/blumin/images/newsitem_icon.gif create mode 100644 themes/blumin/images/next.gif create mode 100644 themes/blumin/images/previous.gif create mode 100644 themes/blumin/images/resume.gif create mode 100644 themes/blumin/images/side_arrow.gif create mode 100644 themes/blumin/images/sort.gif create mode 100644 themes/blumin/images/user-star.gif create mode 100644 themes/blumin/images/user.gif create mode 100644 themes/blumin/include/box.tmpl.php create mode 100644 themes/blumin/include/footer.tmpl.php create mode 100644 themes/blumin/include/header.tmpl.php create mode 100644 themes/blumin/include/side_menu.tmpl.php create mode 100644 themes/blumin/print.css create mode 100644 themes/blumin/readme.txt create mode 100644 themes/blumin/screenshot.gif create mode 100644 themes/blumin/styles.css create mode 100644 themes/blumin/theme.cfg.php create mode 100644 themes/blumin/theme_info.xml create mode 100644 themes/default/confirmmessage.tmpl.php create mode 100644 themes/default/content.tmpl.php create mode 100644 themes/default/directory.tmpl.php create mode 100644 themes/default/editor/arrange_content.tmpl.php create mode 100644 themes/default/editor/edit_content_folder.tmpl.php create mode 100644 themes/default/errormessage.tmpl.php create mode 100644 themes/default/feedbackmessage.tmpl.php create mode 100644 themes/default/forms.css create mode 100644 themes/default/ie_styles.css create mode 100644 themes/default/images/Thumbs.db create mode 100644 themes/default/images/arrow_ltr.gif create mode 100644 themes/default/images/atutor_head.jpg create mode 100644 themes/default/images/atutor_head.psd create mode 100644 themes/default/images/atutor_logo.png create mode 100644 themes/default/images/atutor_logo.psd create mode 100644 themes/default/images/atutor_logo2.png create mode 100644 themes/default/images/continue.gif create mode 100644 themes/default/images/edit.gif create mode 100644 themes/default/images/help.png create mode 100644 themes/default/images/hidemenu.gif create mode 100644 themes/default/images/linkOpaque.gif create mode 100644 themes/default/images/linkTransparent.gif create mode 100644 themes/default/images/mswitch_minus.gif create mode 100644 themes/default/images/mswitch_plus.gif create mode 100644 themes/default/images/newsitem_icon.gif create mode 100644 themes/default/images/next.png create mode 100644 themes/default/images/pencil_bottom.gif create mode 100644 themes/default/images/pencil_bottom_ds.gif create mode 100644 themes/default/images/pencil_bottom_ds.png create mode 100644 themes/default/images/pencil_top.gif create mode 100644 themes/default/images/pencil_top_ds.png create mode 100644 themes/default/images/pencils.gif create mode 100644 themes/default/images/pencils_up.jpg create mode 100644 themes/default/images/pencils_up_bottom.jpg create mode 100644 themes/default/images/pencils_up_top.jpg create mode 100644 themes/default/images/photos_arrange.png create mode 100644 themes/default/images/previous.gif create mode 100644 themes/default/images/previous.png create mode 100644 themes/default/images/profile.gif create mode 100644 themes/default/images/resume.png create mode 100644 themes/default/images/resume2.png create mode 100644 themes/default/images/showmenu.gif create mode 100644 themes/default/images/side_arrow.gif create mode 100644 themes/default/images/sort.png create mode 100644 themes/default/images/texture_bg.png create mode 100644 themes/default/images/tl_corner.gif create mode 100644 themes/default/images/top.gif create mode 100644 themes/default/images/tree/index.html create mode 100644 themes/default/images/tree/tree_collapse.gif create mode 100644 themes/default/images/tree/tree_disabled.gif create mode 100644 themes/default/images/tree/tree_end.gif create mode 100644 themes/default/images/tree/tree_expand.gif create mode 100644 themes/default/images/tree/tree_horizontal.gif create mode 100644 themes/default/images/tree/tree_space.gif create mode 100644 themes/default/images/tree/tree_split.gif create mode 100644 themes/default/images/tree/tree_vertline.gif create mode 100644 themes/default/images/up.png create mode 100644 themes/default/images/user-star.gif create mode 100644 themes/default/images/user.gif create mode 100644 themes/default/images/x.gif create mode 100644 themes/default/include/box.tmpl.php create mode 100644 themes/default/include/fm_footer.tmpl.php create mode 100644 themes/default/include/fm_header.tmpl.php create mode 100644 themes/default/include/footer.tmpl.php create mode 100644 themes/default/include/forms.css create mode 100644 themes/default/include/header.tmpl.php create mode 100644 themes/default/include/side_menu.tmpl.php create mode 100644 themes/default/include/tm_footer.tmpl.php create mode 100644 themes/default/include/tm_header.tmpl.php create mode 100644 themes/default/index.tmpl.php create mode 100644 themes/default/infomessage.tmpl.php create mode 100644 themes/default/login.tmpl.php create mode 100644 themes/default/password_change.tmpl.php create mode 100644 themes/default/password_reminder.tmpl.php create mode 100644 themes/default/password_reminder_feedback.tmpl.php create mode 100644 themes/default/photos/admin/pa_index.tmpl.php create mode 100644 themes/default/photos/admin/pa_preferences.tmpl.php create mode 100644 themes/default/photos/pa_albums.tmpl.php create mode 100644 themes/default/photos/pa_create_album.tmpl.php create mode 100644 themes/default/photos/pa_edit_album.tmpl.php create mode 100644 themes/default/photos/pa_edit_photos.tmpl.php create mode 100644 themes/default/photos/pa_index.tmpl.php create mode 100644 themes/default/photos/pa_organize_photos.tmpl.php create mode 100644 themes/default/photos/pa_photo.tmpl.php create mode 100644 themes/default/photos/pa_profile_albums.tmpl.php create mode 100644 themes/default/photos/pa_search.tmpl.php create mode 100644 themes/default/print.css create mode 100644 themes/default/profile.tmpl.php create mode 100644 themes/default/readme.txt create mode 100644 themes/default/registration.tmpl.php create mode 100644 themes/default/rtl.css create mode 100644 themes/default/screenshot.gif create mode 100644 themes/default/social/activities.tmpl.php create mode 100644 themes/default/social/admin/delete_applications.tmpl.php create mode 100644 themes/default/social/application_settings.tmpl.php create mode 100644 themes/default/social/applications.tmpl.php create mode 100644 themes/default/social/basic_profile.tmpl.php create mode 100644 themes/default/social/connections.tmpl.php create mode 100644 themes/default/social/edit_profile.tmpl.php create mode 100644 themes/default/social/edit_profile/edit_additional.tmpl.php create mode 100644 themes/default/social/edit_profile/edit_contact.tmpl.php create mode 100644 themes/default/social/edit_profile/edit_education.tmpl.php create mode 100644 themes/default/social/edit_profile/edit_personal.tmpl.php create mode 100644 themes/default/social/edit_profile/edit_position.tmpl.php create mode 100644 themes/default/social/edit_profile/edit_representation.tmpl.php create mode 100644 themes/default/social/edit_profile/edit_websites.tmpl.php create mode 100644 themes/default/social/friend_list.tmpl.php create mode 100644 themes/default/social/index_public.tmpl.php create mode 100644 themes/default/social/individual_application.tmpl.php create mode 100644 themes/default/social/notifications.tmpl.php create mode 100644 themes/default/social/oauth/authorize.tmpl.php create mode 100644 themes/default/social/oauth/footer.tmpl.php create mode 100644 themes/default/social/oauth/header.tmpl.php create mode 100644 themes/default/social/profile_picture.html.php create mode 100644 themes/default/social/settings/account_settings.tmpl.php create mode 100644 themes/default/social/settings/application_settings.tmpl.php create mode 100644 themes/default/social/settings/privacy_settings.tmpl.php create mode 100644 themes/default/social/settings/settings_menu.tmpl.php create mode 100644 themes/default/social/sgroup_create.tmpl.php create mode 100644 themes/default/social/sgroup_edit.tmpl.php create mode 100644 themes/default/social/sgroup_invite.tmpl.php create mode 100644 themes/default/social/sgroup_list.tmpl.php create mode 100644 themes/default/social/sgroup_search.tmpl.php create mode 100644 themes/default/social/sgroup_view.tmpl.php create mode 100644 themes/default/social/sgroups.tmpl.php create mode 100644 themes/default/social/sprofile.tmpl.php create mode 100644 themes/default/social/tiny_applications.tmpl.php create mode 100644 themes/default/social/tiny_sgroups.tmpl.php create mode 100644 themes/default/styles.css create mode 100644 themes/default/test_questions/footer.tmpl.php create mode 100644 themes/default/test_questions/header.tmpl.php create mode 100644 themes/default/test_questions/likert.tmpl.php create mode 100644 themes/default/test_questions/likert_qti_1p2.tmpl.php create mode 100644 themes/default/test_questions/likert_qti_2p1.tmpl.php create mode 100644 themes/default/test_questions/likert_result.tmpl.php create mode 100644 themes/default/test_questions/likert_stats.tmpl.php create mode 100644 themes/default/test_questions/long.tmpl.php create mode 100644 themes/default/test_questions/long_qti_1p2.tmpl.php create mode 100644 themes/default/test_questions/long_qti_2p1.tmpl.php create mode 100644 themes/default/test_questions/long_result.tmpl.php create mode 100644 themes/default/test_questions/long_stats.tmpl.php create mode 100644 themes/default/test_questions/manifest_qti_1p2.tmpl.php create mode 100644 themes/default/test_questions/manifest_qti_2p1.tmpl.php create mode 100644 themes/default/test_questions/matching.tmpl.php create mode 100644 themes/default/test_questions/matching_qti_1p2.tmpl.php create mode 100644 themes/default/test_questions/matching_qti_2p1.tmpl.php create mode 100644 themes/default/test_questions/matching_result.tmpl.php create mode 100644 themes/default/test_questions/matching_stats.tmpl.php create mode 100644 themes/default/test_questions/matchingdd.tmpl.php create mode 100644 themes/default/test_questions/matchingdd_qti_1p2.tmpl.php create mode 100644 themes/default/test_questions/matchingdd_qti_2p1.tmpl.php create mode 100644 themes/default/test_questions/matchingdd_result.tmpl.php create mode 100644 themes/default/test_questions/matchingdd_stats.tmpl.php create mode 100644 themes/default/test_questions/multianswer.tmpl.php create mode 100644 themes/default/test_questions/multianswer_qti_1p2.tmpl.php create mode 100644 themes/default/test_questions/multianswer_qti_2p1.tmpl.php create mode 100644 themes/default/test_questions/multianswer_result.tmpl.php create mode 100644 themes/default/test_questions/multianswer_stats.tmpl.php create mode 100644 themes/default/test_questions/multichoice.tmpl.php create mode 100644 themes/default/test_questions/multichoice_qti_1p2.tmpl.php create mode 100644 themes/default/test_questions/multichoice_qti_2p1.tmpl.php create mode 100644 themes/default/test_questions/multichoice_result.tmpl.php create mode 100644 themes/default/test_questions/multichoice_stats.tmpl.php create mode 100644 themes/default/test_questions/ordering.tmpl.php create mode 100644 themes/default/test_questions/ordering_qti_1p2.tmpl.php create mode 100644 themes/default/test_questions/ordering_qti_2p1.tmpl.php create mode 100644 themes/default/test_questions/ordering_result.tmpl.php create mode 100644 themes/default/test_questions/ordering_stats.tmpl.php create mode 100644 themes/default/test_questions/truefalse.tmpl.php create mode 100644 themes/default/test_questions/truefalse_qti_1p2.tmpl.php create mode 100644 themes/default/test_questions/truefalse_qti_2p1.tmpl.php create mode 100644 themes/default/test_questions/truefalse_result.tmpl.php create mode 100644 themes/default/test_questions/truefalse_stats.tmpl.php create mode 100644 themes/default/test_questions/wrapper.tmpl.php create mode 100644 themes/default/theme.cfg.php create mode 100644 themes/default/theme_info.xml create mode 100644 themes/default/tile_search/index.tmpl.php create mode 100644 themes/default/users/browse.tmpl.php create mode 100644 themes/default/users/email_change.tmpl.php create mode 100644 themes/default/users/index.tmpl.php create mode 100644 themes/default/users/password_change.tmpl.php create mode 100644 themes/default/users/pref_wizard/index.tmpl.php create mode 100644 themes/default/users/pref_wizard/initialize.tmpl.php create mode 100644 themes/default/users/preferences.tmpl.php create mode 100644 themes/default/users/profile.tmpl.php create mode 100644 themes/default/warningmessage.tmpl.php create mode 100644 themes/default15/confirmmessage.tmpl.php create mode 100644 themes/default15/content.tmpl.php create mode 100644 themes/default15/errormessage.tmpl.php create mode 100644 themes/default15/feedbackmessage.tmpl.php create mode 100644 themes/default15/forms.css create mode 100644 themes/default15/ie_styles.css create mode 100644 themes/default15/images/arrow_ltr.gif create mode 100644 themes/default15/images/back.gif create mode 100644 themes/default15/images/error-large.gif create mode 100644 themes/default15/images/guide.gif create mode 100644 themes/default15/images/home-acollab.png create mode 100644 themes/default15/images/home-blogs.png create mode 100644 themes/default15/images/home-chat.png create mode 100644 themes/default15/images/home-directory.png create mode 100644 themes/default15/images/home-export_content.png create mode 100644 themes/default15/images/home-faq.png create mode 100644 themes/default15/images/home-file_storage.png create mode 100644 themes/default15/images/home-forums.png create mode 100644 themes/default15/images/home-glossary.png create mode 100644 themes/default15/images/home-links.png create mode 100644 themes/default15/images/home-polls.png create mode 100644 themes/default15/images/home-reading_list.png create mode 100644 themes/default15/images/home-site_map.png create mode 100644 themes/default15/images/home-tests.png create mode 100644 themes/default15/images/home-tile_search.png create mode 100644 themes/default15/images/home-tracker.png create mode 100644 themes/default15/images/instructor.gif create mode 100644 themes/default15/images/mswitch_minus.gif create mode 100644 themes/default15/images/mswitch_plus.gif create mode 100644 themes/default15/images/next.gif create mode 100644 themes/default15/images/pen.gif create mode 100644 themes/default15/images/pen2.gif create mode 100644 themes/default15/images/pencils.jpg create mode 100644 themes/default15/images/previous.gif create mode 100644 themes/default15/images/resume.gif create mode 100644 themes/default15/images/side_arrow.gif create mode 100644 themes/default15/images/sort.gif create mode 100644 themes/default15/images/tree/index.html create mode 100644 themes/default15/images/tree/tree_collapse.gif create mode 100644 themes/default15/images/tree/tree_disabled.gif create mode 100644 themes/default15/images/tree/tree_end.gif create mode 100644 themes/default15/images/tree/tree_expand.gif create mode 100644 themes/default15/images/tree/tree_horizontal.gif create mode 100644 themes/default15/images/tree/tree_space.gif create mode 100644 themes/default15/images/tree/tree_split.gif create mode 100644 themes/default15/images/tree/tree_vertline.gif create mode 100644 themes/default15/images/user-star.gif create mode 100644 themes/default15/images/user.gif create mode 100644 themes/default15/include/box.tmpl.php create mode 100644 themes/default15/include/fm_footer.tmpl.php create mode 100644 themes/default15/include/fm_header.tmpl.php create mode 100644 themes/default15/include/footer.tmpl.php create mode 100644 themes/default15/include/header.tmpl.php create mode 100644 themes/default15/index.tmpl.php create mode 100644 themes/default15/infomessage.tmpl.php create mode 100644 themes/default15/password_change.tmpl.php create mode 100644 themes/default15/password_reminder_feedback.tmpl.php create mode 100644 themes/default15/print.css create mode 100644 themes/default15/profile.tmpl.php create mode 100644 themes/default15/readme.txt create mode 100644 themes/default15/rtl.css create mode 100644 themes/default15/screenshot.gif create mode 100644 themes/default15/styles.css create mode 100644 themes/default15/theme.cfg.php create mode 100644 themes/default15/warningmessage.tmpl.php create mode 100644 themes/default16/forms.css create mode 100644 themes/default16/ie_styles.css create mode 100644 themes/default16/images/Thumbs.db create mode 100644 themes/default16/images/arrow_ltr.gif create mode 100644 themes/default16/images/back.gif create mode 100644 themes/default16/images/continue.gif create mode 100644 themes/default16/images/guide.gif create mode 100644 themes/default16/images/linkOpaque.gif create mode 100644 themes/default16/images/linkTransparent.gif create mode 100644 themes/default16/images/mswitch_minus.gif create mode 100644 themes/default16/images/mswitch_plus.gif create mode 100644 themes/default16/images/newsitem_icon.gif create mode 100644 themes/default16/images/next.gif create mode 100644 themes/default16/images/pencil_bottom.gif create mode 100644 themes/default16/images/pencil_top.gif create mode 100644 themes/default16/images/pencils.gif create mode 100644 themes/default16/images/previous.gif create mode 100644 themes/default16/images/resume.gif create mode 100644 themes/default16/images/side_arrow.gif create mode 100644 themes/default16/images/sort.gif create mode 100644 themes/default16/images/tl_corner.gif create mode 100644 themes/default16/images/top.gif create mode 100644 themes/default16/images/tree/index.html create mode 100644 themes/default16/images/tree/tree_collapse.gif create mode 100644 themes/default16/images/tree/tree_disabled.gif create mode 100644 themes/default16/images/tree/tree_end.gif create mode 100644 themes/default16/images/tree/tree_expand.gif create mode 100644 themes/default16/images/tree/tree_horizontal.gif create mode 100644 themes/default16/images/tree/tree_space.gif create mode 100644 themes/default16/images/tree/tree_split.gif create mode 100644 themes/default16/images/tree/tree_vertline.gif create mode 100644 themes/default16/images/user-star.gif create mode 100644 themes/default16/images/user.gif create mode 100644 themes/default16/include/fm_footer.tmpl.php create mode 100644 themes/default16/include/fm_header.tmpl.php create mode 100644 themes/default16/include/footer.tmpl.php create mode 100644 themes/default16/include/forms.css create mode 100644 themes/default16/include/header.tmpl.php create mode 100644 themes/default16/print.css create mode 100644 themes/default16/readme.txt create mode 100644 themes/default16/rtl.css create mode 100644 themes/default16/screenshot.gif create mode 100644 themes/default16/styles.css create mode 100644 themes/default16/theme.cfg.php create mode 100644 themes/default16/theme_info.xml create mode 100644 themes/default_classic/ie_styles.css create mode 100644 themes/default_classic/images/guide.gif create mode 100644 themes/default_classic/images/mswitch_minus.gif create mode 100644 themes/default_classic/images/mswitch_plus.gif create mode 100644 themes/default_classic/images/tree/index.html create mode 100644 themes/default_classic/images/tree/tree_collapse.gif create mode 100644 themes/default_classic/images/tree/tree_disabled.gif create mode 100644 themes/default_classic/images/tree/tree_end.gif create mode 100644 themes/default_classic/images/tree/tree_expand.gif create mode 100644 themes/default_classic/images/tree/tree_horizontal.gif create mode 100644 themes/default_classic/images/tree/tree_space.gif create mode 100644 themes/default_classic/images/tree/tree_split.gif create mode 100644 themes/default_classic/images/tree/tree_vertline.gif create mode 100644 themes/default_classic/images/user-star.gif create mode 100644 themes/default_classic/include/footer.tmpl.php create mode 100644 themes/default_classic/include/header.tmpl.php create mode 100644 themes/default_classic/print.css create mode 100644 themes/default_classic/rtl.css create mode 100644 themes/default_classic/screenshot.gif create mode 100644 themes/default_classic/styles.css create mode 100644 themes/default_classic/theme.cfg.php create mode 100644 themes/fluid/forms.css create mode 100644 themes/fluid/ie_styles.css create mode 100644 themes/fluid/images/arrow_left.png create mode 100644 themes/fluid/images/continue.gif create mode 100644 themes/fluid/images/layers.png create mode 100644 themes/fluid/images/mswitch_minus.gif create mode 100644 themes/fluid/images/mswitch_plus.gif create mode 100644 themes/fluid/images/next.gif create mode 100644 themes/fluid/images/previous.gif create mode 100644 themes/fluid/images/resume.gif create mode 100644 themes/fluid/images/top.gif create mode 100644 themes/fluid/images/tree/index.html create mode 100644 themes/fluid/images/tree/tree_collapse.gif create mode 100644 themes/fluid/images/tree/tree_disabled.gif create mode 100644 themes/fluid/images/tree/tree_end.gif create mode 100644 themes/fluid/images/tree/tree_expand.gif create mode 100644 themes/fluid/images/tree/tree_horizontal.gif create mode 100644 themes/fluid/images/tree/tree_space.gif create mode 100644 themes/fluid/images/tree/tree_split.gif create mode 100644 themes/fluid/images/tree/tree_vertline.gif create mode 100644 themes/fluid/include/footer.tmpl.php create mode 100644 themes/fluid/include/header.tmpl.php create mode 100644 themes/fluid/print.css create mode 100644 themes/fluid/save_state.php create mode 100644 themes/fluid/screenshot.gif create mode 100644 themes/fluid/styles.css create mode 100644 themes/fluid/theme.cfg.php create mode 100644 themes/fluid/theme_info.xml create mode 100644 themes/greenmin/content.tmpl.php create mode 100644 themes/greenmin/forms.css create mode 100644 themes/greenmin/ie_styles.css create mode 100644 themes/greenmin/images/Thumbs.db create mode 100644 themes/greenmin/images/arrow_ltr.gif create mode 100644 themes/greenmin/images/back.gif create mode 100644 themes/greenmin/images/continue.gif create mode 100644 themes/greenmin/images/linkOpaque.gif create mode 100644 themes/greenmin/images/linkTransparent.gif create mode 100644 themes/greenmin/images/mswitch_minus.gif create mode 100644 themes/greenmin/images/mswitch_plus.gif create mode 100644 themes/greenmin/images/newsitem_icon.gif create mode 100644 themes/greenmin/images/next.gif create mode 100644 themes/greenmin/images/previous.gif create mode 100644 themes/greenmin/images/resume.gif create mode 100644 themes/greenmin/images/side_arrow.gif create mode 100644 themes/greenmin/images/sort.gif create mode 100644 themes/greenmin/images/tree/index.html create mode 100644 themes/greenmin/images/tree/tree_collapse.gif create mode 100644 themes/greenmin/images/tree/tree_disabled.gif create mode 100644 themes/greenmin/images/tree/tree_end.gif create mode 100644 themes/greenmin/images/tree/tree_expand.gif create mode 100644 themes/greenmin/images/tree/tree_horizontal.gif create mode 100644 themes/greenmin/images/tree/tree_space.gif create mode 100644 themes/greenmin/images/tree/tree_split.gif create mode 100644 themes/greenmin/images/tree/tree_vertline.gif create mode 100644 themes/greenmin/images/user-star.gif create mode 100644 themes/greenmin/images/user.gif create mode 100644 themes/greenmin/include/box.tmpl.php create mode 100644 themes/greenmin/include/footer.tmpl.php create mode 100644 themes/greenmin/include/header.tmpl.php create mode 100644 themes/greenmin/include/side_menu.tmpl.php create mode 100644 themes/greenmin/print.css create mode 100644 themes/greenmin/readme.txt create mode 100644 themes/greenmin/screenshot.gif create mode 100644 themes/greenmin/styles.css create mode 100644 themes/greenmin/theme.cfg.php create mode 100644 themes/greenmin/theme_info.xml create mode 100644 themes/mobile/forms.css create mode 100644 themes/mobile/ie_styles.css create mode 100644 themes/mobile/include/header.tmpl.php create mode 100644 themes/mobile/print.css create mode 100644 themes/mobile/readme.txt create mode 100644 themes/mobile/rtl.css create mode 100644 themes/mobile/screenshot.gif create mode 100644 themes/mobile/styles.css create mode 100644 themes/mobile/theme.cfg.php create mode 100644 themes/mobile/theme_info.xml create mode 100644 themes/themes_readme.txt create mode 100644 tools/index.php create mode 100644 tools/prog.php create mode 100644 users/alt_to_audio.inc.php create mode 100644 users/alt_to_text.inc.php create mode 100644 users/alt_to_visual.inc.php create mode 100644 users/atutor_settings.inc.php create mode 100644 users/browse.php create mode 100644 users/contact_instructor.php create mode 100644 users/content_settings.inc.php create mode 100644 users/control_settings.inc.php create mode 100644 users/display_settings.inc.php create mode 100644 users/email_change.php create mode 100644 users/index.php create mode 100644 users/password_change.php create mode 100644 users/pref_wizard/index.php create mode 100644 users/preferences.php create mode 100644 users/private_enroll.php create mode 100644 users/profile.php create mode 100644 users/prog_unused.php create mode 100644 users/remove_course.php create mode 100644 users/search.php create mode 100644 users/tool_settings.inc.php diff --git a/.htaccess b/.htaccess new file mode 100644 index 000000000..a83df4e49 --- /dev/null +++ b/.htaccess @@ -0,0 +1,119 @@ +# BEGIN ATutor + +RewriteEngine On +#RewriteBase /ATutor + +#Takes out ib flag +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)\&ib\=1$ $1 [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/ib/1$ $1/?ib=1 [L] + +#Forums rules +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/forum/([0-9]+)/([0-9]+)/?$ go.php/$1/forum/view.php/fid/$2/pid/$3/ [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/forum/([0-9]+)/([0-9]+)/(([0-9]+)\.html)$ go.php/$1/forum/view.php/fid/$2/pid/$3/page/$4 [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/forum/([0-9]+)/?$ go.php/$1/forum/index.php/fid/$2/ [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/forum/([0-9]+)/([0-9+])\.html$ go.php/$1/forum/index.php/fid/$2/page/$3 [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/forum/?$ go.php/$1/forum/list.php [L] + +#Content rule +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/content/([0-9]+)/?$ go.php/$1/content.php/cid/$2 [L] + +#File storage rule +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/file_storage/?$ go.php/$1/file_storage/index.php [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/file_storage/([0-9]+)/([0-9]+)/?$ go.php/$1/file_storage/index.php/ot/$2/oid/$3/ [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/file_storage/([0-9]+)/([0-9]+)/([0-9]+)/?$ go.php/$1/file_storage/index.php/ot/$2/oid/$3/folder/$4/ [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/file_storage/comments/([0-9]+)/([0-9]+)/([0-9]+)/?$ go.php/$1/file_storage/comments.php/ot/$2/oid/$3/id/$4/ [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/file_storage/revisions/([0-9]+)/([0-9]+)/([0-9]+)/?$ go.php/$1/file_storage/revisions.php/ot/$2/oid/$3/id/$4/ [L] + +#Tests and Survey +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/tests_surveys/([0-9]+)/?$ go.php/$1/tools/test_intro.php/tid/$2/ [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/tests_surveys/([0-9]+)/(begin|cancel)/?$ go.php/$1/tools/test_intro.php/tid/$2/action/$3/ [L] + +#Glossary +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/glossary/?$ go.php/$1/glossary/index.php [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*)/glossary/(.*)/?$ go.php/$1/glossary/index.php/p/$2/ [L] + +#Handles the removal of index.php +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^([^/^.]+)/?$ go.php/$1/index.php [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^([^/^.]+)/(([^/^.]+)|((mods|tools)/([^/^.]+)))/$ go.php/$1/$2/index.php [L] + +#Handles the removal of .php +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^([^/]+)/([^/^.]+)/?$ go.php/$1/$2.php [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^([^/]+)/(((mods)/[^/^.]+)|([^/^.]+))/([^/^.]+)/?$ go.php/$1/$2/$6.php [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^([^/]+)/tools/tests/questions/tid/([0-9]+)/?$ go.php/$1/tools/tests/questions.php/tid/$2/ [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^([^/]+)/tools/tests/results/tid/([0-9]+)/?$ go.php/$1/tools/tests/results.php/tid/$2/ [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^([^/]+)/tools/tests/results_all_quest/tid/([0-9]+)/?$ go.php/$1/tools/tests/results_all_quest.php/tid/$2/ [L] + +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^([^/]+)/(((mods)/[^/^.]+)|([^/^.]+))/([^/^.]+)/([^\.]+)$ go.php/$1/$2/$6.php/$7 [L] + +#Default rule +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule (.*) go.php/$1 [L] + +# END ATutor diff --git a/404.php b/404.php new file mode 100644 index 000000000..1f6575461 --- /dev/null +++ b/404.php @@ -0,0 +1,25 @@ +printInfos($_info); + +$msg->printAll(); + +require (AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/about.php b/about.php new file mode 100644 index 000000000..407fc9cae --- /dev/null +++ b/about.php @@ -0,0 +1,25 @@ + +

+ + + + \ No newline at end of file diff --git a/acl.php b/acl.php new file mode 100644 index 000000000..ed9504d80 --- /dev/null +++ b/acl.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/admin/config_edit.php b/admin/config_edit.php new file mode 100644 index 000000000..5ff4b9fc9 --- /dev/null +++ b/admin/config_edit.php @@ -0,0 +1,423 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + $missing_fields = array(); + + $_POST['site_name'] = trim($_POST['site_name']); + $_POST['home_url'] = trim($_POST['home_url']); + $_POST['default_language'] = trim($_POST['default_language']); + $_POST['contact_email'] = trim($_POST['contact_email']); + $_POST['max_file_size'] = intval($_POST['max_file_size']); + $_POST['max_file_size'] = max(0, $_POST['max_file_size']); + $_POST['max_course_size'] = intval($_POST['max_course_size']); + $_POST['max_course_size'] = max(0, $_POST['max_course_size']); + $_POST['max_course_float'] = intval($_POST['max_course_float']); + $_POST['max_course_float'] = max(0, $_POST['max_course_float']); + $_POST['allow_registration'] = intval($_POST['allow_registration']); + $_POST['allow_browse'] = intval($_POST['allow_browse']); + $_POST['allow_instructor_registration'] = intval($_POST['allow_instructor_registration']); + $_POST['allow_unenroll'] = intval($_POST['allow_unenroll']); + $_POST['master_list'] = intval($_POST['master_list']); + $_POST['email_confirmation'] = intval($_POST['email_confirmation']); + $_POST['email_notification'] = intval($_POST['email_notification']); + $_POST['sent_msgs_ttl'] = intval($_POST['sent_msgs_ttl']); + $_POST['allow_instructor_requests'] = intval($_POST['allow_instructor_requests']); + $_POST['auto_approve_instructors'] = intval($_POST['auto_approve_instructors']); + $_POST['theme_categories'] = intval($_POST['theme_categories']); + $_POST['user_notes'] = intval($_POST['user_notes']); + $_POST['illegal_extentions'] = str_replace(array(' ', ' '), array(' ','|'), $_POST['illegal_extentions']); + $_POST['cache_dir'] = trim($_POST['cache_dir']); + $_POST['latex_server'] = (trim($_POST['latex_server'])==''?$_config['latex_server']:trim($_POST['latex_server'])); + $_POST['course_backups'] = intval($_POST['course_backups']); + $_POST['course_backups'] = max(0, $_POST['course_backups']); + $_POST['check_version'] = $_POST['check_version'] ? 1 : 0; + $_POST['fs_versioning'] = $_POST['fs_versioning'] ? 1 : 0; + $_POST['enable_mail_queue'] = $_POST['enable_mail_queue'] ? 1 : 0; + $_POST['display_name_format'] = intval($_POST['display_name_format']); + $_POST['pretty_url'] = intval($_POST['pretty_url']); + $_POST['course_dir_name'] = intval($_POST['course_dir_name']); + $_POST['max_login'] = intval($_POST['max_login']); //max login attempt + $_POST['use_captcha'] = $_POST['use_captcha'] ? 1 : 0; + + //apache_mod_rewrite can only be enabled if pretty_url is. + if ($_POST['pretty_url']==1){ + $_POST['apache_mod_rewrite'] = intval($_POST['apache_mod_rewrite']); + } else { + $_POST['apache_mod_rewrite'] = 0; + } + + if (!isset($display_name_formats[$_POST['display_name_format']])) { + $_POST['display_name_format'] = $_config_defaults['display_name_format']; + } + + //check that all values have been set + if (!$_POST['site_name']) { + $missing_fields[] = _AT('site_name'); + } + + /* email check */ + if (!$_POST['contact_email']) { + $missing_fields[] = _AT('contact_email'); + } else if (!preg_match("/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,6}$/i", $_POST['contact_email'])) { + $msg->addError('EMAIL_INVALID'); + } + + if ($_POST['cache_dir']) { + if (!is_dir($_POST['cache_dir'])) { + $msg->addError('CACHE_DIR_NOT_EXIST'); + } else if (!is_writable($_POST['cache_dir'])){ + $msg->addError('CACHE_DIR_NOT_WRITEABLE'); + } + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $_POST['site_name'] = $addslashes($_POST['site_name']); + $_POST['home_url'] = $addslashes($_POST['home_url']); + $_POST['default_language'] = $addslashes($_POST['default_language']); + $_POST['contact_email'] = $addslashes($_POST['contact_email']); + $_POST['time_zone'] = floatval($_POST['time_zone']); + + foreach ($_config as $name => $value) { + // the isset() is needed to avoid overridding settings that don't get set here (ie. modules) + if (isset($_POST[$name]) && ($stripslashes($_POST[$name]) != $value) && ($stripslashes($_POST[$name]) != $_config_defaults[$name])) { + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES ('$name', '$_POST[$name]')"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_REPLACE, 'config', mysql_affected_rows($db), $sql); + } else if (isset($_POST[$name]) && ($stripslashes($_POST[$name]) == $_config_defaults[$name])) { + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='$name'"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'config', mysql_affected_rows($db), $sql); + } + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + // special case: disabling the mail queue should flush all queued mail: + if (!$_POST['enable_mail_queue'] && $_POST['old_enable_mail_queue']) { + require_once(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + $mail = new ATutorMailer; + $mail->SendQueue(); + } + + header('Location: '.$_SERVER['PHP_SELF']); + exit; + } +} + +$onload = 'document.form.sitename.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (!isset($_POST['submit'])) { + +} else { + $defaults = $_POST; +} +?> + +
+
+
+ *
+ +
+ +
+
+ + +
+ +
+
+ + + + + + printDropdown($select_lang, 'default_language', 'default_lang'); ?> + +
+ +
+ *
+ +
+ +
+
+ + '; + echo ''; + foreach ($utc_timezones as $zone => $offset){ + if(($offset[1]-$local_offset) == $_config['time_zone']){ + echo ''; + }else{ + echo ''; + + } + } + echo ""; + + + //echo ' '; + + // If PHP 5+ generate a list of timezones +/* + if(phpversion() >= 5){ + $timezone_names = timezone_identifiers_list(); + }else{ + // if less than PHP version 5, read a text file to generate the menu + $timezone_names = file("timezones.txt"); + } + + echo ''; +*/ +echo AT_date(_AT('server_date_format'), '', AT_DATE_MYSQL_DATETIME); +?> +
+ +
+ (: )
+ +
+ +
+ (: )
+ +
+ +
+ (: )
+ +
+ +
+ (: )
+ +
+ +
+ (: )
+ $value): ?> + />
+ +
+ +
+ (: )
+ /> + + /> +
+
+ (: )
+ /> /> +
+
+ (: )
+ /> /> +
+ +
+ (: )
+ /> /> +
+
+ (: )
+ + /> /> + + + +
+
+ (: )
+ /> /> +
+ +
+ (: )
+ /> /> +
+ +
+ (: )
+ /> /> +
+ +
+ (: )
+ /> /> +
+ +
+ (: )
+ /> /> +
+ +
+ (: )
+ /> /> +
+ +
+ (: )
+ /> /> +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ (: )
+ +
+ +
+ (: )
+ +
+ +
+ (: )
+ /> /> +
+ +
+ (: )
+ /> /> +
+ +
+ + (: )
+
+ 2 * 60 * 60)): ?> + + + /> /> + +
+ +
+ (: )
+
+ 2 * 60 * 60)): ?> + + + /> /> + +
+ +
+ (: )
+ onclick="apache_mod_rewrite_toggler(true);"/> onclick="apache_mod_rewrite_toggler(false);"/> +
+ +
+ (: )
+ /> /> +
+ +
+
+ + (: )
+ /> /> + + (: )
+ + +
+ +
+ + +
+
+
+ + + + + + \ No newline at end of file diff --git a/admin/config_template.php b/admin/config_template.php new file mode 100644 index 000000000..5f4aa2316 --- /dev/null +++ b/admin/config_template.php @@ -0,0 +1,139 @@ + \ No newline at end of file diff --git a/admin/cron.php b/admin/cron.php new file mode 100644 index 000000000..7f42e769a --- /dev/null +++ b/admin/cron.php @@ -0,0 +1,34 @@ +getModules(AT_MODULE_STATUS_ENABLED, AT_MODULE_TYPE_CORE + AT_MODULE_TYPE_STANDARD + AT_MODULE_TYPE_EXTRA); +$keys = array_keys($module_list); + +foreach($keys as $dir_name) { + $module =& $module_list[$dir_name]; + + if (!$module->getCronInterval()) { + continue; + } + + $module->runCron(); +} + +// run the mail queue last +if ($_config['enable_mail_queue']) { + require_once(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + $mail = new ATutorMailer; + $mail->SendQueue(); +} +?> \ No newline at end of file diff --git a/admin/cron_config.php b/admin/cron_config.php new file mode 100644 index 000000000..ed5d64748 --- /dev/null +++ b/admin/cron_config.php @@ -0,0 +1,25 @@ + + +
+
+

+
+
+
+ admin/cron.php?k= +
+
+ + \ No newline at end of file diff --git a/admin/error_logging.php b/admin/error_logging.php new file mode 100644 index 000000000..04b266dd6 --- /dev/null +++ b/admin/error_logging.php @@ -0,0 +1,175 @@ + + +
+ + + + + + + + + + + + + + +printNoLookupFeedback('Could not access /content/logs. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + /** + * Run through the logs directory and lets get all the profiles of all the logs of all the dates, sort + * by primary key as date, secondary key is profile name + */ + $logdirs; + + // loop through folder to get files and directory listing + while (($file = readdir($dir)) !== false) { + + /* if the name is not a directory */ + if( ($file == '.') || ($file == '..')) { + continue; + } + + if (is_dir($dir_ . '/' . $file)) { + $logdirs{$file} = $file; // store the day log dir + } + } + closedir($dir); // clean it up + + if (empty($logdirs)) { ?> + + + + $val) { + $log_profiles; // store all the profiles under the dir /content/logs/$val + $log_profiles_bug_count; // store the amount of bugs per profile + + if (!($dir = opendir($dir_ . '/' . $val))) { + $msg->printNoLookupFeedback('Could not access /content/logs/' . $val . '. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + // Open a read pointer to run through each log date directory getting all the profiles + while (($file = readdir($dir)) !== false) { + + if (($file == '.') || ($file == '..') || is_dir($file)) { + continue; + } + + if (strpos($file, 'profile') !== false) { // found a profile, store its md5 key identifier + $tmp_ = substr($file, strpos($file, '_') + 1); + $tmp_ = substr($tmp_, 0, strpos($tmp_, '.log.php')); + $log_profiles{$file} = $tmp_; + } + + } + closedir($dir); // clean it up + + /** + * Open a read pointer to run through each log date directory getting all the bugs associated + * all the profiles in $log_profiles + */ + if (empty($log_profiles)) { + $msg->printNoLookupFeedback('Warning. No profile found in ' . $dir_ . '/' . $val); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + foreach ($log_profiles as $elem => $val_) { + $count = 0; + + /* for each profile get the number of bugs associated with it */ + if (!($dir = opendir($dir_ . '/' . $val))) { + $msg->printNoLookupFeedback('Could not access /content/logs' . $val . '. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + while (($file = readdir($dir)) !== false) { + + // make sure we ignore profiles too!, just look at bug files + if( ($file == '.') || ($file == '..') || is_dir($file) || (strpos($file, 'profile') !== false)) { + continue; + } + + // found a bug that maps to $val_ md5 profile identifer + if (strpos($file, $val_) !== false) { + $count++; + } + } + closedir($dir); + + // store the amount of bugs associated with profile + $log_profiles_bug_count{$val}[$val_] = $count; + } + $log_profiles = array(); + } + /** + * At this point ($log_profiles => key) = ($log_profiles_bug_count => key). + * + * Lets print out + + + + + + +
+ + +
rows corresponding to all profiles found in the following format: + * + * Profile name, profile date, profile bug count. + */ + foreach ($log_profiles_bug_count as $day => $profile) : + foreach ($profile as $stamp => $total) : + ?> +
+ + + \ No newline at end of file diff --git a/admin/error_logging_bundle.php b/admin/error_logging_bundle.php new file mode 100644 index 000000000..66b42d682 --- /dev/null +++ b/admin/error_logging_bundle.php @@ -0,0 +1,282 @@ +addError(array('EMPTY_FIELDS', _AT('recipient_address'))); + + header('Location: ' . $_SERVER['PHP_SELF']); + exit; + } + /* First lets check if they selected any profiles to bundle, run through $POST['file(\d)'] */ + foreach($_POST as $elem => $val) { + if (strpos($elem, 'file') !== false) { + $found = true; + + $work = $val; + + $date = substr($work, 0, strpos($work, ':')); + $id = substr($work, strpos($work, ':') + 1); + /* Parse the variable */ + $profiles{$id} = $date; + } + } + + if ($found === true) { + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + require(AT_INCLUDE_PATH.'classes/zipfile.class.php'); + + $mail = new ATutorMailer; + + $zipfile = new zipfile(); + + $dir_ = AT_CONTENT_DIR . 'logs'; + + foreach($profiles as $elem => $val) { + $store_some; + + // read the dir where this profile and its associated log files are located + if (!($dir = opendir($dir_ . '/' . $val))) { + $msg->printNoLookupFeedback('Could not access /content/logs/' . $val . '. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + // Open a read pointer to run through each log date directory getting all the profiles + while (($file = readdir($dir)) !== false) { + + if (($file == '.') || ($file == '..') || is_dir($file)) { + continue; + } + + // any files mathcing the $elem key correspond to this profile + if (strpos($file, $elem) !== false) { + $store_some{$dir_ . '/'. $val . '/' . $file} = $file; + } + + } + closedir($dir); // clean it up + + // The dir pointer is closed lets add to the zip + foreach($store_some as $val_ => $e) + $zipfile->add_file(file_get_contents($val_), $e); + } + + $zipfile->close(); + + if ($file_handle = fopen($dir_ . '/bundle.log', "w")) { + if (!fwrite($file_handle, $zipfile->get_file())) { } + } else { } + fclose($file_handle); + + $mail->From = $_config['contact_email']; + $mail->addAddress($_POST['email_add']); + $mail->Subject = _AT('log_file_bundle'); + $mail->Body = _AT('see_attached'); + $mail->AddAttachment($dir_ . '/bundle.log'); + + // clean up the file at the redirection point + if(!$mail->Send()) { + $msg->addError('SENDING_ERROR'); + /* Make sure the tmp bundle file never exists past the lifetime of the bundle manager page */ + unlink($dir_ . '/bundle.log'); + header('Location: ' . $_SERVER['PHP_SELF']); + exit; + } + unset($mail); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + /* Make sure the tmp bundle file never exists past the lifetime of the bundle manager page */ + unlink($dir_ . '/bundle.log'); + header('Location: error_logging.php'); + exit; + } else { + $msg->addError('NO_LOGS_SELECTED'); + header('Location: ' . $_SERVER['PHP_SELF']); + exit; + } +} // else step 1 + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$msg->printAll(); + + +?> +

+ +

+ +
+ + + + + + + + + + + + + + +printNoLookupFeedback('Could not access /content/logs. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + /** + * Run through the logs directory and lets get all the profiles of all the logs of all the dates, sort + * by primary key as date, secondary key is profile name + */ + $logdirs; + + // loop through folder to get files and directory listing + while (($file = readdir($dir)) !== false) { + + /* if the name is not a directory */ + if( ($file == '.') || ($file == '..')) { + continue; + } + + if (is_dir($dir_ . '/' . $file)) { + $logdirs{$file} = $file; // store the day log dir + } + } + closedir($dir); // clean it up + + if (empty($logdirs)) { ?> + + + + $val) { + $log_profiles; // store all the profiles under the dir /content/logs/$val + $log_profiles_bug_count; // store the amount of bugs per profile + + if (!($dir = opendir($dir_ . '/' . $val))) { + $msg->printNoLookupFeedback('Could not access /content/logs/' . $val . '. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + // Open a read pointer to run through each log date directory getting all the profiles + while (($file = readdir($dir)) !== false) { + + if (($file == '.') || ($file == '..') || is_dir($file)) { + continue; + } + + if (strpos($file, 'profile') !== false) { // found a profile, store its md5 key identifier + $tmp_ = substr($file, strpos($file, '_') + 1); + $tmp_ = substr($tmp_, 0, strpos($tmp_, '.log.php')); + $log_profiles{$file} = $tmp_; + } + + } + closedir($dir); // clean it up + + /** + * Open a read pointer to run through each log date directory getting all the bugs associated + * all the profiles in $log_profiles + */ + if (empty($log_profiles)) { + $msg->printNoLookupFeedback('Warning. No profile found in ' . $dir_ . '/' . $val); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + foreach ($log_profiles as $elem => $val_) { + $count = 0; + + /* for each profile get the number of bugs associated with it */ + if (!($dir = opendir($dir_ . '/' . $val))) { + $msg->printNoLookupFeedback('Could not access /content/logs' . $val . '. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + while (($file = readdir($dir)) !== false) { + + // make sure we ignore profiles too!, just look at bug files + if( ($file == '.') || ($file == '..') || is_dir($file) || (strpos($file, 'profile') !== false)) { + continue; + } + + // found a bug that maps to $val_ md5 profile identifer + if (strpos($file, $val_) !== false) { + $count++; + } + } + closedir($dir); + + // store the amount of bugs associated with profile + $log_profiles_bug_count{$val}[$val_] = $count; + } + $log_profiles = array(); + } + /** + * At this point ($log_profiles => key) = ($log_profiles_bug_count => key). + * + * Lets print out + + + + + + +
+
+ + + +
rows corresponding to all profiles found in the following format: + * + * Profile name, profile date, profile bug count. + */ + foreach ($log_profiles_bug_count as $day => $profile) : + foreach ($profile as $stamp => $total) : + ?> +
+ + + \ No newline at end of file diff --git a/admin/error_logging_details.php b/admin/error_logging_details.php new file mode 100644 index 000000000..33b3c2e26 --- /dev/null +++ b/admin/error_logging_details.php @@ -0,0 +1,193 @@ +addError('NO_PROFILE_SELECTED'); + header('Location: error_logging.php'); + exit; +} // else we have a profile we can work with + +if (isset($_POST['delete'])) { + + $key = substr($_POST['data'], 0, strpos($_POST['data'], ':')); + $date = substr($_POST['data'], strpos($_POST['data'], ':') + 1); + $dir_ = AT_CONTENT_DIR . 'logs/' . $date; + $delete_store; + + if (!($dir = opendir($dir_))) { + $msg->printNoLookupFeedback('Could not access /content/logs/' . $date . '. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + $cnt = 0; + // Open a read pointer to run through each log date directory getting all the profiles + while (($file = readdir($dir)) !== false) { + + if (($file == '.') || ($file == '..') || is_dir($file)) { + continue; + } + + if (strpos($file, $key) !== false) { // found a bug associated with our profile key + $delete_store{$file} = $file; + } else { + $cnt++; + } + + } + closedir($dir); // clean it up + + if (count($delete_store) > 0) { + // Now run through the files and unlink them all + foreach($delete_store as $elem => $val) + unlink($dir_ . '/' . $elem); + } + + // remove the directory as well if there are no oother profiles in it + if ($cnt == 0) { + rmdir($dir_); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: error_logging.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (isset($_POST['view'])) { + // Grab all the bugs associated with this $_POST['data'] corresponding md5 key + $key = substr($_POST['data'], 0, strpos($_POST['data'], ':')); + $date = substr($_POST['data'], strpos($_POST['data'], ':') + 1); + $dir_ = AT_CONTENT_DIR . 'logs/' . $date; + $log_profiles_bugs; + + ?> + +
+ + + + + + + + + + + + + + + printNoLookupFeedback('Could not access /content/logs/' . $date . '. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + // Open a read pointer to run through each log date directory getting all the profiles + while (($file = readdir($dir)) !== false) { + + if (($file == '.') || ($file == '..') || is_dir($file) || (strpos($file, 'profile') !== false)) { + continue; + } + + if (strpos($file, $key) !== false) { // found a bug associated with our profile key + $log_profile_bugs{$file} = $file; + } + + } + closedir($dir); // clean it up + + if (empty($log_profile_bugs)) { ?> + + + + + $lm) { + // construct timestamp from millis since epoch in bug identifier + $timestamp = substr($lm, strpos($lm, '_') + 1); + $timestamp = substr($timestamp, 0, strpos($lm, '_') + 2); + + $timestamp = AT_Date(_AT('inbox_date_format'), $timestamp, AT_DATE_UNIX_TIMESTAMP); + + $str_prefix = substr($lm, 0, strpos($lm, '_')); + ?> + + + + + + +
+ + + + +
+ + + printNoLookupFeedback('Could not access /content/logs/' . $date . '. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + // Open a read pointer to run through each log date directory getting all the profiles + while (($file = readdir($dir)) !== false) { + + if (($file == '.') || ($file == '..') || is_dir($file)) { + continue; + } + + if (strpos($file, $key) !== false) { // found a bug associated with our profile key + $delete_store{$file} = $file; + } + + } + closedir($dir); // clean it up + + // Now run through the files and unlink them all + foreach($delete_store as $elem => $val) + unlink($dir_ . '/' . $elem); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: ' . $_SERVER['PHP_SELF']); +} +?> \ No newline at end of file diff --git a/admin/error_logging_reset.php b/admin/error_logging_reset.php new file mode 100644 index 000000000..aaa961b46 --- /dev/null +++ b/admin/error_logging_reset.php @@ -0,0 +1,49 @@ +addFeedback('CANCELLED'); + header('Location: ./error_logging.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + + //clean up the db + require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + + if (($result = clr_dir(AT_CONTENT_DIR . 'logs/'))) { + $msg->addFeedback('ERROR_LOG_RESET'); + } else { + $msg->addError('ERROR_LOG_NOT_RESET'); + } + + header('Location: ./error_logging.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +//print confirmation +$hidden_vars['all'] = TRUE; + +$confirm = array('RESET_ERROR_LOG', $_SERVER['PHP_SELF']); +$msg->addConfirm($confirm, $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/admin/error_logging_view.php b/admin/error_logging_view.php new file mode 100644 index 000000000..c72fb1e2e --- /dev/null +++ b/admin/error_logging_view.php @@ -0,0 +1,72 @@ + $val) { + $str_ = substr($elem, 0, 4); + if ($str_ == 'file') { + $files[] = $elem; + } + } + if (empty($files)) { + $msg->addError('NO_LOG_SELECTED'); + header('Location: error_logging.php'); + exit; + } +} + +$back_ref = $_POST['profile_id'] . ':' . $_POST['profile_date']; + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> +
+ + + +
+
+

+
+ +
+ +
printErrors(array('CANNOT_READ_FILE', AT_CONTENT_DIR . 'logs/' . $_POST[$file])); + } + } + } ?> + +
+ +
+
+
+ + \ No newline at end of file diff --git a/admin/fix_content.php b/admin/fix_content.php new file mode 100644 index 000000000..a232fa507 --- /dev/null +++ b/admin/fix_content.php @@ -0,0 +1,56 @@ +
';
+
+echo "cpID\torder\t cID";
+
+$sql    = "SELECT content_id, content_parent_id, ordering, course_id FROM ".TABLE_PREFIX."content ORDER BY course_id, content_parent_id, ordering";
+$result = mysql_query($sql, $db);
+while ($row = mysql_fetch_assoc($result)) {
+	if ($current_course_id != $row['course_id']) {
+		echo "\n\n-- course id $row[course_id]\n\n";
+		$current_course_id = $row['course_id'];
+		unset($current_parent_id);
+		unset($ordering);
+	}
+	echo $row['content_parent_id'] . "\t" . $row['ordering'] . "\t" . $row['content_id'];
+	if ($current_parent_id != $row['content_parent_id']) {
+		$current_parent_id = $row['content_parent_id'];
+		$ordering = 1;
+	}
+
+	if ($row['ordering'] != $ordering) {
+		echo "\t mismatch : expecting $ordering [fixed]";
+		$sql = "UPDATE ".TABLE_PREFIX."content SET ordering=$ordering WHERE content_id=$row[content_id]";
+		mysql_query($sql, $db);
+		write_to_log(AT_ADMIN_LOG_UPDATE, 'content', mysql_affected_rows($db), $sql);
+	}
+
+	 echo "\n";
+
+	$ordering++;
+}
+
+echo' 
'; + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 000000000..635af4bd0 --- /dev/null +++ b/admin/index.php @@ -0,0 +1,270 @@ +addWarning('TRANSLATE_ON'); +} +$smtp_server = ini_get('SMTP'); +if (($smtp_server == '' || $smtp_server == 'localhost') && ini_get('sendmail_path') == '') { + $msg->addWarning('MAIL_NOT_ON'); +} + +// Social networking only switch +if (isset($_POST['social_submit'])) { + $_POST['just_social'] = intval($_POST['just_social']); + + if ($_POST['just_social'] == 1) { + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES ('just_social', '$_POST[just_social]')"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_REPLACE, 'config', mysql_affected_rows($db), $sql); + $msg->addFeedback('ATUTOR_SOCIAL_ONLY'); + + } else if ($_POST['just_social'] == 0) { + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='just_social'"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'config', mysql_affected_rows($db), $sql); + $msg->addFeedback('ATUTOR_SOCIAL_LMS'); + + } + $_config['just_social'] = $_POST['just_social']; +} +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($_config['check_version']) { + $request = @file('http://atutor.ca/check_atutor_version.php?return'); + if ($request && version_compare(VERSION, $request[0], '<')) { + $msg->printFeedbacks('ATUTOR_UPDATE_AVAILABLE'); + } +} + +?> + +
+
+
+

+

+
+
+
+ (: )
+ />
/> +
+ +
+ +
+
+ +
+
+
+

+

+
+ +
+ <?php echo _AT('donate'); ?>

+
+
+ + +
+
+
+

+

+
+ +
+ +
+
+
+ + + parse($patch_list_xml); + $patch_list_array = $patchListParser->getMyParsedArrayForVersion(VERSION); + + if (count($patch_list_array)) { + foreach ($patch_list_array as $row_num => $patch) { + $patch_ids .= '\'' . $patch['atutor_patch_id'] . '\', '; + } + + $sql = "select count(distinct atutor_patch_id) cnt_installed_patches from ".TABLE_PREFIX."patches " . + "where atutor_patch_id in (" . substr($patch_ids, 0, -2) .")". + " and status like '%Installed'"; + + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + $cnt = count($patch_list_array) - $row['cnt_installed_patches']; + + if ($cnt > 0) + { + ?> +
+
+
+

+

+
+ +
+ +
+
+
+ + +
+ + +
+

+ +
+ +
:
+
+ + + +
:
+
+ + +
:
+
+ +
:
+
+ +
:
+
+ +
:
+
+ +
:
+
+ +
:
+
+
+
+
+ +
+ +
+ '; + foreach ($_top_level_pages as $page_info) { + echo '
  • ' . $page_info['title'] . ' '; + + $page_info['url'] = substr($page_info['url'], $path_length); + + if ($_pages[$page_info['url']]['children']) { + echo ''; + } + echo '
  • '; + } + echo ''; +?> +
    + \ No newline at end of file diff --git a/bounce.php b/bounce.php new file mode 100644 index 000000000..59df90619 --- /dev/null +++ b/bounce.php @@ -0,0 +1,505 @@ +getModule(AT_MODULE_DIR_STANDARD.'/statistics'); + if (!$module->isEnabled()) { + return; + } + if ($_SESSION['is_guest']) { + $sql = "INSERT INTO ".TABLE_PREFIX."course_stats VALUES ($_SESSION[course_id], NOW(), 1, 0)"; + } else { + $sql = "INSERT INTO ".TABLE_PREFIX."course_stats VALUES ($_SESSION[course_id], NOW(), 0, 1)"; + } + + $result = @mysql_query($sql, $db); + + if (!$result) { + /* that entry already exists, then update it. */ + if ($_SESSION['is_guest']) { + $sql = "UPDATE ".TABLE_PREFIX."course_stats SET guests=guests+1 WHERE course_id=$_SESSION[course_id] AND login_date=CURDATE()"; + } else { + $sql = "UPDATE ".TABLE_PREFIX."course_stats SET members=members+1 WHERE course_id=$_SESSION[course_id] AND login_date=CURDATE()"; + } + $result = @mysql_query($sql, $db); + } +} + +function get_groups($course_id) { + global $db; + + $groups = array(); + + if (authenticate(AT_PRIV_GROUPS, true)) { + $sql = "SELECT G.group_id FROM ".TABLE_PREFIX."groups G INNER JOIN ".TABLE_PREFIX."groups_types T USING (type_id) WHERE T.course_id=$course_id"; + } else { + $sql = "SELECT G.group_id FROM ".TABLE_PREFIX."groups G INNER JOIN (".TABLE_PREFIX."groups_types T, ".TABLE_PREFIX."groups_members M) ON (G.type_id=T.type_id AND G.group_id=M.group_id) WHERE T.course_id=$course_id AND M.member_id=$_SESSION[member_id]"; + } + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $groups[$row['group_id']] = $row['group_id']; + } + + return $groups; +} + +$_user_location = 'public'; +define('AT_INCLUDE_PATH', 'include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); + +if($_config['just_social'] == 1){ + header('Location: mods/_standard/social/index_mystart.php'); + exit; +} +$set_to_public = false; +if ($_SERVER['PHP_SELF'] == $_base_path."acl.php") { + //search through the auth table and find password that matches get password + $key = $addslashes(key($_GET)); + $sql = "SELECT * FROM ".TABLE_PREFIX."course_access WHERE password='$key' AND (expiry_date > NOW() OR expiry_date+0 = 0) AND enabled=1"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $set_to_public = true; + $_GET['course'] = $row['course_id']; + $_SESSION['member_id'] = 0; + $_SESSION['valid_user'] = false; + $_SESSION['login'] = 'guest'; + } +} + + +if (isset($_GET['admin']) && isset($_SESSION['is_super_admin'])) { + $sql = "SELECT login, `privileges`, language FROM ".TABLE_PREFIX."admins WHERE login='$_SESSION[is_super_admin]' AND `privileges`>0"; + $result = mysql_query($sql, $db); + + if ($row = mysql_fetch_assoc($result)) { + $sql = "UPDATE ".TABLE_PREFIX."admins SET last_login=NOW() WHERE login='$_SESSION[is_super_admin]'"; + mysql_query($sql, $db); + + $_SESSION['login'] = $row['login']; + $_SESSION['valid_user'] = true; + $_SESSION['course_id'] = -1; + $_SESSION['privileges'] = intval($row['privileges']); + $_SESSION['lang'] = $row['language']; + assign_session_prefs(unserialize(stripslashes($_config['pref_defaults']))); + unset($_SESSION['member_id']); + unset($_SESSION['is_super_admin']); + + write_to_log(AT_ADMIN_LOG_UPDATE, 'admins', mysql_affected_rows($db), $sql); + + $msg->addFeedback('LOGIN_SUCCESS'); + + header('Location: admin/index.php'); + exit; + } +} + +if (!empty($_REQUEST['pu'])) { + //request ib stands for 'is bounced', this is to avoid the infinite 302 redirect + //A better way to deal with this rather than using querystring? (Session won't work) + //Session doesn't work,leads to bounce out error as well. + if (!empty($_REQUEST['ib'])) { + return; + } + + //for pretty url iff mod_rewrite is not on + if ($_config['apache_mod_rewrite'] > 0){ + //URL are in pretty format, but not in .htaccess RewriteRule format + //http://www.atutor.ca/atutor/mantis/view.php?id=3426 + $page = url_rewrite($_REQUEST['pu'], AT_PRETTY_URL_NOT_HEADER, true) . '/ib/1'; + } else { + if ($_config['pretty_url']) + { + $orig_url = AT_PRETTY_URL_HANDLER.$_REQUEST['pu']; + $page = (substr($_REQUEST['pu'], -1) == '/') ? ($orig_url. 'ib/1/') : ($orig_url .'/ib/1/'); + } + else + $page = AT_PRETTY_URL_HANDLER.$_REQUEST['pu'] . SEP .'ib=1'; + } +} elseif (!empty($_REQUEST['p'])) { + //For search + $page = urldecode($_REQUEST['p']); +} elseif (($_config['pretty_url'] > 0) && preg_match('/bounce.php\?course=([\d]+)$/', $_SERVER['REQUEST_URI'])==1) { + //for browse, and my start page url rewrite. + $page = url_rewrite($_SERVER['REQUEST_URI'], AT_PRETTY_URL_NOT_HEADER, true).'/index.php'; //force overwrite +} else { + //handles jump menu + if (isset($_POST['jump']) && abs($_POST['course']) > 0){ + $_SESSION['course_id'] = abs($_POST['course']); + } + $page = url_rewrite('index.php'); +} + +if (substr($page, 0, 1) == '/') { + $page = substr($page, 1); +} + +$_SESSION['enroll'] = AT_ENROLL_NO; +$_SESSION['s_cid'] = 0; +$_SESSION['privileges'] = 0; +$_SESSION['is_admin'] = false; + +if ($_SESSION['course_id'] == -1) { + unset($_SESSION['valid_user']); + unset($_SESSION['is_guest']); + unset($_SESSION['login']); + unset($_SESSION['is_admin']); + unset($_SESSION['course_id']); +} + +if (isset($_REQUEST['course'])) { // is set guests access protected course + $course = abs($_REQUEST['course']); +} else if (isset($_REQUEST['p_course'])) { // is set when pretty url is turned on, access public course + $course = abs($_REQUEST['p_course']); +} else { + $course = 0; +} + +if (($course === 0) && $_SESSION['valid_user']) { + $_SESSION['course_id'] = 0; + $_SESSION['last_updated'] = time()/60 - ONLINE_UPDATE - 1; + + if (defined('AT_ENABLE_CATEGORY_THEMES') && AT_ENABLE_CATEGORY_THEMES) { + $th = get_default_theme(); + $_SESSION['prefs']['PREF_THEME'] = $th['dir_name']; + } + + header('Location: users/index.php'); + exit; +} else if (($course === 0) && !$_SESSION['valid_user']) { // guests + header('Location: '.AT_BASE_HREF.'login.php'); + exit; +} else if ($course == -1) { + $_SESSION['course_id'] = 0; + $_SESSION['last_updated'] = time()/60 - ONLINE_UPDATE - 1; + + if (defined('AT_ENABLE_CATEGORY_THEMES') && AT_ENABLE_CATEGORY_THEMES) { + $th = get_default_theme(); + $_SESSION['prefs']['PREF_THEME'] = $th['dir_name']; + } + + header('Location: users/index.php'); + exit; +} + +$sql = "SELECT member_id, content_packaging, cat_id, access, title, UNIX_TIMESTAMP(release_date) AS u_release_date, UNIX_TIMESTAMP(end_date) AS u_end_date FROM ".TABLE_PREFIX."courses WHERE course_id=$course"; +$result = mysql_query($sql,$db); +if (!$row = mysql_fetch_assoc($result)) { + $msg->addError('ITEM_NOT_FOUND'); + if ($_SESSION['member_id']) { + header('Location: '.AT_BASE_HREF.'users/index.php'); + } else { + header('Location: '.AT_BASE_HREF.'login.php'); + } + exit; +} + +if (!$_SESSION['member_id']) { + assign_session_prefs(unserialize(stripslashes($_config['pref_defaults']))); +} + +$owner_id = $row['member_id']; +$_SESSION['packaging'] = $row['content_packaging']; + +$_SESSION['groups'] = array(); +unset($_SESSION['fs_owner_type']); +unset($_SESSION['fs_owner_id']); +unset($_SESSION['fs_folder_id']); + +//check for acl var +if ($set_to_public) { + $row['access'] = "public"; +} + +switch ($row['access']){ + case 'public': + if ($_GET['f']) { + $dest = './'.$page.'?f='.$addslashes($_GET['f']); + } /* else */ + $dest = './'.$page; + + apply_category_theme($row['cat_id']); + + if (!$_SESSION['valid_user'] && ($row['u_release_date'] < time()) && (!$row['u_end_date'] || $row['u_end_date'] > time())) { + $_SESSION['course_id'] = $course; + /* guest login */ + $_SESSION['login'] = 'guest'; + $_SESSION['valid_user'] = false; + $_SESSION['member_id'] = 0; + $_SESSION['is_admin'] = false; + $_SESSION['is_guest'] = true; + + /* add guest login to counter: */ + count_login(); + if ($_config['pretty_url']) + { + if (!strpos($dest, '/p_course/')) $dest .= '/p_course/'.$course; + header('Location: '.$dest); + exit; + } + } else if (!$_SESSION['valid_user']) { + if ($row['u_release_date'] > time()) { + $msg->addError(array('COURSE_NOT_RELEASED', AT_Date(_AT('announcement_date_format'), $row['u_release_date'], AT_DATE_UNIX_TIMESTAMP))); + } else { + $msg->addError(array('COURSE_ENDED', AT_Date(_AT('announcement_date_format'), $row['u_end_date'], AT_DATE_UNIX_TIMESTAMP))); + } + header('Location: '.AT_BASE_HREF.'browse.php'); + exit; + + } else { + $_SESSION['course_id'] = $course; + /* check if we're an admin here */ + if ($owner_id == $_SESSION['member_id']) { + $_SESSION['is_admin'] = true; + $_SESSION['enroll'] = AT_ENROLL_YES; + } else { + $_SESSION['is_admin'] = false; + } + } + + /* title wont be needed. comes from the cache. */ + $_SESSION['course_title'] = $row['title']; + + $sql = "SELECT * FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$_SESSION[member_id] AND course_id=$course"; + $result = mysql_query($sql, $db); + if ($row2 = mysql_fetch_assoc($result)) { + /* we have requested or are enrolled in this course */ + $_SESSION['enroll'] = AT_ENROLL_YES; + $_SESSION['s_cid'] = $row2['last_cid']; + $_SESSION['privileges'] = $row2['privileges']; + } + + if (($row['u_release_date'] > time()) && !($_SESSION['is_admin'] || $_SESSION['privileges'])) { + $msg->addError(array('COURSE_NOT_RELEASED', AT_Date(_AT('announcement_date_format'), $row['u_release_date'], AT_DATE_UNIX_TIMESTAMP))); + header('Location: '.AT_BASE_HREF.'bounce.php?course=0'); + exit; + } else if ($row['u_release_date'] > time()) { + $msg->addInfo(array('COURSE_RELEASE', AT_Date(_AT('announcement_date_format'), $row['u_release_date'], AT_DATE_UNIX_TIMESTAMP))); + + } else if ($row['u_end_date'] && ($row['u_end_date'] < time()) && !($_SESSION['is_admin'] || $_SESSION['privileges'])) { + $msg->addError(array('COURSE_ENDED', AT_Date(_AT('announcement_date_format'), $row['u_end_date'], AT_DATE_UNIX_TIMESTAMP))); + header('Location: '.AT_BASE_HREF.'bounce.php?course=0'); + exit; + } else if ($row['u_end_date'] && $row['u_end_date'] < time()) { + $msg->addInfo(array('COURSE_ENDED', AT_Date(_AT('announcement_date_format'), $row['u_end_date'], AT_DATE_UNIX_TIMESTAMP))); + } + + /* add member login to counter: */ + if (!$_SESSION['is_admin'] && $_SESSION['member_id'] > 0) { + count_login(); + } + + /* update users_online */ + add_user_online(); + + $_SESSION['groups'] = get_groups($course); + + header('Location: '.$dest); + exit; + + break; + + case 'protected': + if (!$_SESSION['valid_user']) { + header('Location: ./login.php?course='.intval($course)); + exit; + } /* else */ + /* we're already logged in */ + $_SESSION['course_id'] = $course; + + apply_category_theme($row['cat_id']); + + /* check if we're an admin here */ + if ($owner_id == $_SESSION['member_id']) { + $_SESSION['is_admin'] = true; + $_SESSION['enroll'] = AT_ENROLL_YES; + } else { + $_SESSION['is_admin'] = false; + /* add member login to counter: */ + count_login(); + } + + $sql = "SELECT * FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$_SESSION[member_id] AND course_id=$course"; + $result = mysql_query($sql, $db); + if ($row2 = mysql_fetch_assoc($result)) { + /* we have requested or are enrolled in this course */ + $_SESSION['enroll'] = AT_ENROLL_YES; + $_SESSION['s_cid'] = $row2['last_cid']; + $_SESSION['privileges'] = $row2['privileges']; + } + + if (($row['u_release_date'] > time()) && !($_SESSION['is_admin'] || $_SESSION['privileges'])) { + $msg->addError(array('COURSE_NOT_RELEASED', AT_Date(_AT('announcement_date_format'), $row['u_release_date'], AT_DATE_UNIX_TIMESTAMP))); + header('Location: '.AT_BASE_HREF.'bounce.php?course=0'); + exit; + } else if ($row['u_release_date'] > time()) { + $msg->addInfo(array('COURSE_RELEASE', AT_Date(_AT('announcement_date_format'), $row['u_release_date'], AT_DATE_UNIX_TIMESTAMP))); + + } else if ($row['u_end_date'] && ($row['u_end_date'] < time()) && !($_SESSION['is_admin'] || $_SESSION['privileges'])) { + $msg->addError(array('COURSE_ENDED', AT_Date(_AT('announcement_date_format'), $row['u_end_date'], AT_DATE_UNIX_TIMESTAMP))); + header('Location: '.AT_BASE_HREF.'bounce.php?course=0'); + exit; + } else if ($row['u_end_date'] && $row['u_end_date'] < time()) { + $msg->addInfo(array('COURSE_ENDED', AT_Date(_AT('announcement_date_format'), $row['u_end_date'], AT_DATE_UNIX_TIMESTAMP))); + } + + + $_SESSION['course_title'] = $row['title']; + + /* update users_online */ + add_user_online(); + + $_SESSION['groups'] = get_groups($course); + + if ($_GET['f']) { + header('Location: ./'.$page.'?f='.$addslashes($_GET['f'])); + exit; + } /* else */ + header('Location: ./'.$addslashes($page)); + exit; + + break; + + case 'private': + if (!$_SESSION['valid_user']) { + /* user not logged in: */ + header('Location: ./login.php?course='.intval($course)); + exit; + } /* else */ + + if ($owner_id == $_SESSION['member_id']) { + /* we own this course. so we dont have to enroll or get the groups */ + + $_SESSION['is_admin'] = true; + $_SESSION['course_id'] = $course; + $_SESSION['course_title'] = $row['title']; + $_SESSION['enroll'] = AT_ENROLL_YES; + + $sql = "SELECT last_cid FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$_SESSION[member_id] AND course_id=$course"; + $result = mysql_query($sql, $db); + $row2 = mysql_fetch_assoc($result); + + $_SESSION['s_cid'] = $row2['last_cid']; + + /* update users_online */ + add_user_online(); + + apply_category_theme($row['cat_id']); + + $_SESSION['groups'] = get_groups($course); + + if (!empty($_GET['f'])) { + header('Location: ./'.$page.'?f='.$addslashes($_GET['f'])); + exit; + } /* else */ + if ($row['u_release_date'] > time()) { + $msg->addInfo(array('COURSE_RELEASE', AT_Date(_AT('announcement_date_format'), $row['u_release_date'], AT_DATE_UNIX_TIMESTAMP))); + } else if ($row['u_end_date'] && $row['u_end_date'] < time()) { + $msg->addInfo(array('COURSE_ENDED', AT_Date(_AT('announcement_date_format'), $row['u_end_date'], AT_DATE_UNIX_TIMESTAMP))); + } + header('Location: ./'.$addslashes($page)); + exit; + } + + /* check if we're enrolled */ + $sql = "SELECT * FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$_SESSION[member_id] AND course_id=$course"; + $result = mysql_query($sql, $db); + + if (!$row2 = mysql_fetch_assoc($result)) { + /* we have not requested enrollment in this course */ + $_SESSION['course_id'] = 0; + header('Location: users/private_enroll.php?course='.intval($course)); + exit; + } /* else */ + + if (($row['u_release_date'] > time()) && !($_SESSION['is_admin'] || $_SESSION['privileges'])) { + $msg->addError(array('COURSE_NOT_RELEASED', AT_Date(_AT('announcement_date_format'), $row['u_release_date'], AT_DATE_UNIX_TIMESTAMP))); + header('Location: '.AT_BASE_HREF.'bounce.php?course=0'); + exit; + } else if ($row['u_release_date'] > time()) { + $msg->addInfo(array('COURSE_RELEASE', AT_Date(_AT('announcement_date_format'), $row['u_release_date'], AT_DATE_UNIX_TIMESTAMP))); + + } else if ($row['u_end_date'] && ($row['u_end_date'] < time()) && !($_SESSION['is_admin'] || $_SESSION['privileges'])) { + $msg->addError(array('COURSE_ENDED', AT_Date(_AT('announcement_date_format'), $row['u_end_date'], AT_DATE_UNIX_TIMESTAMP))); + header('Location: '.AT_BASE_HREF.'bounce.php?course=0'); + exit; + } else if ($row['u_end_date'] && $row['u_end_date'] < time()) { + $msg->addInfo(array('COURSE_ENDED', AT_Date(_AT('announcement_date_format'), $row['u_end_date'], AT_DATE_UNIX_TIMESTAMP))); + } + /* we have requested or are enrolled in this course */ + + apply_category_theme($row['cat_id']); + + $_SESSION['enroll'] = AT_ENROLL_YES; + $_SESSION['s_cid'] = $row2['last_cid']; + + if ($row2['approved'] == 'n') { + /* we have not been approved to enroll in this course */ + $_SESSION['course_id'] = 0; + header('Location: users/private_enroll.php?course='.$course); + exit; + } /* else */ + + /* enrollment has been approved or student is alumni */ + if ($row2['approved'] == 'a') { + $_SESSION['enroll'] = AT_ENROLL_ALUMNUS; + } + /* we're already logged in */ + $_SESSION['course_id'] = $course; + + /* check if we're an admin here */ + $_SESSION['privileges'] = $row2['privileges']; + $_SESSION['course_title'] = $row['title']; + + /* update users_online */ + add_user_online(); + + $_SESSION['groups'] = get_groups($course); + + /* add member login to counter: */ + count_login(); + + if($_GET['f']){ + header('Location: '.$page.'?f='.$addslashes($_GET['f'])); + exit; + } /* else */ + header('Location: '.$addslashes($page)); + exit; + break; +} // end switch + + +?> \ No newline at end of file diff --git a/browse.php b/browse.php new file mode 100644 index 000000000..d084fc2c5 --- /dev/null +++ b/browse.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/confirm.php b/confirm.php new file mode 100644 index 000000000..79cbc3c80 --- /dev/null +++ b/confirm.php @@ -0,0 +1,187 @@ +addFeedback('CANCELLED'); + header('Location: '.$_base_href.'login.php'); + exit; +} + +if (isset($_GET['e'], $_GET['id'], $_GET['m'])) { + $id = intval($_GET['id']); + $m = $_GET['m']; + $e = $addslashes($_GET['e']); + + $sql = "SELECT creation_date FROM ".TABLE_PREFIX."members WHERE member_id=$id"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $code = substr(md5($e . $row['creation_date'] . $id), 0, 10); + + if ($code == $m) { + $sql = "UPDATE ".TABLE_PREFIX."members SET email='$e', last_login=NOW(), creation_date=creation_date WHERE member_id=$id"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('CONFIRM_GOOD'); + + header('Location: '.$_base_href.'users/index.php'); + exit; + } else { + $msg->addError('CONFIRM_BAD'); + } + } else { + $msg->addError('CONFIRM_BAD'); + } + +} else if (isset($_GET['id'], $_GET['m'])) { + $id = intval($_GET['id']); + $m = $_GET['m']; + + $sql = "SELECT email, creation_date FROM ".TABLE_PREFIX."members WHERE member_id=$id AND status=".AT_STATUS_UNCONFIRMED; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $code = substr(md5($row['email'] . $row['creation_date'] . $id), 0, 10); + + if ($code == $m) { + if (defined('AUTO_APPROVE_INSTRUCTORS') && AUTO_APPROVE_INSTRUCTORS) { + $sql = "UPDATE ".TABLE_PREFIX."members SET status=".AT_STATUS_INSTRUCTOR.", creation_date=creation_date, last_login=NOW() WHERE member_id=$id"; + } else { + $sql = "UPDATE ".TABLE_PREFIX."members SET status=".AT_STATUS_STUDENT.", creation_date=creation_date, last_login=NOW() WHERE member_id=$id"; + } + $result = mysql_query($sql, $db); + + if (isset($_REQUEST["en_id"]) && $_REQUEST["en_id"] <> "") + { + $msg->addFeedback('CONFIRM_GOOD'); + + $member_id = $id; + require (AT_INCLUDE_PATH.'html/auto_enroll_courses.inc.php'); + unset($_SESSION['valid_user']); + unset($_SESSION['member_id']); + + $table_title=" +
    +

    " . _AT('auto_enrolled_msg'). "

    +
    "; + + require(AT_INCLUDE_PATH.'header.inc.php'); + echo "
    "; + require(AT_INCLUDE_PATH.'html/auto_enroll_list_courses.inc.php'); + echo '

    ' . _AT("go_to_my_start_page") . '

    '; + echo "
    "; + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + else + { + $msg->addFeedback('CONFIRM_GOOD'); + + // enable auto login student into "my start page" + $_REQUEST["auto_login"] = 1; + $_REQUEST["member_id"] = $id; + } + } else { + $msg->addError('CONFIRM_BAD'); + } + } else { + $msg->addError('CONFIRM_BAD'); + } +} else if (isset($_POST['submit'])) { + $_POST['email'] = $addslashes($_POST['email']); + + $sql = "SELECT member_id, email, creation_date, status FROM ".TABLE_PREFIX."members WHERE email='$_POST[email]'"; + $result = mysql_query($sql, $db); + + if ($row = mysql_fetch_assoc($result)) { + + if ($row['status'] == AT_STATUS_UNCONFIRMED) { + $code = substr(md5($row['email'] . $row['creation_date']. $row['member_id']), 0, 10); + + if ($_POST["en_id"] <> "") + $confirmation_link = $_base_href . 'confirm.php?id='.$row['member_id'].SEP.'m='.$code.'&en_id='.$_POST["en_id"]; + else + $confirmation_link = $_base_href . 'confirm.php?id='.$row['member_id'].SEP.'m='.$code; + + /* send the email confirmation message: */ + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + $mail = new ATutorMailer(); + + $mail->From = $_config['contact_email']; + $mail->AddAddress($row['email']); + $mail->Subject = SITE_NAME . ': ' . _AT('email_confirmation_subject'); + $mail->Body = _AT('email_confirmation_message', $_base_href, $confirmation_link)."\n\n"; + $mail->Send(); + + $msg->addFeedback('CONFIRMATION_SENT'); + } else { + $msg->addFeedback('ACCOUNT_CONFIRMED'); + } + + header('Location: '.$_base_href.'login.php'); + exit; + } else { + $msg->addError('EMAIL_NOT_FOUND'); + } +} + +if (isset($_REQUEST['auto_login'])) +{ + + $sql = "SELECT M.member_id, M.login, M.preferences, M.language FROM ".TABLE_PREFIX."members M WHERE M.member_id=".$_REQUEST["member_id"]; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) + { + $_SESSION['valid_user'] = true; + $_SESSION['member_id'] = $_REQUEST["member_id"]; + $_SESSION['course_id'] = 0; + $_SESSION['login'] = $row[login]; + if ($row['preferences'] == "") + assign_session_prefs(unserialize(stripslashes($_config["pref_defaults"]))); + else + assign_session_prefs(unserialize(stripslashes($row['preferences']))); + $_SESSION['is_guest'] = 0; + $_SESSION['lang'] = $row[lang]; + session_write_close(); + + header('Location: '.AT_BASE_HREF.'bounce.php?course='.$_POST['course']); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); ?> + +
    + +
    +
    +

    +
    + +
    + *
    + + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/contact_instructor.php b/contact_instructor.php new file mode 100644 index 000000000..1daa7010a --- /dev/null +++ b/contact_instructor.php @@ -0,0 +1,142 @@ +addFeedback('CANCELLED'); + header('Location: ' . $to); + exit; +} + +$row = array(); + +$id = intval($_REQUEST['id']); +if (isset($system_courses[$id], $system_courses[$id]['member_id'])) { + $sql = "SELECT M.member_id, M.first_name, M.last_name, M.email FROM ".TABLE_PREFIX."members M WHERE M.member_id={$system_courses[$id][member_id]}"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); +} + +if ($row) { + $instructor_name = get_display_name($row['member_id']); + $instructor_email = AT_print($row['email'], 'members.email'); +} else { + $msg->addError('INST_INFO_NOT_FOUND'); + header('Location: ' . $to); + exit; +} + +if (isset($_POST['submit'])) { + $missing_fields = array(); + + $to_email = $_POST['email']; + $_POST['subject'] = trim($_POST['subject']); + $_POST['body'] = trim($_POST['body']); + + if ($_POST['subject'] == '') { + $missing_fields[] = _AT('subject'); + } + + if ($_POST['body'] == '') { + $missing_fields[] = _AT('body'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + if (empty($_POST['from_email'])) { + $_POST['from_email'] = $instructor_email; + } + if (empty($_POST['from'])) { + $_POST['from'] = ''; + } + + $mail = new ATutorMailer; + + $mail->From = $_POST['from_email']; + $mail->FromName = $_POST['from']; + $mail->AddAddress($instructor_email, $instructor_name); + $mail->Subject = stripslashes($addslashes($_POST['subject'])); + $mail->Body = stripslashes($addslashes($_POST['body'])); + + if(!$mail->Send()) { + $msg->addError('SENDING_ERROR'); + header('Location: ' . $to); + exit; + } + unset($mail); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: ' . $to); + exit; + } + +} + +require (AT_INCLUDE_PATH.'header.inc.php'); +?> +
    + + +
    +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/content.php b/content.php new file mode 100644 index 000000000..b8c825abc --- /dev/null +++ b/content.php @@ -0,0 +1,255 @@ +getContentPage($cid); + +if (!($content_row = mysql_fetch_assoc($result))) { + $_pages['content.php']['title_var'] = 'missing_content'; + $_pages['content.php']['parent'] = 'index.php'; + $_pages['content.php']['ignore'] = true; + + + require(AT_INCLUDE_PATH.'header.inc.php'); + + $msg->addError('PAGE_NOT_FOUND'); + $msg->printAll(); + + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} /* else: */ + +if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $course_base_href = 'get.php/'; +} else { + $course_base_href = 'content/' . $_SESSION['course_id'] . '/'; +} + +/* the "heading navigation": */ +$path = $contentManager->getContentPath($cid); + +if ($content_row['content_path']) { + $content_base_href = $content_row['content_path'].'/'; +} + +$parent_headings = ''; +$num_in_path = count($path); + +/* the page title: */ +$page_title = ''; +$page_title .= $content_row['title']; + +for ($i=0; $i<$num_in_path; $i++) { + $content_info = $path[$i]; + if ($_SESSION['prefs']['PREF_NUMBERING']) { + if ($contentManager->_menu_info[$content_info['content_id']]['content_parent_id'] == 0) { + $top_num = $contentManager->_menu_info[$content_info['content_id']]['ordering']; + $parent_headings .= $top_num; + } else { + $top_num = $top_num.'.'.$contentManager->_menu_info[$content_info['content_id']]['ordering']; + $parent_headings .= $top_num; + } + if ($_SESSION['prefs']['PREF_NUMBERING']) { + $path[$i]['content_number'] = $top_num . ' '; + } + $parent_headings .= ' '; + } +} + +if ($_SESSION['prefs']['PREF_NUMBERING']) { + if ($top_num != '') { + $top_num = $top_num.'.'.$content_row['ordering']; + $page_title .= $top_num.' '; + } else { + $top_num = $content_row['ordering']; + $page_title .= $top_num.' '; + } +} + +$parent = 0; + +foreach ($path as $i=>$page) { + // When login is a student, remove content folder from breadcrumb path as content folders are + // just toggles for students. Keep content folder in breadcrumb path for instructors as they + // can edit content folder title. + if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && + $contentManager->_menu_info[$page['content_id']]['content_type'] == CONTENT_TYPE_FOLDER) { + unset($path[$i]); + continue; + } + + if ($contentManager->_menu_info[$page['content_id']]['content_type'] == CONTENT_TYPE_FOLDER) + $content_url = 'mods/_core/editor/edit_content_folder.php?cid='.$page['content_id']; + else + $content_url = 'content.php?cid='.$page['content_id']; + + if (!$parent) { + $_pages[$content_url]['title'] = $page['content_number'] . $page['title']; + $_pages[$content_url]['parent'] = 'index.php'; + } else { + $_pages[$content_url]['title'] = $page['content_number'] . $page['title']; + $_pages[$content_url]['parent'] = 'mods/_core/editor/edit_content_folder.php?cid='.$parent; + } + + $_pages[$content_url]['ignore'] = true; + $parent = $page['content_id']; +} +$last_page = array_pop($_pages); +$_pages['content.php'] = $last_page; + +reset($path); +$first_page = current($path); + +/* the content test extension page */ +$content_test_ids = array(); //the html +$content_test_rs = $contentManager->getContentTestsAssoc($cid); +while ($content_test_row = mysql_fetch_assoc($content_test_rs)){ + $content_test_ids[] = $content_test_row; +} + +/*TODO***************BOLOGNA***************REMOVE ME**********/ +/* the content forums extension page*/ +$content_forum_ids = array(); //the html +$content_forum_rs = $contentManager->getContentForumsAssoc($cid); +while ($content_forum_row = mysql_fetch_assoc($content_forum_rs)){ + $content_forum_ids[] = $content_forum_row; +} + +// use any styles that were part of the imported document +// $_custom_css = $_base_href.'headstuff.php?cid='.$cid.SEP.'path='.urlEncode($_base_href.$course_base_href.$content_base_href); + +if ($content_row['use_customized_head'] && strlen($content_row['head']) > 0) +{ + $_custom_head .= $content_row['head']; +} + +global $_custom_head; +$_custom_head .= ' + +'; + +save_last_cid($cid); + +if (isset($top_num) && $top_num != (int) $top_num) { + $top_num = substr($top_num, 0, strpos($top_num, '.')); +} +// used by header.inc.php +$_tool_shortcuts = $contentManager->getToolShortcuts($content_row); + +/* if i'm an admin then let me see content, otherwise only if released */ +$released_status = $contentManager->isReleased($cid); + +if ($released_status === TRUE || authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) { + //if it has test and forum associated with it, still display it even if the content is empty + if ($content_row['text'] == '' && (empty($content_test_ids) && empty($content_forum_ids))){ + $msg->addInfo('NO_PAGE_CONTENT'); + $savant->assign('body', ''); + } else { + if ($released_status !== TRUE) { + /* show the instructor that this content hasn't been released yet */ + $infos = array('NOT_RELEASED', AT_date(_AT('announcement_date_format'), $released_status, AT_DATE_UNIX_TIMESTAMP)); + $msg->addInfo($infos); + unset($infos); + } + + $pre_test_id = $contentManager->getPretest($cid); + + if (intval($pre_test_id) > 0) + { + if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) { + $msg->addInfo('PRETEST'); + } + else { + header('Location: '.url_rewrite('mods/_standard/tests/test_intro.php?tid='.$pre_test_id.SEP.'cid='.$cid, AT_PRETTY_URL_IS_HEADER)); + } + } + + // if one of the prerequisite test(s) has expired, student cannot view the content + if (intval($pre_test_id) != -1 || authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) + { + // find whether the body has alternatives defined + list($has_text_alternative, $has_audio_alternative, $has_visual_alternative, $has_sign_lang_alternative) + = provide_alternatives($cid, $content_row['text'], true); + + // apply alternatives + if (intval($_GET['alternative']) > 0) { + $content = provide_alternatives($cid, $content_row['text'], false, intval($_GET['alternative'])); + } else { + $content = provide_alternatives($cid, $content_row['text']); + } + + $content = format_content($content, $content_row['formatting'], $glossary); + + $content_array = get_content_table($content); + + $savant->assign('content_table', $content_array[0]); + $savant->assign('body', $content_array[1]); + $savant->assign('cid', $cid); + $savant->assign('has_text_alternative', $has_text_alternative); + $savant->assign('has_audio_alternative', $has_audio_alternative); + $savant->assign('has_visual_alternative', $has_visual_alternative); + $savant->assign('has_sign_lang_alternative', $has_sign_lang_alternative); + + //assign test pages if there are tests associated with this content page + if (!empty($content_test_ids)){ + $savant->assign('test_message', $content_row['test_message']); + $savant->assign('test_ids', $content_test_ids); + } else { + $savant->assign('test_message', ''); + $savant->assign('test_ids', array()); + } + + /*TODO***************BOLOGNA***************REMOVE ME**********/ + //assign forum pages if there are forums associated with this content page + if (!empty($content_forum_ids)){ + $savant->assign('forum_message',''); + $savant->assign('forum_ids', $content_forum_ids); + } else { + $savant->assign('forum_message', ''); + $savant->assign('forum_ids', array()); + } + } + } +} else { + $infos = array('NOT_RELEASED', AT_date(_AT('announcement_date_format'), $released_status, AT_DATE_UNIX_TIMESTAMP)); + $msg->addInfo($infos); + unset($infos); +} + +$savant->assign('content_info', _AT('page_info', AT_date(_AT('inbox_date_format'), $content_row['last_modified'], AT_DATE_MYSQL_DATETIME), $content_row['revision'], AT_date(_AT('inbox_date_format'), $content_row['release_date'], AT_DATE_MYSQL_DATETIME))); + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$savant->display('content.tmpl.php'); + +//save last visit page. +$_SESSION['last_visited_page'] = $server_protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + +require (AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/documentation/add_note.php b/documentation/add_note.php new file mode 100644 index 000000000..e9c6ab6c5 --- /dev/null +++ b/documentation/add_note.php @@ -0,0 +1,105 @@ + + + + + <?php get_text('add_note'); ?> + + + + +
    + + + +
    +
    +

    +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + +
    + + + + + \ No newline at end of file diff --git a/documentation/admin/administrators.php b/documentation/admin/administrators.php new file mode 100644 index 000000000..dd0a3dd2a --- /dev/null +++ b/documentation/admin/administrators.php @@ -0,0 +1,23 @@ + + +

    Administrators

    +

    An ATutor installation can be maintained by multiple administrators, each with their own privileges. The three kinds of administrator accounts are described below.

    + +
    +
    Super Administrator
    +
    This administrator has no restrictions and has access to all of the administrator options. This is the only administrator type that can create and delete other administrator accounts. There must always be at least one Super Administrator account.
    + +
    Active Administrator
    +
    An administrator account whose access is limited. This administrator only has privileged access to sections that they were assigned to when their account was created by the Super Administrator.
    + +
    Inactive Administrator
    +
    An administrator account that has not been assigned any access privileges. As a result, this administrator cannot login.
    +
    + +

    Create Administrator Account

    +

    To make a new administrator, follow the Create Administrator Account link, enter the login name, password, real name and email and select the appropriate administrative privileges to be assigned to this account.

    + +

    Administrator Activity Log

    +

    The Administrator Activity Log lists all actions made to the ATutor database tables. Viewing a log entry will give detailed information about the selected activity. The log can be reset by using the Reset Log feature.

    + + diff --git a/documentation/admin/auto_enroll.php b/documentation/admin/auto_enroll.php new file mode 100644 index 000000000..dfd984d83 --- /dev/null +++ b/documentation/admin/auto_enroll.php @@ -0,0 +1,15 @@ + + +

    Auto-Enroll at Registration

    +

    Administrators can use this tool to generate a unique URL for the ATutor registration screen, so that when new users follow the link and register, they are automatically enrolled in a specified number of courses. Once the URL is generated, make it available to perspective students so they can register, enroll, and login in one easy step. When students access the registration form using the link, the selected courses will be listed on the form, and when the registration is complete, those courses will be added to the student's My Start Page, and he or she will be logged in.

    +

    Click on Create/Edit Auto Enrollment to create an auto-enroll URL

    + +
    +
    Title
    +
    This should be a general reference that encompasses the grouping of courses. For example, if students will be enrolled in introductory Boilogy 101, Botany 101, and Zoology 101, the title might be "First Year Natural Science" .
    + +
    Courses to Enroll
    +
    Select from the available courses on the system to have them added to the auto-enroll grouping of courses for the title described above. Once all the required courses have been choosen, press the Save button to generate the link
    +
    + + diff --git a/documentation/admin/backups.php b/documentation/admin/backups.php new file mode 100644 index 000000000..3bf21f9d0 --- /dev/null +++ b/documentation/admin/backups.php @@ -0,0 +1,20 @@ + + +

    Backups

    +

    A course backup includes all available course material as an archive in a format specific to ATutor. Backups are forwards compatible with future versions of ATutor but may not be backwards compatible with previous versions of ATutor. Once a backup is created, it can be downloaded for safe-keeping, imported into another ATutor installation, used as the basis for a newly created course, and available in the originating course's Backup Manager. Instructor can also create their own course backups from within a course.

    + +

    Creating Backups

    +

    To create a backup, use the Create Backup link in the sub-navigation. The number of backups a single course can keep on the server is defined by the System Preferences Course Backups option.

    + +

    Administrators can create backups for any course, while instructors can only create backups of courses they own.

    + +

    Restoring Backups

    + +

    Restoring a backup as an administrator is similar to restoring a backup as an instructor, with the added option of being able to select which course the backup should be restored into.

    + +

    For details on restoring a backup into a course, see the Backup Manager's Restoring Backups section in the Instructor Documentation.

    + +

    Managing Backups

    +

    Backups can be downloaded to the administrator's hard-drive for safe-keeping by using the Download button. Backups can also be edited or deleted.

    + + diff --git a/documentation/admin/categories.php b/documentation/admin/categories.php new file mode 100644 index 000000000..68dc9690c --- /dev/null +++ b/documentation/admin/categories.php @@ -0,0 +1,6 @@ + + +

    Course Categories

    +

    Categories are used for grouping related courses. This is helpful when viewing courses in the course browser. Courses can be associated with categories when they are created, or at a later time by editing their properties. Course categories can also be associated with Themes if the System Preferences Theme Specific Categories option is enabled.

    + + \ No newline at end of file diff --git a/documentation/admin/configuration.php b/documentation/admin/configuration.php new file mode 100644 index 000000000..8a0c97c2e --- /dev/null +++ b/documentation/admin/configuration.php @@ -0,0 +1,6 @@ + + +

    Administrator Home

    +

    The Administrator Home page is the first screen after logging in. It displays a linked list of the administrative tools, shows new and pending Instructor Requests, provides the ability to check for the latest version of ATutor by connecting to the atutor.ca website, and basic installation statistics and information.

    + + \ No newline at end of file diff --git a/documentation/admin/courses.php b/documentation/admin/courses.php new file mode 100644 index 000000000..3e7502794 --- /dev/null +++ b/documentation/admin/courses.php @@ -0,0 +1,6 @@ + + +

    Courses

    +

    An administrator can create and manage courses, shared forums, and course categories without having to login as a course instructor.

    + + diff --git a/documentation/admin/create_patches.php b/documentation/admin/create_patches.php new file mode 100644 index 000000000..7bb34b8de --- /dev/null +++ b/documentation/admin/create_patches.php @@ -0,0 +1,42 @@ + + +

    Creating Patches

    + +

    If you happen to come across a bug you can fix, or have a new feature you would like added to the ATutor public distribution, you can use Create Patch to put your changes into a form that can be easily added to the ATutor public code. Or, if you have a feature you are adding to one ATutor installation that you would like to have added to another, Create Patch is ideal for reproducing your feature across installations. If you are creating new features that are not going to become part of the ATutor public source code, you can build them into a patch so they can be reapplied from version to version as you upgrade your ATutor system. Creating patches does require knowledge of PHP, and of SQL if you plan on creating a patch that changes the ATutor database. Please refer to the appropriate documentation for information on PHP and SQL.

    + +

    +
    ATutor Patch ID
    +
    The patch id you give to your patch must be different from all patches available for the particular version of ATutor it applies to. It is suggested you prefix your patches with a special identifier that represents the author or the authoring organization. If for example the University of Toronto is creating the patch, a patch ID might look like "uoft_0002."
    +
    ATutor Version to Apply
    +
    This needs to be the exact version number of the ATutor version the patch applies to (e.g 1.6). including any minor version numbers (e.g 1.6.1.2) The exact version number can be found on the Administrator open screen under "Statistics and Information." In most cases when applying a patch created for an older version of ATutor, the "ATutor version to Apply" will need to be adjusted. Or, this can be adjusted manually in the patch.xml file included with the source code of the patch. +
    +
    Description
    +
    This should be a detailed description of what the patch does. Example might include "fixes problem uploading files to filemanager" for a bug fix, or "added a timer function to tests" for an added feature, or "removes registration tab" for a feature adjustment, etc. Include enough detail so those applying the patch understand exactly what it will do..
    +
    SQL Statement
    +
    This optional field can be used to insert SQL commands which modify the ATutor database. It might be used to write an SQL statement to modify and existing table, such as changing a data type, or a field size, or to add or remove an field. It can also be used to insert SQL that generates a new table for a new feature created by a patch, or it can be used to insert data into a table used by a feature created by the patch. Any SQL can be included in this field. Be careful when running SQL, that that SQL is not going to interfere with upgrade SQL. If you are changing table structures and those same tables are being altered during an upgrade, the upgrade may fail.
    +
    Dependant Patches
    +
    It is common for later patches to require changes from earlier patches before they can be installed. If this is the case for the patch you are creating, enter the patch IDs into the Dependant Patch ID field. Click on Add Dependent Patch if additional dependencies are required. Be sure to check the patches on the opening screen of the Patcher to see if the file you are modifying with your patch is being modified by an update.atutor.ca patch. If they are modifying the same files, you may need to include the ID numbers for those patches in the Dependant Patches for the patch your are creating.
    +
    Files
    +
    This area is where most ATutor patches are created. Click on Add File to generate a patch block. A patch block can include one of four actions on the file being modified, as described below. As many patch blocks as required can be added to a patch.
    +
    +
    Add File
    +
    The Add action can be used to add a new file to ATutor. This action is often used in conjuction with other patch blocks that alter or delete files, to add a replacement file for one deleted, or to perhaps add a required or include file needed by a modified section in the file being changed. In the File Name field enter the file name to be assigned to the file when it is installed. In the Directory field enter the relative path from the ATutor root directory in which the modified file is or will exist. Select from Upload File using the Browse button to locate the file in your local computer's file system. Note that the upload file can have any name. It will be renamed to the file name listed in the File Name field when it is installed.
    +
    Alter File
    +
    This option is used when you wish to make changes to a piece of code within an existing source code file. In the File Name field enter the name of the file in the ATutor source code that will be altered. In the Directory field enter the relative path to the directory in which the to be alter file exists, relative to the ATutor root directory. In the Code To Replace From field copy the code from the original file the will be replaced, or appended to, and in the Code To Replace To field enter to code that will replace the code above in the From field. Or, if you are adding code instead of replacing code, include the code from the field above so it gets added back if you are only using that code as a way of identifying a location in the file where new code is being added.
    +
    Delete File
    +
    This option will remove files from ATutor. In the File Name field enter the name of the file to be deleted. In the Directory field enter the path to the directory in which the to be delete file exists, relative to the root directory of the ATutor installation.
    +
    Overwrite File
    +
    This option is used to replace an existing file in ATutor with a new one. In the File Name field enter the name of the file to be replaced. In the Directory field enter the path to the directory in which the to be replaced file exists, relative to the root directory of the ATutor installation. In the Upload File field use the Browse button to choose a file from your local computer to replace the specified file. The upload file may be named anything. It will be renamed to the file it is replacing when the patch is installed.
    +
    +
    Create Patch
    +
    Click on this button to build the patch into a downloadable zip file. This zip file can then be uploaded in the Upload field on the main Patcher screen to apply a patch to a system.
    +
    Save Patch
    +
    Click on this button to save the developing patch to the ATutor database for future reference. Though it is not required, you should save a copy of the patch in this way, so it can be retrieved and edited if necessary. Or, if a patch takes more than a single sitting to build, you can save it, then retrieve it later to continue.
    +
    Cancel
    +
    Press this button to ignore the latest changes to the patch, and return to My Own Patches Screen.
    + +
    + + + + \ No newline at end of file diff --git a/documentation/admin/creating_courses.php b/documentation/admin/creating_courses.php new file mode 100644 index 000000000..23353bdf3 --- /dev/null +++ b/documentation/admin/creating_courses.php @@ -0,0 +1,17 @@ + + +

    Creating Courses

    + +

    See Creating Courses documentation for Instructors.

    + +

    In addition, administrators have access to the following properties:

    + +
    +
    Course Quota
    +
    Defines the maximum size of a course. That is, the amount of space each course's file manager can have.
    + +
    Max File Size
    +
    Defines the maximum size allowed for a file being uploaded to a course's file manager.
    +
    +

    Note that Max File Size limitations can not be set higher than that allowed in the PHP settings for the system. The maximum allowable upload size can be increased by editing the values of upload_max_filesize and post_max_size in the system's php.ini configuration file.

    + \ No newline at end of file diff --git a/documentation/admin/creating_themes.php b/documentation/admin/creating_themes.php new file mode 100644 index 000000000..aedc396b5 --- /dev/null +++ b/documentation/admin/creating_themes.php @@ -0,0 +1,6 @@ + + +

    Creating Themes

    +

    The process for creating a theme is basically to export an existing theme from ATutor, import it back into ATutor, then modify the files of the copied theme. Details about creating themes can be found in the themes_readme.txt file found in the /themes directory of your ATutor installation. Also see the comments included in the files of the Default ATutor theme for additional details.

    + + diff --git a/documentation/admin/cron_setup.php b/documentation/admin/cron_setup.php new file mode 100644 index 000000000..0a47346d7 --- /dev/null +++ b/documentation/admin/cron_setup.php @@ -0,0 +1,32 @@ + + +

    Cron Set-Up

    + +

    ATutor operates best with the help of an automated event scheduler, commonly known as a cron job. The cron interval should be set at between 5-30 minutes, depending on server resources. Setting the cron to 10-15 minutes is recommended.

    + +

    The cron is run by requesting a specific ATutor page, and can be initiated by any machine that has an Internet connection and access to the ATutor installation.

    + +

    Notice that the URL being used will be unique for each installation and that for security reasons the requested URL includes a secret six-character alpha-numerica authentication key. The cron will not run if the key is incorrect or missing.

    + +

    The Mail Queue feature requires the cron to be set-up and running correctly before it can be enabled.

    + +

    Unix Setup

    +
      +
    1. Enter your hosts cron utility, either using an existing web interface or from the shell with the command crontab -e.
    2. +
    3. To run the cron every 10 minutes enter one of the following lines into the crontab editor:
      + */10 * * * * wget -q -O /dev/null http://your-server.com/atutor/admin/cron.php?k=SECRET-KEY
      + Or
      + */10 * * * * lynx -dump http://your-server.com/atutor/admin/cron.php?k=SECRET-KEY > /dev/null +

      Replace your-server.com/atutor/ with the full server and path to your ATutor installation.

      +

      Replace SECRET-KEY with the key provided on the Cron Configuration page in your ATutor Administration section.

      +

      Replace 10 with the desired interval.

      +
    4. +
    + +

    Note: If your site uses SSL then replace http with https and you may also need to add --no-check-certificate to wget.

    + +

    Windows et al Setup

    +

    webcron.org offers free web-based cron services and is available in multiple languages.

    + + + \ No newline at end of file diff --git a/documentation/admin/default_preferences.php b/documentation/admin/default_preferences.php new file mode 100644 index 000000000..b9c66402e --- /dev/null +++ b/documentation/admin/default_preferences.php @@ -0,0 +1,7 @@ + + +

    Default Preferences

    + +

    The default preferences are applied to newly created accounts at the time of their creation, and to guests when accessing public courses. Members may alter their settings once they have logged in by going to the Preferences area in the main navigation. For more information, see Preferences in the general help area.

    + + \ No newline at end of file diff --git a/documentation/admin/default_side_menu.php b/documentation/admin/default_side_menu.php new file mode 100644 index 000000000..d4b4a0783 --- /dev/null +++ b/documentation/admin/default_side_menu.php @@ -0,0 +1,7 @@ + + +

    Default Side Menu

    + +

    Administrators are able to set the side menu dropdowns for newly created courses by specifying side menu defaults. Instructors can alter these settings after a course is created by going to Properties under the Manage area. For more information, see Side Menu in the instructor help area.

    + + \ No newline at end of file diff --git a/documentation/admin/default_student_tools.php b/documentation/admin/default_student_tools.php new file mode 100644 index 000000000..ac0e294da --- /dev/null +++ b/documentation/admin/default_student_tools.php @@ -0,0 +1,7 @@ + + +

    Default Course Tools

    + +

    Administrators are able to set the tools that will appear in the main navigation and the home page for newly created courses by specifying student tool defaults. Instructors can alter these settings after a course is created by going to Course Tools area under the Manage tab.

    + + \ No newline at end of file diff --git a/documentation/admin/email_users.php b/documentation/admin/email_users.php new file mode 100644 index 000000000..bd96b9b6d --- /dev/null +++ b/documentation/admin/email_users.php @@ -0,0 +1,6 @@ + + +

    Email Users

    +

    The Email Users feature allows an administrator to send an email to all students, instructors, or both. Unconfirmed and Disabled accounts are not included in the mailing. The email address specified in the System Preferences is used as the reply-to address for the email(s).

    + + \ No newline at end of file diff --git a/documentation/admin/en/index.php b/documentation/admin/en/index.php new file mode 100644 index 000000000..85abfdf3b --- /dev/null +++ b/documentation/admin/en/index.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/documentation/admin/enrollment.php b/documentation/admin/enrollment.php new file mode 100644 index 000000000..e2be8c66a --- /dev/null +++ b/documentation/admin/enrollment.php @@ -0,0 +1,8 @@ + + +

    Enrollment

    +

    The Enrollment list for a particular course determines which of your students have access to the course content and course management tools. Instructors can create, import and export student lists.

    + +

    To administer members of a course, select the Enrollment tab, then select a course and press the Filter button.

    + + diff --git a/documentation/admin/enrollment_privileges.php b/documentation/admin/enrollment_privileges.php new file mode 100644 index 000000000..c9f30bfbd --- /dev/null +++ b/documentation/admin/enrollment_privileges.php @@ -0,0 +1,8 @@ + + +

    Privileges

    + +

    Students who are enrolled in a course can be assigned course administrative privileges. This allows students to perform actions like managing content, creating and marking tests, managing groups, or moderating forums or the chat. This tool is useful for creating teaching assistants, or for creating multiple instructors for a course. Select the users you wish to give privileges to, and use the Privileges button. Then choose which tools you want each user to have access to and use the Save button.

    + + + diff --git a/documentation/admin/error_logging.php b/documentation/admin/error_logging.php new file mode 100644 index 000000000..e92beecf1 --- /dev/null +++ b/documentation/admin/error_logging.php @@ -0,0 +1,6 @@ + + +

    Error Logging

    +

    Error logging is available to administrators as a trouble shooting tool. Should the system be giving error messages, a daily list of these errors are collected and available for review or to be bundled up and sent to the ATutor team for investigation. When using the atutor.ca support forums, attaching any error logs may be helpful in finding solutions to problems on your system.

    + + diff --git a/documentation/admin/feeds.php b/documentation/admin/feeds.php new file mode 100644 index 000000000..260c74edf --- /dev/null +++ b/documentation/admin/feeds.php @@ -0,0 +1,6 @@ + + +

    News Feeds

    +

    Instructors may display syndicated feeds in the side menu of their courses. The choice of feeds available to them is controlled by the administrator. Feeds may be managed by going to the News Feeds link under the System Preferences tab.

    + + diff --git a/documentation/admin/forums.php b/documentation/admin/forums.php new file mode 100644 index 000000000..ec45b1073 --- /dev/null +++ b/documentation/admin/forums.php @@ -0,0 +1,6 @@ + + +

    Forums

    +

    This section allows administrators to create regular course-specific forums as well as shared cross-course forums. A shared forum is available to all courses specified to use it, allowing users from different courses to communicate with eachother in one forum. Only administrators can create shared forums, though instructors or privileged users in any of the courses sharing a forum can manage its messages.

    + + \ No newline at end of file diff --git a/documentation/admin/fr/index.php b/documentation/admin/fr/index.php new file mode 100644 index 000000000..83b4909db --- /dev/null +++ b/documentation/admin/fr/index.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/documentation/admin/google_key.php b/documentation/admin/google_key.php new file mode 100644 index 000000000..f892c1005 --- /dev/null +++ b/documentation/admin/google_key.php @@ -0,0 +1,7 @@ + + +

    Google Key

    + +

    For the Web Search module to function, an administrator must first create an account and obtain a license key at google.com/apis and enter it on this page. The google web search can then be used by students as an external embedded service within ATutor.

    + + \ No newline at end of file diff --git a/documentation/admin/importing_themes.php b/documentation/admin/importing_themes.php new file mode 100644 index 000000000..b92096cd1 --- /dev/null +++ b/documentation/admin/importing_themes.php @@ -0,0 +1,8 @@ + + +

    Importing/Exporting Themes

    +

    Themes can be imported into, or exported from, ATutor using the Themes manager in the ATutor administrators' configuration tools. An existing theme can be exported, then imported back into an ATutor installation to create a copy, after which the copy can be modified to create a new theme. Themes can be exported and shared with others. See the Themes page on atutor.ca for a list of available themes, and for a place to share your themes.

    + +

    To import a theme the ./themes/ directory must be writable. On Windows machines using multiple user accounts, that directory will have to be shared to provide write access to it. On Unix machines the command chmod a+rw themes should be used to make the directory writable.

    + + \ No newline at end of file diff --git a/documentation/admin/index.php b/documentation/admin/index.php new file mode 100644 index 000000000..04121b3aa --- /dev/null +++ b/documentation/admin/index.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/documentation/admin/installation.php b/documentation/admin/installation.php new file mode 100644 index 000000000..f69f56d5e --- /dev/null +++ b/documentation/admin/installation.php @@ -0,0 +1,6 @@ + + +

    Installation

    +

    This section describes the requirements and important considerations needed for running ATutor. It also details the steps involved in installing and upgrading an installation.

    + + diff --git a/documentation/admin/instructor_requests.php b/documentation/admin/instructor_requests.php new file mode 100644 index 000000000..33f3d6645 --- /dev/null +++ b/documentation/admin/instructor_requests.php @@ -0,0 +1,10 @@ + + +

    Instructor Requests

    +

    If the System Preferences Allow Instructor Requests option is enabled and the Auto Approve Instructor Requests option is disabled, then pending instructor account requests will be listed on this page.

    + +

    Using the Deny or Approve buttons after selecting an entry will remove it from the list and take the appropriate action. An email message will be sent to the account holder notifying them of the change.

    + +

    Note that the number of pending Instructor Requests is always listed on the Administrator Home page.

    + + \ No newline at end of file diff --git a/documentation/admin/introduction.php b/documentation/admin/introduction.php new file mode 100644 index 000000000..977b8ee41 --- /dev/null +++ b/documentation/admin/introduction.php @@ -0,0 +1,6 @@ + + +

    Introduction

    +

    Welcome to the ATutor Administrator Documentation! This documentation is intended for those who manage ATutor systems. Also see the Instructor Documentation for more about creating and managing courses.

    + + \ No newline at end of file diff --git a/documentation/admin/languages.php b/documentation/admin/languages.php new file mode 100644 index 000000000..8661a5e2d --- /dev/null +++ b/documentation/admin/languages.php @@ -0,0 +1,58 @@ + + +

    Languages

    +

    ATutor can be displayed in many different languages! Through the Langauge Manager completed languages packs can be selected and imported directly from the atutor.ca website.

    + +

    Managing Existing Languages

    +

    Installed languages can be edited, deleted or exported as an ATutor language pack for redistribution. When exporting a language, a download prompt will appear asking to download a zip file of the language pack.

    + +

    Editing the language properties allows you to change the following: +

      +
    • Language Code
    • +
    • Locale
    • +
    • Character Set
    • +
    • Direction
    • +
    • Left to Right, Right to Left
    • +
    • Regular Expression
    • +
    • Language name translated
    • +
    • Language name in English
    • +
    +

    + +

    Note that the default language (as specified in the System Preferences Default Language) cannot be disabled or deleted unless another language has been installed.

    + + +

    Importing Languages

    +

    Language packs can be imported either manually by retreiving the package and then importing it into ATutor, or automatically by having ATutor connect to the atutor.ca language repository directly.

    + +

    To manually import a new language pack:

    +
      +
    1. Visit atutor.ca/atutor/translate/ to download one of the available language packs for your version.
    2. +
    3. Use the Browse... button to find the downloaded language pack.
    4. +
    5. Use the Import button to import the language.
    6. +
    + +

    If your ATutor installation is connected to the Internet and can contact the atutor.ca website, then it will try to retrieve the list remotely. To automatically import a new language pack from within ATutor:

    + +
      +
    1. Select the language you want to import from the drop down.
    2. +
    3. Use the Import button to import the selected language.
    4. +
    + +

    If your installation cannot retrieve the language list from atutor.ca, a message indicating so will be presented rather than a drop down list. In this case you will have to use the manual method described above.

    + + +

    Translating ATutor

    + +

    Administrators have the ability to customize an installation's language. In order to translate a language, +

      +
    1. Set the AT_DEVEL_TRANSLATE constant in /include/vitals.inc.php to '1'
    2. +
    3. Set the session language to the language you wish to translate by using the language selector at the bottom of the screen.
    4. +
    5. Use the Translate button to pop up a translation window.
    6. +
    +

    + +

    You can contribute to the ATutor community by exporting a language pack from your ATutor installation, and attaching it to a message in the atutor.ca Translation Forum. Also see the Translator Documentation for further details about translating ATutor.

    + + + diff --git a/documentation/admin/link-out.gif b/documentation/admin/link-out.gif new file mode 100644 index 0000000000000000000000000000000000000000..ecef0947aa06e62f442a1aa33b6830322fb78c64 GIT binary patch literal 52 zcmZ?wbhEHbWM^PwXkcLY|Nnn;bF<=47Dfgj&;b!383rb9mZrpM)jO^vu3N{%U=09c Cehr@h literal 0 HcmV?d00001 diff --git a/documentation/admin/managing_existing_themes.php b/documentation/admin/managing_existing_themes.php new file mode 100644 index 000000000..0965c0cea --- /dev/null +++ b/documentation/admin/managing_existing_themes.php @@ -0,0 +1,28 @@ + + +

    Managing Existing Themes

    +

    All available themes on an ATutor system are listed in the Administrator's Themes section.

    + +
    +
    Preview
    +
    Use the Preview button to test the theme to make sure it doesn't break. If a previewed theme breaks, simply log-out and login again to restore the default theme. The Preview button can also be used to preview disabled themes. This feature is available in ATutor 1.5.1+.
    + +
    Enable/Disable
    +
    Enabled themes are available to users in their Preferences. Themes can be disabled, helpful if you are modifying a theme. If a student's preferred theme is disabled, the system default theme will be used in its place.
    + +
    Set as Default
    +
    If a theme is set as the Default Theme, it will display for students who have not selected a prefered theme, and it will be displayed on public pages, such as the Login screen or Registration screen.
    + +
    Export
    +
    Any theme can be exported from an ATutor installation to share with others. It can also be imported back into an ATutor installation as a copy, available to be modified for creating a new theme.
    + +
    Delete
    +
    A theme is removed from the system if the Delete button is used. The Default theme can not be deleted.
    +
    + +

    Category Themes

    +

    If there are Course Categories defined and the System Preferences Theme Specific Categories has been enabled, themes can be assigned to categories of courses so they are all displayed with the same look and feel. When defining course categories while Category Themes is enabled, a list of available themes will appear to select from, and assign to each category.

    + +

    Note that when Category Themes has been enabled, users will no longer be able to select themes from their personal preference settings.

    + + \ No newline at end of file diff --git a/documentation/admin/master_student_list.php b/documentation/admin/master_student_list.php new file mode 100644 index 000000000..5f1b61600 --- /dev/null +++ b/documentation/admin/master_student_list.php @@ -0,0 +1,20 @@ + + +

    Master Student List

    +

    If the System Preferences Authenticate Against A Master Student List option is enabled, this page will allow an administrator to manage that list. If enabled, only new registrations that validate against the master list will be successful. The master list is flexible and can be used to validate any two fields, one of which is publicly viewable to Administrators, while the other is hidden. A common use of this feature would be to authenticate students using a previously assigned Student ID & Birth Date combination. Two extra fields will appear on the Registration screen when master list authentication is enabled.

    + +

    Subsequently, when a student registers for an ATutor account on the system, he/she must provide this authenticating information (such as their student ID and Birth Date). Once an account is authenticated and created, the user will then be associated with the appropriate entry in the Master Student List. If Require Email Confirmation Upon Registration is enabled in System Preferences, the user must confirm his/her account using that email before the account is activated.

    + +

    Viewing the Master Student List shows Student ID-Username pairs. Student IDs in the Master Student List that are not associated with any student account are considered to not have been created.

    + +

    Importing Student IDs

    +

    Importing Student IDs into the Master Student List requires a specifically formatted file. This file can be uploaded under the "Upload List" heading.

    + +

    The master list must be imported from a plain text file, where each row in the file contains two fields seperated by a single comma. The first field will be used as the Student ID. The second field will be the PIN or Password which will be encrypted by the ATutor system, once the list is uploaded, so that it cannot be viewed and read by anyone. Those two fields together will be used to authenticate students when creating new accounts. The fields may optionally be enclosed by double quotes. Such a file is known as a CSV file and can be generated manually using a text editor, or by any spreadsheet application (such as MS Excel).

    + +

    In the example below, a student number and a birth date are used to construct a master list:

    +
    "12345", "10/07/54"
    +"12346", "23/04/76"
    +"12347", "30/05/68"
    + + \ No newline at end of file diff --git a/documentation/admin/modules.php b/documentation/admin/modules.php new file mode 100644 index 000000000..177187800 --- /dev/null +++ b/documentation/admin/modules.php @@ -0,0 +1,34 @@ + + +

    Modules

    +

    Import Modules

    +

    As of version 1.6.2 the preferred method of installing modules is by importing modules from atutor.ca using the Module Manager. Read the descriptions for modules you want to install to be sure all prerequisites are met, and note the version numbers the module has been tested with. Though in most cases older modules will function without any trouble in newer versions of ATutor, occassionally they need to be updated to work with a current version. Please report any problems you experience with modules to the Modules Forum on atutor.ca

    + +

    Uninstalling Imported Modules

    +

    Modules that have been installed using the Import method above, can be uninstalled using the Module Manager's Uninstall button. After pressing the Uninstall button you will be given the option to remove the data and the directories associated with the module. Select from the checkboxes, and press continue. If you are removing the module completely, you will probably want to delete the data and directories. If you are upgrading or reinstalling a module, you will want to save the data and directories so they can be reused with the upgraded or reinstalled version of the module.

    + +

    Upload Module

    +

    Modules can be downloaded uploaded through the Module Upload form at the top of the Install Modules screen. Uploaded modules must be in ZIP format, so if you have older modules, they may need to be axtracted from the tar.gz format, then re-archived in ZIP format before uploading.

    + +

    Missing Modules

    +

    When an ATutor system is upgraded modules need to be reinstalled. Those modules will be listed as missing in the Module Manager. + +

    Partially Installed Modules

    +

    When a module is uninstalled, but its data and/or directories are saved, it will be listed as Partially Uninstalled. The same status will appear for modules for which the uninstall process was not completed for one reason or another. To remove these modules completely, they must either be reinstalled then uninstalled once more, or they must be removed by deleting the database tables and/or directories manually.

    + +

    Manual FTP Module Install

    +

    In most cases you should use the Import method described above. Manually installing a module through an FTP client is useful if you are installing a third party module not available from atutor.ca. Or, if you are a module developer and need control over the module files for editing etc. you will want to use the manual FTP method. Manually installed modules must be uninstalled manually if they need to be removed.

    + +

    To install a module manually it must first be extracted into a unique subdirectory within the ./mods directory of your ATutor installation. It will then be listed on the Install Modules page where more details can be viewed, and the module installed.

    + +

    After extracting a module, see the readme file in the module's top directory for any additional installation instructions or requirements.

    + +

    Creating Modules

    + See the ATutor Module Development Documentation for information about creating ATutor modules, and review the module files in the Hello World demo module (and other modules) as a model that can be duplicated and modified to quickly add new addon features to an ATutor installation.

    + +

    Export Module

    +

    If you want to take a copy of a module from your ATutor system to import into another ATutor installation, or to bundle a copy of a module you have created to submit to atutor.ca to be included in the module repository, select the module from the Module Manager screen, then press Export. This will create a standard module ZIP file that you can download.

    + +

    Visit the ATutor Modules Site for a list of add-on modules for ATutor.

    + + \ No newline at end of file diff --git a/documentation/admin/my_account.php b/documentation/admin/my_account.php new file mode 100644 index 000000000..bd9eec1f2 --- /dev/null +++ b/documentation/admin/my_account.php @@ -0,0 +1,6 @@ + + +

    My Account

    +

    My Account allows the Administrator to change his/her account password, name, or email address.

    + + \ No newline at end of file diff --git a/documentation/admin/new_installation.php b/documentation/admin/new_installation.php new file mode 100644 index 000000000..4901b7b54 --- /dev/null +++ b/documentation/admin/new_installation.php @@ -0,0 +1,46 @@ + + +

    New Installation

    +

    Please review the requirements section before attempting to install ATutor. The latest version of ATutor can always be found on the atutor.ca downloads page.

    + +

    Windows Considerations

    +

    To extract the ATutor .tar.gz archive you will need an application like WinZip or WinRar.

    + +

    Unix Considerations

    +

    To extract the ATutor .tar.gz archive, use the command tar -zxvf ATutor-version_number.tar.gz, which will create a directory called ATutor in your current working directory.

    + +

    Extracting the files on a Windows machine and then uploading them via FTP is not recommended, as it may not preserve the case-sensitive file names.

    + +

    Installing on a Unix machine requires some knowledge of file and directory permissions. You will be required to create a content directory and set permissions for that directory and for the include/config.inc.php file, so that the web server can write to them. The installation will not be successful if the permissions are not correctly set on that file and directory.

    + +

    Changing Unix file permissions from the shell prompt: chmod a+rw filename or chmod a+rwx directoryname.

    + +

    Changing Unix file permissions from an FTP client: Many FTP clients allow you to change a file's permissions. The option may be labled as "Unix Permissions", "CHMOD", or simply as "Properties" or "Attributes" and will display a window with Read, Write, and Execute checkboxes for Owner, Group, and World; checking the appropriate boxes will change that file's permissions. In our case we need the include/config.inc.php to be Readable and Writeable by World, and the content directory to be Readable, Writeable, and Executable by World.

    + +

    Installation Procedure

    +

    Extract the downloaded archive using the method specified for your system (either Windows or Unix). Open a web browser and enter the address to your new ATutor installation, http://your_server.com/path_to_atutor/ATutor/, then follow the step-by-step instructions:

    + +
      +
    1. Terms of Use
      + The usage of ATutor is restricted by the GNU General Public License (GPL). Your agreement with the GPL is required if you wish to use ATutor. See the Licensing section for more details.
    2. + +
    3. Database
      + Enter the required details needed to connect to your MySQL database. The optional Table Prefix (e.g. "AT_") allows ATutor to share an existing database with other applications and tables. The ATutor installation script will attempt to create the database specified, if it does not already exist. This requires that your MySQL user account has permission to create databases and permission to create tables. If this step fails, contact your system administrator to have your MySQL account upgraded to allow creation of new databases, or ask your administrator to create the database for you.
    4. + +
    5. Accounts & Preferences
      + The Super Administrator account is used for managing your ATutor installation. The Super Administrator can also create additional Administrators each with their own privileges and roles once ATutor is installed. The personal account can be used to enroll in or create courses.
    6. + +
    7. Content Directory
      + Create a content directory, preferably outside your web server's document directory for added security, and set permissions as described above. On a Unix machine you will need to manually change the permissions on the listed files and directories in this step. No action is usually required on a Windows server, though in some circumstances Windows users may need to adjust the properties of the specified files and directories to make them writable. Copy the path of the created directory into the text box provided. Ensure that there are no shortcuts or symbolic links in the path.
    8. + +
    9. Save configuration
      + Before reaching the final step the include/config.inc.php file needs to be writable, otherwise an error will appear. Follow the instructions on the screen if the file permissions need to be changed. If the file does not exist in the include/ directory, then you will need to create an empty text file with the filename config.inc.php.
    10. + +
    11. Anonymous Usage Collection
      + To assist the development team in serving the ATutor community, you can anonymously submit basic system information to the atutor.ca server.
    12. + +
    13. Done!
      + ATutor installation has been successful and you may now log-in with your personal account or the administrator account created in Step 3.
    14. +
    + + \ No newline at end of file diff --git a/documentation/admin/pages.inc.php b/documentation/admin/pages.inc.php new file mode 100644 index 000000000..7ee538ff9 --- /dev/null +++ b/documentation/admin/pages.inc.php @@ -0,0 +1,41 @@ + diff --git a/documentation/admin/patcher.php b/documentation/admin/patcher.php new file mode 100644 index 000000000..89c184ce1 --- /dev/null +++ b/documentation/admin/patcher.php @@ -0,0 +1,22 @@ + + +

    Patcher

    +

    The Patcher was introduce in ATutor 1.6 to allow administrators to update their systems with feature adjustments, security fixes, and other ATutor code changes in between ATutor releases. The Patcher is included as a standard module with ATutor 1.6.1+, and installs as an extra module for 1.6.

    +
    +
    The Patch List
    +
    On the opening screen of the Patcher will appear a list of patches available for the version of ATutor you are using, along with a description of each patch. This list is retrieved from update.atutor.ca, as are the patches themselves, so you must be connected to the Internet. Patches are retrieved from update.atutor.ca by ATutor and applied as necessary.
    +
    File Permissions
    +
    In most cases you will be asked to temporarily grant write permission to the files that need to be updated or replaced, then once the patch has been applied, you will be asked to change the permissions back to read only. It is important that you follow the instructions after patches have been applied, otherwise you run the risk of opening a security hole. +
    +
    Types of Patches
    +
    Patches come in various forms. Some patches replace code in a file with new code. Others replace a file with a new file. Others may do both on multiple files and multiple code changes. Other patches delete files that are no longer required.
    +
    Required and Non-Required Patches
    +
    In most cases you will want to install patches in the order they appear in the patch list, but not all patches are required patches. Some feature patches can be ignored if you do not need the features they would add or modify on your system. Other patches will have dependencies, requiring the administrator to install earlier patches before installing a later one. You will be prompted to install previous patches if there are dependencies.
    +
    Checks and File Backups
    +
    If you have made changes to a file the Patcher wishes to change, you will be prompted to continue or not. The patcher compares your local file with the same file in the ATutor code repository, and if they differ the prompt will display. In many cases the Patcher can apply patches without changing the code you have modified, but if the code to be replaced was modified, the patch will fail, or if the patch replaces a file, your changes will be lost. In all cases the patcher will create a backup of the files that were modified, identified by the filename plus the patch number added as a suffix. Rename the file to its original name to restore that file back to its original state. You can list these files by clicking the view messages button next to the patch listing after the patch is installed. After you have confirmed that the patches were applied and are working properly, it is safe to delete the backup files, though it does not hurt to keep them around.
    +
    Private Patches
    +
    In some cases private patches can be applied by uploading a patch file through the upload form below the patch list. Private patches are often those used to apply changes that are not being applied to the ATutor default source code, or to apply custom features, or to share patches between users, etc. When uploading a patch, be sure the patch id, defined in the patch.xml file, is unique .
    +
    + + + \ No newline at end of file diff --git a/documentation/admin/requirements_recommendations.php b/documentation/admin/requirements_recommendations.php new file mode 100644 index 000000000..829c9aaee --- /dev/null +++ b/documentation/admin/requirements_recommendations.php @@ -0,0 +1,81 @@ + + +

    Requirements & Recommendations

    +

    The first step when installing or upgrading ATutor is to check if the minimum requirements are met. The following describes those checks.

    + +

    File Integrity

    +

    The Case Sensitivity check verifies that file names were not converted to lower-case during the extraction process. This is not an issue on case-insensitive operating systems like MS Windows, but is an issue on case-sensative ones like Linux.

    + +

    Web Server

    +

    The ATutor development and testing processes are done primarily on Apache 1.3 and Apache 2 (using pre-forking), and as such we strongly recommend them for production environments. ATutor has been successfully installed on other web servers, including, Zeus, lighttpd, Abyss, Zazou Mini Web Server, Microsoft IIS, and Jana-Server.

    + +

    The web server can be configured with SSL for added security or to use a non-standard port and ATutor will function without modification.

    + + +

    PHP

    +

    ATutor is written in the PHP language. The PHP configuration file contains many configuration settings that can be changed. The following are the minimum requirements needed to install and use ATutor.

    + +
    +
    PHP 5.0.2+
    +
    Version 5.2.0 or higher is recommended.
    + +
    zlib
    +
    Zlib support must be enabled in PHP; It is used for compressing and uncompressing ZIP files.
    + +
    mysql
    +
    MySQL support must be enabled in PHP.
    + +
    mbstring
    +
    MBstring support must be compiled into PHP to support UTF-8 lamguage characters.
    + +
    curl (optional)
    +
    Curl support must be compiled into PHP for ATutor Social (Networking) .
    + +
    safe_mode = Off
    +
    safe_mode must be disabled in PHP. ATutor cannot function with the restrictions enforced when safe_mode is enabled.
    + +
    file_uploads = On
    +
    File uploads support must be enabled in PHP.
    + +
    upload_max_filesize >= 2 MB
    +
    This option specifies the maximum size of files that can be uploaded to ATutor.
    + +
    post_max_size >= 8 MB
    +
    This value must be larger than the upload_max_filesize.
    + +
    sessions
    +
    Sessions support must be enabled in PHP.
    + +
    session.auto_start = 0
    +
    session.auto_start must be disabled in PHP.
    + +
    session.save_path
    +
    session.save_path must be set to a real path that can store session data.
    + +
    . in include_path
    +
    . must be in the list of paths in the include_path option.
    +
    + + +

    Additionally, the following php.ini configuration settings are recommended:

    +
    display_errors          = Off
    +arg_separator.input     = ";&"
    +register_globals        = Off
    +magic_quotes_gpc        = Off
    +magic_quotes_runtime    = Off
    +allow_url_fopen         = On
    +allow_url_include       = Off
    +register_argc_argv      = Off
    +zlib.output_compression = On
    +session.use_trans_sid   = 0
    +
    + +

    MySQL

    +

    Currently ATutor only supports the MySQL database. MySQL 4.1.10 or higher is required.

    + +

    A database user account with database creation privileges is required if your database does not already exist. That same user will then need table creation privileges for the chosen database. See the MySQL chapter How the Privilege System Works for additional information.

    + +

    Web Browser

    +

    ATutor makes use of many new HTML features that are only supported in recent web browsers. Though ATutor is designed to function effectively in older browsers we strongly recommend using the latest version of your favorite browser. We recommend FireFox for either Windows, *nix or Mac OS X.

    + + \ No newline at end of file diff --git a/documentation/admin/styles.css b/documentation/admin/styles.css new file mode 100644 index 000000000..b6bae62be --- /dev/null +++ b/documentation/admin/styles.css @@ -0,0 +1,131 @@ +pre { + font-family: trebuchet ms, Arial, sans-serif; +} + +body{ + background-color: #fafafa; + font-family: Arial,sans-serif; + font-size: small; +} +h1,h2,h3,p, table, ul { + font-family: "Trebuchet MS",Verdana,Arial,sans-serif; +} +h1 { + border-bottom: 1px dashed #cfcfcf; + margin-left: -5px; + padding-left: 15px; + margin-right: -5px; + padding-right: 15px; +} +h2{ + color: #666; + border-bottom: 1px dashed #cfcfcf; + margin-left: -5px; + padding-left: 15px; + margin-right: -5px; + padding-right: 15px; +} +p{padding-bottom:1em} + +a { + text-decoration: none; + border-bottom: 1px solid; + font-weight: bold; +} +a:hover { + border-bottom: 0px; +} +td,th { + font-size: 85%; +} + +kbd { + padding: 0px 1px 0px 1px; + border-width: 1px 2px 2px 1px; + border-style: solid; + border-color: #edd #baa #baa #eed; + white-space: pre; +} + +code { + font-family: "Trebuchet MS",Verdana,Arial,sans-serif; + background-color: #efefef; + padding: 0px 4px 0px 4px; + border-width: 1px 1px 1px 1px; + border-style: solid; + border-color: #edd #baa #baa #eed; +} + + +div#toc { + color: #f0f0f0; + padding-bottom: 15px; +} + +div#toc ul { + list-style: none; +} +div#toc li { + padding-top: 2px; + padding-bottom: 0px; +} + +ol { + margin-top: 0px; +} + +ol li { + padding-bottom: 3px; +} + +dl { + margin: 0 10px +} +dl dd { + padding-top: 0px; + padding-left: 5px; + margin-left: 5%; + border-left: 1px solid #d0d0d0; + margin-bottom: 10px; +} + +acronym { + cursor: help; +} + +a[href*="http"] { + padding-right: 8px; + background-image: url('link-out.gif'); + background-repeat: no-repeat; + background-position: right 4px; + margin-right: 2px; +} + +div#nav-links { + margin-left: 20px; + margin-right: 20px; + margin-bottom: 20px; +} + +div#nav-links a { + color: black; + text-decoration: none; + border-bottom: 1px solid; +} + +pre { + font-family: Courier, monospace; + background-color: #EEEEFF; + padding: 5px; + margin-left: 20px; + color:#761596; + margin-top: 0px; + width: 50%; + font-size: smaller; +} + +div.seq { + font-size: smaller; + padding: 5px; + border: 1px dashed #cfcfcf; +} \ No newline at end of file diff --git a/documentation/admin/system_preferences.php b/documentation/admin/system_preferences.php new file mode 100644 index 000000000..65a291877 --- /dev/null +++ b/documentation/admin/system_preferences.php @@ -0,0 +1,106 @@ + + +

    System Preferences

    +
    +
    Site Name
    +
    The name of the course server's website. This name will appear at the top of all the public pages, in the web browser's title bar, and as the From name when sending non-personal emails.
    + +
    Home URL
    +
    This will be the web address for the 'Home' link in the public area. Leave empty to remove this link.
    + +
    Default Language
    +
    The default language to use if the client's browser settings cannot be detected. Must be one of the languages already installed. See the Languages section on installing and managing existing languages.
    + +
    Contact Email
    +
    The reply address used for emails sent for instructor requests and other system emails.
    + +
    Time Zone Offset
    +
    Add or subtract hours from the times and dates displayed in ATutor, so they match the local time for the ATutor installation. Valid values range from -12 to 12. The positive sign is not required when adding hours. The minus sign is required when subtracting hours. Individual users may also modify their own Time Zone Offset setting, if their local time differs from that of the ATutor installation.
    + +
    Maximum File Size
    +
    Maximum allowable file size in Bytes that can be uploaded to the course's File Manager. This does not override the value set for upload_max_filesize in php.ini.
    + +
    Maximum Course Size
    +
    Total maximum allowable course size in Bytes. This is the total amount of space a course's File Manager can use.
    + +
    Maximum Course Float
    +
    How much a course can be over its Maximum Course Size limit while still allowing a file to upload or import. Makes the course limit actually be Max Course Size + Max Course Float. When Max Course Float is reached, no more uploads will be allowed for that course until files are deleted and the course's space usage falls under the Maximum Course Size.
    + +
    Maximum login attempts
    +
    The amount of times the user can attempt logging in before the system freeze the account for an hour. Enter 0 for infinite times.
    + +
    Display Name Format
    +
    The Display Name Format option controls how non-administrator users' names appear. This option is available in ATutor 1.5.4+.
    + +
    Authenticate Against A Master Student List
    +
    Whether or not to enable Master Student List authentication. If enabled, only new accounts that validate against the master list will be created. See the Master Student List section for additional details on using this feature.
    + +
    Allow Self-Registration
    +
    If enabled, users can self-register. Disable to remove registration functions
    + +
    Allow Instructors to enroll users from the system registration list.
    +
    If enabled, instructors are allowed to enroll users from the system registration list.
    + +
    Allow the use of CAPTCHA
    +
    This requires the GD library installed (FreeType library is recommanded to have for better effect). If enabled, users will be asked to enter an additional field for the alphanumeric sequence of the CAPTCHA image. The CAPTCHA image can be mended in various ways depending on your need, please visit phpCaptcha for more details.
    + +
    Allow Students to Unenroll
    +
    If enabled, students can unenroll themselves from courses. If disabled, the Unenroll functions are removed.
    + + +
    Require Email Confirmation Upon Registration
    +
    If email confirmation is enabled, before they can login, registrants must confirm their registration by replying to a message sent to the email address they registered with.
    + +
    Allow Instructor Requests
    +
    If enabled, students will be allowed to request that their account be upgraded to an instructor account. Instructor account requests must be approved by administrators using the Instructor Requests section. If disabled then the Create Course link used for requesting an instructor account will be removed and only the administrators will be able to create instructor accounts.
    + +
    Instructor Request Email Notification
    +
    If enabled, and if Allow Instructor Requests is enabled, then an email notification message will be sent to the Contact Email each time a new instructor account request is made. This does not affect whether or not instructor requests can be made, only whether or not a notification message is sent out each time.
    + +
    Auto Approve Instructor Requests
    +
    If Allow Instructor Requests is enabled, then existing students requesting instructor accounts will be upgraded automatically, bypassing the approval process. Additionally, any newly created accounts will be created as instructors rather than as students. Useful for setting up a demo version of ATutor.
    + +
    Theme Specific Categories
    +
    Theme specific categories allows for the association between themes and categories. Courses belonging to a specific category will always be presented using that category's associated theme. This option disables the personalised theme preference. Use the Categories section to create and manage course categories, and the Themes section to install and manage themes.
    + +
    User Contributed Handbook Notes
    +
    If enabled will allow anyone viewing the Handbook to contribute notes. User contributed notes must then be approved by an administrator by logging in on the main Handbook page. This option is available in ATutor 1.5.1+.
    + +
    Illegal File Extensions
    +
    A list of all the file types, by extension, that are not allowed to be stored on the server. Any file that is being imported or uploaded with an extension in the specified list will be ignored and not saved. The list must contain only the file extensions seperated by commas without the leading dot.
    + +
    Cache Directory
    +
    Where cached data is stored. On a Windows machine the path should look like C:\Windows\temp\, while on Unix it should look like /tmp/cache/. On some Linux/Unix based systems, a shared memory device can also be used /dev/shm/ if it is available. Leave empty to disable caching.
    + +
    LaTex Server
    +
    The URL of the mimeTex server. A public ATutor mimeTex web service is currently available at 'http://www.atutor.ca/cgi/mimetex.cgi?'. For production use, please do not use the public mimeTeX web service. Install mimeTeX on your own server instead. Please read http://www.forkosh.com/mimetex.html for installation details.
    + +
    Course Backups
    +
    The maximum number of backups that can be stored per course. The stored backups do not count towards the course's Max Course Size.
    + +
    Number of Days to Keep Copied Sent Messages for
    +
    All sent messages are copied to the sender's Sent Messages area. This option specifies the number of days old a copied message has to be before it is automatically deleted. The recipient's message is not affected.
    + +
    Check for ATutor Updates Automatically
    +
    If enabled, ATutor will check the atutor.ca web site for updates whenever the administrator logs in. This option is available since ATutor 1.5.2.
    + +
    Maintain File Storage Version Control
    +
    If enabled, every file revision in the File Storage area will be saved. If space is a concern, the administrator may wish to disable this feature.
    + +
    Enable Mail Queue
    +
    The administrator may wish to set up a cron job (automated event scheduler) for email. If enabled, and if the cron has been set up, system email will be sent out at a certain time instead of immediately. This can help speed up email capable features where a slower mail server is being used.
    + +
    Automatically Install New Language Packs
    +
    If enabled, and if the cron job (automated event scheduler) has been set up, new language packs published on atutor.ca will be imported automatically This option is available in ATutor 1.5.3.2+.
    + +
    Pretty URL
    +
    If enabled, all the public accessible pages will automatically converts their URLs to "pretty url". Pretty URL will remove the traditional URL query stirngs, and replace them with slashes (/). This option is available in ATutor 1.6.1+.
    + +
    Course Directory Name
    +
    If enabled, and only if the Pretty URL is enabled. The course id in the pretty URL will be replaced by a custom course directory name. This name can be setup individually and uniquely in the course property. This option is available in ATutor 1.6.1+.
    + +
    Apache mod_rewrite
    +
    Allows ATutor to use the Apache mod_rewrite function. The mod_rewrite module must be loaded in the conf/httpd.conf file in order for this to work. Please contact your server administrator for more details. If enabled, the accessible pages will be shortened to the predefined rules. Generally, go.php will be taken out.
    +
    + + \ No newline at end of file diff --git a/documentation/admin/themes.php b/documentation/admin/themes.php new file mode 100644 index 000000000..e2a23b1f0 --- /dev/null +++ b/documentation/admin/themes.php @@ -0,0 +1,8 @@ + + +

    Themes

    +

    Themes are used for changing the look and feel of an ATutor installation. Themes can be set as a personal preference or forced to display by default using the System Preferences Theme Specific Categories option.

    + +

    ATutor's architecture allows for the separation of presentation elements from code or logic elements. As a result, it is easy for any XHTML and CSS proficient person to create customized themes and layouts without having to know anything about PHP or the inner workings of ATutor itself.

    + + \ No newline at end of file diff --git a/documentation/admin/troubleshooting.php b/documentation/admin/troubleshooting.php new file mode 100644 index 000000000..2ee6a39b0 --- /dev/null +++ b/documentation/admin/troubleshooting.php @@ -0,0 +1,30 @@ + + +

    Troubleshooting

    +

    A variety of strategies are available for troubleshooting an ATutor installation that may not be functioning properly.

    +
    +
    AT_DEVEL
    +
    Near the top of the include/vitals.inc.php file, set the value of AT_DEVEL to true. This will display your session variables at the bottom of the screen. It will also display the variable names associated with all feedback messages, so they are easier to find through the language manager if you wish to modify their language. The debug() function will also become available, allowing testers to print out any type of variable in an easily readable format.
    + +
    debug(mixed variable [, string title])
    +
    It is possible to display the value of variables using debug(). variable is the PHP variable to output. title is an optional title that can be printed inside the debugging box to easily identify which variable is being outputted. +
    debug($_SESSION); // print current session variables
    +debug($_REQUEST); // print all GET, POST, and COOKIE variables
    +
    + +
    +
    Error Logging
    +
    View the error log through the System Preferences section. There may be information in the error reports that can help you identify where or how an error occured. The output from the error log can be sent to the ATutor team to aid them in finding a solution to your problem.
    + +
    phpinfo()
    +
    Often, system problems can be fixed by reviewing the phpinfo page. This will show all of the configuration options for your system. Review the Requirement & Recommendations for different values that should be set and displayed in the phpinfo output. Below is the contents of a phpinfo file. Viewing this page in a browser will show the system variables.

    +
    +<?php
    +phpinfo();
    +?>
    +
    +
    +

    Also see the Developer Documentation for details about modifying the source code.

    + +
    + \ No newline at end of file diff --git a/documentation/admin/upgrading.php b/documentation/admin/upgrading.php new file mode 100644 index 000000000..d44efac6b --- /dev/null +++ b/documentation/admin/upgrading.php @@ -0,0 +1,50 @@ + + +

    Upgrading an Existing ATutor Installation

    +

    Considerations Before Upgrading

    + +

    Note that Release Candidates (RC) and nightly build upgrades are not supported using this method and that depending on the size of the old courses, some steps of the upgrade may require considerable time to complete (in particular steps 2 and 6).

    + +

    Be sure that Language Packs you have installed on your old version of ATutor are available for the new version, or be prepared to translate the missing language. The old language will be removed during upgrade. If they are not available, you might volunteer to help finish any remaining language that needs to be translated for these languages. See the Translator Documentation for more details. If you have made custom changes the the language, you may wish to export the customized language using the language manager. +

    If you have Custom Themes created, export those before upgrading, then import them back into the new version of ATutor after the upgrade is complete (you may need to make a few adjustment after reimporting the custom themes if there have been changes in the ATutor)

    +

    If you have Extra Modules installed, be sure the modules are either compatible, or are available for the new version. Modules must be reinstalled after an upgrade.

    +

    Also be sure the System Requirements are still met.

    + + +

    Before upgrading, rename your old ATutor directory. Download the latest version of ATutor and extract the new version into the same directory that the old one was in. Example: If the old ATutor installation was in /htdocs/ATutor and renamed to /htdocs/ATutor_old, then the new ATutor installation should be in /htdocs/ATutor, such that both the old and new installations are at the same directory level. On Windows you may use WinZip or WinRar, while on Unix use the command tar -zxvf ATutor-version_number.tar.gz. Once extracted, an ATutor directory will be created alongside your old ATutor directory. Open a web browser and enter the address to your new installation, http://your_server.com/path_to_atutor/ATutor/, then follow the step-by-step instructions.

    + +

    Steps for Upgrading ATutor

    +

    Important: It is highly recommended that you backup your old ATutor database before attempting an upgrade.

    + + +

    The following eight steps describe the upgrade process as they are presented by the ATutor installer:

    +
      +
    1. Locate Old Version
      + Specify the directory name of the old ATutor installation you wish to upgrade (e.g. ATutor_old). The new and old ATutor directories must be at the same directory level.
    2. +
    3. UTF-8 Conversion (1.6+)
      + If you have not already, you are strongly advised to backup you database before completing this step. + If ATutor is being upgraded from a previous version that was not using UTF-8 language packs, options will be provided to convert the database to UTF-8 all-at-once, used for single language ATutor installations, or to convert on a course-by-course basis, used if courses in different languages are present on the system. If the system is already setup with UTF-8, you'll skip this step. NOTE: This step can take a very long time for installations with many courses
    4. +
    5. Database
      + The upgrade will use the old version's settings to connect to the database and then update the old database tables with any changes to bring them up to date with the new version.
    6. + +
    7. Preferences
      + In some cases, the newer version will introduce new configuration options and preferences that have to be set or confirmed. Review the Preferences and modify them if necessary.
    8. + +
    9. Directories
      + Create a content directory , preferably outside your web server's document directory for added security, and set permissions to make the content directory writable (chmod a+rwx content). On a Unix machine you will need to manually change the permissions on the content directory during this step, if you are using a directory other than the one used in the version of ATutor being upgraded. No action is usually required on a Windows server, though in some circumstances Windows users may need to adjust the properties of the specified files and directories to make them writable. Copy the path of the directory into the text box provided. Ensure there are no shortcuts (Windows), or symbolic links (Unix) are contained in the path. The path can be the same as that to the content directory use in the version being upgraded from if the directory is outside the old ATutor installation.
    10. + +
    11. Save configuration
      + Before reaching the final step the include/config.inc.php file needs to be writable, otherwise an error will appear. Follow the instructions on the screen if the file permissions need to be changed (chmod a+rwx include/config.inc.php). Once your upgrade is complete and you have confirmed it was successfuly, you should set the configuration file back to read only to secure it (chmod a-w include/config.inc.php)
    12. + +
    13. Content Files
      + All the old course content files and chat messages will be copied over to the new installation. Depending on the size of your old installation, this process may take a few seconds to several minutes or more to complete.
    14. + +
    15. Submit Usage Information
      + To assist the development team in serving the ATutor community, submit some basic information collected about the system you are running. All information is private. Though you are encouraged to list the location of your ATutor installation, you may remain anonymous by choosing not to submit the URL to your ATutor server along with the system information during this step.
    16. + +
    17. Done!
      + ATutor upgrade has been successful and you may now log-in with your personal account or your administrator account.
    18. + +
    + + \ No newline at end of file diff --git a/documentation/admin/users.php b/documentation/admin/users.php new file mode 100644 index 000000000..0ae0ddf71 --- /dev/null +++ b/documentation/admin/users.php @@ -0,0 +1,28 @@ + + +

    Users

    + +

    The Users section allows managment of students, instructors, and administrators. Note that administrators are not +considered regular users of the system; an administrator account can not normally be used to login to a course. They can however login temporarily as the course instructor, using the View button in the administrator's Courses listing. For the +purposes of documentation the term "users" will be reserved for any account type that is not an administrator.

    + +

    There are four types of user accounts that can exist in an ATutor installation, as defined by their Status: +

    +
    Disabled
    +
    Only administrators may disable an account. Disabled accounts cannot login to the ATutor installation, and will not appear in a course's Enrollment Manager.
    +
    Unconfirmed
    +
    Unconfirmed accounts are created only when the System Preferences Require Email Confirmation Upon Registration option is enabled.
    +
    Student
    +
    A regular account which can enroll, but not create courses.
    + +
    Instructor
    +
    A regular account which can enroll as well as create courses.
    +
    +

    + +

    Creating User Accounts

    + +

    Administrators can manually add users to the system by using Create User Account. Manually created accounts are automatically confirmed and the account status is set to Student, Instructor, or disabled as choosen in the Account Status field of the user account creation form.

    +

    User accounts can also be created by individuals using the Registration form available through the public pages of ATutor. Instructors can also generate user accounts by importing a course list in the Enrollment Manager.

    + + diff --git a/documentation/approve_note.php b/documentation/approve_note.php new file mode 100644 index 000000000..8e73f3b85 --- /dev/null +++ b/documentation/approve_note.php @@ -0,0 +1,59 @@ + \ No newline at end of file diff --git a/documentation/common/body_footer.inc.php b/documentation/common/body_footer.inc.php new file mode 100644 index 000000000..2c63d8dc2 --- /dev/null +++ b/documentation/common/body_footer.inc.php @@ -0,0 +1,86 @@ + + + + +
    + + + +

    +
    + + 0): ?> + +
    +
    + + + +
    +

    +

    +
    + + +
    + + + +
    + + :
    + + + + : + +
    + +
    +
    + All text is available under the terms of the GNU Free Documentation License. +
    + + \ No newline at end of file diff --git a/documentation/common/body_header.inc.php b/documentation/common/body_header.inc.php new file mode 100644 index 000000000..11b033dcb --- /dev/null +++ b/documentation/common/body_header.inc.php @@ -0,0 +1,89 @@ + + + + + <?php get_text('atutor_documentation'); ?> + + + + + + +
    + + :
    + + + + : + +
    + + +
    + +
    + + + +
    + +
    + \ No newline at end of file diff --git a/documentation/common/folder.gif b/documentation/common/folder.gif new file mode 100644 index 0000000000000000000000000000000000000000..e4802fb356f11484f75bdbd54e7ca7eee3b99008 GIT binary patch literal 65 zcmZ?wbhEHbR1O)v5|6lPZ3nNHC2SkA68JL7x_%ekRwPskmZ_A9{ MYxT*qm6gF703$yT_y7O^ literal 0 HcmV?d00001 diff --git a/documentation/common/fr/text.php b/documentation/common/fr/text.php new file mode 100644 index 000000000..5bb89305e --- /dev/null +++ b/documentation/common/fr/text.php @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/documentation/common/frame_header.php b/documentation/common/frame_header.php new file mode 100644 index 000000000..2b7f4d274 --- /dev/null +++ b/documentation/common/frame_header.php @@ -0,0 +1,80 @@ + + + + + + <?php get_text('atutor_documentation'); ?> + + + + + +
    + + + + + | + + | + + +
    + + + \ No newline at end of file diff --git a/documentation/common/frame_toc.php b/documentation/common/frame_toc.php new file mode 100644 index 000000000..e71c81332 --- /dev/null +++ b/documentation/common/frame_toc.php @@ -0,0 +1,227 @@ +'; + foreach ($pages as $page_key => $page_value) { + echo '
  • '; + if (is_array($page_value)) { + echo ''.$_pages[$page_key].''; + hb_print_toc($page_value, $section); + } else { + echo ''.$_pages[$page_value].''; + } + echo '
  • '; + } + echo ''; +} +?> + + + + + <?php get_text('handbook_toc'); ?> + + + + + + array( + 'requirements_recommendations.php', + 'new_installation.php', + 'upgrading.php' + ), + 'configuration.php' => array('my_account.php'), + 'system_preferences.php' => array( + 'default_preferences.php', + 'languages.php', + 'auto_enroll.php', + 'themes.php' => array( + 'importing_themes.php', + 'managing_existing_themes.php', + 'creating_themes.php' + ), + 'error_logging.php', + 'feeds.php', + 'google_key.php', + 'cron_setup.php' + ), + 'enrollment.php' => array( + 'enrollment_privileges.php', + ), + 'users.php' => array( + 'instructor_requests.php', + 'master_student_list.php', + 'email_users.php', + 'administrators.php' + ), + 'courses.php' => array( + 'forums.php', + 'creating_courses.php', + 'default_student_tools.php', + 'default_side_menu.php', + 'backups.php', + 'categories.php' + ), + 'modules.php', + 'patcher.php' => array( + 'create_patches.php' + ), + 'troubleshooting.php', + ); + + hb_print_toc($pages, 'admin'); + +} else if ($section == 'instructor'){ + $pages = array( + 'introduction.php' => array('creating_courses.php','student_tools.php', 'fha_student_tools.php','side_menu.php'), + 'announcements.php', + 'assignments.php', + 'backups.php' => array( + 'creating_restoring.php', + 'downloading_uploading.php', + 'editing_deleting.php' + ), + 'chat.php', + 'content.php' => array( + 'creating_editing_content.php' => array( + 'creating_editing_content_folder.php', + 'content_edit.php', + 'content_properties.php', 'glossary_terms.php', + 'content_preview.php', + 'arrange_content.php', + 'content_alternatives.php', 'accessibility.php', + 'content_tests.php' + ), + 'content_packages.php', + 'content_usage.php', + 'tile_repository.php', + 'scorm_packages.php' + ), + 'course_email.php', + 'enrollment.php' => array( + 'enrollment_privileges.php', + 'enrollment_alumni.php', + 'enrollment_course_list.php' + ), + 'file_manager.php' => array( + 'managing_files_folders.php', + 'extracting_zip_archives.php' + ), + 'forums.php' => array( 'managing_threads.php' => array('managing_posts.php'), + 'forum_export.php' + ), + 'faq.php', + 'glossary.php', + 'groups.php', + 'links.php', + 'polls.php', + 'properties.php' => array('authenticated_access.php', 'delete_course.php'), + 'reading_list.php', + 'statistics.php', + 'tests_surveys.php' => array( + 'creating_tests_surveys.php', + 'question_database.php' => array('creating_questions.php'), + 'question_categories.php', + 'edit_delete_tests.php', + 'preview.php', + 'add_questions.php', + 'student_submissions.php', + 'test_statistics.php' + ), + 'feeds.php', + 'gradebook.php' => array( + 'gradebook_add.php', + 'gradebook_update.php', + 'gradebook_external_marks.php', + 'gradebook_edit_marks.php', + 'gradebook_scales.php' + ), + 'web_search.php', + ); + hb_print_toc($pages, 'instructor'); +} else { + + $pages = array( + 'introduction.php', + 'login.php', + 'register.php', + 'browse_courses.php', + 'password_reminder.php', + 'my_start_page.php' => array( + 'my_courses.php' => array('create_course.php'), + 'profile.php', + 'preferences.php', + 'inbox.php' + ), + 'inside_course.php' => array( + 'export_content.php', + 'packages.php', + 'tile.php', + 'file_storage.php' + ), + 'my_network.php' => array( + 'my_contacts.php', + 'my_groups.php', + 'my_profile.php', + 'my_gadgets.php', + 'my_settings.php' + ), + 'pa_index.php' => array( + 'pa_albums.php', + 'pa_photo.php', + 'pa_comments.php' + ) + ); + hb_print_toc($pages, 'general'); +} ?> + + + diff --git a/documentation/common/link-out.gif b/documentation/common/link-out.gif new file mode 100644 index 0000000000000000000000000000000000000000..ecef0947aa06e62f442a1aa33b6830322fb78c64 GIT binary patch literal 52 zcmZ?wbhEHbWM^PwXkcLY|Nnn;bF<=47Dfgj&;b!383rb9mZrpM)jO^vu3N{%U=09c Cehr@h literal 0 HcmV?d00001 diff --git a/documentation/common/paper.gif b/documentation/common/paper.gif new file mode 100644 index 0000000000000000000000000000000000000000..3e8a06df55e5c167ede6fd7c7967468a5d0c2562 GIT binary patch literal 65 zcmZ?wbhEHb + + + + + <?php get_text('doc_title'); ?> + + + +'; +get_text('back_to_chapters'); +echo ''; + +foreach ($_pages as $file => $title) { + if (($req_lang != 'en') && (file_exists('../'.$section.'/'.$req_lang.'/'.$file))) { + $string = file_get_contents('../'.$section.'/'.$req_lang.'/'.$file); + } else if ($req_lang != 'en') { + ?> +
    + +
    + ]*)>([^<]+)#is', + '#]*)>([^<]+)#i'); + + $replacements = array('$3', + '$3 [$1]'); + + echo preg_replace($patterns, $replacements, $string); +} +?> + + \ No newline at end of file diff --git a/documentation/common/search.php b/documentation/common/search.php new file mode 100644 index 000000000..01663d8c3 --- /dev/null +++ b/documentation/common/search.php @@ -0,0 +1,95 @@ + + + + + + <?php get_text('atutor_documentation'); ?> + + + + +'; +get_text('back_to_contents'); +echo ''; + +if ($_GET['query']) { + $_GET['query'] = str_replace(',', ' ', $_GET['query']); + $_GET['query'] = str_replace('"', '', $_GET['query']); + + if (strlen($_GET['query']) > 3) { + $_GET['query'] = strtolower($_GET['query']); + + $search_terms = explode(' ', $_GET['query']); + + $results = array(); + if ($req_lang == 'en') { + $files = glob('../'.$section . '/*.php'); + } else { + $files = glob('../'.$section . '/'.$req_lang.'/*.php'); + } + if (is_array($files)) { + foreach ($files as $filename) { + + $count = 0; + $filename = basename($filename); + + if ($req_lang == 'en') { + $contents = strtolower(file_get_contents('../'.$section.'/'.$filename)); + } else { + $contents = strtolower(file_get_contents('../'.$section. '/'.$req_lang.'/'.$filename)); + } + + foreach ($search_terms as $term) { + $term = trim($term); + if ($term) { + $count += substr_count($contents, $term); + } + } + if ($count) { + $results[$filename] = $count; + } + } + } + + if ($results) { + arsort($results); + echo '
      '; + foreach ($results as $file => $count) + { + if (($req_lang != 'en') && (file_exists('../'.$section.'/'.$req_lang.'/'.$file))) + $full_file = '../'.$section.'/'.$req_lang.'/'.$file; + else + $full_file = '../'.$section.'/'.$file; + + echo '
    1. '.$_pages[$file].'
    2. '; + } + echo '
    '; + } else { + echo '

    '; + get_text('no_results_found'); + echo '

    '; + } + } else { + echo '

    '; + get_text('search_term_longer_3_chars'); + echo '

    '; + } +} +?> + + \ No newline at end of file diff --git a/documentation/common/styles.css b/documentation/common/styles.css new file mode 100644 index 000000000..49b48ae6a --- /dev/null +++ b/documentation/common/styles.css @@ -0,0 +1,161 @@ +pre { + font-family: Verdana, Arial, sans-serif; +} +body{ + background-color: #fafafa; + font-family: Verdana, Arial,sans-serif; + font-size: small; + line-height:150%; +} +h1,h2,h3,p, table, ul { + font-family: Verdana,Arial,sans-serif; +} +h1 { + border-bottom: 1px dashed #cfcfcf; + margin-left: -5px; + padding-left: 15px; + margin-right: -5px; + padding-right: 15px; +} +h2{ + color: #666; + border-bottom: 1px dashed #cfcfcf; + margin-left: -5px; + padding-left: 15px; + margin-right: -5px; + padding-right: 15px; +} +a { + text-decoration: none; + border-bottom: 1px solid; + font-weight: bold; +} +a:hover { + border-bottom: 0px; +} +td,th { + font-size: 85%; +} + +kbd { + padding: 0px 1px 0px 1px; + border-width: 1px 2px 2px 1px; + border-style: solid; + border-color: #edd #baa #baa #eed; + white-space: pre; +} + +code { + font-family: Verdana,Arial,sans-serif; + background-color: #efefef; + padding: 0px 4px 0px 4px; + border-width: 1px 1px 1px 1px; + border-style: solid; + border-color: #edd #baa #baa #eed; +} + + +div#toc { + color: #f0f0f0; + padding-bottom: 15px; +} + +div#toc ul { + list-style: none; +} +div#toc li { + padding-top: 2px; + padding-bottom: 0px; +} + +ol { + margin-top: 0px; +} + +ol li { + padding-bottom: 3px; +} + +dl { + margin: 0 10px +} +dl dd { + padding-top: 0px; + padding-left: 5px; + margin-left: 5%; + border-left: 1px solid #d0d0d0; + margin-bottom: 10px; +} + +dd p { + margin-top: 0px; + margin-bottom: 0px; +} +dd p:last-child { + padding-bottom: 0px; +} +acronym { + cursor: help; +} +a[href*="http"] { + padding-right: 8px; + background-image: url('link-out.gif'); + background-repeat: no-repeat; + background-position: right 4px; + margin-right: 2px; +} +div#nav-links { + margin-left: 20px; + margin-right: 20px; + margin-bottom: 20px; +} +div#nav-links a { + color: black; + text-decoration: none; + border-bottom: 1px solid; +} +pre { + font-family: Courier, monospace; + background-color: #eef; + padding: 5px; + margin-left: 20px; + color:#761596; + margin-top: 0px; + width: 50%; + font-size: smaller; +} +div.seq { + font-size: smaller; + padding: 5px; + border: 1px dashed #cfcfcf; +} +div.tag { + margin: 10px; + font-size: smaller; + padding: 5px; + line-height: 110%; + color: #cfcfcf; +} +div.add-note { + background-color: #f0f0f0; + padding: 5px; +} +div.add-note h3 { + margin-top: 0px; + margin-bottom: 0px; +} +div.note { + background-color: #fefefe; + padding: 5px; + margin-bottom: 5px; +} +div.note h4 { + margin-top: 0px; + margin-bottom: 0px; +} +div.note h5 { + margin-top: 0px; + margin-bottom: 0px; + float: right; + font-weight: normal; +} \ No newline at end of file diff --git a/documentation/common/text.php b/documentation/common/text.php new file mode 100644 index 000000000..362fabdff --- /dev/null +++ b/documentation/common/text.php @@ -0,0 +1,44 @@ +enabled. Administrator Login.'; +$text['doc_title'] = 'ATutor Handbook'; +$text['doc_welcome'] = 'Welcome to the official ATutor Handbook!'; +$text['doc_unapproved_notes'] = 'Un-Approved User Contributed Notes'; +$text['doc_approved_confirm'] = 'Are you sure you want to approve this note?'; +$text['doc_approve'] = 'Approve'; +$text['doc_delete'] = 'Delete'; +$text['doc_delete_confirm'] = 'Are you sure you want to delete this note?'; +$text['doc_no_notes'] = 'There are no un-approved user contributed notes.'; +$text['doc_logged_in'] = 'Logged in as notes moderator. Log-out.'; +$text['user_contributed_notes'] = 'User Contributed Notes'; +$text['no_notes_on_page'] = 'There are no user contributed notes for this page.'; +$text['delete'] = 'Delete'; +$text['are_you_sure_delete_note'] = 'Are you sure you want to delete this note?'; +$text['add_note'] = 'Add Note'; +$text['add_note_blurb'] = 'If you ask a question, report a bug, or request a feature, your note will not be posted. Notes must be approved by an administrator before they are posted.'; +$text['email_name'] = 'Your email address (or name)'; +$text['your_note'] = 'Your note'; +$text['note_added'] = 'Your note has been saved. It will appear only after it has been approved by an administrator.'; +$text['back_to_chapters'] = 'Back to Chapters'; +?> \ No newline at end of file diff --git a/documentation/common/vitals.inc.php b/documentation/common/vitals.inc.php new file mode 100644 index 000000000..b8356c813 --- /dev/null +++ b/documentation/common/vitals.inc.php @@ -0,0 +1,132 @@ + $v) { + if (!in_array($k, $noUnset) && isset($GLOBALS[$k])) { unset($GLOBALS[$k]); } + } +} + +unregister_GLOBALS(); + +function debug($var, $title='') { + + echo '
    ';
    +	if ($title) {
    +		echo '

    '.$title.'

    '; + } + + ob_start(); + print_r($var); + $str = ob_get_contents(); + ob_end_clean(); + + $str = str_replace('<', '<', $str); + + $str = str_replace('[', '[', $str); + $str = str_replace(']', ']', $str); + $str = str_replace('=>', '=>', $str); + $str = str_replace('Array', 'Array', $str); + echo $str; + echo '
    '; +} + +function get_text($var, $return = FALSE) { + global $req_lang, $lang, $section; + + static $req_lang_text, $lang_text; + + if (!isset($req_lang_text) && ($req_lang != 'en')) { + $text = array(); + if (file_exists(dirname(__FILE__) . '/'.$req_lang.'/text.php')) { + require(dirname(__FILE__) . '/'.$req_lang.'/text.php'); + } + + $req_lang_text = $text; + } else if (!isset($lang_text)) { + $text = array(); + require(dirname(__FILE__) . '/text.php'); + $lang_text = $text; + } + + if (isset($req_lang_text[$var])) { + if ($return) { + return $req_lang_text[$var]; + } + echo $req_lang_text[$var]; + } else if (isset($lang_text[$var])) { + if ($return) { + return $lang_text[$var]; + } + echo $lang_text[$var]; + } else { + if ($return) { + return $var; + } + echo $var; + } +} + +function get_lang() { + $path = dirname(__FILE__); + if (is_dir($path.'/'.$_SESSION['lang'])) { + return $_SESSION['lang']; + } + else { + return 'en'; + } +} + +define('AT_HANDBOOK', true); +session_name('ATutorID'); +session_start(); +// $lang is the language we've found to display +// $req_lang is the language we're requesting + + +$_available_sections = array('admin' => 'admin', 'instructor' => 'instructor', 'general' => 'general', 'index' => 'index'); +$available_languages = array('en' => 'en', 'fr'=>'fr'); + +$parts = pathinfo($_SERVER['PHP_SELF']); +$this_page = $parts['basename']; + +$dir_parts = explode('/', $parts['dirname']); +$last_dir_name = end($dir_parts); +$second_last_dir_name = prev($dir_parts); + +if (isset($_available_sections[$second_last_dir_name])) { + $lang = $req_lang = $last_dir_name; + $section = $second_last_dir_name; + $rel_path = '../../'; +} else if (isset($_available_sections[$last_dir_name])) { + $section = $last_dir_name; + $rel_path = '../'; + $lang = $req_lang = get_lang(); +} else { + foreach ($_available_sections as $section_name) { + if (isset($_GET[$section_name])) { + $section = $section_name; + unset($_GET[$section]); + break; + } + } + if ($section) { + $rel_path = '../'; + } else { + $section = 'general'; + $rel_path = '../'; + } + $lang = $req_lang = get_lang(); +} + +?> \ No newline at end of file diff --git a/documentation/config.inc.php b/documentation/config.inc.php new file mode 100644 index 000000000..ad8b3b3cd --- /dev/null +++ b/documentation/config.inc.php @@ -0,0 +1,43 @@ + \ No newline at end of file diff --git a/documentation/delete_note.php b/documentation/delete_note.php new file mode 100644 index 000000000..9c6933582 --- /dev/null +++ b/documentation/delete_note.php @@ -0,0 +1,64 @@ + \ No newline at end of file diff --git a/documentation/developer/database.gif b/documentation/developer/database.gif new file mode 100644 index 0000000000000000000000000000000000000000..270dbdc4ffbc265cc9ec7b92313f2f6247812330 GIT binary patch literal 195789 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 z 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

    c%-2N2gq$IC-?)2 zawLV29*s@1DlvufSSwX4@J6pMsCI2KvWFMK#C2(&=~37|N7 znv5%e2Fh>#)+Wk3AkfE9WxryQv?Y_>nRs~F<#VfaYU{1Ie(`1yd90WM6r$*Gf`JNYT7)Tv`~k^G zE(FT#mQFC@Y$7iR!k{O+%4#eOnTQGAvWb{yZMcR_QAo3Yf+t9Yd>}FdyWpOe>%8>V zYwx}I(ugVk483Ns4M6+gL{Y%)OaqWL+8j(U!X7jrEGuoAl`a^EqOn$JqULFT{lp}b4Ltp(sIwBx@_qO z8tcNb5K9}84^&Bu0kWM<|7uric3DIbcL-ef!gvf7n-=?=vxkuTGPh6e`|rR9FFb3N zLwx@EA$2vNq2`?(tyW*k_hIRtq-UoV=Z#d*gbPc!?)>F|3$yzQwU<`>jN>zNHCMe4 z@BaJn$1lG*?W@qQ2B>?Ozw{zp2W;FVNQa0x(s@e(aNR~gauK!z+)1nDax3$#`TEYfI+<>P@IIYB)-*wKoB45T0j3I4ex zBF%~BFr+8m=(ej-gMsvrhCASK0X=ZTl97~4#t=!h2@;`V3uBliFYt(=h?10}yciS- z$;wu`@|BbO;|MwVftA#SXSPV$MLgMsOyqjn}7i;H49lPk(W%9 z#4kY=5)2JfKv4NiQF`*90{x|qunea-$4Sn5DH42CIcFUh*#dDn1!KE`R6ZmnnPYDn>qj_Sw9k~b%O<-jzlAxl~RR;B} zXickH8wZA=?i7Lm{ZX`nxto17%A^Aw&o@Uph+rD&ts#4`}%ooY%)2E@>#_{D{?u#jI> zH4DQa5p}J4Ts0nI}-H%j*(njM4jEdTZZ>8 z_rCxR@I>#|FuH7&Xg?GfBn@VT(Lv)Mb3-Clb75Kx>d}-8v2A-^+TN@9SHK_+v52iG zkv{6s!PnrdHfA<7$OLV3CN@d)bhcf%VvWB7>o8mMg2ZDE zYfxa*du=V9RxJ}elxMdMM_6PRoWhQ`yyq<|iM-nysO1z2I$K^ezK|b(>Rhv^9Sa*@ z4>8YmKmwsO-6vq#qv1Y57cZ9!U$3WIZ!w(0k>d>vw}9N->R2s#?FEa|`{XE3d4>s| zW{1#v4c7j^!?1Bnf0}||-*^NNa!!vb{6hn|c)4;gE_22}X(j0v*Bx-q4oZgd!R?Uq z8%(b9s$2c)zj$xdU}(!*$AId~;ndLvXelXgO8#^%<4K0-A{~z0z+bO1_0J+6rCd*A z>^RVxsU`?h&HSvdw`4MhT$bt3@58^xcgW-Y7)@W#*zmx&u6!q21dFG%Wc z5Pt91&;IuF3iyXCYLyEO4WH~0%`t#!c4h#S1o~_~{J?0gkavqxdgFF?MQ0T5#&;kG zbp9uPc_%YwvvJgt9PW347?^=_@OxB2{%5oyb}}amrsjW{7cJ_bb|mVRhWf+(Sm4Z@H(3nLh!dp zAXtCLgK9?P0iba}0OT`Hb~zFEKQATdRPRchH!=kGpOZ(Bxo`5R|aG_7zWbkSt1|F3;syd$@|M*ovu^K!gE?2Zn60un&JAMyK>y zpY;tBlrZ==28U<{mxg})5{0w=W{eNQWhg*_3n7HA_>9o_Wu#V1B!FVSh)Rtj{sI=Om$>- zNQ;mLT@KKBX|p*T01EzdL<;vXt7eGE*j;Hrj4SYv({_Mpfr$`tkNUWg90^%t=7%5= zY2yHpg9tYoq7ORZe9$L@ARsIwSdIpVh&mTZJEaTOSeEtfgtI9|&F2wk*%?BVf;h4i9mzY_bwmCdQRas5e1CqIKtoa7!G7tZPaLy&1!-*KhnViIloWW_F z&gonZ6P?HDoXd%v2zQ;=shrliozn@O-07X&`JIR%h`7?6+KHXgDV*RboLr=y;c1-b ziJkAcp3hmG(@CB3nVH7BW4dp&t67AR3|~>YW zSem6;Dx?Yop6eN(``HBdIi}+2p75!k@tLOf`K4uQoaDKsa2ls_YL}^FStEL + + + + + ATutor Developer Documentation + + + + + + +

    + +

    Table of Contents

    + + + +top +

    0. Version Considerations

    +

    This document is found in ATutor's documentation/ directory and is maintained along with the rest of the code in the code repository. The latest version of this document will always be available in the repository. Versions bundled with ATutor releases are specific to that release. If you are modifying a previous version of ATutor then you should refer to the version of these guidlines as they are available with that specific version.

    + +top +

    1. Introduction

    +

    ATutor, as an open source project, encourages PHP developers to develop their own custom features, and potentially contribute those features so they become a permanent part of ATutor. To ensure that code is easy to maintain, we urge developers to follow the guidelines outlined below. These rules and recommendations were created to standardize the distributed development process.

    + +

    The latest version of this document can always be found at ATutor.ca.

    +

    1.1 API Documentation

    +

    Detailed API documentation is distributed throughout the ATutor source code, describing functions and classes, and how they are used in developing features. API documentation can be extracted into a relatively compact form for easy scanning and searching using the phpDocumentor module. Download the module from atutor.ca, and install it like any other module, then run the application to extract the API documentation from ATutor.

    + +

    phpDocumentor Module

    +top +

    2. Conventions Used in This Document

    +

    This section covers the typographical conventions used in this document.

    + +

    2.1 Typographic Conventions

    +
    +
    Constant width
    +

    Used for commands and code examples. Example: Use the debug() function to view a variable.

    + +
    `Constant width surrounded by quotes`
    +

    Constant-width font with surrounding quotes is used for filenames and path names. Example: The `vitals.php` file is important.

    + +
    Square brackets (`[` and `]`)
    +

    In syntax descriptions, square brackets (`[` and `]`) are used to indicate optional words or clauses. Not to be confused with the use of square brackets in array definitions. For example, in the following statement, version is optional: ./bundle.sh [version].

    + +
    Pipe (`|`)
    +

    When a syntax element consists of a number of alternatives, the alternatives are separated by pipes (`|`).

    +
    + +

    Example code is to be used as examples only and not as tested production code. In most cases its usefulness in the context of the example outweighs its correctness as workable code. In other cases the syntax and style used in the example itself are irrelevant and do not follow the coding guidelines outlined below. For example, arrays may be documented using string keys without quoting their value, $_SESSION[prefs], while in practice it is always best to escape the key with quotes: $_SESSION['prefs'].

    + +

    2.2 Links

    +

    All the links in this document open in the current browser. Links that are not obviously to external sites are supplemented with title text. All other links are assumed to be anchors within this document.

    + +

    2.3 Function Definitions (Prototypes)

    +

    The usage of the square brackets (`[` and `]`) around parameters imply that they are optional, in which case the function documentation will then state what the default value for that variable is. Please pay close attention to the return types of functions: If a function is described to return boolean then it will return either TRUE or FALSE and not an integer such as 0 or 1. Optional arguments to functions must always be listed as the last parameters in the list.

    + +

    returned_type function_name( param_type $param_name [, opt_param_type $opt_param_name])

    +
    +
    returned_type
    +

    Type of value this function returns. See Types and PHP type comparison tables.

    + +
    function_name
    +

    The function name.

    + +
    param_type and opt_param_type
    +

    The type of the parameter that the function expects. See Types and PHP type comparison tables.

    + +
    $param_name and $opt_param_name
    +

    The names of the parameters as they are used in the function. The $opt_param_name is optional.

    +
    +

    + +top +

    3. Setup

    +

    Please review the ATutor requirements and ensure that your development environment meets the minimum requirements.

    + + + +

    3.1 PHP Configuration

    +

    If you have a dedicated development environment that doesn't share a web server with other production code, then it is best to use the bundled php.ini-dist configuration file as the default--simply rename it to php.ini. Listed below are the essential configuration options and their recommended value:

    +
    +safe_mode            = Off
    +error_reporting      = E_ALL
    +display_errors       = On
    +arg_separator.input  = ";&"
    +register_globals     = Off
    +magic_quotes_gpc     = Off
    +magic_quotes_runtime = Off
    +allow_url_fopen      = On
    +register_argc_argv   = Off

    + +top +

    4. Subversion (SVN)

    +

    We have adopted Subversion (SVN) in place of the aging CVS, allowing us to open up the development repository while still maintaining control over the core project. Additional information can be found on the Subversion homepage.

    + +

    4.1 Installing an SVN Client & Checking-Out

    +

    To obtain a working copy of ATutor, you need to install an SVN client and do a checkout from our repository of the latest ATutor source code. Checking out the repository creates a copy of it on your local machine with the necessary version control information. The location of the copied files should be web accessible, i.e. a directory under your web server's path.

    + +

    For windows users, we recommend using TortoiseSVN, which is a feature-rich SVN extension for Windows that supports many of the options of other SVN clients (file browser and filters not being one of them). Once TortoiseSVN has been successfully installed and you've rebooted your computer, go to a working directory where you wish to checkout the ATutor source code. Right-click to pop-up the context menu and select the command "Checkout...".

    + +

    For repository URL, enter http://atutorsvn.atrc.utoronto.ca/repos/atutor/trunk. If you enter a folder name that does not yet exist, a directory with that name will be created. Press "OK", and the source will be downloaded to your computer.

    + +

    For non-Windows users, download an appropriate SVN package from the Subversion homepage and follow the installation instructions. These packages use the command-line client. Checking out the most recent ATutor source code can be done with the following command: svn checkout http://atutorsvn.atrc.utoronto.ca/repos/atutor/trunk. Unix users also have available a GUI tool called RapidSVN

    +

    The checkout includes:

    +
      +
    • bundle.sh - Execute this shell script to generate an installable bundle of the ATutor.
    • +
    • docs/ - The ATutor source code
    • +
    • mods/ - Extra module source code
    • +
    +

    Extra modules can also be checked out into the ATutor mods/ directory, from where they can be easily installed using the ATutor administor's Install Modules tool. See the ATutor Module Documentation for more about creating and modifying modules. From within the docs/mods/ directory of your development version of ATutor, use svn checkout http://atutorsvn.atrc.utoronto.ca/repos/atutor/trunk/mods/[module_name].

    +

    4.2 Installing ATutor From SVN

    +

    Before starting the installation process you should create an empty `config.inc.php` file in the `include/` directory: use touch config.inc.php on UNIX. Browse to the location of your checked out files in a web browser and proceed with the ATutor installation instructions. This will set up the database and user accounts for your ATutor system. The resulting ATutor installation will be your development environment.

    + +

    4.3 Updating ATutor From SVN

    +

    You should regularly perform updates from the repository to make sure you are working with the developers' latest files. To do this with TortoiseSVN, right click on the working folder and select "Update" from the context menu. For command-line svn clients, use: svn update.

    + +

    Always review the latest SVN SQL upgrade file to ensure your database structure is up-to-date. The file will contain schema changes of the current pre-released source. Example: If the current working source will be version 1.9, then the upgrade file to keep track of will be named atutor_upgrade_x.y.z_to_1.9.sql, where x.y.z is the version of the currently available stable release.

    + +

    4.4 Keywords

    +

    Until Subversion supports server wide configuration options, the svn:keyword="Id" option will have to be set on a file-by-file basis via the client. After the keyword property has been set for the given file, add the line // $ I d (without the spaces) at the top of the file directly below the main comment block to identify the author, date and time of the last commit.

    + +

    After the file has been added and committed to the repository the keyword will be expanded into // $Id: guidelines.html 3071 2005-01-12 21:53:13Z joel $. It is helpful to read that line when first opening a file as it lets you know if you were the last person to edit that file and if not, to look out for possible changes.

    + +

    To set svn:keyword="Id" on a file using TortoiseSVN, right-click on a file, select "Properties" then select the "TortoiseSVN" tab, enter svn:keywords in the drop-down and Id in the text box then use the "Set" button.

    + +top +

    5. Communication

    +

    All communication between developers should occur in the Development Forum. Please try to keep discussions public including any feature proposals.

    + +top +

    6. Patches

    +

    As of ATutor 1.6 the Patcher module is available for applying and developing patches to repair bugs, and to add or modify features. Details on creating and applying patches can be found in the ATutor Handbook for version 1.6.1+, or on the ATutor Wiki.

    +

    Patcher Documentation

    + +top +

    7. Editor Tips

    +

    We use EditPlus, but you can use whichever editor and settings you feel comfortable with. The most important part when editing is to ensure that tabs meet the coding guidelines on indentation. Unix KDE users might use Kate as their text editor.

    + +

    A few desirable features for a good text editor are listed below: +

      +
    • Column and row numbering.
    • +
    • Jump to line number.
    • +
    • Word wrap toggle.
    • +
    • Being able to specify soft or hard tabs.
    • +
    • Syntax highlighting.
    • +
    • Trim trailing whitespace.
    • +
    • DOS to UNIX CR/LF conversions. Your editor must be able to save files with UNIX style line breaks. This means the \n character instead of the \r (Mac style) or \r\n (Windows style).
    • +

    + + +top +

    8. Proposed Features

    +

    The Proposed Features page lists features which have been requested by the ATutor community. ATutor.ca members can vote on features to establish a priority, while potential developers may then assign themselves to tasks. New feature requests should be posted to the ATutor Feature Requests forum.

    + +top +

    9. Bug Tracking

    +

    Please report bugs to the ATutor Bug Reports forum, or directly to the Mantis Bug Tracker. Be sure to indicate the code version being used, such as a release candidate, stable release, nightly build, or SVN checkout, etc. Also be sure to describe the details of the system that ATutor is being developed or tested on, such as the operating system, web server and version, PHP version, etc. Browse the Current Bug Summary for a list of active bug fixing.

    + +top +

    10. Creating Bundles

    +

    The file `bundle.sh` is located in a directory above `docs` and is used for creating bundles from the working `docs` directory. The Shell script must be run on UNIX and will retrieve the latest version of the language from the database, disables debugging, creates an empty config.inc.php filr, and lastly create a .tar.gz file. Note, by default this script will generate a bundle from the atutorsvn source code repository. You may change this path near the top of the file to point to a local version of the ATutor source code, if for instance you have your own customizations you'd like to build into an installable bundle. Usage: + +

    +
    ./bundle.sh [version_number]
    +

    Note that you will need execute permissions on the script to use it, and if it isn't in your PATH then you will have to prefix it with a ./. The optional version_number argument will be used for suffixing onto the file name. For example, a version number of 1.8RC1 will generate a file named ATutor-1.8RC1.tar.gz.

    +
    +

    + +top +

    11. Writing Portable Code

    +

    When writing your PHP code please try to use functions that exist since (the minimum requirement) PHP version 4.3.0. If you have to use a function that only exists in later versions of PHP, provide an alternative for older versions. To check if the function is available use either version_compare(phpversion(), $min_version) or function_exists($function_name). + +

    Code has to work on both Windows and UNIX. You should never use exec() or system(). In most cases we prefer to write code that works on both systems as is, without the need for if-statements that check for the operating system, since duplicating the functionality twice (once for each operating system) can be a source of bugs. Review the PHP Configuration section for details on how best to set-up your development environment.

    + +top +

    12. Coding Style

    +

    This section should help those who would like to modify or add code. Anyone who wishes to contribute code must adhere to these guidelines or the code may not be accepted. Please try to write code that is easy to read and maintain with appropriate comments as needed. Correctness and efficiency are easier to certify if code is simple to read and understand.

    + +

    12.1 Indentation

    +

    The importance of indentation for code organization cannot be overstated. Although indentation is not mandatory in PHP, it is a powerful visual organization tool that should consistently be applied to code.

    +
      +
    • Indent using 4 spaces for each level.
    • +
    • Think carefully about when too many nested levels has been reached. (Usually 4-5 is a good limit).
    • +
    • Use Hard tabs, as described below.
    • +
    + +

    Internally, we have used hard tabs as we all use the same editor, but we may decide to move to soft tabs in the future. Hard tabs are regular tabs while soft tabs are not really tabs at all; each soft tab is actually represented by a certain number of regular spaces. The benefit of using soft tabs is that they always appear the same, regardless of the editor's tab-spacing. With soft tabs set and enforced, it is easy to maintain consistent indentation and whitespace treatment throughout code. When hard tabs are used, especially if there are multiple developers using different editors, it is easy for mixed levels of indentation to be introduced, confusing the code's layout.

    + +

    12.2 Line Length

    +

    Split the long lines into multiple lines: +

    +if (($month = 'jan') || ($month = 'feb') || ($month = 'mar') || ($month = 'apr')) {
    +    return 1;
    +}
    +

    + +

    You can indent the second line to signify the association with the upper. For particularly long lines, you can indent and align every condition: +

    +if (($month = 'jan') || 
    +    ($month = 'feb') || 
    +    ($month = 'mar') || 
    +    ($month = 'apr')) {
    +
    +    return 1;
    +}
    +

    + +

    This methodology works equally well for function parameters: +

    +echo format_content($content_row['text'], 
    +                    $content_row['formatting'], 
    +                    $glossary,
    +                    $indent);
    +

    + +

    12.3 Using Whitespace

    +

    Whitespace can be used to provide and reinforce logical structure in the code. For example, it can be effectively used to group assignments and show associations. The following example is poorly formatted and difficult to read: + +

    +$password = 'mypassword';
    +$website_url = 'http://atutor.ca';
    +$first_name = 'Joel';
    +$last_name = 'Kronenberg';
    +

    + +

    But this code block can be improved by using whitespace to logically group related assignments together and align them on the equal sign (=): +

    +$pet_type    = 'mypassword';
    +$website_url = 'http://atutor.ca';
    +$first_name  = 'Joel';
    +$last_name   = 'Kronenberg';
    +

    + +

    12.4 SQL Guidelines

    +

    Similar formatting and layout rules applied to PHP can be applied to SQL queries as well. SQL queries, especially in database systems that support complex subqueries, can become convoluted. As with PHP code, whitespace and line breaks should be used in SQL code as needed. Consider the following: + +

    +select employees.first_name, employees.last_name from 
    +               employees where employees.vacation_time > 0 order by employees.last_name";
    +
    +

    + +

    This is a simple query, but it is poorly organized. Its organization can be improved in a number of ways, including the following: +

      +
    • Capitalize SQL keywords.
    • +
    • Break lines on SQL keywords.
    • +
    • Use table aliases to keep code clean.
    • +
    + +
    +SELECT   S.first_name, S.last_name 
    +FROM     students S
    +WHERE    S.email = '' 
    +ORDER BY S.last_name";
    +

    + +

    12.5 SQL/99 Joins

    +

    ANSI SQL/99 features ANSI compliant joins. There are several advantages in using this new syntax, one of which is the separation of the JOIN condition from the WHERE clause. +

    +SELECT	M.email, M.login 
    +FROM	members M, forums_subscriptions S 
    +WHERE	S.member_id=M.member_id 
    +AND     M.email <> ''
    +
    +SQL/99 makes a clear distinction between the fields in the JOIN condition and the WHERE clause: +
    +SELECT	M.email, M.login
    +FROM	members M
    +JOIN	forums_subscriptions S USING (member_id)
    +WHERE	M.email <> ''
    +

    + +

    12.6 Control Flow Constructs

    +

    +

      +
    • Always use <?php ?> instead of the short form <? ?>. This implies that you must not use the <?=$var;?> short form either.
    • +
    • Always include the optional semicolon in single line PHP blocks: <?php echo $something; ?> +
    • Use ' instead of " if there are no variables or special characters.
    • +
    • Use spaces around string concatenating. echo 'str' . $value . 'str2';
    • +
    • Parenthesis `( )` should come right after a function name. function() not function ()
    • +
    • Parenthesis `( )` should have a space right after a language construct (if, while, for). Examples: for (...), while (condition)
    • +
    • Avoid using continue and break as it makes debugging more difficult.
    • +
    • Braces formatting is illustrating below. We use K&R style where the initial brace is placed on the same line as the keyword and the trailing brace inline on its own line with the keyword: +
      +if (condition) {
      +    ...
      +} else if (condition) {
      +    ...
      +} else {
      +    ...
      +}
      +
    • +
    • Arrays should be referenced with no spaces. $arr['index'] not $arr[ 'index' ]
    • +
    • Avoid using short if-statement construct ($var = ($query ? $val1 : $val2)) except in very rare cases. It is confusing and has a lot of bug potential.
    • +
    +

    + +

    12.7 Commenting

    +

    Avoid using Shell/Perl-style (## this is a comment) comments entirely. Use C-style comments (/* ... */) for large comment blocks and C++-style comments (// ...) for single-line comments only: +

    +/* This is a comment block
    + * it is used for describing
    + * the code below.
    + */
    +...
    +// this is a single line comment
    +

    +

    Please, document while your code. See phpdoc, like javadoc, for details how to document functions, classes, methods, and variables. Coding is often hurried, but it will save a lot of time in the end to do this type of documenting! It looks like this: +

    +   /**
    +   * what the function does in one line.
    +   * more detailed description on 0-n lines, if needed.
    +   * @access  [public|static|pseudostatic]
    +   * @param   [string|int|double|bool|array|object|mixed] $paramName1 desc
    +   * @param   [string|int|double|bool|array|object|mixed] $paramName2 desc
    +   *  ...
    +   * @param   [string|int|double|bool|array|object|mixed] $paramNameN desc
    +   * @return  datatype  description
    +   * @throws  not until PHP 5
    +   * @see     some_function()
    +   * @todo    description
    +   * @since   ATutor version, PHP version   (comma separated list)
    +   * @status  stable|experimental           (if not set then considered stable)
    +   * @pattern singleton|factory|mvc|observer|facade|...
    +   * @author  description                   (comma separated list)
    +   */
    +   function something() {
    +   }
    +   
    + Note that the description should be given as plain text not HTML. The @pattern singleton means that the constructor returns a reference to an already existing instance, if there is one.

    + +

    12.8 Naming Conventions

    +
      +
    • 12.8.1 Naming Variables: +
        +
      • Use capital letters for constants. E.g. define('CONSTANT', 1) and use the capital form of TRUE, FALSE and NULL
      • +
      • Otherwise, use all lower case
      • +
      • Use _ to separate words. E.g. $green_colour_value
      • +
      • Loop variables can be of the usual variety: $i, $j, $k, etc.
      • +
      • Count variables should follow the format $*_count. E.g. $bug_count, and always initialised to 0
      • +
      • Temporary variables should be prefixed with temp_
      • +
      • $sql, $result, and $row should be used for SQL query, results, and rows respectively
      • +
      +
    • + +
    • 12.8.2 Naming Functions: +
        +
      • Use all lower case
      • +
      • Use _ to separate words. E.g. setup_page_breaks()
      • +
      • Keep functions to 5 words or less
      • +
      • Functions that print should be prefixed with print_.
      • +
      • Try to use prefixes to group functions (E.g., email_, news_, etc.)
      • +
      +
    • + +
    • 12.8.3 Naming Files: +
        +
      • Use all lower case
      • +
      • Use _ to separate words. E.g. view_new_bugs_page.php
      • +
      • Use .php file extensions (not .html or .php3)
      • +
      • Filenames must be less than 32 characters in length as this works with older file systems like MacOS
      • +
      • Included files should be suffixed by .inc.php
      • +
      • Files containing classes should be suffixed by .class.php
      • +
      • Exception: Files being included as part of an external library should not be renamed
      • +
      +
    • +
    + +top +

    13. Directory Structure

    +

    The following is a short explanation of the important components of the ATutor directory structure.

    + +

    13.1 Directories

    +
    +
    `admin/`
    +
    This directory contains files used for the administration area (when a user logs in as an administrator). This includes files for system statistics, instructor requests, management of users, courses, categories and languages, and server configuration.
    + +
    `editor/`
    +
    This directory contains files used by a course instructor (or privileged user) to edit course content such as the glossary, course content, forums, announcements, and polls.
    + +
    `include/`
    +
    This directory contains files that are required or included into other files.
    + +
    `include/classes/`
    +
    This directory contains classes, essential to certain ATutor functions, such as the phpMailer, XML, Savant templating, and content management classes.
    + +
    `include/html/`
    +
    This directory contains files that output HTML, usually displayed on (included into) multiple pages.
    + +
    `include/lib/`
    +
    This directory contains library files that hold functions and constants used throughout ATutor code.
    + +
    `install/include/`
    +
    This directory contains files used during the installation process, including each step of the fresh install and upgrade processes.
    + +
    `install/db/`
    +
    This directory contains the SQL files necessary to set up or upgrade the ATutor database.
    + +
    `jscripts/`
    +
    This directory contains all JavaScript files.
    + +
    `themes/`
    +
    This directory contains the different themes installed on an ATutor system, each with its own subdirectory.
    + +
    `mods/`
    +
    This directory contains the module installed in an ATutor system.
    + +
    + +

    13.2 Files

    +

    The following is a description of some of the important files.

    +
    +
    `include/config.inc.php`
    +
    This file is created during installation and contains specific configuration information for an ATutor system.
    + +
    `include/vitals.inc.php`
    +
    This file is included by every directly accessible page. It connects to the database, initiates the user session, includes common libraries and constants, and defines frequently used functions.
    + +
    `themes/themes_readme.txt`
    +
    This file contains detailed information on how to create and install a theme.
    + +
    `include/header.inc.php`
    +
    This file outputs the page's header using the correct template and theme.
    + +
    `include/html/footer.inc.php`
    +
    This file outputs the page's footer using the correct template and theme.
    + +
    `include/lib/output.inc.php`
    +
    Most output formatting is done in this file, including things such as language, dates, a paginator, and content formatting, among other things .
    +
    + + +top +

    14. Database Structure

    +

    A database model diagram (153 KB GIF) created from the ATutor 1.4.1 database schema is available. (somewhat outdated)

    + +top +

    15. Localisation

    +

    All language terms and phrases are stored in the ATutor database. See the _AT() function for details on displaying text. There are three tables that are used for managing languages, their roles are as follows: +

    +
    `language_pages`
    +
    This table is used to cross reference language terms with pages. It allows selecting, via a JOIN, only the terms needed for a particular page. The JOIN may be slow at first but once the result is cached, subsequent calls are many times faster such that only the language needed for a particular page is restored from cache.
    + +
    `language_text`
    +
    This table holds all of the text for an ATutor installation.
    + +
    `languages`
    +
    This table holds the list of all available languages on the system and their attributes.
    + +

    + +

    15.1 Adding & Editing Language

    +

    Please see the Translator Documentation for more details on translating a language within ATutor.

    + +top +

    16. Error and Feedback Messages

    +

    All messages are handled using the Message class. The main purpose of the Message class is to encapsulate the functionality of tracking + and managing various message types during a session by providing a nice layer of abstraction between you and the interface to $_SESSION, where the messages are stored.

    + +

    At the moment six types of messages are supported: +

    +
    Error
    +
    Messages reflecting negative feedback to the user, indicating issues that need resolving or addressing.
    + +
    Feedback
    +
    Messages reflecting positive feedback, aknowledging a users action was successfull.
    + +
    Warning
    +
    Messages warning the user of a possible action with undesireable effects.
    + +
    Help
    +
    Messages with helpful information about the current page.
    + +
    Info
    +
    Messages with useful information.
    + +
    Confirmation
    +
    Messages requiring a confirmation in order to execute an action.
    + +

    + + +

    Please note that using the old method of passing messages is not supported anymore.

    + +

    Messages can be passed between pages and can be accessed at any time, without any time restriction other than a session timeout.

    + +

    16.1 Internals

    +

    Essentially the internals of the class are divided into two segments, a section responsible for printing graphics via Savant templates and another that manages the storage of Messages.

    + +

    Tracking messages is accomplished by storing message codes and their optional arguments associatively in $_SESSION. Printing messages is accomplished via Savant and built in templates which can be found in `templates/`.

    + +

    The relational tracking structure is organized in the following manner inside $_SESSION in no particular order. +

      +
    • message +
        +
      • error +
          +
        • AT_ERROR_{*} +
            +
          • AT_ERROR_{*}
          • +
          • [argument_1]
          • +
          • [argument_2]
          • +
          +
        • +
        +
      • + +
      • warning +
          +
        • AT_WARNING_{*} +
            +
          • AT_WARNING_{*}
          • +
          • [argument_1]
          • +
          +
        • +
        +
      • + +
      • info +
          +
        • AT_INFOS_{*} +
            +
          • AT_INFOS_{*}
          • +
          • [argument_1]
          • +
          +
        • +
        +
      • + +
      • help +
          +
        • AT_HELP_{*} +
            +
          • AT_HELP_{*}
          • +
          • [argument_1
          • +
          +
        • +
        +
      • + +
      • feedback +
          +
        • AT_FEEDBACK_{*} +
            +
          • AT_FEEDBACK_{*}
          • +
          • [argument_1]
          • +
          +
        • +
        +
      • + +
      • confirm +
          +
        • AT_CONFIRM_{*} +
            +
          • AT_CONFIRM_{*}
          • +
          • [argument_1]
          • +
          +
        • +
        +
      • +
      +
    • +
    +

    + +

    Messages are automaticaaly purged from $_SESSION following an appropriate print command.

    + +

    16.2 Adding Messages

    + $msg->add{Error|Warning|Info|Feedback|Help}($code);

    + +

    $code is a String of a language _msgs code or an array with element 0 being the language _msgs code followed by optional arguments. Refer to the language_text table in the database.

    + +

    Important: One important thing to note is that $code must be stripped of the prefix for that type of message. By prefix it is meant AT_[code_type]_. For example: + +

      +
    • `AT_ERROR_FORUM_NOT_FOUND` should be specified as `FORUM_NOT_FOUND`, by stripping off the `AT_ERROR_` prefix.
    • +
    • `AT_WARNING_DELETE_MESSAGE` as `DELETE_MESSAGE` by stripping off `AT_WARNING_`.
    • +
    • `AT_FEEDBACK_LOGOUT` as `LOGOUT` by stripping off `AT_FEEDBACK_`.
    • +
    • `AT_INFOS_MSG_SEND_LOGIN` as `MSG_SEND_LOGIN` by stripping off `AT_INFOS_`.
    • +
    • `AT_HELP_FILE_EXPORTABLE` as `FILE_EXPORTABLE` by stripping off `AT_HELP_`.
    • +
    +

    + +

    There are two ways of adding messages: a single code or a code with an array fo arguments. You can mix-and-match, a combination of both is supported even for the same code. Below is description of the formats:

    + +

    +

    Adding single code
    +
    $msg->addHelp('FILE_EXPORTABLE');
    +
    +
    OR
    +
    +
    Adding array with arguments
    +
    $f = array('FILE_EXPORTABLE', 'arg', 'arg2', ...);
    +
    $msg->addHelp($f);
    +

    + +

    A nice feature implemented is that you do not have to provide all + the arguments for a particular code at one time. Subsequent adding of + the same code will just append the argument. This allows for greater + manipulative flexibility in your source, without writing redundant code. Also note + that encoding Feedback codes is no longer necessary for redirection.

    + +

    Example 1: +

    $feedback=array('FORUM_ADDED', 'ac_access_groups');
    +
    $msg->addFeedback($feedback);

    + +
    // Before we print lets addr another one to the same code
    +
    $feedback2=array('FORUM_ADDED', 'about_atutor_help_text');
    +
    $msg->addFeedback($feedback2);

    + +
    // No need to url_encode the code
    +
    $filename = 'archive.zip';
    +
    $f = array('FILE_UPLOADED_ZIP', $file_name);
    +
    $msg->addFeedback($f);
    + +

    + +

    Snapshot of a portion of $_SESSION as a result:

    +
    +	[feedback] => Array
    +                (
    +                    [AT_FEEDBACK_FORUM_ADDED] => Array
    +                        (
    +                            [0] => AT_FEEDBACK_FORUM_ADDED
    +                            [1] => ac_access_groups
    +                            [2] => about_atutor_help_text
    +                        )
    +                        
    +                    [AT_FEEDBACK_FILE_UPLOADED_ZIP] => Array
    +                        (
    +                            [0] => AT_FEEDBACK_FILE_UPLOADED_ZIP
    +                            [1] => archive.zip
    +                        )
    +
    +
    +                )
    +	...
    +
    + +

    +

    header('Location: file_manager.php');
    +
    exit;
    +

    + +

    16.3 Printing Messages

    +$msg->print{Errors|Warnings|Infos|Feedbacks|Helps|All}($optional);

    + +

    Each will dump all the corresponding tracked messages of that type onto +the page at that given line with appropriate graphics defined by its templates file.

    + +

    printAll() allows all Messages of all types to be dumped immediately.

    + +

    One thing to remember is that once a type of Message is printed +all tracked data relating to that type are gone. There is no need to worry +about purging messages from $_SESSION. The class manages this.

    + +

    Notice $optional as the argument to this function. This allows you +to shortcut the process of adding and printing Message's in one go. For example, +suppose you want to add a Message and print it right away. Thus, you pass as an +argument ANY argument that you would pass when adding a Message of that type. +Essentially, two lines of code are accomplished in one.

    +
    Example:
    +
    +	$msg->addError('MAX_ATTEMPTS');
    +	$msg->printErrors();
    +	
    +	can also be accomplished as:
    +	
    +	$msg->printErrors('MAX_ATTEMPTS');
    +
    + +

    Printing String inside Feedback style box

    + +printNoLookup($str);

    + +

    Print $str inside a Feedback Message type style box. Performs +no dialog with $_SESSION or any language mappings in the language_text DB table. +Strictly used in /resources/links/index.php for compatibility. +

    + +

    Checking for existance of specific Message type

    + +contains{Errors|Warnings|Feedbacks|Helps|Infos}();

    + +

    Returns true if the type of Message is being tracked and contains +some kind of data. Useful for branching conditions in knowning +when to print a Message or not. Otherwise returns false.

    + +

    Manually Deleting a specific Message from storage

    + +delete{Error|Warning|Feedback|Help|Info}($codcuee);

    + +

    Will delete anything related to $code from $_SESSION

    +
    Example:
    +
    +	$msg->deleteFeedback('CANCELLED');
    +
    + +

    Example Code

    + +
    +
    +...
    +
    +require_once(AT_INCLUDE_PATH . '/classes/Message/Message.class.php');
    +
    +global $savant;
    +
    +$msg = new Message($savant); 
    +
    +$msg->addError('FORUM_NOT_FOUND');
    +$msg->addWarning('SAVE_YOUR_WORK');
    +$msg->addInfo('NO_SEARCH_RESULTS');
    +$msg->addFeedback('FORUM_ADDED');
    +
    +/* State of relevant section of $_SESSION at this point 
    +[message] => Array
    +        (
    +            [error] => Array
    +                (
    +                    [AT_ERROR_FORUM_NOT_FOUND] => AT_ERROR_FORUM_NOT_FOUND
    +                )
    +
    +            [warning] => Array
    +                (
    +                    [AT_WARNING_SAVE_YOUR_WORK] => AT_WARNING_SAVE_YOUR_WORK
    +                )
    +
    +            [info] => Array
    +                (
    +                    [AT_INFOS_NO_SEARCH_RESULTS] => AT_INFOS_NO_SEARCH_RESULTS
    +                )
    +
    +            [feedback] => Array
    +                (
    +                    [AT_FEEDBACK_FORUM_ADDED] => AT_FEEDBACK_FORUM_ADDED
    +                )
    +
    +        )
    +*/
    +
    +// Now print them
    +$msg->printErrors();
    +$msg->printWarnings();
    +$msg->printInfos();
    +$msg->printFeedbacks();
    +
    +/* State of relevant section of $_SESSION at this point
    + [message] => Array
    +        (
    +        )
    + */
    +
    +// Let's add an array of arguments
    +$feedback=array('FORUM_ADDED', 'ac_access_groups');
    +$msg->addFeedback($feedback);
    +
    +// Before we print lets another another one to the same code
    +$feedback2=array('FORUM_ADDED', 'about_atutor_help_text');
    +$msg->addFeedback($feedback2);
    +
    +$msg->addHelp('DEMO_HELP2');
    +
    +$help=array('DEMO_HELP2', $_my_uri);
    +$msg->addHelp($help);
    +
    +$help2=array('ADD_TEST', $_my_uri);
    +$msg->addHelp($help2);
    +
    +// No need to url_encode the code
    +$filename = 'archive.zip';
    +$f = array('FILE_UPLOADED_ZIP', $file_name);
    +$msg->addFeedback($f);
    +
    +/* State of relevant section of $_SESSION at this point. Notice the second addFeddback call above
    + * had its arguments appended
    +
    + [message] => Array
    +        (
    +            [feedback] => Array
    +                (
    +                    [AT_FEEDBACK_FORUM_ADDED] => Array
    +                        (
    +                            [0] => AT_FEEDBACK_FORUM_ADDED
    +                            [1] => ac_access_groups
    +                            [2] => about_atutor_help_text
    +                        )
    +                        
    +                    [AT_FEEDBACK_FILE_UPLOADED_ZIP] => Array
    +                        (
    +                            [0] => AT_FEEDBACK_FILE_UPLOADED_ZIP
    +                            [1] => archive.zip
    +                        )
    +
    +                )
    +
    +            [help] => Array
    +                (
    +                    [AT_HELP_DEMO_HELP2] => Array
    +                        (
    +                            [0] => AT_HELP_DEMO_HELP2
    +                            [1] => /~Jay/docs/index.php?
    +                        )
    +
    +                    [AT_HELP_ADD_TEST] => Array
    +                        (
    +                            [0] => AT_HELP_ADD_TEST
    +                            [1] => /~Jay/docs/index.php?
    +                        )
    +
    +                )
    +
    +        }
    +*/
    +
    +$msg->printAll();
    +
    +/* State of relevant section of $_SESSION at this point
    + [message] => Array
    +        (
    +        )
    + */
    + 
    + ...
    +
    +top +

    16.4 UTF-8 and Multibyte language

    +

    As of ATutor 1.6, string parsing is done with multibtyle string functions, either the mbstring function in PHP itself, or the multibyte string functions in the UTF-8 library included with ATutor. By default ATutor will attempt to use the PHP mbstring functions, and will fall back on the ATutor library if this fails. In order to support both methods of string processing, a set of custom string parsing functions have been created. The only time the ATutor functions will be used is when the mbstring check during the installation or upgrade process has been disabled. The functions mirror the standard string parsing function in PHP, but with the $ prepended. So, where ever you would normally use a PHP function like substr() , instead use $substr(). These functions are located in the vital.inc.php +

    + if (extension_loaded('mbstring')){
    +	 $strtolower = 'mb_strtolower';
    +	 $strtoupper = 'mb_strtoupper';
    +	 $substr = 'mb_substr';
    +	 $strpos = 'mb_strpos';
    +	 $strrpos = 'mb_strrpos';
    +	 $strlen = 'mb_strlen';
    + } else {
    + 	 $strtolower = 'utf8_stringtolower';
    +	 $strtoupper = 'utf8_stringtoupper';
    +	 $substr = 'utf8_substr';
    +	 $strpos = 'utf8_strpos';
    +	 $strrpos = 'utf8_strrpos';
    +	 $strlen = 'utf8_strlen';
    + }
    +
    +
    + + +

    String functions

    +

      +
    • string $strtolower(string $str) -- UTF-8 aware strtolower function. Make a string lowercase
    • +
    • string $strtoupper(string $str) -- UTF-8 aware strtolower function. Make a string uppercase
    • +
    • int $strpos(string $haystack , string $needle ) -- UTF-8 aware strpos function. Find position of first occurrence of string in a string
    • +
    • int $strrpos(string $haystack , string $needle ) -- UTF-8 aware strrpos function. Find position of last occurrence of a string in a string
    • +
    • int $strlen(string $str) -- UTF-8 aware strlen function. Get string length
    • +
    • string validate_length(string $input, int $len, int $forDisplay) -- Checks if the input data has exceeded the database perferred length, if so, the input will be truncated and returned.
    • +

    + +

    Description

    +

    Starting from 1.6, the above string functions will replace the original string functions when dealing with strings in order to adapt the utf-8 environment.

    + +

    Location

    +

    `include/vitals.inc.php`

    + +

    Example 1

    +

    To alter a text to lowercase: +

    +		$str = 'Αρχαίο Πνεύμα';
    +		echo $strtolower($str);
    +		

    + +

    Example 2

    +

    To alter a text to uppercase: +

    +		$str = 'Αρχαίο Πνεύμα';
    +		echo $strtoupper($str);
    +		

    + +

    Example 3

    +

    To find the middle character of a string: +

    +		$str = 'Αρχαίο Πνεύμα';
    +		$mid = $strlen;
    +		echo $strpos($mid, $str);
    +		

    + +

    Example 4

    +

    To check if the string has the appropriate length: +

    +		$str = 'Αρχαίο Πνεύμα';
    +		$title = validate_length($str, 40);
    +		//now the title is safe to insert into the db
    +		

    + +top +

    17. Useful Variables

    +

    Most of the variables documented here are required for most pages to function correctly. constant variables, although not explicitly declared as constants, should be considered as such, and not altered.

    + +

    17.1 $db

    +

    Description

    +

    constant resource $db

    + +

    $db is the main database handler. Use it to connect to the ATutor database.

    + +

    Location

    +

    `include/vitals.inc.php`

    + +

    17.2 $addslashes

    +

    See $addslashes().

    + +

    17.3 $_base_href

    +

    Description

    +

    constant string $_base_href

    +

    The full URL to ATutor's base installation. Supports both regular and SSL protocols. Example: http://myserver.org/files/ATutor/.

    + +

    Location

    +

    `include/lib/constants.inc.php`

    + +

    17.4 $_base_path

    +

    Description

    +

    constant string $_base_path

    +

    The full absolute path to ATutor's base installation. Example: /files/ATutor/.

    + +

    Location

    +

    `include/lib/constants.inc.php`

    + +

    17.5 $_user_location

    +

    Description

    +

    constant $_user_location

    +

    $_user_location can be one of five values: `public`, `admin`, `users`, `prog` or empty. This variable must be set before requiring `vitals.inc.php`, and specifies what kind of general user authentication the page declaring it needs.

    + +

    `public` pages can be viewed by any user, signed-in as a member or not. `admin` pages (those in the `admin/` directory) can only be viewed by the administrator. `users` pages (those in the `users/` directory) can only be viewed by logged in members. If $_user_location is empty, it is assumed the page can only be accessed when a user is signed-in and viewing a course. `prog` is reserved for the pop-up window displaying the progress bar.

    + +

    This variable was added as a way of specifying which template to use (public, admin, or member). Its role as a way of authenticating is not thoroughly established.

    + +

    Location

    +

    Declared on every page that is directly accessible.

    + + +

    17.6 $_rel_url

    +

    Description

    +

    constant string $_rel_url

    +

    The absolute path and file name relative to ATutor's base installation. If ATutor was installed in http://myserver.org/files/ATutor/, the $_rel_url of the Site-Map page, for example, would evaluate to /tools/sitemap/index.php.

    + +

    This URL will always be the same for a given page, regardless of the location or path of an installation. This variable was added as a way of standardising the `page` value in the `lang_base_pages` table.

    + +

    Location

    +

    `include/lib/constants.inc.php`

    + +

    17.7 $_my_uri

    +

    Description

    +

    constant string $_my_uri

    +

    The full path and file name to the current page in a format that is ready to accept additional URL arguments. The argument separator will be defined as SEP. For example, if the current URL is http://myserver.org/index.php?cid=806;disable=PREF_MENU;menu_jump=2, then $_my_uri would be /index.php?cid=806;.

    + +

    So, $_my_uri is the URL to the current page without the temporary switch arguments. The following URL arguments are removed: + + enable, disable, expand, menu_jump, g, collapse, f, e, save, and lang.

    + +

    Location

    +

    `include/lib/vitals.inc.php`

    + +

    17.8 $contentManager

    +

    Description

    +

    constant ContentManager $contentManager

    +

    The $contentManager object provides access to methods for operating on content. All access to the `content` table should be done through this object.

    + +

    Locations

    +

    `include/vitals.inc.php`
    + `include/classes/ContentManager.class.php`

    + +

    17.9 $_section

    +

    Deprecated in ATutor 1.5

    +

    Description

    +

    array $_section

    + +

    This variable is a two-dimensional array used to display a page's breadcrumbs. The first index identifies the page starting from 0 such that $_section[0] defines the first page in the hierarchy, $_section[1] the second, and so on. The second index is used to assign that page's properties, defined as follows: index 0 is the page title, and index 1 is the URL. This variable must be defined before the `header.inc.php file is required.

    + +

    The URL for the last page (i.e.. the current page) is optional.

    + +

    Example 1

    +

    To assign the path to the site-map: +

    +// the Site-Map is a sub-page of the Tools page, hence we define the path:
    +
    +$_section[0][0] = _AT('tools');              // the Tools' page title
    +$_section[0][1] = 'tools/';                  // the Tools' page URL
    +$_section[1][0] = _AT('sitemap');            // the Site-Map's page title
    +$_section[1][1] = 'tools/sitemap/index.php'; // the Site-Map's page URL
    +

    + +

    Location

    +

    Declared on every page that is directly accessible.

    + + +top +

    18. Useful Functions

    +

    The functions listed below provide vital functionality for ATutor pages. Developers will most likely end up using most, if not all, of the functions below. Please review the Function Definitions (Prototypes) section for an explanation of the syntax being used. Additional javadoc comments can be found with each function's definition.

    + +

    18.1 authenticate()

    +

    authenticate() -- Authenticates the current user against a specified privilege.

    + +

    Description

    +

    mixed authenticate( integer $privilege [, boolean $check] )

    + +

    Authenticates the current user against $privilege. If $check is AT_PRIV_RETURN then the function will return the status of the authentication with TRUE meaning the user has been successfully authenticated or FALSE otherwise. With $check set to FALSE (default), the function will call exit to abort the script if the user cannot be authenticated and return TRUE otherwise.

    + +

    The instructor user will always return TRUE.

    + +

    $privilege is set to one of the constants defined in `include/lib/constants.inc.php`.

    + +

    Please use only AT_PRIV_RETURN or FALSE as possible values for $check as additional options may be added and the value of the constant changed.

    + +

    Location

    +

    `include/vitals.inc.php`

    + +

    Example 1

    +

    To authenticate an entire page using the announcements management privilege: +

    +define('AT_INCLUDE_PATH', '../include/');
    +require (AT_INCLUDE_PATH . 'vitals.inc.php');
    +/**
    +/* exit if the user cannot be authenticated
    + * otherwise continue loading the page.
    + */
    +authenticate(AT_PRIV_ANNOUNCEMENTS);
    +

    +

    Example 2

    +

    To authenticate a block of code using the forums management privilege: +

    +if (authenticate(AT_PRIV_FORUMS, AT_PRIV_RETURN)) {
    +    // ... this user has been authenticated
    +}
    +

    + +

    18.2 _AT()

    +

    _AT() -- Returns translated terms.

    + +

    Description

    +

    string _AT( string $term [, string $argument1 [, string $argument2 ...]] )

    + +

    This function returns a translated version of $term based on the user's current language preference as defined in $_SESSION[lang]. If $term cannot be found in the language database, it will return `[ $term ]` to signify that it was not found.

    + +

    Terms may require supplements in the form of additional arguments (see example 2). A term may require zero or more arguments. If a term requires arguments, then they must all be provided; no argument can be left out.

    + +

    Location

    +

    `include/lib/output.inc.php`

    + +

    Example 1

    +

    Printing a single term: +

    +/* echo a translated version of the 'tools' string.
    + * i.e. 'Tools' in English, 'Outils' in French, etc..
    + */
    + $_SESSION['lang'] = 'en';
    +echo '<h2>' . _AT('tools') . '</h2>';
    +
    + $_SESSION['lang'] = 'fr';
    +echo '<h2>' . _AT('tools') . '</h2>';
    +

    + +

    Result: +

    <h2>Tools</h2>
    +<h2>Outils</h2>
    +

    + +

    Example 2

    +

    Printing a phrase with arguments: +

    +$username = 'Jon';
    +echo _AT('welcome_message', $username);
    +

    +

    Result: +

    Hello, Jon. Welcome back.

    + + +

    18.3 AT_print()

    +

    AT_print() -- Transforms and formats user data for printing.

    + +

    Description

    +

    string AT_print( string $input, string $field_name [, boolean $runtime_html] )

    + +

    This function returns a transformed version of $input based on the rules specified by $field_name. $input is assumed to originate from the database, but it may be generalised in the future.

    + +

    $field_name is the unique name of the $input field in the form of table_name.field_name. The formatting options for the given field are defined in `include/lib/constants.inc.php`. If $field_name is not a valid option as defined in the constants file then the function will return $input unchanged.

    + +

    The boolean $runtime_html is used by fields which have an optional HTML formatting field. $runtime_html should be the associated HTML formatting field for that data. If set to FALSE then HTML elements will be escaped from $input and new line characters converted to <br />s.

    + +

    No data from the database should be printed without passing it through this function first.

    + +

    Location

    +

    `include/lib/output.inc.php`

    + +

    Example 1

    +

    Printing a field where HTML is not allowed: +

    +$username = 'my_name<b>_is';
    +$value    = AT_print($username, 'members.login'); // escape the '<b>' tag
    +echo $value

    + +

    Result: +

    my_name<b>_is

    + + +

    18.4 AT_date()

    +

    AT_date() -- Returns a localised version of a date.

    + +

    Description

    +

    string AT_date( [string $format [, integer $timestamp [, integer $format_type]]] )

    + +

    This function returns the string representation of the given $timestamp as transformed by $format. Uses the same options as PHP's date() function, but requires a % in front of each argument. If $timestamp is not specified, then the current time will be used.

    + +

    $format_type specifies the type of time stamp being provided. Available types are defined in `include/lib/constants.inc.php`. Possible options are: +

    +
    AT_DATE_MYSQL_DATETIME
    +
    The default. Format YYYY-MM-DD HH:MM:SS.
    + +
    AT_DATE_MYSQL_TIMESTAMP_14
    +
    Format YYYYMMDDHHMMSS.
    + +
    AT_DATE_UNIX_TIMESTAMP
    +
    A regular UNIX time stamp; seconds since epoch.
    + +
    AT_DATE_INDEX_VALUE
    +
    A special case specifying that only the single value of $format should be returned. The index into a specified date array. Only available for the following date options: %D, %l, %F, %M.
    +
    +

    + +

    The following arguments are language dependent: +

    +
    %D
    +
    A three-letter textual representation of a day, Mon through Sun
    + +
    %F
    +
    A full textual representation of a month, January through December
    + +
    %l (lowercase 'L')
    +
    A full textual representation of the day of the week, Monday through Sunday
    + +
    %M
    +
    A three-letter textual representation of a month, Jan through Dec
    +

    + +

    The following arguments are not yet supported to be language dependent, but may be in the future: +

    +
    %S
    +
    English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with %j
    + +
    %a
    +
    Lowercase Ante meridiem and Post meridiem am or pm
    + +
    %A
    +
    Uppercase Ante meridiem and Post meridiem AM or PM
    +

    + +

    In most (soon to be all) cases, $format will be specified using a call to _AT() to retrieve the correct date format for that language. See Example 2 below.

    + +

    Location

    +

    `include/lib/output.inc.php`

    + +

    Example 1

    +

    Returning a specified date using a UNIX time stamp: +

    +$time = mktime(0, 0, 0, 7, 14, 2004);
    +echo AT_Date('%l %F %j, %Y', $time, AT_DATE_UNIX_TIMESTAMP);
    +

    + +

    Result: +

    Wednesday July 14, 2004

    + +

    Example 2

    +

    Returning a specified date using a UNIX time stamp that is also language dependent: +

    +$time = mktime(0, 0, 0, 7, 14, 2004);
    +
    +$_SESSION['lang'] = 'en';
    +echo AT_Date(_AT('announcement_date_format'), $time, AT_DATE_UNIX_TIMESTAMP);
    +
    +echo '<br />';
    +$_SESSION['lang'] = 'fr';
    +echo AT_Date(_AT('announcement_date_format'), $time, AT_DATE_UNIX_TIMESTAMP);
    +

    + +

    Result: +

    Wednesday July 14, 2004
    +mercredi, 14 juillet 2004

    + +

    Example 3

    +

    Returning a single month name: +

    +$_SESSION['lang'] = 'fr';
    +The second month in French is: <?php echo AT_date('%F', 2, AT_DATE_INDEX_VALUE) ?>
    +

    + +

    Result: +

    +The second month in French is: f憝rier
    +

    + +

    18.5 $addslashes()

    +

    $addslashes() -- Quotes a string with slashes.

    + +

    Description

    +

    string $addslashes( string $str )

    + +

    If get_magic_quotes_gpc is disabled, then this variable function maps onto addslashes(), otherwise it maps onto my_add_null_slashes() which simply returns the input $str unchanged.

    + +

    Location

    +

    `include/vitals.inc.php`

    + +

    Example 1

    +

    With magic_quotes_gpc enabled: +

    +$str = "What's going on?";
    +
    +// prints magic_quotes_gpc: 1
    +echo 'magic_quotes_gpc: ' . get_magic_quotes_gpc();
    +
    +// maps to my_add_null_slashes(). prints What\'s going on? [correct]
    +echo $addslashes($str);
    +
    +// prints What\\\'s going on? [wrong]
    +echo addslashes($str);
    +

    + +

    Example 2

    +

    With magic_quotes_gpc disabled: +

    +$str = "What's going on?";
    +
    +// prints magic_quotes_gpc: 0
    +echo 'magic_quotes_gpc: ' . get_magic_quotes_gpc();
    +
    +// maps to addslashes(). prints What\'s going on? [correct]
    +echo $addslashes($str);
    +
    +// prints What\'s going on? [correct]
    +echo addslashes($str);
    +

    + + +

    18.6 debug()

    +

    debug() -- Outputs a variable.

    + +

    Description

    +

    void debug( mixed $var [, string $title] )

    + +

    This function is used for printing variables for debugging. $var can be of any type. The output is nicely formatted and easy to read. debug() will not output anything if the constant AT_DEVEL evaluates to FALSE.

    + +

    $title is available to label the debugging box for distinguishing it from other debugging boxes on the same page.

    + +

    Location

    +

    `include/vitals.inc.php`

    + +

    Example 1

    +

    Viewing the contents of an array: +

    +$my_array = array('apple' => 'green', 4 => 'something');
    +debug($my_array, 'my_array');
    +

    + +

    Result: +

    +    my_array
    +Array
    +(
    +    [apple] => green
    +    [4] => something
    +)
    +

    + +

    18.7 get_login()

    +

    get_login() -- Returns the login name of a member.

    + +

    Description

    +

    string get_login( integer $id )

    + +

    Returns the login name of the member whose ID is $id. There is no error handling.

    + +

    Location

    +

    `include/vitals.inc.php`

    + + +

    18.8 urlencode_feedback()

    +

    urlencode_feedback() -- Encodes a feedback code.

    + +

    Description

    +

    mixed urlencode_feedback( mixed $f )

    + +

    This function returns a URL safe encoding of a feedback code. Its purpose is to encode the feedback into the URL so that the page being redirected to will then output the feedback. $f may be an array of feedback codes, where additionally, each feedback code may be an array consisting of supplementary arguments. If $f is an array then the return value will be its string representation, otherwise the function will return $f unchanged.

    + +

    The way feedback is implemented may change in the future, so it is best to use this function even if the feedback code is not an array.

    + +

    Location

    +

    `include/vitals.inc.php`

    + +

    Example 1

    +

    To encode a feedback array: +

    +$f[] = array(AT_FEEDBACK_FILE_UPLOADED_ZIP, $file_name);
    +
    +header('Location: file_manager.php?f='.urlencode_feedback($f));
    +exit;
    +

    + +

    18.9 url_rewrite()

    +

    url_rewrite() -- Change an url to a pretty url.

    + +

    Description

    +

    string url_rewrite( string $url, boolean $is_rewriting_header, boolean $force)

    + +
    $url
    +

    the Url should be a relative link to the file.

    + +
    $is_rewriting_header
    +

    Determine if url_rewrite is rewriting a header (location: ..) path or not. Available values are AT_PRETTY_URL_IS_HEADER, AT_PRETTY_URL_NOT_HEADER(default). Use AT_PRETTY_URL_IS_HEADER if url_rewrite is being called inside header(Location:...)

    + +
    $force
    +

    Apply url_rewrite forcefully if it is true, default value is false.

    + +

    This function returns a pretty URL from the given parameter. Its purpose is to change the URLs to a "prettier" format in order to extend user friendliness in the system, and to create indexes for Google search. This function will authenticate itself towards the current pages. In our definition, admins, login, registration pages should not have pretty url applied. However, if one want to use url_rewrite on these pages, please set $force to TRUE, this will force it to change the urls to pretty urls. Please note that the admin's system preference will still overwrite the force effect. $url may be an string of URL, '?' and the seprators in the URL query will be replaced by slashes. Please note that the PHP_SELF variable might break some of the form action/header redirect because the Pretty URI is a virtual path. To address this problem, set $is_rewriting_header to AT_PRETTY_URL_IS_HEADER, as demonstrated in example 3.

    + +

    Location

    +

    `include/vitals.inc.php`

    + +

    Example 1

    +

    To change an URL: +

    +$url = 'forum/view.php?fid=3&pid=4';
    +echo url_rewrite($url);		//output go.php/1/forum/view.php/fid/3/pid/4
    +

    + +

    Example 2

    +

    To change an URL for mods: +

    +$url = 'mods/hello_world/index.php';
    +echo url_rewrite($url, true);		//output go.php/1/mods/hello_world/index.php
    +

    + +

    Example 3

    +

    To change an URL for header redirection: +

    +header('Location: '. url_rewrite('forum/index.php', AT_PRETTY_URL_IS_HEADER)); 
    +

    + +top +

    19. Useful Constants

    +

    These constants are part of the vital functionality of ATutor.

    + +

    19.1 AT_INCLUDE_PATH

    +

    Description

    +

    The AT_INCLUDE_PATH must be defined before you require or include any files. The constant defines the relative path to the `include/` directory. For security reasons no file should ever be called-in without using this constant!

    + +

    Example 1

    +

    Requiring the vitals file from the tools page: +

    define('AT_INCLUDE_PATH', '../include/');
    +require (AT_INCLUDE_PATH . 'vitals.inc.php');

    + +

    Location

    +

    Declared on every page that is directly accessible.

    + +

    19.2 SEP

    +

    Description

    +

    For XHTML compliance, we have created this constant to use when applying variables to URL arguments. The constant is one of the values defined by PHP's arg_separator.input, with a preference for the semi-colon (`;`), if available, over the ampersand (`&`).

    + +

    Example 1

    +

    Printing a dynamic link: +

    +$arg1 = 'one';
    +$arg2 = 'two';
    +
    +echo "SEP is ".SEP."\n";
    +echo '<a href="index.php?arg1=' . $arg1 . SEP . 'arg2=' . $arg2 . '">link</a>';

    + +

    Result: +

    +SEP is ;
    +<a href="index.php?arg1=one;arg2=two">link</a>
    +

    + +

    Location

    +

    `include/lib/constants.inc.php`

    + + +

    19.3 AT_DEVEL

    +

    Description

    +

    Enables or disables usage of the debug() function.

    + +

    Location

    +

    `include/vitals.inc.php`

    + + +

    19.4 TABLE_PREFIX

    +

    Description

    +

    The TABLE_PREFIX constant holds the prefix to the ATutor database tables. It is needed when creating SQL queries.

    + +

    Location

    +

    `include/config.inc.php`

    + + +

    19.5 VERSION

    +

    Description

    +

    The version number of this ATutor installation.

    + +

    Location

    +

    `include/lib/constants.inc.php`

    + + + +top +

    20. Install & Upgrade Scripts

    +

    Installing or upgrading ATutor is done with the scripts found in the `install/` directory. Two files in particular control the installation or upgrading flow and are appropriately named `install.php` and `upgrade.php`. For actual documentation on installing or upgrading please see ATutor's official documentation.

    + +

    The `install/` directory contains two other subdirectories that are used during the installation or upgrade process. The `install/db/` directory contains database schema files and are clearly labeled. The `install/include/` directory contains scripts that are common to both processes and labeled according to their order in either process.

    + +

    20.1 Install Script

    +

    The `install.php` script requires each step as it's needed as the $step counter variable is incremented. To add or edit a step, edit the `install.php` file.

    + +

    20.2 Upgrade Script

    +

    The `upgrade.php` script works exactly the same as the install script does, but requires different files (step prefixed with a 'u'). To upgrade the database schema an SQL file named `atutor_upgrade_from_to_to.sql` must be created, where from is the exact version of the previous ATutor version, and to is the next stable version.

    + + +
    top +

    21. Accessibility

    +

    Part of ATutor's philosophy is to be "designed with accessibility and adaptability in mind", we advise you to consider carefully the practices outlined on the following sites:

    + + +top +

    22. Validation

    +

    Please use the W3C Markup Validation Service, a free service, to check your pages for XHTML conformance, W3C Recommendations and other standards.

    + +top +

    23. Sample Script

    +

    The following is sample code that shows the general flow of an ATutor script. The example shows how you may use some of the variables and functions available to you. Some scripts may not need all the variables and functions used below. +

    <?php
    +// 0. insert the SVN keywords
    +// $Id: guidelines.html 1275 2004-07-28 16:02:49Z joel $
    +
    +// 1. define relative path to `include` directory:
    +define('AT_INCLUDE_PATH', '../include/');
    +
    +// 2. require the `vitals` file before any others:
    +require (AT_INCLUDE_PATH . 'vitals.inc.php');
    +
    +// 3. authenticate this user:
    +authenticate(AT_PRIV_SPECIAL_PRIV);
    +
    +// 4. define the breadcrumbs path:
    +$_section[0][0] =  _AT('tools');
    +$_section[0][1] = 'tools/';
    +$_section[1][0] =  _AT('my_page');
    +$_section[1][1] =  'tools/page.php;
    +
    +// 5. handle any post request:
    +if (isset($_POST['submit'])) {
    +  // 5.1 check for errors:
    +  if ($_POST['some_field'] == '') {
    +      $msg->addError('SOME_ERROR_CODE');
    +  }
    +
    +  if (!$msg->containsErrors()) {
    +      //5.2 complete the desired action here. (example: insert into DB)
    +
    +      //5.3 set the feedback message and add it to the URL:
    +      $msg->addFeedback('MESSAGE');
    +      $url = $_base_href . 'tools/index.php?arg1=yes';
    +
    +      //5.4 redirect after successful operation:
    +      header('Location: ' . $url);
    +      exit;
    +  }
    +}
    +
    +// 6. start the page display by calling the header
    +require (AT_INCLUDE_PATH . 'header.inc.php');
    +
    +// 7. display any feedback or error messages that my occur:
    +require (AT_INCLUDE_PATH . 'html/feedback.inc.php');
    +
    +/*
    + * 8. display the page contents here.
    + */
    +
    +// 9. finish the page by calling the footer
    +require (AT_INCLUDE_PATH . 'footer.inc.php');
    +?>

    + +top +

    24. Credits & Additional Sources

    + + + + diff --git a/documentation/developer/modules.html b/documentation/developer/modules.html new file mode 100644 index 000000000..1c572bab6 --- /dev/null +++ b/documentation/developer/modules.html @@ -0,0 +1,945 @@ + + + + ATutor Module Documentation + + + + + + + +

    Module Development Documentation

    + + + +

    Introduction

    +

    ATutor 1.5.2 introduced the concept of modules, providing developers with a framework to implement additional functionality in a coherent and loosely coupled way.

    + +

    The framework defines methods for assigning privileges, backing-up and restoring content, deleting course specific content, and adding side menu blocks, student tools, course management and administrative tools, as well as public tools and other types of added functionality .

    + +

    The intent is to allow for the development and distribution of modules independent of the ongoing development and release of ATutor. The module structure does allow for the creation of modules that run software that is not distributed under the GNU General Public License, but distributed separately under their own, perhaps commercial licenses.

    + +

    The Hello World example module is included with each ATutor distribution for developers who want to investigate how modules work. The module is found in the mods/hello_world directory. A copy of the Hello World module works well as a starting point for creating a new module, since it implements (in a simple way) just about all the features found in modules. Also see the files from other modules that operate like you expect your module to operate.

    + + +

    Structure

    +

    Modules are stored under ATutor's mods directory. Core modules are stored in the mods/_core subdirectory and are made available with every release of ATutor. These modules cannot be disabled by the administrator as they are vital to ATutor's functionality. Standard modules are stored in the mods/_standard subdirectory and are also made available with every release of ATutor. Standard modules can be disabled by the administrator. Extra modules are stored in the mods directory and are installed and distributed independently of ATutor. Although the process of developing modules is the same for each type of module, only extra modules can be distributed separately, while core and standard modules are added to the ATutor code repository (i.e. SVN trunk).

    + +

    Whenever a module identifier is needed within code, it should appear in lowercase with spaces converted to underscores.

    + +

    The module name, and hence the directory and function names (see below for additional details), must be unique across all possible modules. A module should not be made available if an existing module is already being distributed under that same name. It is up to the module developer to ensure that their module name is unique.

    + +

    Directory Name

    +

    The name given to the directory must be chosen carefully. The name is used to namespace the module's function by prefixing required functions with that directory name. For example, a module named Example Maker should be placed in a directory named example_maker and the delete function would be named example_maker_delete().

    + +

    Files

    +

    The following files should exist under the module's top level directory: mods/module_name.

    + +
    +
    module.php
    +
    This is the main module file which gets included whenever a page is loaded. Required.
    + +
    module.xml
    +
    This file is used only for identifying the module for distribution and is only used when viewing a module's details. Required.
    + +
    module_install.php
    +
    This file is used when installing the module. Required.
    + +
    module_uninstall.php
    +
    This file is used to remove the module from the system. Required for ATutor 1.6.2+.
    + +
    module_backup.php
    +
    This file is used when backing-up and restoring course content. Optional.
    + +
    module_delete.php
    +
    This file is used when deleting course specific content. Optional.
    + + +
    module_cron.php
    +
    This file is used to run module related commands at specified intervals. Optional.
    + +
    module.sql
    +
    This file is used to add tables and/or modify data in the ATutor database. Also used to insert language into the language_text table. Optional.
    +
    + +

    The module.xml File

    +

    The module.xml file is used for displaying information about the module before it is installed and is useful when distributing the module.

    +
    +<?xml version="1.0" encoding="ISO-8859-1"?> 
    +<module version="0.1"> 
    +    <name lang="en">Example Maker</name> 
    +    <description lang="en">This is an example module that makes examples.</description> 
    +    <maintainers>
    +        <maintainer> 
    +            <name>ATutor Team</name> 
    +            <email>info@atutor.ca</email> 
    +        </maintainer>
    +        <maintainer> 
    +            <name>John Doe</name> 
    +            <email>jd@example.com</email> 
    +        </maintainer>
    +    </maintainers> 
    +    <url>http://www.example.com</url> 
    +    <license>BSD</license> 
    +    <release> 
    +        <version>0.2</version> 
    +        <date>2005-08-22</date> 
    +        <state>stable</state> 
    +        <notes>Fixes several bugs in previous version.</notes> 
    +    </release> 
    +</module>
    +
    + + +

    The module.php File

    +

    The module.php file is typically used to set permissions, and to link module components into the ATutor navigation elements, as tool icons, navigation tabs, sub navigation menus, or side menu blocks, as administrator, course management, and student tools. It is also used to link tools to My Start Page, or to various public pages that appear where a user is browsing the system without being logged in. The module.php file can also be used to run module specific functionality. It runs everytime a module screen is viewed, loading whatever settings it may contain, or running any scripts that may be required by the module.

    +
    +<?php
    +/*******
    + * doesn't allow this file to be loaded with a browser.
    + */
    +if (!defined('AT_INCLUDE_PATH')) { exit; }
    +
    +/******
    + * this file must only be included within a Module obj
    + */
    +if (!isset($this) || (isset($this) && (strtolower(get_class($this)) != 'module'))) { exit(__FILE__ . ' is not a Module'); }
    +
    +
    +/******
    +* modules sub-content to display on course home detailed view
    +*/
    +$this->_list['hello_world'] = array('title_var'=>'hello_world','file'=>'mods/hello_world/sublinks.php');
    +
    +/*******
    + * assign the instructor and admin privileges to the constants.
    + */
    +define('AT_PRIV_EXAMPLE_MAKER',       $this->getPrivilege());
    +define('AT_ADMIN_PRIV_EXAMPLE_MAKER', $this->getAdminPrivilege());
    +
    +/*******
    + * create a side menu box/stack.
    + */
    +$this->_stacks['example_maker'] = array('title_var'=>'example_maker', 'file'=>'mods/example_maker/side_menu.inc.php');
    +// ** possible alternative: **
    +// $this->addStack('example_maker', array('title_var' => 'example_maker', 'file' => './side_menu.inc.php');
    +
    +/*******
    + * if this module is to be made available to students on the Home or Main Navigation.
    + */
    +$_student_tool = 'mods/example_maker/index.php';
    +// ** possible alternative: **
    +// $this->addTool('./index.php');
    +
    +/*******
    + * add the admin pages when needed.
    + */
    +if (admin_authenticate(AT_ADMIN_PRIV_EXAMPLE_MAKER, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) {
    +	$this->_pages[AT_NAV_ADMIN] = array('mods/example_maker/index_admin.php');
    +	$this->_pages['mods/example_maker/index_admin.php']['title_var'] = 'example_maker';
    +	$this->_pages['mods/example_maker/index_admin.php']['parent']    = AT_NAV_ADMIN;
    +}
    +
    +/*******
    + * instructor Manage section:
    + */
    +$this->_pages['mods/example_maker/index_instructor.php']['title_var'] = 'example_maker';
    +$this->_pages['mods/example_maker/index_instructor.php']['parent']   = 'tools/index.php';
    +// ** possible alternative: **
    +// $this->pages['./index_instructor.php']['title_var'] = 'example_maker';
    +// $this->pages['./index_instructor.php']['parent']    = 'tools/index.php';
    +
    +/*******
    + * student page.
    + */
    +$this->_pages['mods/example_maker/index.php']['title_var'] = 'example_maker';
    +$this->_pages['mods/example_maker/index.php']['img']       = 'mods/example_maker/example_maker.jpg';
    +
    +
    +/* public pages */
    +$this->_pages[AT_NAV_PUBLIC] = array('mods/example_maker/index_public.php');
    +$this->_pages['mods/example_maker/index_public.php']['title_var'] = 'example_maker';
    +$this->_pages['mods/example_maker/index_public.php']['parent'] = 'login.php';
    +$this->_pages['login.php']['children'] = array('mods/example_maker/index_public.php');
    +
    +/* my start page pages */
    +$this->_pages[AT_NAV_START]  = array('mods/example_maker/index_mystart.php');
    +$this->_pages['mods/example_maker/index_mystart.php']['title_var'] = 'example_maker';
    +$this->_pages['mods/example_maker/index_mystart.php']['parent'] = 'users/index.php';
    +$this->_pages['users/index.php']['children'] = array('mods/example_maker/index_mystart.php');
    +?>
    +
    + +

    The module.sql File

    +

    A very simple module.sql file such as the following, creates a table for the module, and inserts it's language into the ATutor language_text table. Module language can then be managed from the ATutor Language Manager. The module.sql file can contain any number of SQL statements used to add tables or insert data into the ATutor database.

    + +
    +# sql file for example maker module
    +
    +CREATE TABLE example_maker (
    +   `course_id` mediumint(8) unsigned NOT NULL,
    +   `value` VARCHAR( 30 ) NOT NULL ,
    +   PRIMARY KEY ( `course_id` )
    +);
    +
    +INSERT INTO `language_text` VALUES ('en', '_module','example_maker','Example Maker',NOW(),'');
    +INSERT INTO `language_text` VALUES ('en', '_module','AT_ERROR_GOES_HERE','Example Maker Error Message',NOW(),'');
    +
    +
    + +

    Installation

    +

    The module_install.php script gets executed during the installation process, using the administrator's Install Module feature. If the script's execution results in $msg->containsErrors() evaluating to TRUE, then the errors are displayed and the user is prompted to correct them. The process is then repeated until errors are no longer being generated and the module is installed successfully. Ultimately, it is up to the module to determine the logical steps involved in its installation. For example, it might be better to create the data directories before trying to create any database tables since creating the directory may require several attempts. Typically the flow we describe here should be suitable in most cases.

    + +

    Theoretically, the install script's execution is wide-open and does not have to adhere to the process outlined below or make use of any special privileges, provided it generates errors as appropriate.

    + +
    # pseudo-code for installing a module:
    +while (there are errors)
    +    print the error message
    +
    +    # inside module_install.php:
    +    define the privileges used
    +
    +    if (create database tables is unsuccessful) then
    +        generate an error message
    +
    +    if (there are no errors AND there is an SQL file) then
    +        execute the SQL file
    +end while
    +
    +add the module to the system using the defined privileges
    +
    + + +

    The module_install.php File

    +

    The module_install.php file is typically used to run any installation related files, such as an sql file that sets up a database table, or installs the language for the module. The following is an example module_install.php file

    +
    +<?php
    +/*******
    + * the line below safe-guards this file from being accessed directly from
    + * a web browser. It will only execute if required from within an ATutor script,
    + * in our case the Module::install() method.
    + */
    +if (!defined('AT_INCLUDE_PATH')) { exit; }
    +
    +/*******
    + * Note: the many options for these variables are used to decrease confusion.
    + *       TRUE | FALSE | 1 will be the convention.
    + *
    + * $_course_privilege
    + *     specifies the type of instructor privilege this module uses.
    + *     set to empty | FALSE | 0   to disable any privileges.
    + *     set to 1 | AT_PRIV_ADMIN   to use the instructor only privilege.
    + *     set to TRUE | 'new'        to create a privilege specifically for this module:
    + *                                will make this module available as a student privilege.
    + *
    + * $_admin_privilege
    + *    specifies the type of ATutor administrator privilege this module uses.
    + *    set to FALSE | AT_ADMIN_PRIV_ADMIN   to use the super administrator only privilege.
    + *    set to TRUE | 'new'                  to create a privilege specifically for this module:
    + *                                         will make this module available as an administrator privilege.
    + *
    + *
    + * $_cron_interval
    + *    if non-zero specifies in minutes how often the module's cron job should be run.
    + *    set to 0 or not set to disable.
    + */
    +$_course_privilege = TRUE; // possible values: FALSE | AT_PRIV_ADMIN | TRUE
    +$_admin_privilege  = TRUE; // possible values: FALSE | TRUE
    +$_cron_interval    = 35; // run every 30 minutes
    +
    +
    +/********
    + * the following code is used for creating a module-specific directory.
    + * it generates appropriate error messages to aid in its creation.
    + */
    +$directory = AT_CONTENT_DIR .'example_maker';
    +
    +// check if the directory is writeable
    +if (!is_dir($directory) && !@mkdir($directory)) {
    +	$msg->addError(array('MODULE_INSTALL', '<li>'.$directory.' does not exist. Please create it.</li>'));
    +} else if (!is_writable($directory) && @chmod($directory, 0666)) {
    +	$msg->addError(array('MODULE_INSTALL', '<li>'.$directory.' is not writeable. On Unix issue the command <kbd>chmod a+rw</kbd>.</li>'));
    +}
    +
    +
    +/******
    + * the following code checks if there are any errors (generated previously)
    + * then uses the SqlUtility to run any database queries it needs, ie. to create
    + * its own tables.
    + */
    +if (!$msg->containsErrors() && file_exists(dirname(__FILE__) . '/module.sql')) {
    +	// deal with the SQL file:
    +	require(AT_INCLUDE_PATH . 'classes/sqlutility.class.php');
    +	$sqlUtility = new SqlUtility();
    +
    +	/*
    +	 * the SQL file could be stored anywhere, and named anything, "module.sql" is simply
    +	 * a convention we're using.
    +	 */
    +	$sqlUtility->queryFromFile(dirname(__FILE__) . '/module.sql', TABLE_PREFIX);
    +}
    +
    +?>
    +
    + + + +

    Specifying Privileges

    +

    Privileges control who has access to the course management and administrative sections.

    + +

    See the Authentication & Privileges section for additional details using the privileges.

    + +
    +
    $_course_privilege
    +

    This variable controls access to a course's management section and can take one of the following values:

    +
      +
    • TRUE: To use a custom assignable privilege level. If a custom privilege is created, the module will appear as an option when assigning privileges to enrolled students.
    • +
    • AT_PRIV_ADMIN: To use the instructor privilege. Only the instructor will be given access to the module's management section.
    • +
    • FALSE: To disable the privilege if there is no management section for the module.
    • +
    +
    + +
    $_admin_privilege
    +

    This variable can take one of the following values:

    +
      +
    • TRUE: To use a custom assignable privilege level. If a custom privilege is created then the module will appear as an option when assigning privileges to administrators
    • +
    • AT_ADMIN_PRIV_ADMIN: To use the super administrator privilege. Only the super administrator will be given access to the module's administration section.
    • +
    +
    +
    + +

    Note that creating a privilege is not in itself enough to make the module appear in the Manage section! The hierarchy and navigation path to the management page must be set correctly. See the Navigation & Hierarchy section for additional details.

    + +

    Creating a Data Directory

    +

    It is best to keep the directory within the AT_CONTENT_DIR directory as it should already allow the creation of files and directories by the web server. It is then up to the module to create individual course directories as needed.

    +
    +$directory = AT_CONTENT_DIR .'example_maker';
    +
    +// check if the directory is writable
    +if (!is_dir($directory) && !@mkdir($directory)) {
    +    $msg->addError(array('MODULE_INSTALL', '<li>'
    +                         .$directory
    +                         .' does not exist. Please create it.</li>'));
    +} else if (!is_writable($directory) && @chmod($directory, 0666)) {
    +    $msg->addError(array('MODULE_INSTALL', '<li>'
    +                         . $directory
    +                         .' is not writable.</li>'));
    +} // else: it was created successfully.
    +
    + +

    Executing an SQL File

    +

    If the module requires its own database tables or custom language, then it will have to create them itself. The SQL can either be executed inline using PHP database execution directly, or using the SQLUtility class to execute an external SQL file. Also see module.sql for more about creating an SQL file.

    +
    +if (!$msg->containsErrors() && file_exists(dirname(__FILE__) . '/module.sql')) {
    +    // deal with the SQL file:
    +    require(AT_INCLUDE_PATH . 'classes/sqlutility.class.php');
    +    $sqlUtility = new SqlUtility();
    +    $sqlUtility->queryFromFile(dirname(__FILE__) . '/module.sql', TABLE_PREFIX);
    +}
    +
    + +

    Generating Errors Messages

    +

    It is up to the module to generate and check for any errors that occur during the installation. An error message can be generated using $msg->addError(array('MODULE_INSTALL', '<li>your error msg goes here</li>'));. Note that the text supplied to the error message is not translated in this case. If the language should be localised, then the appropriate language vairable should replace the text. Something like $msg->addError(array('MODULE_INSTALL', 'AT_ERROR_GOES_HERE')); . The corresponding SQL INSERT statement should then be found in the module.sql file, so the language gets added to the language_text table in ATutor.

    + +

    To check if any errors have been generated, use $msg->containsErrors() which evaluates to TRUE if a previous error has been generated.

    + +

    See the Error and Feedback section of the Developer Documentation for more details about displaying messages.

    + + +

    Uninstalling a Module

    +

    As of ATutor 1.6.2 a module_uninstall.php file is required with each module. This file uses the original module.sql file to deletes any database tables, and any language that may have been installed with module.sql, and removes any directories used by the module, and deletes the module directory itself.

    + +
    +
    +/*******
    + * module_uninstall.php performs reversion of module_install.php
    + */
    +
    +/*******
    + * the line below safe-guards this file from being accessed directly from
    + * a web browser. It will only execute if required from within an ATutor script,
    + * in our case the Module::uninstall() method.
    + */
    +if (!defined('AT_INCLUDE_PATH')) { exit; }
    +
    +/********
    + * the following code is used for removing a module-specific directory created in module_install.php.
    + * it generates appropriate error messages to aid in its creation.
    + */
    +$directory = AT_CONTENT_DIR .'example_maker';
    +
    +// check if the directory exists
    +if (is_dir($directory)) {
    +	require(AT_INCLUDE_PATH.'lib/filemanager.inc.php');
    +
    +	if (!clr_dir($directory))
    +		$msg->addError(array('MODULE_UNINSTALL', '
  • '.$directory.' can not be removed. Please manually remove it.
  • ')); +} + +/****** + * the following code checks if there are any errors (generated previously) + * then uses the SqlUtility to run reverted database queries of module.sql, + * ie. "create table" statement in module.sql is run as drop according table. + */ +if (!$msg->containsErrors() && file_exists(dirname(__FILE__) . '/module.sql')) { + // deal with the SQL file: + require(AT_INCLUDE_PATH . 'classes/sqlutility.class.php'); + $sqlUtility = new SqlUtility(); + + /* + * the SQL file could be stored anywhere, and named anything, "module.sql" is simply + * a convention we're using. + */ + $sqlUtility->revertQueryFromFile(dirname(__FILE__) . '/module.sql', TABLE_PREFIX); +} + +
    + + +

    Authentication & Privileges

    +

    See the Installation: Specifying Privileges section on creating privileges during the installation process.

    + +

    Authentication uses constants for the privilege levels. The privileges should be declared in the module.php file using the $this->getPrivilege() and $this->getAdminPrivilege() methods, respectively.

    + +
    +define('AT_PRIV_FORUMS',       $this->getPrivilege()      );
    +define('AT_ADMIN_PRIV_FORUMS', $this->getAdminPrivilege() );
    +
    + +

    Once declared, a page can then authenticate against those privileges using either the authentication() or the admin_authenticate() functions.

    + +
    +define('AT_INCLUDE_PATH', '../include/');
    +require(AT_INCLUDE_PATH.'vitals.inc.php');
    +// authenticate the administrator forums section:
    +admin_authenticate(AT_ADMIN_PRIV_FORUMS);
    +
    + +

    Localisation

    +

    Although a module can be created with all hard-coded language, we recommended you use ATutor's localisation functions. All of ATutor's language is stored in the database, which is then retrieved using the _AT() function for simple terms and the $msg object for feedback and error messages.

    + +

    Additional details on localising ATutor can be found on the Thing You Should Know Before Translating and in the ATutor Developer Documentation.

    + +

    Module-specific language should be inserted into the language_text table during the installation process. The fields in the table are as follows:

    + +
    +
    language_code
    +
    The ISO-639-1 language code plus locale.
    + +
    variable
    +
    Set to _module for modules.
    + +
    term
    +
    The variable used for retrieving the language.
    + +
    text
    +
    The language text.
    + +
    revised_date
    +
    Set to NOW() for modules.
    + +
    context
    +
    Short description of the language text.
    +
    + +

    Each language item should have a corresponding SQL INSERT line in the module.sql file, that gets inserted into the ATutor language_text table during installation. Do not include a prefix (e.g. "AT_") on the language_text table name. The installer will detect the right prefix, and automatically prepend it to tables names. Also see the section module.sql for information about creating a file to install the module's language.

    +
    +# Insert module specific language:
    +INSERT INTO `language_text` VALUES ('en',
    +                                    '_module',
    +                                    'example_maker',
    +                                    'Example Maker',
    +                                     NOW(),
    +                                    'the module title');
    +
    +

    (Introduced in ATutor 1.5.4) On occassion it may be necessary to modify existing ATutor language to accommodate module functionality that alters the way ATutor itself functions by default. For example, when the payments module is installed the message displayed when a student enrolls in a course that requires a payment, needs to include mention of how to make a payment. In the SQL statement below, the second value (i.e. "variable" in the language_text table) is prefixed with "_c" for custom language. Possible custom language variables are _c_msgs, _c_template, and _c_module.

    + +
    +# Insert custom language:
    +INSERT INTO `language_text` VALUES ('en', 
    +					'_c_msgs',
    +					'AT_INFOS_EC_PAYMENTS_TURNED_OFF','
    +					Your request has been made. You will be notifed when your request has been approved. If course fees are pending, they will be listed under the Payments tab above, where they can be paid.',
    +					NOW(),'');
    +
    + + +

    Configuration Options

    +

    Module configuration options can be stored in the ATutor config table, and retrieved using a $_config[] array variable. All module configuration key/value pairs are automatically loaded from the table into global memory while ATutor is running, and can be accessed from any of the module scripts (or from anywhere is ATutor for that matter). The value for a particular configuration option is retrieved by entering its key into the array. To retrieve a URL for the Example Maker module for example, you might use $_config['example_maker'].

    +

    In the following example of an index_admin.php file, the form below accepts a URL to an external application used by the Example Maker module (though it could be any value). In this case imagine a third party application has been installed, and the URL to the application is being stored as a configuration option for the example_maker module. When the form is submitted, $_POST['uri'] is inserted into the config table as the value for the example_maker key. The following is an example module administrator script used to add/edit a config option for the example_maker module.

    + +

    The index_admin.php File

    +
    +
    +<?php
    +// make sure user is allowed to see this page (admins only)
    +
    +admin_authenticate(AT_ADMIN_PRIV_EXAMPLE_MAKER);
    +	
    +if (isset($_POST['submit'])) {
    +	// trim whitespace from the value submitted
    +	$_POST['uri'] = trim($_POST['uri']);
    +
    +	// display an error message if the value is empty
    +	if (!$_POST['uri']){
    +		$msg->addError('EXAMPLE_MAKER_ADD_EMPTY');
    +	}
    +	
    +	// if no errors, insert the key "example_maker" and value "$_POST['uri']" into the config table	
    +	if (!$msg->containsErrors()) {
    +		$_POST['uri'] = $addslashes($_POST['uri']);
    +		$sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES ('example_maker', '$_POST[uri]')";
    +		mysql_query($sql, $db);
    +		$msg->addFeedback('EXAMPLE_MAKER_URL_SAVED');
    +
    +		header('Location: '.$_SERVER['PHP_SELF']);
    +		exit;
    +	}
    +}
    +
    +require (AT_INCLUDE_PATH.'header.inc.php');
    +
    +/*******
    + *  First check to see if there is a value for the example_maker key $_config['example_maker']
    + *  If there isn't a value then a missing value message is displayed
    + *  The form below that has a single field for submitting a value, in this case a URL
    + *  If the value exists in the config table, then display it in the text field using  $_config['example_maker']
    + */
    +	
    +?>
    +
    +<?php if ($_config['example_maker']): ?>
    +	<div class="input-form">
    +		<div class="row">
    +			<p><?php echo _AT('example_maker_text'); ?></p>
    +		</div>
    +	</div>
    +<?php else: ?>
    +	<div class="input-form">
    +		<div class="row">
    +			<p><?php echo _AT('example_maker_missing_url');  ?></p>
    +		</div>
    +	</div>
    +<?php endif; ?>
    +
    +<form action="<?php  $_SERVER['PHP_SELF']; ?>" method="post">
    +	<div class="input-form">
    +		<div class="row">
    +			<p><label for="uri"><?php echo _AT('example_maker_url'); ?></label></p>
    +	
    +			<input type="text" name="uri" value="<?php echo $_config['example_maker']; ?>" id="uri" size="60" style="min-width: 65%;" />
    +		</div>
    +		<div class="row buttons">
    +			<input type="submit" name="submit" value="<?php echo _AT('save'); ?>"  />
    +		</div>
    +	</div>
    +</form>
    +
    + + +

    Custom Style Sheets

    +

    A custom style sheet can be linked into pages by setting $_custom_css to be the absolute path to the style sheet. This variable must be set on every page that requires that style sheet.

    + +
    +define('AT_INCLUDE_PATH', '../../include/');
    +require(AT_INCLUDE_PATH.'vitals.inc.php');
    +// using a custom style sheet:
    +$_custom_css = $_base_path . 'mods/example_maker/module.css';
    +
    + + +

    Side Menu Boxes

    +

    Side menu boxes generally appear in a column at the side of a course (though this layout can be altered by a theme). A module may implement one or more side menu boxes.

    + +

    Side menus are specified using the $_module_stacks array in module.php. $_module_stacks have the attributes title_var (or title) and file. The title_var value is the language key used for that box; the title will be generated by executing _AT($title_var). If title is set instead, a hard-coded title will be used. The file attribute specifies the absolute path to the side menu's include file.

    + +

    The key to the $_module_stacks should be the name of the module.

    + +
    +$_module_stacks['example_maker'] = array('title_var' => 'example_maker', 
    +                                         'file' => dirname(__FILE__).'\side_menu.inc.php');
    +
    + +

    Creating a side menu box involves using the $savant template object and assigning the output of the box to the dropdown_contents variable.

    + +
    +<?php global $savant;
    +
    +$box_content = 'This is my side menu box';
    +
    +$savant->assign('dropdown_contents', $box_content);
    +
    +$savant->assign('title', _AT('example_maker'));
    +$savant->display('include/box.tmpl.php');
    +?>
    +
    + +

    Course tool details using sublinks.php

    + +

    When viewing the detailed course home page, it is possible to display with each module icon, the latest changes, current information for the module, or just a text description of the module. This information is contained in a module sublinks.php file. The following example sublinks.php is free form PHP that gets a list of the three current tests so they can be accessed directly from the course home page along with the link to the Tests & Survey Tool.

    + +
    +
    +if (!defined('AT_INCLUDE_PATH')) { exit; }
    +global $_base_path, $include_all, $include_one;
    +global $savant;
    +global $db;
    +
    +$tests_limit = 3;		//Maximum number of items to display on the course homepage
    +
    +$sql = "SELECT test_id, title, UNIX_TIMESTAMP(start_date) AS sd, UNIX_TIMESTAMP(end_date) AS ed FROM ".TABLE_PREFIX."tests WHERE course_id=$_SESSION[course_id] ORDER BY end_date DESC LIMIT $tests_limit";
    +$result = mysql_query($sql, $db);
    +
    +if (mysql_num_rows($result) > 0) {
    +	while ($row = mysql_fetch_assoc($result)) {
    +		if ( ($row['sd'] <= time()) && ($row['ed'] >= time() ))		
    +			$tests_list[] = array('sub_url' => $_base_path.url_rewrite('tools/test_intro.php?tid=' . $row['test_id']) , 'sub_text' => $row['title']); 
    +	}
    +	return $tests_list;	
    +} else {
    +	return 0;
    +}
    +
    +
    + +

    Student Tools

    +

    Student tools are pages linked from the home page or the main navigation of courses. A module can only implement one student tool. An instructor controls which student tools are available to a course using the Student Tools section found under Manage.

    + +

    The tool main page must be specified using the $_student_tool variable in the module.php file. The value of that variable must be the relative path to the file from the ATutor base directory (not the module directory). Example: $_student_tool = 'mods/example_maker/index.php';.

    + +

    For the tool to correctly appear its Navigation & Hierarchy must be defined correctly. If the tool is to have an instructor management section then the parent must be specified as being tools/index.php and the module must have a non-zero privilege level.

    + + +

    Group Tools

    +

    Group tools are student tools that can also be group specific. A module can only implement one group tool. An instructor controls which group tools are available to a each group during the group creation process. Only group tools that are made available to students via the home page or main navigation are available for selection.

    + +

    The group tool main page must be specified using the $_group_tool variable in the module.php file. The value of that variable must be the relative path to the file from the ATutor base directory (not the module directory). Example: $_group_tool = 'mods/example_maker/index.php';.

    + + +

    Navigation & Hierarchy

    +

    Every page in ATutor must have an entry in the global $_pages array where the key to the array is the relative path to the file from ATutor's base directory. Module pages are specified using the $_module_pages array, which are then merged into the $_pages array when the module.php file is loaded. The array supports the following attributes:

    + +
    +
    title_var
    +
    The language variable to be used with _AT().
    + +
    title
    +
    The hard-coded version of the language title. If set, overrides the usage of _AT(title_var). This version is not language independent.
    + +
    parent
    +
    The relative ATutor path to the parent page. Omit for Student Tools.
    + +
    img
    +
    The relative ATutor path to the icon to use. Only for Student Tools.
    + +
    children
    +
    An array whose values are relative ATutor paths to sub pages.
    + +
    guide
    +
    The the section of the handbook that the module page should link to. Not used for modules at this time.
    +
    + +

    For pages to appear in the instructor Manage section, their parent field must be set to tools/index.php.

    + +
    +$path = 'mods/example_maker/';
    +
    +// the student tool:
    +$_module_pages[$path.'index.php']['title_var'] = 'example_maker';
    +$_module_pages[$path.'index.php']['img']       = $path.'icon.gif';
    +$_module_pages[$path.'index.php']['children']  = array($path.'sub.php', $path.'more.php');
    +
    +    $_module_pages[$path.'sub.php']['title_var'] = 'sub_page';
    +    $_module_pages[$path.'sub.php']['parent']    = $path.'index.php';
    +
    +    $_module_pages[$path.'more.php']['title_var'] = 'more_page';
    +    $_module_pages[$path.'more.php']['parent']    = $path.'index.php';
    +
    +// the instructor page:
    +$_module_pages[$path . 'inst_index.php']['title_var'] = 'example_maker';
    +$_module_pages[$path . 'inst_index.php']['parent']    = 'tools/index.php';
    +
    + +

    Course Deletion

    +

    When a course is being deleted, or when a back-up is being restored by overriding (i.e. deleting) existing content, a module has to ensure that the content for that course is also deleted. If the module maintains course data directories, then those directories have to either be emptied or deleted. If the module uses database tables for course content, then it has to delete the appropriate entries for that course.

    + +

    The function used to delete the course content for that module must be stored in the module_delete.php file and named module_name_delete(). The delete function takes a single argument which is the ID of the course to delete.

    + +
    +<?php
    +function example_maker_delete($course) {
    +    global $db;
    +
    +    // delete directory
    +    $path = AT_CONTENT_DIR . 'example_maker/' . $course . '/';
    +    clr_dir($path);
    +
    +    // delete from database
    +    $sql = "DELETE 
    +            FROM ".TABLE_PREFIX."example_content 
    +            WHERE course_id=$course";
    +    mysql_query($sql, $db);
    +}
    +?>
    +
    + + + +

    Module News module_news.php

    +

    In ATutor 2.0 the Things Current area of My Start Page was added. It collects changing information from modules and displays it for users so it is one of the first things they see when they login, allowing them to quickly access things like recent forum messages, new news items, or recently added files for download. Things Current scans through all the module_news.php files, and outputs current activity relevant to each user, and each course. The example below is typical of the type of information that might be output to Things Current, but it can be virtually any data generated by a module. See the Forum module module_news.php for another more complex example where the latest forum messages are output.

    + +
    +<?php
    +/* Typical module_news.php file
    +* Rename the function to match the name of the module. Names of all news functions must be unique
    +* across all modules installed on a system.
    +*/
    +
    +function mymod_news() {
    +	$sql = "SELECT something FROM a table WHERE date < NOW() LIMIT 3";
    +	if($result = mysql_query($sql, $db){
    +	    while($row = mysql_fetch_assoc($result)){
    +		$news[] = $row['something'];
    +	     }
    +	}
    +	return $news;
    +}
    +
    +
    +?>
    +
    + + + + + + + + + + + +

    Custom content manipulation using module_format_content.php

    + +

    ATutor uses the include/lib/output.inc.php file to maniputae how content gets rendered when it is displayed. For example, the [media][/media] get replaced by an appropriate object element inorder to display a given media type. It is possible to extend the output.inc.php rendering of content by creating a module_format_content.php file containing PHP string replacement functions.

    + +

    For example, replace a special tag "[black]The font is black.[/black]" with html "<span style="color: black;">The font is black.</span>". Also see the module_format_content.php for the flowplayer module for a more detailed example of content manipulation.

    + +
    +<?php
    +/*******
    + * This file extends the content string manipulation. 
    + * Input parameter: global variable $_input. This variable contains the text input 
    + * of the content being displayed.
    + * Output: $_input. Please make sure to assign the manipulated string back to $_input.
    + */
    +
    +/*******
    + * Global input string. DO NOT CHANGE.
    + */
    +global $_input;
    +
    +/*******
    + * Example, replace special tag "[black][/black]" with html
    + */
    +$_input = str_replace('[black]','<span style="color: black;">',$_input);
    +$_input = str_replace('[/black]','</span>',$_input);
    +?>
    +
    + +

    Backing-Up and Restoring

    +

    It is possible for a module to include its content when a course backup is being created or restored. Backups support database tables with foreign-key constraints as well as course specific directories.

    + +

    Directories

    +

    A module can backup as many directories as it requires, all specified using the $dirs array variable.

    + +

    The example below uses the special ? token as the place holder for the course ID. When the course is backed-up, the question mark will be replaced with the correct course ID. The key to the array is the unique name of the directory to be used inside the backup archive file. The same information to create the backup is also used to restore it, so no additional details are required.

    +
    +$dirs = array();
    +$dirs['example_maker/'] = AT_CONTENT_DIR . 'example_maker/?/';
    +
    + + +

    Database Tables

    +

    There are two parts to backing-up and restoring a module's database tables. First, the SQL queries must be specified using the $sql array variable and then the restore functions must convert the rows so that they can be inserted into the database tables.

    + +

    The example below uses the special ? token as the place holder for the course ID. When the course is backed-up the question-mark will be replaced with the correct course ID. The key to the array is the unique name of the CSV file to save in the backup archive, without the extension. The SQL query itself must only select the fields that will be backed up. If there are foreign key constraints to preserve then the key will have to be retrieved as well so that it can be used when restoring the tables.

    + +
    +$sql = array();
    +$sql['example']  = 'SELECT title FROM '.TABLE_PREFIX.'example WHERE course_id=?';
    +
    + +

    For each key in the $sql array there must be a function with the same name, but suffixed with _convert. The tbl_name_convert() function must return the newly transformed row with respect to the version of ATutor that was used to generate the CSV file. The function accepts the following arguments:

    + +
    +
    $row
    +
    An array which represents a single row in the CSV file.
    + +
    $course_id
    +
    The course ID which this content should be associated with.
    + +
    $table_id_map
    +
    An associative array representing previously restored tables and their new keys. Used to preserve foreign key constraints.
    + +
    $version
    +
    The version of ATutor that was used to generate this file.
    +
    + +
    +function example_convert($row, $course_id, $table_id_map, $version) {
    +    $new_row = array();
    +    $new_row[0]  = 0; // auto-increment field
    +    $new_row[1]  = $course_id;
    +    $new_row[2]  = $row[1]; // the title
    +    if (version_compare($version, '1.5.2', '<')) {
    +        // this field did not exist prior to 1.5.2
    +        $new_row[3] = '';
    +    } else {
    +        $new_row[3] = $row[2];
    +    }
    +
    +    return $new_row;
    +}
    +
    + +

    Running Cron/Scheduling

    +

    The module_cron.php file can be used to run module related functions at specified intervals. The following example doesn't do much of anything..., but you get the idea. If cron has been enabled in ATutor, all the module_cron.php files from each module are run at the interval specified when the Cron is setup. See the Cron Setup sections in the Administrator Handbook, and in ATutor in the Administrator's System Preferences.

    + +
    +<?php
    +/*******
    + * this function named [module_name]_cron is run by the global cron script at the module's specified
    + * interval.
    + */
    +
    +function hello_world_cron() {
    +	global $db;
    +	debug('yay i am running!');
    +}
    +
    +?>
    +
    + + \ No newline at end of file diff --git a/documentation/developer/styles.css b/documentation/developer/styles.css new file mode 100644 index 000000000..146dd46b8 --- /dev/null +++ b/documentation/developer/styles.css @@ -0,0 +1,137 @@ +pre { + font-family: trebuchet ms, Arial, sans-serif; +} + +body{ + background-color: #FFF; + font-family: "Trebuchet MS",Verdana,Arial,sans-serif; +} +h1,h2,h3,p, table, ul { + margin: 0 10px +} +h1 { + color: #FFF; + border-bottom: 1px dashed #FFF; + margin-left: -5px; + padding-left: 15px; + margin-right: -5px; + padding-right: 15px; +} +h2{color: #FFF; + border-bottom: 1px dashed #FFF; + margin-left: -5px; + padding-left: 15px; + margin-right: -5px; + padding-right: 15px; +} +h3{color: #f0f0f0} +p{padding-bottom:1em} +h2{padding-top: 0.3em} + +/* for the curves */ +div.nifty { margin: 10px 2%; background: #9BD1FA; } +b.rtop, b.rbottom{display:block;background: #FFF} +b.rtop b, b.rbottom b{display:block;height: 1px; overflow: hidden; background: #9BD1FA} +b.r1{margin: 0 5px} +b.r2{margin: 0 3px} +b.r3{margin: 0 2px} +b.rtop b.r4, b.rbottom b.r4{margin: 0 1px;height: 2px} + + +a { + text-decoration: none; + border-bottom: 1px solid white; + color: white; + font-weight: bold; +} +a:hover { + border-bottom: 0px; +} +td,th { + font-size: 85%; +} + +kbd { + padding: 0px 1px 0px 1px; + border-width: 1px 2px 2px 1px; + border-style: solid; + border-color: #edd #baa #baa #eed; + white-space: pre; +} + +code { + font-family: "Trebuchet MS",Verdana,Arial,sans-serif; + background-color: #efefef; + padding: 0px 4px 0px 4px; + border-width: 1px 1px 1px 1px; + border-style: solid; + border-color: #edd #baa #baa #eed; +} + + +div#toc { + color: #f0f0f0; + padding-bottom: 15px; +} + +div#toc ul { + list-style: none; +} +div#toc li { + padding-top: 2px; + padding-bottom: 0px; +} + +ol { + margin-top: 0px; +} + +ol li { + padding-bottom: 3px; +} + +dl { + margin: 0 10px +} +dl dd { + padding-top: 0px; + padding-left: 5px; + margin-left: 5%; + border-left: 1px solid #f0f0f0; + margin-bottom: 10px; +} + +acronym { + cursor: help; +} + +a[href*="http"] { + padding-right: 8px; + background-image: url('link-out.gif'); + background-repeat: no-repeat; + background-position: right 4px; + margin-right: 2px; +} + +div#nav-links { + margin-left: 20px; + margin-right: 20px; + margin-bottom: 20px; +} + +div#nav-links a { + color: black; + text-decoration: none; + border-bottom: 1px solid; +} + +pre { + font-family: Courier, monospace; + background-color: #EEEEFF; + padding: 5px; + margin-left: 20px; + color:#761596; + margin-top: 0px; + width: 50%; + font-size: smaller; +} \ No newline at end of file diff --git a/documentation/developer/themes.html b/documentation/developer/themes.html new file mode 100644 index 000000000..be4b8c24c --- /dev/null +++ b/documentation/developer/themes.html @@ -0,0 +1,357 @@ + + + + ATutor Theme Designer Documentation + + + + + + + +
    +

    Theme Designer Documentation

    + + +

    + +

    Introduction

    +

    An ATutor theme is a set of template files with images and a stylesheet that change the overall look and feel of an ATutor installation. An installation may have one or more themes installed at one time; a user is given the ability of selecting a single theme to be used while they are logged-in.

    + +

    ATutor themes are based on Savant2 templating system. Visit the Savant site for full documentation: + Savant

    + + +

    Installing a New Theme

    +

    Importing a Theme (automatic install)

    +

    Access the Theme Manager area of ATutor in the Administrator tools. Before importing a theme, the ATutor theme directory will need to be writable by the Web server. If the directory is not writable, you will see a message explaining how to make the directory writable, in place of the theme import tool. Note that themes imported this way will be owned by the Web server user, thus once imported, you will not be able to modify the files in the theme.

    +

    FTP a Theme (automatic install)

    +

    If you are planning to modify the theme to create a custom version, you will want to install the theme using the FTP method.

    + +

    To avoid loosing the ability to edit theme files, you should upload the theme's zip file into the themes/ directory using an FTP application, and unzipping the file yourself once it has been uploaded to the server. You may be able to unzip the files using your FTP client, or through applications like CPanel, or other common ISP provided site management tools. Once the theme is uploaded and unzipped, manually add an entry to the themes table in the ATutor database to make the theme available to the ATutor Theme Manager. If you are using a tool like phpMyAdmin to access your database, you can create a new row in the themes table, and enter the information manually.

    + +

    Or, run an SQL statement like the following.

    + + +
    +	INSERT INTO `AT_themes` 
    +	(`title`, `version`, `dir_name`, `last_updated`, `extra_info`, `status`) 
    +	VALUES
    +	('My Theme Name', '1.6.1', 'my_theme_dir', NOW(), 'This is a customized theme based on the default theme.', 1);
    +	
    + +

    Be sure the last value for "status" is set to "1" to begin with, which means the theme is installed and enabled, but is not the default theme. Once your theme has been finalized, this value can be changed to "2" to make it the system's default theme. This value should be set using the Theme Manager.

    + +

    File & Directory Structure

    + +

    All theme specific files are located in /themes/[theme_name]/, where `[theme_name]` is the directory name (dir_name) of the theme. The `[theme_name] need not be the exact name of the theme (i.e. a theme titled `Blueberry Cheesecake` may exist in a directory called `bb_cc` etc.). The actual name of the theme is specified in that theme's configuration file.

    + +

    The Default theme contains the following files:

    + +
    +# confirmmessage.tmpl.php
    +# content.tmpl.php
    +# directory.tmpl.php
    +# errormessage.tmpl.php
    +# feedbackmessage.tmpl.php
    +# forms.css
    +# images/
    +#- arrow_ltr.gif
    +#- back.gif
    +#- error-large.gif
    +#- guide.gif
    +#- instructor.gif
    +#- next.gif
    +#- pen.gif
    +#- pen2.gif
    +#- pencils.jpg
    +#- previous.gif
    +#- resume.gif
    +#- side_arrow.gif
    +#- sort.gif
    +#- user-star.gif
    +#- user.gif
    +# include/
    +#- box.tmpl.php
    +#- fm_footer.tmpl.php
    +#- fm_header.tmpl.php
    +#- footer.tmpl.php
    +#- header.tmpl.php
    +# index.tmpl.php
    +# infomessage.tmpl.php
    +# login.tmpl.php
    +# password_change.tmpl.php
    +# password_reminder.tmpl.php
    +# password_reminder_feedback.tmpl.php
    +# print.css
    +# profile.tmpl.php
    +# readme.txt
    +# registration.tmpl.php
    +# rtl.css
    +# screenshot.gif
    +# styles.css
    +# theme.cfg.php
    +# theme_info.xml
    +# test_questions/
    +#- likert.tmpl.php
    +#- likert_qti_1p2.tmpl.php
    +#- likert_qti_2p1.tmpl.php
    +#- likert_result.tmpl.php
    +#- likert_stats.tmpl.php
    +#- long.tmpl.php
    +#- long_qti_1p2.tmpl.php
    +#- long_qti_2p1.tmpl.php
    +#- long_result.tmpl.php
    +#- long_stats.tmpl.php
    +#- manifest_qti_1p2.tmpl.php
    +#- manifest_qti_2p1.tmpl.php
    +#- matching.tmpl.php
    +#- matching_qti_1p2.tmpl.php
    +#- matching_qti_2p1.tmpl.php
    +#- matching_result.tmpl.php
    +#- matching_stats.tmpl.php
    +#- matchingdd.tmpl.php
    +#- matchingdd_qti_1p2.tmpl.php
    +#- matchingdd_qti_2p1.tmpl.php
    +#- matchingdd_result.tmpl.php
    +#- matchingdd_stats.tmpl.php
    +#- multianswer.tmpl.php
    +#- multianswer_qti_1p2.tmpl.php
    +#- multianswer_qti_2p1.tmpl.php
    +#- multianswer_result.tmpl.php
    +#- multianswer_stats.tmpl.php
    +#- multichoice.tmpl.php
    +#- multichoice_qti_1p2.tmpl.php
    +#- multichoice_qti_2p1.tmpl.php
    +#- multichoice_result.tmpl.php
    +#- multichoice_stats.tmpl.php
    +#- ordering.tmpl.php
    +#- ordering_qti_1p2.tmpl.php
    +#- ordering_qti_2p1.tmpl.php
    +#- ordering_result.tmpl.php
    +#- ordering_stats.tmpl.php
    +#- truefalse.tmpl.php
    +#- truefalse_qti_1p2.tmpl.php
    +#- truefalse_qti_2p1.tmpl.php
    +#- truefalse_result.tmpl.php
    +#- truefalse_stats.tmpl.php
    +#- wrapper.tmpl.php
    +# users/
    +#- browse.tmpl.php
    +#- email_change.tmpl.php
    +#- index.tmpl.php
    +#- password_change.tmpl.php
    +#- preferences.tmpl.php
    +#- profile.tmpl.php
    +# warningmessage.tmpl.php
    +
    + + +

    Theme Configuration File - theme.cfg.php

    +

    Each theme must have a configuration file named theme.cfg.php. If the theme.cfg.php file cannot be found in the theme's directory then the theme will not be made available to use. The fields in the theme.cfg.php file are documented in the file; they describe such things as the name of the theme, its author, and the version of ATutor the theme should be installed with.

    + + +

    Creating a Theme

    +

    Here are a couple ways to create a new theme:

    +
      +
    1. One way to create a theme, and for many the preferred way, is to copy the Primary Theme Files from the default theme into a new theme directory. You can copy these files from other themes that may look closer to the style you want to create, but the default theme will always be most up to date. In many cases it is only necessary to modify the stylesheet (styles.css) to adjust colours, fonts, and some layout styles etc, to create a new theme. A theme can be made up of a single stylesheet file and a theme.cfg.php file. Most other templates used to layout various tool screens, table displays, test items etc. you probably won't need to include in your theme. If templates are not found in the new theme, they will be inherited from the default theme.

      + +In some cases you may want to make more significant changes to the layout than can be done with stylesheet adjustments alone, so in such a case you can copy the header and footer files from the default theme into the new theme's /[theme_name]/include/ directory, then edit those files. You will probably need to create the include/ directory first.

      + +These files are generally all that are required to create a new theme:

      + +Primary Theme Files
      +
        +
      1. styles.css
      2. +
      3. theme.cfg.php
      4. +
      5. theme_info.xml
      6. +
      7. screenshot.jpg
      8. +
      9. include/ +
          +
        • header.tmpl.php
        • +
        • footer.tmpl.php
        • +
        +
      10. +


      +
    2. + +
    3. If you have limited access to the ATutor files on the server, export one of the themes displayed in the administrator's Theme Manager. The theme is exported onto your computer as a '.zip' file. Unzip the file, then make changes to theme files it contains. Be sure to edit the theme_info.xml file to give your theme a new name. Once you've made your changes, zip the files together again and import the file back into ATutor with the Theme Manager. The name of the zip file you create should closely represent the name you want to give your theme, substituting '_' for spaces.
    4. +
    + +

    The theme files described above are basically HTML files with some PHP mixed in. You do not need to know much about PHP to create a theme; most of the syntax is straight forward and uses basic if-statements to control if some feature displays or not, and foreach-loops to display repetitive or tabular information. For additional information on PHP see php.net.

    + +

    The theme files contain variables which look like $this->[something] mixed in among HTML markup. Those variables get set by ATutor and may contain simple text or in some cases arrays of values.

    + +

    You will probably want to add the developing theme to the ATutor database "themes" table while you are working on it, so you can see the changes you're making as they occur. The following are the fields in the "themes" table.

    + +

    The fields in the themes table are as follows:

    +
    +`title` (the name of the theme, any text string)
    +`version` (the version of ATutor the theme is intended for)
    +`dir_name` (the name of the directory the theme will be located in, alphabetic string, no spaces)
    +`type` (the type of the theme, "Desktop" or "Mobile)
    +`last_updated` (The last date the theme was modified, use "NOW()")
    +`extra_info` (A description of the theme etc. )
    +`status` (theme's enable status, 0:disabled, 1:enabled, 2:set as default theme)
    +
    +

    Below is an example of the SQL used to create an entry in the themes table for a new theme. You can also use a tool like phpMyAdmin to add a new entry to the "themes" table. Be sure to adjust the "AT_" prefix if you are using the SQL statement, to match the prefix you are using in your ATutor installation. Be careful when setting the value for the status field. If you set the status to "set as default" ( 2 ) and your theme is broken, you may find you can no longer access ATutor.

    + +
    +INSERT INTO `AT_themes` 
    +     (`title`, `version`, `dir_name`, `type`, `last_updated`, `extra_info`, `status`) 
    +VALUES
    +     ('Blueberry Cheesecake', '1.6.1', 'bb_cc', `Desktop`, NOW(), 'This is a blue theme.', 1);
    +
    + +

    ATutor detects the type of the devices where the requests are from. If the request is from a desktop computer, the default desktop theme is automatically applied. Otherwise, if the request is from a mobile device, the default mobile is applied.

    + + +

    Theme Variables

    +

    The following is a complete list of variables that can be used in themes as of 1.6.2. These variables contain various data from the ATutor application, that when inserted into a theme template, display some value, or values. The most up to date list of theme variables can be found in the include/header.tmpl.php file of the default theme for the version of ATutor you are using. See the ATutor Developer Documentation for details about using the debug() function to print out a list of the values to help while you are designing a theme.

    + +
    +* $this->lang_code			the ISO language code
    +* SITE_NAME				the site name from the config file
    +* $this->page_title		the name of this page to use in the <title>
    +* $this->lang_charset		the ISO language character set
    +* $this->content_base_href	the <base href> to use for this page
    +* $this->base_path			the absolute path to this atutor installation
    +* $this->rtl_css			if set, the path to the RTL style sheet
    +* $this->banner_style{-}deprecated-
    +* $this->theme				the directory name of the current theme
    +* $this->base_href			the full url to this atutor installation
    +* $this->onload			javascript onload() calls
    +* $this->img				the absolute path to this theme's images/ directory
    +* $this->sequence_links	associative array of 'previous', 'next', and/or 'resume' links
    +* $this->path				associative array of path to this page: aka bread crumbs
    +* $this->rel_url			the relative url from the installation root to this page
    +* $this->nav_courses		associative array of this user's enrolled courses
    +* $this->section_title		the title of this section (course, public, admin, my start page)
    +* $this->top_level_pages	associative array of the top level navigation
    +* $this->current_top_level_page	the full path to the current top level page with file name
    +* $this->sub_level_pages	associate array of sub level navigation
    +* $this->back_to_page		ithe path and file name to the part of this page (if parent is not a top level nav)
    +* $this->current_sub_level_page	the full path to the current sub level page with file name
    +* $this->guide				the full path and file name to the guide page
    +* $this->icon the path to a course icon *(new from 1.6)*
    +* ======================================
    +* top_level_pages           array(array('url', 'title'))     the top level pages. ATutor default creates tabs.
    +* section_title string the name of the current section. either name of the course, administration, my start page, etc.
    +* page_title                string                           the title of the current page.
    +* path                      array(array('url', 'title'))     the path to the current page.
    +* back_to_page              array('url', 'title')     the link back to the part of the current page, if needed.
    +* current_top_level_page    string                    full url to the current top level page in "top_leve_pages"
    +* current_sub_level_page    string                    full url to the current sub level page in the "sub_level_pages"
    +* sub_level_pages           array(array('url', 'title'))     the sub level pages.
    +* is_mobile_device          true or false                    the request is from a mobile device or a desktop device
    +* mobile_device_type        One of the values: ipod, blackberry, android, unknown
    +
    + +

    Testing A Theme

    +

    To make ATutor use your theme, login as an ATutor user, go to "Preferences" page, "ATutor Settings" tab. Depending on the theme type, your theme will be displayed either in "Desktop Theme" or in "Mobile Theme". Find and select your theme name and save. ATutor will automatically switch to the saved theme. Note that if your theme is defined as a mobile theme, it will only be applied when you access ATutor via a mobile device. Vice versas for destop theme.

    + + +

    Updating an Old Theme

    + +

    Occasionally new features are added to themes, and adjustments are needed to allow older themes to work with newer versions of ATutor, or to add new functionality to themes that was not available in older versions. For a list of theme changes, see the Theme Change Log.

    + +

    NOTE: There is not a direct upgrade path from the 1.6 series themes to the 2.0 series themes. We suggest you start as you would creating a new theme, copying the files from the 2.0 default theme, then copy the stylesheet from the old theme into the stylesheet for the new theme, as well as copying any images from the old to the new.

    + +

    Sharing Themes

    +

    Once you've perfected your theme, consider contributing it to the ATutor community so others can use it. Visit the Themes section of ATutor.ca to contribute a theme. Login with your atutor.ca user account, then submit your theme to receive credit for your work. Contributing a theme will automatically raise your ATutor contributor status to the Bronze level. Or, if you're not interested in being credited, just send the theme to the ATutor team (info//AT//atutor.ca), or post it to one of the forums, and the ATutor team will submit the theme for you.

    + +

    Submit a Theme

    + +
    + + \ No newline at end of file diff --git a/documentation/general/browse_courses.php b/documentation/general/browse_courses.php new file mode 100644 index 000000000..7b8c7a475 --- /dev/null +++ b/documentation/general/browse_courses.php @@ -0,0 +1,9 @@ + + +

    Browse Courses

    + +

    The Browse Courses page lists all courses presently available on the ATutor system.

    + +

    If a course is Public, it may be accessed without logging in first. Protected and Private courses require that you be logged in. Private courses are available only to those who have been approved and enrolled in the course.

    + + \ No newline at end of file diff --git a/documentation/general/create_course.php b/documentation/general/create_course.php new file mode 100644 index 000000000..1f5e87f08 --- /dev/null +++ b/documentation/general/create_course.php @@ -0,0 +1,7 @@ + + +

    Create Course

    + +

    Only Instructors may create courses, though if enabled, students can request instructor accounts by selecting the Create Course link. View the Instructor Documentation on creating courses.

    + + \ No newline at end of file diff --git a/documentation/general/en/index.php b/documentation/general/en/index.php new file mode 100644 index 000000000..85abfdf3b --- /dev/null +++ b/documentation/general/en/index.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/documentation/general/export_content.php b/documentation/general/export_content.php new file mode 100644 index 000000000..4cba32284 --- /dev/null +++ b/documentation/general/export_content.php @@ -0,0 +1,9 @@ + + +

    Export Content

    + +

    The Export Content feature creates a "Content Package" that can be downloaded and viewed offline in the viewer included with each package. If this feature is turned on for a course, it may be accessed through a Student Tool icon on the home page, and/or a link in the main navigation. Choose which section you wish to download as a content package the use the Export button. Export Content is also linked from top level content pages or all content pages (depending on what the instructor has set) in the Shortcuts box. Using this link will package the current page and all of its sub pages into a single "zip" file, and prompt the user to download the file.

    + +

    The downloaded file can be unpacked with a common archiving application (e.g. WinZip, PKZip, Unzip). Unzip the file into an empty directory then open the index.html file into a Web browser.

    + + diff --git a/documentation/general/file_storage.php b/documentation/general/file_storage.php new file mode 100644 index 000000000..3f4c2bca1 --- /dev/null +++ b/documentation/general/file_storage.php @@ -0,0 +1,28 @@ + + +

    File Storage

    + +

    Students, assistants, and instructors can access a personal file management tool using the File Storage area, if enabled for a course. Workspaces can be selected for storing files, their presence depending on one's access rights: +

      +
    • Course Files - The default workspace. Managed by the instructor, or assistants with file storage privileges, course resource files are made available for download by course members.
    • +
    • My Files - Private files only the user can access and manage.
    • +
    • Groups - Shared files managed by group members, and accessible to instructors and assistants with group privileges.
    • +
    • Assignments - Students can submit assignments from these workspaces, and instructors or assistants with assignment privileges can manage submissions from the File Storage utiilty. To submit an assignment, upload a file to MyFiles, or to a group workspace, then select the file you wish to hand in and press the Handin button to view a list is assignments. Choose the assignment for which you are hinding in the file, then press Submit
    • + +
    + +To move between workspaces, select them from the dropdown menu and use the Go! button. To view a file in the File Storage area, download and open it locally on your own computer. +

    +

    Create Folders and Adding Files

    +

    To organize files, folders can be created using the Create Folder feature at the top right of the file area. To upload a new file, with an optional description of its contents, use the New File feature at the top left. The file will be uploaded to the currently opened directory.

    + +

    Managing Files

    +

    It is also possible to Download files to your hard drive, Hand In a file for an assignment, Edit file details and rename folders, Move files around within a workspace, as well as Delete files and folders. Depending on the type of workspace, the buttons that appear will vary.

    + +

    File Revisions

    + +

    If enabled, file revisions can be kept, so a history of the document or file is available. Uploading a file of the same name as one that exists, will create a second version of the file, and so on. With each revision comments can be added, to summarize the changes from authors, and to perhaps collect feedback from reviewers.

    + + + + \ No newline at end of file diff --git a/documentation/general/fr/index.php b/documentation/general/fr/index.php new file mode 100644 index 000000000..83b4909db --- /dev/null +++ b/documentation/general/fr/index.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/documentation/general/inbox.php b/documentation/general/inbox.php new file mode 100644 index 000000000..9b14d94cd --- /dev/null +++ b/documentation/general/inbox.php @@ -0,0 +1,10 @@ + + +

    Inbox

    +

    The Inbox is used for privately messaging other users in your courses. Inbox messages appear in a table, with new messages flagged. Selecting a message will display its contents at the top of the screen.

    + +

    In the sub-navigation there is a link to send a message. Choose the recipient, enter a subject and a message, and use the Send button.

    + +

    Note: use the Inbox Notification preference to receive emails when a new Inbox message is received.

    + + \ No newline at end of file diff --git a/documentation/general/index.php b/documentation/general/index.php new file mode 100644 index 000000000..04121b3aa --- /dev/null +++ b/documentation/general/index.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/documentation/general/inside_course.php b/documentation/general/inside_course.php new file mode 100644 index 000000000..bd092c82b --- /dev/null +++ b/documentation/general/inside_course.php @@ -0,0 +1,9 @@ + + +

    Course Features

    + +

    After a student has entered into a course, he/she is presented with the course Home page. The Home page may contain a course banner, links to Student Tool, and course announcements.

    + +

    A few of the course features are explained here, as they may be a little tricky for new users. Other features are fairly straight forward and should be intuitive to use. Look for the handbook link while using a tool, to open its help page directly.

    + + \ No newline at end of file diff --git a/documentation/general/introduction.php b/documentation/general/introduction.php new file mode 100644 index 000000000..243f1f5d9 --- /dev/null +++ b/documentation/general/introduction.php @@ -0,0 +1,7 @@ + + +

    Introduction

    +

    Welcome to the ATutor General User documentation. The information found here is applicable to both instructors and students. Also see the the Instructor Documentation for details of other tools that might be used to author content while in a group environment.

    + + + \ No newline at end of file diff --git a/documentation/general/login.php b/documentation/general/login.php new file mode 100644 index 000000000..a92dcb25c --- /dev/null +++ b/documentation/general/login.php @@ -0,0 +1,7 @@ + + +

    Login

    + +

    A user may login to the system with the Login Name or Email address, and the Password entered during registration. Logging in gives users access to Protected courses, lets them enroll, and lets them participate in courses.

    + + diff --git a/documentation/general/my_contacts.php b/documentation/general/my_contacts.php new file mode 100644 index 000000000..6e3817384 --- /dev/null +++ b/documentation/general/my_contacts.php @@ -0,0 +1,9 @@ + + +

    My Contacts

    + +
    +
    My Contacts
    +
    This is a list of people in your social network. You can find others on the network by using the Search People tool, and you can request that you and they become contacts. When you find a person, click on the green plus sign (plus) to make your request. To remove a person in your Contacts, click on the red X ( ex). View a contact's profile from My Contacts by clicking on their thumbnail photo or their name.
    +
    + diff --git a/documentation/general/my_courses.php b/documentation/general/my_courses.php new file mode 100644 index 000000000..539d70796 --- /dev/null +++ b/documentation/general/my_courses.php @@ -0,0 +1,8 @@ + + +

    My Courses

    +

    Courses that the user is in enrolled in, or courses that are pending enrollment, are listed on the My Courses page.

    + +

    To enroll into a public or protected course, follow the Browse Courses link and locate it, then enter the course and use the Enroll Me link located beside the course title. If the course is private, enrollment must be requested first. Admission into the course will be allowed once the instructor has approved the request.

    + + \ No newline at end of file diff --git a/documentation/general/my_gadgets.php b/documentation/general/my_gadgets.php new file mode 100644 index 000000000..05914c370 --- /dev/null +++ b/documentation/general/my_gadgets.php @@ -0,0 +1,20 @@ + + +

    Gadgets

    + +

    Gadgets are applications you can add to your Social Networking environment that provide a whole range of potential networking functionality. They are much like Apps on an IPhone. You can find many gadgets by searching around the Internet, They can be added by copying the URL to the Gadget's XML file into the "Add gadget by URL" field on the Add Gadgets screen of ATutor Social.

    + +

    You can use a search engine to search for variations of the terms "open social gadgets" to find many more. Once a gadget has been added to ATutor, it becomes available for everyone on that ATutor server to add to their own networking environement by following Show Available Gadgets

    + +

    Use Find Gadgets to search through the Google gadget repository. When you find a gaadget you want to add, click on "Add to your webpage" then "Get the Code." In the code that gets generated, look for the URL of an XML file, then copy that URL into the "Add gadget by URL" field, then click the "Add Gadget" button.

    + +

    Sample Gadgets

    +

    To install a gadget, copy the path to the gadget's XML file into the "Add gadget by URL" field:

    + +
      +
    • OpenSocialDev App: - http://osda.appspot.com/gadget/osda-igoogle.xml
    • +
    • Babylon Dictionary: - http://www.labpixies.com/campaigns/babylon/babylon.xml
    • +
    • Todo List: - http://www.labpixies.com/campaigns/todo/todo.xml
    • +
    + + diff --git a/documentation/general/my_groups.php b/documentation/general/my_groups.php new file mode 100644 index 000000000..e2be03e57 --- /dev/null +++ b/documentation/general/my_groups.php @@ -0,0 +1,7 @@ + + +

    Network Groups

    + +

    Network Groups can be created by anyone, for any purpose. They are a place to post information and discuss common interests. You may search for groups and join them. Once you have created or joined a group, you can Invite others to join, and view a list of people in the group. If you created the group, you also have the option to disband it.

    + + diff --git a/documentation/general/my_network.php b/documentation/general/my_network.php new file mode 100644 index 000000000..9bc6b787a --- /dev/null +++ b/documentation/general/my_network.php @@ -0,0 +1,24 @@ + + +

    My Network

    + + +

    ATutor Social is a social networking environment that allows ATutor users to develop a network of contacts, create and participate in social groups, and develop a social profile. It is based on the Google Open Social Standard. Many Open Social applications, or gadgets as they are called, are available around the Internet and can be linked into your social networking environment to customize it to your liking.

    + +

    Basic Social Networking Features

    + +
    +
    Network Activity
    +
    This is an ongoing report of what others in your network are doing. When one of your contacts posts a message, joins a group, adds a gadget, or updates their profile for instance, you will know about it (if they have not turn this off in privacy settings).
    + +
    My Contacts
    +
    This is a list of people in your social network. You can find others on the network by using the Search People tool, and you can request that you and they become contacts. When you find a person, click on the green plus sign (plus) to make your request. To remove a person in your Contacts, click on the red X ( ex). View a contact's profile from My Contacts by clicking on their thumbnail photo or their name.
    + +
    Once people have been added to your contacts, other suggested contacts will be listed under People you Might Know,” those being people in your Contacts' Networks.
    + +
    My Network Groups
    +
    These are social groups in ATutor Social that you belong to. You can create new groups, search for and join groups that interest you, and send requests to others to join a group.
    +
    + + + diff --git a/documentation/general/my_profile.php b/documentation/general/my_profile.php new file mode 100644 index 000000000..00298abe2 --- /dev/null +++ b/documentation/general/my_profile.php @@ -0,0 +1,11 @@ + + +

    Network Profile

    + +

    Your Network Profile contains information about you that others might like to know, such as your work experience, education, perhaps your personal interests, or maybe your personal Web site. Click on the edit icon (ex) while viewing your Network Profile to add and make changes to your personal information. Also see Settings for details about controlling what parts of your profile others can see.

    + +

    Also view Activities for a list of things you have recently done in your social network.

    + +

    View your Contacts' profiles while viewing your own.

    + + diff --git a/documentation/general/my_settings.php b/documentation/general/my_settings.php new file mode 100644 index 000000000..bb9833ca8 --- /dev/null +++ b/documentation/general/my_settings.php @@ -0,0 +1,37 @@ + + +

    Social Settings

    + +

    Privacy Setting

    + +

    Privacy settings are used to control who can see which parts of your profile: +

    Profile Visability

    +
    +
    Basic Profile
    +
    Your basic ATutor Profile information. If hidden, it becomes unavailable to view through your network profile, but is still available for classmates in a course to view.
    +
    Detailed Profile
    +
    All other information in your social profile that is not part of your Basic Profile
    +
    Activities
    +
    Choose these settings to hide you network activity from others. This is the information that appears in the Network Activity area of your networking tools.
    +
    My Contacts
    +
    Choose from these settings to limit who can see people in your Contacts list.
    +
    Education
    +
    Hide the education information you add to your detailed profile. You may turn on your Detailed Profile, then hide this section from it.
    +
    Position
    +
    Hide the position information you add to your detailed profile. You may turn on your Detailed Profile, then hide this section from it.
    +
    + +

    Who sees your profile.

    +
    +
    World Network
    +
    Allow anyone, using any ATutor Social system, to view (coming soon).
    +
    My Contacts
    +
    Any anyone in your contact list to view.
    +
    Contacts of Your Contacts
    +
    Allow contacts of anyone in your contacts list to view.
    +
    Local Network
    +
    Allow anyone on your local ATutor Social system to view.
    +
    Groups
    +
    Allow anyone who is a member of groups you belong to, to view.
    +
    + diff --git a/documentation/general/my_start_page.php b/documentation/general/my_start_page.php new file mode 100644 index 000000000..40251bbb9 --- /dev/null +++ b/documentation/general/my_start_page.php @@ -0,0 +1,7 @@ + + +

    My Start Page

    + +

    My Start Page is a personal area displayed after logging in. The My Courses section lists the courses that the user either teaches, or is enrolled in. From here one can also create a new course, or browse through courses. There is also a Profile section for editing personal details (including changing a password or an email address), and a Preferences section for editing some system preferences such as the way information is displayed and the preferred theme.

    + + \ No newline at end of file diff --git a/documentation/general/pa_albums.php b/documentation/general/pa_albums.php new file mode 100644 index 000000000..1acf6ffaf --- /dev/null +++ b/documentation/general/pa_albums.php @@ -0,0 +1,21 @@ + + +

    Albums

    + +

    An Album is a collection of photos. Clicking on an album thumbnail image will display the photos in that album.

    + +

    Uploading a photo

    +

    To upload a photo, click on the "Open Upload Manager" button. A new section will appear below where you click "Browse" to upload any photos in the format of gif/jpg/png from your local hard drive. After selecting a photo from the File Upload, ATutor will automatically resize the image and append its details at the top of the pending list that appears after selecting an image. Details include file name, file size, a thumbnail of the image, and a delete button. The delete button allows you to remove the pending photos anytime during the upload process. At the right bottom of this section, you will see a "Memory Usage: x.xx/ 8 MB" message, which tells you how much memory you have used. Once the memory limit is reached, an error message will display in the pending list. Finally, click "Upload" to finish the upload process. The photos should now be listed in the album.

    + +

    Search

    +

    The search tab is displayed at the top left of the Photo Gallery home page. Type in any text to search for relevant albums and photos. The search will return matches found in the photo album's description or location; and photo's description or alternative text. Be sure when adding photos to your albums, to fill in this information so people can find you photos.

    + +

    Edit Photos

    +

    Each photo can have a description and alternative text. Description (a.k.a. caption on some other sites) will be shown at the bottom of the photo, describing what this photo is. Alternative text acts as a replacement for the image, whenever the image does not load, or can not be seen for any reason. This information should summerize the essence of the image rather than describing ever detail. Alternative text should be used to comply the accessibility rules. Below each of the photo thumbnails, their is an option to select that photo as the album cover, or to remove it by checking the "delete" button. When you are done editing, click "Save".

    + +

    Organize Photos

    +

    Photos can be rearranged within each album by simply dragging the photo with your mouse, or using [Ctrl] + [Up/Down/Left/Right] arrow keys on your keyboard. The rearranged order is saved automatically. Click on "Organize Photos" to rearrange them.

    + +

    Also view Comments for details on posting comments.

    + + diff --git a/documentation/general/pa_comments.php b/documentation/general/pa_comments.php new file mode 100644 index 000000000..4151ac261 --- /dev/null +++ b/documentation/general/pa_comments.php @@ -0,0 +1,7 @@ + + +

    Comments

    + +

    Users can post comments to an album or a photo. The author of a comment can edit it by clicking on the comment itself, the comment will turn into an editable field. When done editing, press Enter to save the changes. Note: The owner of the album can edit and delete any comments within the album.

    + + diff --git a/documentation/general/pa_index.php b/documentation/general/pa_index.php new file mode 100644 index 000000000..54108bf9d --- /dev/null +++ b/documentation/general/pa_index.php @@ -0,0 +1,27 @@ + + +

    Photo Gallery

    + +

    A photo gallery is designed with accessibility in mind, allowing members to share course related photos and personal photos.

    + +

    Basic Photo Album Features

    + +
    +
    Photo Gallery
    +
    This is where your personal albums can be found. Both Private Albums and Shared Albums will be listed here.
    + +
    Profile Album
    +
    This is an album where you keep your profile pictures. Photos collected here can be linked into your ATutpr profile page, or into your ATutor Social profile.
    + +
    Course Albums
    +
    This is where you keep all the course albums. Anyone enrolled in the course is allowed to upload photos to a course album; students are allowed to edit and delete the photos they have uploaded. Instructors have the privilege to edit and delete any photos within their course albums.
    + +
    Shared Albums
    +
    Here all albums will be listed that have the "Shared" permission set. All users have permission to browse and comment on photos in shared albums, but are not allowed to upload or edit photos.
    + +
    Create Album
    +
    To create an album, specify the album name and its permissions. Location (geographically), and description are optional. If you are an instructor, you will have an extra option of "Album Type", which allows you to create "My Album" or "Course Album". Under "Album Permission", "Private" means this album is not accessible by any other users except yourself, "Shared" means this album will be displayed under the "Shared Albums" tab, and will be accessible by ALL users.
    +
    + + + diff --git a/documentation/general/pa_photo.php b/documentation/general/pa_photo.php new file mode 100644 index 000000000..052cd8ef3 --- /dev/null +++ b/documentation/general/pa_photo.php @@ -0,0 +1,9 @@ + + +

    Photo

    + +

    This page display a photo and its description. You can click the "Previous" and "Next" arrows to move through the photos in the album you are viewing. Owners of a photo can also edit the description by simply clicking on the description itself, or via the "Edit Photo" link.

    + +

    Also view Comments for details on posting comments.

    + + diff --git a/documentation/general/packages.php b/documentation/general/packages.php new file mode 100644 index 000000000..511add85a --- /dev/null +++ b/documentation/general/packages.php @@ -0,0 +1,7 @@ + + +

    Packages

    + +

    If instructors have included SCORM compliant Sharable Content Objects (SCOs), they will be avilable for viewing using the Packages tool. Note that the SCORM Run Time Environment (RTE) requires the Java JRE 1.5 to function properly, as well as LiveConnect, which is enabled by default in the JRE 1.5. Download an install with latest Java JRE from the SUN site, if you need to upgrade your browser's java support.

    + + \ No newline at end of file diff --git a/documentation/general/pages.inc.php b/documentation/general/pages.inc.php new file mode 100644 index 000000000..bf023ab23 --- /dev/null +++ b/documentation/general/pages.inc.php @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/documentation/general/password_reminder.php b/documentation/general/password_reminder.php new file mode 100644 index 000000000..97ac22043 --- /dev/null +++ b/documentation/general/password_reminder.php @@ -0,0 +1,7 @@ + + +

    Forgot Your Password

    + +

    If you have forgotten your password, use the Forgot your password? link on the Login screen. The form will email the login name to you, along with a link you must follow to change your password.

    + + \ No newline at end of file diff --git a/documentation/general/preferences.php b/documentation/general/preferences.php new file mode 100644 index 000000000..d68412cfd --- /dev/null +++ b/documentation/general/preferences.php @@ -0,0 +1,74 @@ + + +

    Preferences Wizard

    +

    At any time (except while viewing the Preferences screen) the Preferences Wizard can be opened by clicking the wand icon next to your login name to the upper right (location may vary across themes). Any of the settings you can set through the Preferences panels described below, can also be set using the wizard.

    + +

    Preferences

    +

    The following preferences allow a user to control how some features function, and how information is displayed.

    +

    ATutor Settings

    +
    +
    Theme
    +
    Themes are used for changing the look and feel.
    + +
    Time Zone Offset
    +
    Add or subtract hours from the times and dates displayed in ATutor, so they match your local time. Valid values range from -12 to 12. The positive sign is not required when adding hours. The minus sign is required when subtracting hours.
    + +
    Inbox Notification
    +
    If enabled, an email notification message will be sent each time an Inbox message is received.
    + +
    Topic Numbering
    +
    If enabled, content topics will be numbered.
    + +
    Direct Jump
    +
    If enabled, using the Jump feature will redirect to the selected course and load the same section that was being viewed in the previous course (instead of the usual course Home page).
    + +
    Auto-Login
    +
    If enabled, users are automatically logged in when they open ATutor. You should only enable this if you are accessing ATutor from a private computer, otherwise others will be able to login with your account information.
    + +
    Form Focus On Page Load
    +
    If enabled, the cursor will be placed at the first field of the form when a page loads.
    + +
    Show Context Sensitive Handbook Pages
    +
    Once you are familiar with ATutor you may wish to hide the links included with various tools to their associated handbook page. You can always access the handbook using the link in the ATutor footer area.
    + +
    Content Editor
    +
    This preference controls how content is entered. Choose between Plain Text for entering content text that will escape any HTML markup and will be formatted as entered; HTML for entering HTML content manually; and HTML - Visual Editor for entering HTML content using the visual (also known as a WYSIWYG) editor which represents the content as it will be displayed. It is also possible to change the editor manually for each item.
    +
    +

    Text Settings

    +

    These settings are used to control the overall colours and fonts displayed.

    + +
    +
    Text
    +
    Select from the various text formatting options to control how text and colours are displayed in ATutor.
    +
    +

    Content Settings

    +

    These settings are used to control which versions of content are displayed, if for example the primary version is not accessible to you, or you prefer an alternate format. These settings will be ignored if the alternative versions you prefer are not available with the content you are viewing. Instructors and content authors should review Alternate Content for information on including alternate formats with ATutor content.

    +
    +
    Alternatives to Text
    +
    If you are a person with a print related disability, or you prefer content in mutli-modal forms, select from these options to have alternate forms either replace text versions of the content, or have the alternate forms appended to the content.
    + +
    Alternatives to Audio
    +
    If you are a person with an auditory disability, or if you prefer to read along with audio, or view visual alternatives to audio, select from these options to have alternatives replace or append where ever there is audio content.
    + +
    Alternatives to Visual
    +
    If you are a person with a visual disability, of you prefer content without the usually larger, slow to load, visual information in content, select from these options to have alternatives to visual information either replace, or append to, visual information in the primary version of the content.
    +
    +

    Learner Supports

    +

    These settings are used to control which learning tools are available to you in a side menu block.

    + +
    +
    Learner Supports
    +
    Select from the various tools, the ones you would like available to you when in your ATutor courses.
    +
    +

    Navigations

    +

    These settings are used to enable or disabled various ATutor navigation tools.

    + +
    +
    Navigation
    +
    Choose to show a Table of Contents at the top of each content page that can be used to navigate to sub sections within the page. Note that a Table of Contents is generated based on the headings (i.e. HTML H1 to H6), so it is important for content authors to structure their content properly with appropriate headings and sub-headings.
    +
    Choose to show Next/Previous Navigation links to aid navigation through content in the order pages are intended to be viewed, or to provide quick access back to the content page you left off on, when you return to viewing content in a current or future session.
    + +
    Choose to display Breadcrumb Navigation at the top of every page to provide up and down navigation through hierarchies of topics and sub-topics, or to keep a display of your current location within ATutor in view at all times.
    + +
    + \ No newline at end of file diff --git a/documentation/general/profile.php b/documentation/general/profile.php new file mode 100644 index 000000000..a879e2d25 --- /dev/null +++ b/documentation/general/profile.php @@ -0,0 +1,8 @@ + + +

    Profile

    +

    This section allows a user to change elements of his/her personal profile.

    + +

    Although the login name cannot be altered, password, email address, and other personal information may be edited. There is also an option to keep the email address hidden.

    + + \ No newline at end of file diff --git a/documentation/general/register.php b/documentation/general/register.php new file mode 100644 index 000000000..60ca2e368 --- /dev/null +++ b/documentation/general/register.php @@ -0,0 +1,9 @@ + + +

    Register

    + +

    In order for a user to login to the ATutor system, a unique system account needs to be created. Use the Register link in the main navigation to access the registration form. If email-confirmation has been enabled by the system administrator, a message will be sent to the email address entered, containing a link that must be followed to confirm the new account. Once this has been done, the login name or email address, and the password entered during registration can now be used on the Login screen.

    + +

    Note that if a system administrator has specified users to be checked against a Master List of allowed Student IDs and PINs (for example), this information must also be entered during registration.

    + + \ No newline at end of file diff --git a/documentation/general/tile.php b/documentation/general/tile.php new file mode 100644 index 000000000..f4b1416ce --- /dev/null +++ b/documentation/general/tile.php @@ -0,0 +1,9 @@ + + +

    TILE Repository Search

    + +

    External content packages can be downloaded from the TILE repository by entering a search term and using the Search button. Use the Preview link next to a search result to open the TILE content browser, or use Download to retrieve the content package from the TILE repository. Once downloaded, the file can be unpacked with a common archiving application (e.g. WinZip, PKZip, Unzip). Unzip the file into an empty directory and browse the package's content.

    + +

    Visit the TILE web site for more information about using the repository.

    + + \ No newline at end of file diff --git a/documentation/index.php b/documentation/index.php new file mode 100644 index 000000000..5b041ada3 --- /dev/null +++ b/documentation/index.php @@ -0,0 +1,136 @@ + + + + + ATutor Handbook + + + + + + +
    + INACCESSIBLE PAGE +
    + + + + + + + + + + + + <h1>Administrator Documentation</h1> + <p><a href="frame_toc.html">Table of Contents</a></p> + + + + diff --git a/documentation/index/en/index.php b/documentation/index/en/index.php new file mode 100644 index 000000000..85abfdf3b --- /dev/null +++ b/documentation/index/en/index.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/documentation/index/fr/index.php b/documentation/index/fr/index.php new file mode 100644 index 000000000..83b4909db --- /dev/null +++ b/documentation/index/fr/index.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/documentation/index/index.php b/documentation/index/index.php new file mode 100644 index 000000000..3d3c71cb4 --- /dev/null +++ b/documentation/index/index.php @@ -0,0 +1,164 @@ +0"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $_SESSION['handbook_admin'] = true; + header('Location: '.$_SERVER['PHP_SELF']); + exit; + } + } else if (isset($_GET['logout'])) { + header('WWW-Authenticate: Basic realm="Administrator Login"'); + header('HTTP/1.0 401 Unauthorized'); + + unset($_SERVER['PHP_AUTH_USER']); + unset($_SERVER['PHP_AUTH_PW']); + unset($_SESSION['handbook_admin']); + session_write_close(); + header('Location: '.$_SERVER['PHP_SELF']); + exit; + } +} + +if (!defined('AT_HANDBOOK_ENABLE')) { + // use local config file + require('../config.inc.php'); + + if (isset($_POST['submit'])) { + // try to validate $_POST + if (($_POST['username'] == AT_HANDBOOK_ADMIN_USERNAME) && ($_POST['password'] == AT_HANDBOOK_ADMIN_PASSWORD)) { + $_SESSION['handbook_admin'] = true; + header('Location: '.$_SERVER['PHP_SELF']); + exit; + } + } else if (key($_GET) == 'logout') { + header('WWW-Authenticate: Basic realm="Administrator Login"'); + header('HTTP/1.0 401 Unauthorized'); + + unset($_SERVER['PHP_AUTH_USER']); + unset($_SERVER['PHP_AUTH_PW']); + unset($_SESSION['handbook_admin']); + session_write_close(); + header('Location: '.$_SERVER['PHP_SELF']); + exit; + } +} + +if (!$db && defined('AT_HANDBOOK_ENABLE') && AT_HANDBOOK_ENABLE) { + $db = @mysql_connect(AT_HANDBOOK_DB_HOST . ':' . AT_HANDBOOK_DB_PORT, AT_HANDBOOK_DB_USER, AT_HANDBOOK_DB_PASSWORD); + @mysql_select_db(AT_HANDBOOK_DB_DATABASE, $db); + $enable_user_notes = true; +} +?> + + + + <?php get_text('doc_title'); ?> + + + + +
    + +
    + + +

    +

    + +
      +
    1. +
    2. +
    3. +
    4. +
    5. +
    6. +
    + +
      +
    1. atutor.ca
    2. +
    3. atutor.ca/forums/
    4. +
    5. atutor.ca/atutor/docs/
    6. +
    + + +
    +

    +
    + + +

    + + +
    +

    +
    + + 0)): ?> + +
    +
    + | + +
    +

    +

    +
    + + +
    + + + + + + \ No newline at end of file diff --git a/documentation/index_list.php b/documentation/index_list.php new file mode 100644 index 000000000..37c544d96 --- /dev/null +++ b/documentation/index_list.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/documentation/instructor/accessibility.php b/documentation/instructor/accessibility.php new file mode 100644 index 000000000..b363c7434 --- /dev/null +++ b/documentation/instructor/accessibility.php @@ -0,0 +1,30 @@ + + +

    Accessibility

    +

    Clicking the Accessibility icon performs an analysis of the content for accessibility problems. Recommendations are given and you are given the option to implement or reverse corrections.

    + +

    After opening the Accessibility checker, review the report, and notice the number of known and potential problems

    + +

    Correct the known problems by reviewing the report, then modify the HTML in the window below. Re-run the accessibility checker again when the known problems are corrected to see a Conditional Pass. Select from the choices available in the potential problems listed, then press Make Decisions to update the report. When all known problems are corrected, and decisions have been made on all potential problems, a Full Pass will be displayed, after which you can be sure the content will be accessible to all your students.

    + +

    Content Editor Accessiblity

    +

    The Content Editor includes a number of added features to help make it accessible to assistive technology users, The accesskeys listed below allow navigation through the editor by keyboard. In addition to the accessibility checker in the ATutor content editor, the visual editor includes its own accessibility checker, as well as prompts while authoring, that prevent making accessibility mistakes.

    +

    Content Editor AccessKeys

    +
      +
    • [Alt-n] Content Tab
    • +
    • [Alt-p] Properties Tab
    • +
    • [Alt-g] Glossary Tab
    • +
    • [Alt-r] Preview Tab
    • +
    • [Alt-a] Accessibility Tab
    • +
    • [Alt-s] Save
    • +
    + +

    Visual Editor AccessKeys

    +
      +
    • [Alt-q] jump to button bar
    • +
    • [Alt-z] jump to content area of editor/li> +
    • [Alt-x] jump to element path (bottom)/li> +
    + + + \ No newline at end of file diff --git a/documentation/instructor/add_questions.php b/documentation/instructor/add_questions.php new file mode 100644 index 000000000..6cfe0e847 --- /dev/null +++ b/documentation/instructor/add_questions.php @@ -0,0 +1,9 @@ + + +

    Test Questions

    + +

    To manage the questions in a test, choose the test from the Test/Surveys Manager and then use the Questions button. Questions in the Question Bank can be added to your test by using the Add Questions link. Check the questions and/or categories of questions to be added to the test and use the Add to Test/Survey button. After confirming this action, the added questions will appear in the Question Manager. Beside each question is a box in which to enter a weight or mark for that question. If this is for a survey, leave the weight box empty. Note that Likert questions do not get marked and therefore do not require a weight. Reorder questions by changing the numbers in the Order column.

    + +

    It is also possible to Edit or Remove questions by using the links beside each question. Editing a question will alter it in the Question Bank, and thus affect all tests and surveys using that question. Removing the question only removes it from the test and will not delete the question from the Question Bank.

    + + \ No newline at end of file diff --git a/documentation/instructor/announcements.php b/documentation/instructor/announcements.php new file mode 100644 index 000000000..0c6b7d6c0 --- /dev/null +++ b/documentation/instructor/announcements.php @@ -0,0 +1,8 @@ + + +

    Announcements

    +

    Announcements added through the Manage section will appear on the course Home page, sorted by date in descending order.

    + +

    If the Syndicate Announcements options is enabled in course Properties, it is possible to subscribe to the feed using a RSS reader.

    + + diff --git a/documentation/instructor/arrange_content.php b/documentation/instructor/arrange_content.php new file mode 100644 index 000000000..b881384a7 --- /dev/null +++ b/documentation/instructor/arrange_content.php @@ -0,0 +1,8 @@ + + +

    Arrange Content

    + +

    Both folders and content pages can be arranged in any order. After selecting the Arrange Content tab, select the radio button for the content you want to move. The press a downward pointing arrow next to another page or folder to move the selected page or folder after that item. Or, use the upward pointing arrow to move it before that item. Use the plus sign (+) to make the selected folder or page a child or sub-topic of the item.

    : + + + \ No newline at end of file diff --git a/documentation/instructor/assignments.php b/documentation/instructor/assignments.php new file mode 100644 index 000000000..7cf7ffd58 --- /dev/null +++ b/documentation/instructor/assignments.php @@ -0,0 +1,14 @@ + + +

    Assignments

    +

    The assignment manager works alongside the File Storage area by letting instructors create virtual assignment drop-boxes within it. A student can submit files to the assignment workspace, and the instructor can view and download the submissions through the assignment manager or the file storage area directly.

    + +

    Add & Manage Assignments

    +

    To add a new assignment submission area, follow the Add Assignment link and specify the assignment title, who to assign it to (everyone or a specific Group Type), the due date if there is one, and how to handle late submissions. Be sure to enable File Storage for groups assignments are assigned to. Using the Save button will create a special folder named with the assignment title within the Assignment Submissions area of the File Storage area. Within each assignment folder, additional folders will be created for each student or group (depending on the "Assign to" setting). These folders are read-only and cannot be changed.

    + +

    It is possible to Edit an assignment's properties after it has been created, though not the "Assign to" element. Also note that if you Delete an assignment, all of its submissions will be deleted. Therefore, it is advised that the instructor first download the submissions to her/his harddrive for safe keeping before deleting an assignment entry.

    + +

    Downloading Submissions

    +

    Only instructors and assistants with Assignment privileges may access assignment folders. Students and groups will not be able to access any submitted files. To download submitted assignments, select an assignment and use the Submissions button. This will redirect to the File Storage area where an instructor can download submissions. Alternatively, this area can be accessed directly by going to the File Storage area, and selecting the name of the assignment from the Workspace dropdown.

    + + diff --git a/documentation/instructor/authenticated_access.php b/documentation/instructor/authenticated_access.php new file mode 100644 index 000000000..c9058ea8f --- /dev/null +++ b/documentation/instructor/authenticated_access.php @@ -0,0 +1,6 @@ + + +

    Authenticated Access

    +

    Since version 1.5.4, instructors may enable this feature to generate a unique URI that may be distributed to authenticate guest access to the protected or private course.

    + + \ No newline at end of file diff --git a/documentation/instructor/backups.php b/documentation/instructor/backups.php new file mode 100644 index 000000000..37d039400 --- /dev/null +++ b/documentation/instructor/backups.php @@ -0,0 +1,10 @@ + + +

    Backups

    +

    ATutor offers the facility to create backups of your course and restore the contents of those backups at any time. This is useful for duplicating a course or saving the entire course content for safe keeping in the case of accidental loss.

    + +

    The Backups utility is found under the Manage section and is available to Instructors and System Administrators.

    + +

    Each course can store only a finite number of backups (default 5). That limit can be altered by the Administrator using the System Preferences option.

    + + diff --git a/documentation/instructor/chat.php b/documentation/instructor/chat.php new file mode 100644 index 000000000..53c85b530 --- /dev/null +++ b/documentation/instructor/chat.php @@ -0,0 +1,8 @@ + + +

    Chat

    +

    The chat section is used for managing chat transcripts. An active transcript will record all of the chat messages as they are posted. There can be only one active transcript at a time.

    + +

    A transcript is started by using the Start Transcript link. A unique name must be given to a new transcript when it is started.

    + + diff --git a/documentation/instructor/content.html b/documentation/instructor/content.html new file mode 100644 index 000000000..fb017cf99 --- /dev/null +++ b/documentation/instructor/content.html @@ -0,0 +1,140 @@ + + + + + ATutor 1.5 Instructor Documentation + + + + + +
    +

    2. Content

    +

    In order to add and manage content for a course, you must first be logged in as an instructor. By selecting a course, you can manage your content from the Manage tab, or by the Content Navigation tree.

    + +

    Table of Contents

    + + +
    + + +
    +

    2.1 Creating and Editing Content

    + +

    Once you have selected a course, you can create or edit course content by selecting the Manage tab, then selecting the Content option.

    + +

    You should now see a table of all the content in your current course. If you have no content, this list will be empty. If you already have content in your course, you can select an item and click the Edit button to begin editing. If you want to create new content, select the Create Content link along the top.

    + +

    Whether editing or creating new content, the interface is the same for both actions.

    + +

    2.1.1 Content

    +

    Content can be created in either 'plain text' or 'HTML' modes. Plain text mode is useful for quickly writing up text content. Conversely, HTML mode allows for extra features like text formatting and layout, but is a little more complex to use.

    + +
    +
    Formatting: Plain Text
    +
    If using plain text mode, just type the content in the Body window. Note that any extra spaces between characters will be removed (i.e. two or more spaces), but any blank lines will be saved with the text.
    + +
    Formatting: HTML
    +
    If using HTML mode, you can type HTML tags in the Body window along with your text. If you are unfamiliar with HTML, you can use the visual editor by clicking the Switch to visual editor button.
    + +
    File Manager
    +
    +

    The File Manager is a tool that allows you to upload files from your local system to be used in your course.

    + +

    Create Folder creates a folder on the ATutor system so you can better organize your uploaded files. You can create folders and/or move files into that folder at any time you like.

    + +

    Browse... opens a local file browser window so you can select the file you want to upload.

    + +

    Upload will upload the specified file to the ATutor system. You can specify a file by either typing the path and filename in the text field or by using the Browse button.

    + +

    Create a New File link will display a new interface where you can quickly create a new text or HTML file. If using Text mode, any blank lines will be saved with the file. If using HTML, you should be familiar with using HTML tags as no visual editor is provided. Clicking Save will save a new file with your specified information (filename and content) into the ATutor system and bring you back to the File Manager. Cancel will discard the file and bring you back to the File Manager browser.

    + +

    Rename button renames a single, selected file.

    + +

    Delete and Move buttons deletes or moves the selected files and folders (and its contents) from the ATutor system. You can select multiple files and folders for deletion or moving.

    + +

    Insert appears under the Action column for files that can be inserted into the content. Clicking the Insert button will add appropriate HTML code into the body of the content. If you are formatting in plain text you will need to switch to HTML formatting in order for it to display properly.

    +
    + +
    Terms
    +

    In either plain text, or HTML formatting modes, you can insert terms which are used to tell the ATutor system the words you want to define in the glossary.

    + +

    Clicking the Add Term link will add [?][/?] into your content, and any text you put after [?] and before [/?] will specify the term you want to define. Alternatively, you can manually type [?][/?] into your text without having to click the Add Term.

    + +

    Once you have specified the terms you would like to define, you can go to the Glossary Terms tab to write the definitions. Once done, the terms and their definitions will appear in the glossary and in the content.

    + +
    Code
    +

    In either plain text, or HTML formatting modes, you can insert code which is useful for differentiating blocks of text (like math equations, program code, or quotations)from the rest of the text content.

    + +

    Clicking the Add Code link will add [code][/code] into your content, and whatever text you put after [code] and before [/code] will specify the text you want to differentiate. Alternatively, you can manually type [code][/code] into your text without having to click the Add Code link.

    +
    + +
    Colours
    +
    Like code and terms you can add colour to your text content in the same way. You can either click the appropriate colour icon to insert colour tags into the content. Valid colour options are blue, red, green, orange, purple, and gray. You can also type the colour codes manually by using the following tags: [blue][/blue], [red][/red], [green][/green], [orange][/orange], [purple][/purple], and [gray][/gray]
    . + +
    Upload from File
    +
    Instead of typing your content, you can upload it from a text or HTML file on your local file system. Once uploaded, the content of that file will be displayed in the Body window. Keep in mind that uploading in this manner will replace any content in the Body window.
    + +
    Save and Close
    +
    At any point of editing or creating content, you can chose to Save your content, or Cancel your changes. +
    +
    + +

    2.1.2 Properties

    +

    In the properties tab, you can specify a Release Date, keywords for easier searching, and specify related topics.

    + +
    +
    Release Date
    +
    The release date is the date in which the content will be visible to the course. You can schedule a release in the future by specifying a later date. Also, if you specify a release date that has past, it will be released immediately.
    + +
    Keywords
    +
    Words in the Keywords are given greater emphasis during searching. Therefore they will be placed higher in a list of search results than if there were no keywords. Keywords are also used to as Learning Object Metadata.
    + +
    Related Topics
    +

    For each piece of content you have in the course, you can specify what other content in the course is related. Moving content up the related topics list places that content closer to the top of the Content Navigation window. The opposite is true if you move content down the Related Topics list.

    +

    You can also make content a child of another piece of content. In this case, the child content will appear as sub-content on the Content Navigation window. All sub-content children and their parent will appear in the Related Topics window.

    +
    +
    + +

    2.1.3 Glossary Terms

    +

    If you specified Terms in the Content tab, you have the opportunity to define those terms here. For each term specified in the Content tab, there is a Definition window where you provide your explanation. If there are no glossary terms listed, you can add glossary terms by going to the Content tab and adding Term tags.

    + +

    In order to set a Related Term, you must have at least two defined terms in the glossary.

    + +

    2.1.4 Preview

    +

    Under the Preview tab, you can see how your content will appear to a user, including Terms, Code, and Colors you added to the content.

    + +

    2.1.5 Accessibility

    +

    +
    + +
    +

    2.2 Content Packages

    +

    ATutor provides importing and exporting course content using IMS 1.1.3/SCORM 1.2 content package specification. This allows ATutor content to be viewed offline, and transferred to other systems.

    + +

    2.2.1 Exporting Content

    +

    An entire course or a single chapter can be exported as an IMS 1.1.3/SCORM 1.2 content package. Exported packages are archived into a single file using ZIP compression. All content is exported including the terms and glossary, colours, and code.

    + +

    To export content, select the scope by choosing an option from the What to export menu. Then, clicking Export will generate a download through your browser. Optionally, you can choose to export the content directly to TILE if you have a TILE authoring account.

    + +

    2.2.2 Viewing Exported Content

    +

    To view a content package exported from ATutor, you will either need a IMS 1.1.3/SCORM 1.2 viewer, or a web browser. To view the content in a web browser, first extract the contents of the ZIP file and then open the file index.html in the browser.

    + +

    2.2.3 Importing Content

    +

    To import a content package into ATutor, it must conform to IMS 1.1.3/SCORM 1.2 content package specifications.

    + +

    Before importing, you must specify where in the course structure the new content is to be placed by using the Import into menu.

    + +

    Select the content package to upload by supplying the file from your local filesystem by typing in the path into the textfield or by using the Browse button. You can also import a content package over the Web by providing an URL.

    + +

    Clicking Import will upload the content into the course and at the hierarchy location specified.

    + +
    + + + \ No newline at end of file diff --git a/documentation/instructor/content.php b/documentation/instructor/content.php new file mode 100644 index 000000000..a0520a62b --- /dev/null +++ b/documentation/instructor/content.php @@ -0,0 +1,8 @@ + + +

    Content

    +

    Content in ATutor can be managed in many ways and can be imported and created from many different sources. Content can be entered manually, created from HTML files, standards compliant content packages, or from a learning objects content repository. Content that already exists in ATutor can also be exported into any of the mentioned formats. Only instructors, and assistants who are given content privileges, can manage course content.

    + +

    Existing content pages can be managed using the Shortcuts links available when viewing a content page, or through the Content section under the Manage tab.

    + + diff --git a/documentation/instructor/content_alternatives.php b/documentation/instructor/content_alternatives.php new file mode 100644 index 000000000..fc52c03dd --- /dev/null +++ b/documentation/instructor/content_alternatives.php @@ -0,0 +1,30 @@ + + +

    Adapted Content

    +

    Based on the IMS AccessForAll and ISO FDIS 24751 standards, the Adapted Content utility allows authors to enhance their content with different forms presenting the same information. Adapted forms can be used to replace or supplement content for people with disabilities who may not be able to access the original version, or it can be used to supplement the original content by including the same content in multi-modal forms, allowing learners to experience the content through multiple senses.

    + +

    Define Adaptations for Files in Original Content

    +

    Down the left of the Adapted Content screen is displayed a list of files found in the original content, such as audio files, videos, images, documents etc. for which adapted forms can be defined. Down the right is a trimmed down version of the ATutor File Manager, from which files there can be defined as adaptations.

    + +
    +
    Original Resources
    +
    Along the left side of the screen will appear a list of files linked into the original content, referred to as original resources. For each, define the type of resource it is by selecting the appropriate check box (Auditory, Textual, or Visual). Also select the natural language of each original resource and run Update Resource Properties to apply the settings. To add an adapted form for a particular original resource, click the radio button next to the filename of the original resource on the left, then select from the available files in the File Manager to the right, then click Add to insert the file as an adapted resource.
    + +
    Adapted Resources
    +
    Once an adaptation has been Added, it will be listed below the original resource it is associated with as an Adapted Resource. Define the Adapted Resource Type for the alternative, either Auditory, Sign Language, Textual, or Visual, and select the natural language of the content if applicable. Once again run Update Resource Properties to save the settings. Depending on users' preference settings, different versions of the content may be displayed for different users. For instance, if a text transcript is provided as an alternative for an audio file, learners who are deaf, or do not have an audio player for instance, may receive the transcript instead of (or in addition to) the original audio content. You may add as many adaptations of an original resource as you like.
    + +
    To remove an adapted resource, click the Delete link next to the filename of the adapted resource. This removes the association between the original and adapted resources, but does not delete the actual files. They can still be found in the File Manager.
    + +
    + + + + +

    See the Entering Content for information about using the content editor.

    + +

    See Import/Export Content for information about including adapted content in exported content packages.

    + +

    See Preferences for information about user content preference settings.

    + + diff --git a/documentation/instructor/content_edit.php b/documentation/instructor/content_edit.php new file mode 100644 index 000000000..6b2aea9db --- /dev/null +++ b/documentation/instructor/content_edit.php @@ -0,0 +1,89 @@ + + +

    Adding/Editing Content

    +

    Content can be created in either 'plain text' or 'HTML' mode. Plain text mode is useful for quickly writing up text content. HTML mode allows for extra features like text formatting and layout, but is a little more complex to use.

    + +
    +
    Title
    +

    The main heading that will appear at the top of the page when viewed.

    + +
    Formatting: Plain Text
    +

    If using plain text mode, just type the content in the Body window. Note that any extra spaces between characters will be removed (i.e. two or more spaces), but any blank lines will be saved with the text.

    + +
    Formatting: HTML
    +

    If using HTML mode, you can type HTML tags in the Body window along with your text. If you are unfamiliar with HTML, you can use the visual editor by clicking the Switch to visual editor button.

    + +
    Formatting: Web Link
    +

    Selecting Web Link replaces the content editor window with a text field into which a URL to an external Web site can be entered. When a student views a content page formatted as a Web Link, the content of the external site becomes the content of the ATutor page.

    + +

    Content Editor Toolbar

    + +
    Preview
    +

    Click on the Preview icon to open the content you are currently editing in a popup window to see how it will appear.

    + +
    Accessibility
    +

    Clicking on the Accessibility icon will gather the HTML of the page you are editing, send it off to the AChecker accessibility checker, which will return a report outlining any potential barriers that might be present (note that AChecker only works through ATutor using an IP address or qualified domain name, and not when using localhost). Review the details of the potential barriers listed, make adjustments to your content to correct them, then run the accessibility checker again.

    + +
    Scripts/CSS
    +

    HTML that normally appears in the head area of a Web page can be entered here. This can include things like links to stylesheets, or the actual stylesheet markup, or you may insert links to scripts, or the scripts themselves. Additional metadata can also be entered here. HTML content created in an external editor will have its head information displayed here when Pasting from a file (see below) after which you can upload the additional files like stylesheets or scripts, and adjust the links to point to the files in the course File Manager. Note that when importing eXe content, the stylesheet supplied with its content is replaced to avoid conflicts between eXe styles and ATutor styles.

    + +
    Paste
    +

    Rather than typing out content, it can be uploaded from a text or HTML file on your local file system. Once uploaded, the content of that file will be displayed in the Body window. Keep in mind that uploading in this manner will replace any existing content in the Body window.

    + + +
    Files
    +
    +

    The File Manager can be opened by clicking the Files icons in the content editor tool bar. It allows you to upload files from your local system to be used in your course. The popup File Manager can be open alongside the Content Editor then clicking the Insert button beside files to insert them into your content.

    + +

    See the File Manager section for details.

    +
    +
    Forums
    +
    +

    Click on the Forums button to open a list of the available forums for the current course, then select a forum to associate it with the content you are editing as a learning activity. Forums are exported with Common Cartridges, and are setup automatically when a Common Cartidge is imported into a course. In future versions of ATutor, any tool available in a course can be used to add activities to content, based on the IMS Learning Tool Interoperability (LTI) standard.

    +
    + +

    Content Body

    +
    TinyMCE Editor
    +
    +

    The Body area of the content editor by default includes a version of the TinyMCE WYSIWYG Javascript HTML editor. It includes a simple mode, and an advanced mode, which can be toggled on or off by clicking the arrow icon at the top left of the editor. The HTML editor in the body area can be replaced with a plain text editor, or with a simple text input field where a link to an external Web site can be added. Click on the Formatting options above to switch editor modes.

    +
    +

    Formatting Codes

    +

    A variety of formatting codes are available that can be used for various purposes in your content. These are described below:

    +
    Terms
    +
    +

    In either plain text or HTML formatting mode, you can insert terms to tell the ATutor system which words you wish to mark as glossary terms. In advanced mode in TinyMCE, click on the question mark icon to insert a glossary term.

    + +

    Or, type [?][/?] into your content, and any text you put after [?] and before [/?] will specify the term you want to define. Alternatively, you can manually type [?][/?] into your text without having to use the Add Term link.

    + +

    Once you have specified the terms you would like to define, you can go to the Glossary Terms tab to write the definitions. Once this is done, the terms and their definitions will appear in the glossary and in the content.

    +
    + +
    Code
    +
    +

    In either plain text or HTML formatting mode, you can insert code which is useful for differentiating blocks of text (like math equations, program code, or quotations) from the rest of the text content.

    + +

    Using the Add Code link will add [code][/code] into your content, and any text you put after [code] and before [/code] will specify the text you want to differentiate. Alternatively, you can manually type [code][/code] into your text without having to use the Add Code link.

    +
    + +
    Previous/Next
    +

    Links can be generated by inserting the [pid] and the [nid] codes in your ATutor content. When the page is displayed these codes get converted to the URL/Link for the previous or next pages in the sequence of content pages. For example

    <a href="[pid]">previous</a> <a href="[nid]">Next</a>

    + +

    Or, pasted the [pid] and [nid] tags into the Link URL field in the visual editor.

    +
    + +
    Colours
    +

    Like code and terms, colour may be added to text content in the same way. Use the appropriate colour icon to insert colour tags into the content. Valid colour options are blue, red, green, orange, purple, and gray. Also, colour codes can be typed in manually by using the following tags: [blue][/blue], [red][/red], [green][/green], [orange][/orange], [purple][/purple], and [gray][/gray].

    + +
    LaTeX
    +

    Type in [tex][/tex] to embed LaTeX equations into your content. In the TinyMCE editor while in advanced mode, click on the TEX icon to insert the LaTeX tags.

    + +
    Multimedia
    +

    Type the [media][/media] tags, along with a URL to an external media file, or a relative URL for a media file in the course File Manager (e.g. movies/mymovie.flv), to embed multimedia into your content. Supported formats currently include: mpeg, mov, wmv, swf, mp3, wav, ogg, mid, flv, and YouTube hosted videos. The media tag can take two parameters to define the width and height of the play when it displays [media|640|480]http://www.youtube.com/watch?v=bxcZ-dFffHA[/media]. If the parameters are not defined, the player size will default to 425x350. While in TinyMCE click on the film slides icon to insert the media tags.

    + +
    Save and Close
    +

    While editing or creating content, it is wise to frequently Save your content. When you are finished, use Close to close the content editor. Note that this does not save your content first so any unsaved content will be lost.

    + + +
    + + \ No newline at end of file diff --git a/documentation/instructor/content_packages.php b/documentation/instructor/content_packages.php new file mode 100644 index 000000000..0e81c054c --- /dev/null +++ b/documentation/instructor/content_packages.php @@ -0,0 +1,29 @@ + + +

    Import/Export Content

    + +

    ATutor provides importing and exporting of course content as IMS Content Packages, or as IMS Common Cartridges.

    +

    Exported content packages can be viewed offline, and transferred to other systems that will import IMS conformant content. If enabled, students can also export content for offline viewing. See course Properties to learn how to enable content exporting for students.

    + +

    Exporting Content

    +

    An entire course, a chapter, or a single page of content can be exported as an IMS Content Package. Exported packages are archived into a single ZIP file.

    + +

    Similarly, an entire course, a chapter, or a page can be exported as an IMS Common Cartridge. Cartridges can include content, tests, and activity tools (forum discussions currently) as a single unit of content.

    + +

    To export content, select the scope by choosing an option from the What to export menu. Select the checkbox to export AccessForAll adapted content as an IMS Access4All integrated content package or common cartridge, if adaptations exist for the content being exported. Then, using Export will generate a downloadable ZIP file through your browser.

    + +

    Viewing Exported Content Packages

    +

    To view a content package offline that has been exported from ATutor, you will need an IMS or SCORM 1.2 viewer, or a web browser, and an application to unzip the package. To view the content in a web browser, first extract the contents of the ZIP file into an empty folder on your computer, and then open the file index.html in your browser. Note that tests and adapted content are not currently viewable with the content package viewer, nor is content in a common cartridge.

    + +

    Importing Content

    +

    To import content into ATutor, it must conform to IMS or SCORM 1.2 content package specifications, or to IMS Common Cartridge 1.0 specifications.

    + +

    Before importing, specify where in the course structure the new content is to be placed by using the Import into menu.

    + +

    Select the content to upload by choosing the ZIP file from your local file system, either by typing the path into the Upload a Content Package or Common Cartridge text field, or by using the Browse button. You can also import a cartridge or package over the Web by entering a URL.

    + +

    Select the checkboxes to Import available Tests, or to Import available AccessForAll content, if they are included with the package being imported. QTI test packages should be imported through Tests & Surveys if they are not part of a content package.

    + +

    Using Import will upload the zipped content into the course, and unpack it into the specified location in the course.

    + + \ No newline at end of file diff --git a/documentation/instructor/content_preview.php b/documentation/instructor/content_preview.php new file mode 100644 index 000000000..d4bf891a0 --- /dev/null +++ b/documentation/instructor/content_preview.php @@ -0,0 +1,7 @@ + + +

    Previewing Content

    +

    The Preview tab displays the content page as it looks with the formatting given. This is how the content page will appear to a user, with custom HTML, Glossary Terms, Code, and colours.

    + + + diff --git a/documentation/instructor/content_properties.php b/documentation/instructor/content_properties.php new file mode 100644 index 000000000..369407765 --- /dev/null +++ b/documentation/instructor/content_properties.php @@ -0,0 +1,21 @@ + + +

    Properties

    +

    In the properties tab, you can move the content page, select a Release Date, enter keywords for easier searching, and specify its related topics.

    + +
    +
    Move
    +
    In the left column of the Properties screen in the Content Editor choose the 'up arrow' to move the current content Before another item. Choose the 'down arrow' to move the content After that item. Choose the 'plus sign' to make the current content a Child of, or sub-topic, for that item.
    + +
    Release Date
    +
    The release date specifies when the content page will be visible to students. Content can be scheduled for release by specifying date in the future. Specifying a release date that has past will release the content immediately. The release date of a page affects all of its sub-pages as well, such that a sub-page is released only when the most distant release date of all its parent pages has passed. By default, the Release Date is set as the current date and time.
    + +
    Keywords
    +
    Words entered into the Keywords area are given greater emphasis during searching. Therefore they will be placed higher in a list of search results than if there were no keywords. Keywords are also used as Learning Object Metadata when a content package is generated.
    + +
    Related Topics
    +

    For each content page in the course, it is possible to specify other content pages as being related. Related topics can appear in the side menu, allowing students to quickly jump to a topic. Related topics are cross-refrenced meaning the content page chosen to be related will also be related to the current page.

    +
    +
    + + \ No newline at end of file diff --git a/documentation/instructor/content_tests.php b/documentation/instructor/content_tests.php new file mode 100644 index 000000000..1cc2e61e5 --- /dev/null +++ b/documentation/instructor/content_tests.php @@ -0,0 +1,12 @@ + + +

    Associating Tests with Content

    +

    Under the Tests & Surveys tab of the content editor is a list of tests available for the current course. One or more tests can be associated with a content page, allowing students to test their knowledge immediately after reviewing the content. Associating a test with a content page inserts a link at the bottom of the page, and inserts a link in the content navigation menu as a sub-menu item for the associated page.

    + +

    In addition to choosing a test to associate with a content page by selecting the appropriate checkbox(es), enter a Custom Test Message that will appear along with the link to the test.

    + +

    Also see Import/Export Content for details on importing and exporting tests with content packages.

    + + + + \ No newline at end of file diff --git a/documentation/instructor/content_usage.php b/documentation/instructor/content_usage.php new file mode 100644 index 000000000..a9ccc60b6 --- /dev/null +++ b/documentation/instructor/content_usage.php @@ -0,0 +1,9 @@ + + +

    Content Usage

    +

    Instructors can view Content Usage statistics, or Student Specific Usage data. Content Usage will list the overall number of Visits to each content page, the number of Unique Visits, the Average Duration of each visit, the Total Duration of all visits to each page, as well as the Details of all visits to each page. If enabled, students can also view their own content usage using My Tracker.

    + +

    Use the Reset link to empty the Content Usage data.

    + + + diff --git a/documentation/instructor/course_email.php b/documentation/instructor/course_email.php new file mode 100644 index 000000000..f7c94cdc3 --- /dev/null +++ b/documentation/instructor/course_email.php @@ -0,0 +1,13 @@ + + +

    Course Email

    +

    Using the Course Email option, you can send an email to all assistants (students with privileges), enrolled, un-enrolled, and /or alumni students in your current course. A copy of the email is also sent to your registered email address.

    +

    The following tags can be added to course emails to customize the message to the recipients. They are replaced with their personal information.

    +
      +
    • {AT_FNAME} Replaced with recipient's first name in the body or subject line.
    • +
    • {AT_LNAME} Replaced with recipient's last name in the body or subject line.
    • +
    • {AT_EMAIL} Replaced with recipient's email in the body.
    • +
    • {AT_USER} Replaced with recipient's login name in the body.
    • +
    + + \ No newline at end of file diff --git a/documentation/instructor/creating_courses.php b/documentation/instructor/creating_courses.php new file mode 100644 index 000000000..7c5081425 --- /dev/null +++ b/documentation/instructor/creating_courses.php @@ -0,0 +1,38 @@ + + +

    Creating Courses

    + +

    After logging in, use the Create Course link from My Start Page. Properties set here can be modified through Manage > Properties

    + +

    Some course properties include:

    + +
    +
    Description
    +
    Enter a meaningful but brief paragraph describing the course, to be displayed under the course name in Browse Courses.
    + +
    Export Content
    +
    Choose the availability of the "Export Content" link on course content pages.
    + +
    Syndicate Announcements
    +
    Enable this setting if you wish to make an RSS feed of the course announcements available for display on another website.
    + +
    Access
    +
    Determines who can have access to the course content - any user, only logged in users, or logged in and enrolled users.
    + +
    Release Date
    +
    An optional date from when the course can be accessed by non-privileged students.
    + +
    End Date
    +
    An optional date from when the course can no longer be accessed by non-privileged students.
    + +
    Banner
    +
    HTML that forms a custom banner or splash screen for the course home page. Appears above the course announcements, if there are any.
    + +
    Initial Content
    +
    Initialise the course content to be either empty, basic place-holder content, or a restored backup from other courses you own.
    +
    + +

    Enter the necessary information and use the Save button to proceed into the newly created course.

    + + + \ No newline at end of file diff --git a/documentation/instructor/creating_editing_content.php b/documentation/instructor/creating_editing_content.php new file mode 100644 index 000000000..4a451fff0 --- /dev/null +++ b/documentation/instructor/creating_editing_content.php @@ -0,0 +1,7 @@ + + +

    Creating and Editing Content

    + +

    Using the Tabs in the Content Manager, content can be created, its location, keywords, and date properties set, and glossary terms defined. It can also be previewed and have its accessibility checked. While creating content, save often. Unsaved information is indicated by a red bullet in the corresponding Content Manager tab. When content editing is complete, choose the Close after saving option, then press Save.

    + + \ No newline at end of file diff --git a/documentation/instructor/creating_editing_content_folder.php b/documentation/instructor/creating_editing_content_folder.php new file mode 100644 index 000000000..4cc49ce13 --- /dev/null +++ b/documentation/instructor/creating_editing_content_folder.php @@ -0,0 +1,8 @@ + + +

    Create/Edit Content Folders

    + +

    A folder can be created to group related content. A release date can be set to automatically make content in the folder available to students at a certain date and time. Prerequisite test(s) can be set from those available in course, to grant access based on taking or passing a test. Tests are first setup using the Tests and Surveys Manager.

    : + + + \ No newline at end of file diff --git a/documentation/instructor/creating_questions.php b/documentation/instructor/creating_questions.php new file mode 100644 index 000000000..4d305d11a --- /dev/null +++ b/documentation/instructor/creating_questions.php @@ -0,0 +1,32 @@ + + +

    Creating Test Questions

    +

    Test questions are created in the Question Bank. Options differ depending on the type of question being created. All questions are saved to the Question Database where they can then be added to Tests or Surveys. The following questions are supported:

    + +
    +
    Likert
    +
    Likert questions require the respondent to specify their choice based on the scale provided. Keep in mind that Likert questions are not assigned a point value, so if they are included in a randomized test with other questions that do have a point value, they must be included as required question, otherwise test statistics will not be accurate.
    + +
    Matching (Graphical)
    +
    Matching questions require the respondent to match value pairs. The graphical version creates coloured lines when pairs are created and allows for drag-and-drop interaction.
    + +
    Matching (Simple)
    +
    Matching questions require the respondent to match value paris. The simple version does not create coloured lines and does not support drag-and-drop interaction.
    + +
    Multiple Answer
    +
    Multiple answer questions require the respondent to answer a question by selecting two or more correct answers.
    + +
    Multiple Choice
    +
    Multiple choice questions require the respondent to answer a question by selecting only one correct answer.
    + +
    Open Ended
    +
    Open ended questions require the respondent to enter text in the specified text area.
    + +
    Ordering
    +
    Ordering questions require the respondent to correctly assign given items in a particular logical order or rank.
    + +
    True or False
    +
    True or false questions require the respondent to specify whether or not a given statement is true or false.
    +
    + + \ No newline at end of file diff --git a/documentation/instructor/creating_restoring.php b/documentation/instructor/creating_restoring.php new file mode 100644 index 000000000..f4775cc1c --- /dev/null +++ b/documentation/instructor/creating_restoring.php @@ -0,0 +1,8 @@ + + +

    Creating & Restoring Backups

    +

    To create a backup of the current course, use the Create link found on the Backups page. All created backups are stored securely on the ATutor server. The space required for the backups does not affect the course's size quota. Once a backup is created, it will be listed on the main Backups page where it can be managed.

    + +

    Backups can be restored by selecting a backup and using the Restore button. The restoration process will present details on what is stored in the backup and allow instructors to select which course material they wish to restore.

    + + diff --git a/documentation/instructor/creating_tests_surveys.php b/documentation/instructor/creating_tests_surveys.php new file mode 100644 index 000000000..633ea1383 --- /dev/null +++ b/documentation/instructor/creating_tests_surveys.php @@ -0,0 +1,63 @@ + + +

    Creating/Editing Tests & Surveys

    +

    To begin creating a test, use the Create Test/Survey link. Filling out the information on the Create Test/Survey page will address all the administrative options for a test. Actual questions are added to the test in a separate step.

    + +

    Test properties include:

    + +
    +
    Title (Mandatory field)
    +
    Test title
    + +
    Description
    +
    Test description
    + +
    Attempts Allowed
    +
    Tests used for evaluation could be set to 1 attempt, while self=tests may be set to Unlimited attempts
    + +
    Link from My Courses
    +
    Will display a link to the test on the My Courses page, in the course listing. Students will be made aware that the current test is available before they enter the course. This may be useful for creating a pretest to determine students' level of knowledge before taking a course.
    + +
    Anonymous
    +
    Set this to No in most cases, or set it to Yes if you are creating a survey or poll.
    +
    Note: Please be aware that the instructor can not modify the anonymous option when submissions have been made on a test.
    + +
    Allow Guest
    +
    Set this if you wish to allow users who are not logged into a course to take the test. In Release Results, set to "Once quiz has been submitted" to allow guest users to see the results of the test after they have completed it. Also see Authenticated Access for information about guest access to protected and private courses.
    + +
    Display
    +
    Controls how test questions are displayed: Either all on one page, or one at a time.
    + +
    Pass Score
    +
    Define the pass score by points or percentage or no pass score. If the pass score/percentage is define, the pass/fail feedback is displayed on student's test result page and instructor can filter by passed/failed students in test submission statistics page.
    + +
    Pass feedback
    +
    Displayed in test result page for passed student.
    + +
    Fail feedback
    +
    Displayed in test result page for failed student.
    + +
    Release Results
    +
    Defines the availability of test results to students, either once the test has been submitted, once submitted and completely marked, or not at all. In the latter case, the Release Results property can later be changed to Once quiz has been submitted to make results available to students once all submissions have been marked.
    + +
    Randomized Questions
    +
    Will display the number of questions specified, chosen randomly from the pool of available questions for that test. It is important that either all questions be assigned the same point value, or that those questions with different point values from the others be included as required questions, otherwise tests' "out scores" will differ from student to student. If including Likert questions in a randomized test, they must be included as required questions.
    + +
    Start & End Dates
    +
    Define the window of time in which the test will be available to students. It is possible to define the start date to be in the future, meaning the test will not be available until that date is reached.
    + +
    Assign to Groups
    +
    Specifies the groups (Created in the Group Manager) permitted to take this test. By default, tests are available to Everyone in the course if no group is selected..
    + +
    Instructions
    +
    Notes that will appear at the top of the test, which might include instructions for taking the test, or include other information relevant to the test.
    + +
    Specifies the groups (created using the Group Manager) permitted to take this test. By default, tests are available to Everyone in the course.
    + +
    + +

    Surveys are created in the same way as regular tests, with the exception that no marks are assigned to questions and no results are released, and in some cases it might be preferable to treat submissions as Anonymous. This can be done by choosing Yes from the Anonymous property setting.

    + +

    Once the initial properties have been saved, the test or survey will be listed in the Test/Survey Manager. From here, one can Edit the test properties, add Questions to a test, Preview the test questions, view the Submissions received so far, view the test Statistics, or Delete the test.

    + + \ No newline at end of file diff --git a/documentation/instructor/delete_course.php b/documentation/instructor/delete_course.php new file mode 100644 index 000000000..6923832cb --- /dev/null +++ b/documentation/instructor/delete_course.php @@ -0,0 +1,6 @@ + + +

    Delete Course

    +

    An instructor can delete a course using the Properties manager. Once a course has been deleted from the system, it can not be restored (unless there is a backup). There will be prompts to confirm the action before actual deletion occurs.

    + + \ No newline at end of file diff --git a/documentation/instructor/downloading_uploading.php b/documentation/instructor/downloading_uploading.php new file mode 100644 index 000000000..f5e5ff41f --- /dev/null +++ b/documentation/instructor/downloading_uploading.php @@ -0,0 +1,10 @@ + + +

    Downloading & Uploading Backups

    +

    Backups can be downloaded and stored locally by selecting from the list of backups created and using the Download button. Locally stored backups can be uploaded back into the original course, into a new course, or into another installation of ATutor.

    + +

    The backup file itself is a compressed archive in a format specific to ATutor. Backups cannot be used by any other system other than ATutor (see Import/Export Content for information about reuseable content). Extracting the backup archive to view and change its contents is strongly discouraged as it may currupt the backup, making it impossible to restore.

    + +

    Backups are forwards compatible, but not backwards compatible with older versions of ATutor. That is, backups can be used with all future versions of ATutor, but cannot be used with versions of ATutor older than the version originally used in the backup's creation.

    + + diff --git a/documentation/instructor/edit_delete_tests.php b/documentation/instructor/edit_delete_tests.php new file mode 100644 index 000000000..b38fd6d1c --- /dev/null +++ b/documentation/instructor/edit_delete_tests.php @@ -0,0 +1,9 @@ + + +

    Editing & Deleting Tests

    + +

    In the Test/Survey Manager, choose the test who's properties you wish to change and use the Edit button. This will display a screen like the one for Creating Tests & Surveys, where the test's properties can be altered and saved.

    + +

    To delete a Test or Survey, choose it from the Test/Survey Manager and use the Delete button. Aftering confirming the delete action, the test will be removed. Note that the questions within the test will not be deleted as they are stored in the Question Bank.

    + + \ No newline at end of file diff --git a/documentation/instructor/editing_deleting.php b/documentation/instructor/editing_deleting.php new file mode 100644 index 000000000..b324f8f95 --- /dev/null +++ b/documentation/instructor/editing_deleting.php @@ -0,0 +1,8 @@ + + +

    Editing & Deleting Backups

    +

    Selecting a backup and using the Delete button will delete that backup.

    + +

    Use the Edit button to edit the description of a selected backup. This will not change the backup's contents.

    + + diff --git a/documentation/instructor/en/index.php b/documentation/instructor/en/index.php new file mode 100644 index 000000000..85abfdf3b --- /dev/null +++ b/documentation/instructor/en/index.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/documentation/instructor/enrollment.php b/documentation/instructor/enrollment.php new file mode 100644 index 000000000..810f0f31d --- /dev/null +++ b/documentation/instructor/enrollment.php @@ -0,0 +1,8 @@ + + +

    Enrollment

    +

    The Enrollment list for a particular course determines which of your students have access to the course content and course management tools. Instructors can create, import and export student lists.

    + +

    To administer members of a course, log in as the instructor and select the Enrollment option from the Manage screen.

    + + diff --git a/documentation/instructor/enrollment_alumni.php b/documentation/instructor/enrollment_alumni.php new file mode 100644 index 000000000..d9806cce9 --- /dev/null +++ b/documentation/instructor/enrollment_alumni.php @@ -0,0 +1,7 @@ + + +

    Alumni

    + +

    Instructors can mark students who have completed the course as alumni. Alumni have access to all course content with the exception of tests and surveys. They can particpate in activities to help new students master the content of the course, such as contributing to the forums. Select students from the Enrolled list, then use Mark Alumni to add the students to the Alumni list. Re-enrolling or removing alumni can be done from the Alumni tab.

    + + \ No newline at end of file diff --git a/documentation/instructor/enrollment_course_list.php b/documentation/instructor/enrollment_course_list.php new file mode 100644 index 000000000..3ca47e560 --- /dev/null +++ b/documentation/instructor/enrollment_course_list.php @@ -0,0 +1,20 @@ + + +

    Course Lists

    +

    It is possible to enter or import a course enrollment list into your ATutor course. Those on the list can be added to the Enrolled list immediately, or added to the Not Enrolled list and later moved to the Enrolled list when the time comes to give students access to the course. When users are added or moved to the Enrolled list, they are sent an email with instructions on how to access the course.

    + +

    Creating a Course List

    +

    You have the option of manually generating the student list by selecting the Create Course List option. This option is useful if there is only a small number of users to be added to the course. With many students, the Import feature may be a more efficient option.

    + +

    Creating a Course Enrollment List for Import

    +

    To import a class list from your local system into ATutor, create a plain text file with the format "firstname", "lastname", "email", with one student per line. This file can be generated from a spreadsheet application, a database, or created manually in a plain text editor.

    + +

    Importing Course Enrollment Lists

    +

    To import a course list (in the file format mentioned above), use the Import Course List link. Choose the course list file on your system by using the Browse button, and then use the Import Course List button.

    + +

    When importing an enrollment list, ATutor will automatically generate login names for each new user based on their first and last names. There is an option to choose a format for this - either separating the username with an underscore or a period. (i.e. J_Smith, or J.Smith).

    + +

    Exporting Course Enrollment Lists

    +

    A course enrollment list can easily be exported from ATutor and is useful for creating a backup or for importing the list into other courses. Choose which subsets of users to export (enrolled students, not enrolled students, and alumni) and use Export to download the list. The exported list is in the same comma-separated format as that described in Creating a Course Enrollment List for Import above.

    + + diff --git a/documentation/instructor/enrollment_privileges.php b/documentation/instructor/enrollment_privileges.php new file mode 100644 index 000000000..3ba81a393 --- /dev/null +++ b/documentation/instructor/enrollment_privileges.php @@ -0,0 +1,8 @@ + + +

    Privileges

    + +

    Students who are enrolled in a course can be assigned course administrative privileges. This allows your students to perform actions like managing content, creating and marking tests, managing groups, or moderating forums or the chat. This tool is useful for creating teaching assistants, or for creating multiple instructors for a course. Select the users you wish to give privileges to, and use the Privileges button. Then choose which tools you want each student to have access to and use the Save button.

    + + + diff --git a/documentation/instructor/extracting_zip_archives.php b/documentation/instructor/extracting_zip_archives.php new file mode 100644 index 000000000..464f8c55e --- /dev/null +++ b/documentation/instructor/extracting_zip_archives.php @@ -0,0 +1,9 @@ + + +

    Extracting Zip Archives

    +

    After uploading a ZIP file to the File Manager, select the Extract Archive icon next to the file name. This will display the contents of the zip file and suggest a directory name in which to unzip the archive. Use the Extract button in the ZIP file viewer to unzip the file into the specified directory.

    + +

    Illegal file types will not be extracted, and file names containing illegal characters will be renamed. The viewer will show illegal file types crossed out, and files with illegal characters pointing ( => ) to the renamed file that will be extracted.

    + + + diff --git a/documentation/instructor/faq.php b/documentation/instructor/faq.php new file mode 100644 index 000000000..e764f7490 --- /dev/null +++ b/documentation/instructor/faq.php @@ -0,0 +1,8 @@ + + +

    Frequently Asked Questions (FAQs)

    +

    If an instructor would like to compile a list of frequently asked questions (and answers) for course members, they may do so by going to the FAQ section of the Manage area. Topics must first be created, and then questions can be created and associated with a particular topic.

    + +

    Since version 1.5.2., the FAQ is a Course Tool and can therefore be enabled or disabled, and linked from the main menu or from the home page.

    + + diff --git a/documentation/instructor/feeds.php b/documentation/instructor/feeds.php new file mode 100644 index 000000000..fefd93efd --- /dev/null +++ b/documentation/instructor/feeds.php @@ -0,0 +1,6 @@ + + +

    Syndicated Feeds

    +

    Since version 1.5.2, System Administrators are able to add syndicated news feeds to the system, making them available to instructors to use in their courses. When available, instructors can display the news feeds in the side menu of their courses by using the Side Menu editor of Student Tools, under Manage section, and selecting a feed from the dropdowns.

    + + diff --git a/documentation/instructor/fha_student_tools.php b/documentation/instructor/fha_student_tools.php new file mode 100644 index 000000000..11ab36d1d --- /dev/null +++ b/documentation/instructor/fha_student_tools.php @@ -0,0 +1,8 @@ + + +

    Student Tools Setup

    +

    Tools that might be added through the Course Tools utility, can be added to a separate tools page, potentially removing tools from the course home page. Select from the available tools those that you want to appear on the Student Tools page. To enable the separate student tools page add it when configuring Course Tools, either as a home page icon, or a main navigation tab.

    + + + + \ No newline at end of file diff --git a/documentation/instructor/file_manager.php b/documentation/instructor/file_manager.php new file mode 100644 index 000000000..e9fdb54d1 --- /dev/null +++ b/documentation/instructor/file_manager.php @@ -0,0 +1,30 @@ + + +

    File Manager

    +

    ATutor has a file system used for storing course content resource files, and it is managed with the File Manager. The File Manager allows instructors to include files associated with course content into content pages. The File Manager also allows you to create, edit, move, and delete files. The File Manager should not be confuse with the File Storage area.

    + +

    The File Manager can be found in the Manage area, linked from the Content Editor so it can be opened while authoring content pages, or linked throughout the Test Question authoring screens so files can be managed while assembling tests.

    + + +

    Creating Folders

    +

    Using the Create Folder button creates a folder for better organizing uploaded files. It is possible to create folders and move files into folders at any time.

    + +

    Uploading Files

    +

    Uploading files using the File Manager is one way of adding content to your course. After uploading a file, it can be added to a course by using the popup File Manager linked form the Content Editor and the Insert button that appears next to each file. This will either create a link to a file, or insert an image into a content page. For various types of mutli media, the insert button will insert the [media] tag.

    + +

    Browse... opens a local file browser window in which to choose the file for upload.

    + +

    Upload will upload the specified file to the ATutor system. Specify a file by either typing the path and filename in the text field or by using the Browse... button.

    + +

    Multi File Upload will upload more than one file at a time using the Fluid Multi-File Uploader utility. Click the checkbox to turn it on, then choose Upload Files, followed by Browse Files to select the files to upload. If the browser you are using does not have a Flash plugin, required by the Fluid Uploader, only the single file uploader will be available to you.

    + +

    Creating New Files

    +

    The Create a New File area allows for quick creation of a new text or HTML file. If using Text mode, any blank lines will be saved with the file. If using HTML mode, HTML tags will be permitted. Selecting Save will save a new file with the entered information (filename and content) into the ATutor system and return to the File Manager. Cancel will discard the file and return to the File Manager.

    + +

    Editing Files

    +

    Text or HTML files created using the File Manager, or uploaded from another source, can be edited by selecting the Edit icon next to the file name listed in the File Manager.

    + +

    Previewing Files

    +

    Use the link on the filename in the File Manager to preview that file. Files that can be viewed online, such as images, text, or html files, will open in a preview window. Files that can not be displayed online, or require a plugin, will prompt you with a download confirmation message.

    + + \ No newline at end of file diff --git a/documentation/instructor/forum_export.php b/documentation/instructor/forum_export.php new file mode 100644 index 000000000..84e2cbecd --- /dev/null +++ b/documentation/instructor/forum_export.php @@ -0,0 +1,7 @@ + + +

    Export Forums

    + +

    This utility can be used to take a static copy of a course forum as a standalone archive of messages contained in the forum exported. An exported forum might be uploaded into the File Manager, unzipped there, and linked from a content page in ATutor to make past discussions available to current students. Or, an exported forum might be uploaded to an external Web site to make it available as an archive outside of ATutor.

    + + diff --git a/documentation/instructor/forums.php b/documentation/instructor/forums.php new file mode 100644 index 000000000..377e2f5ec --- /dev/null +++ b/documentation/instructor/forums.php @@ -0,0 +1,19 @@ + + +

    Forums

    +

    A Forum is an area that allows course members to communicate in a structured manner through messages. The forums can be enabled or disabled and linked from the main navigation, the course home page, or displayed as a menu module. These preferences can be specified in the Course Tools section of the Manage area.

    + +

    Course instructors and students with forum privileges can manage and mediate the forums by deleting, locking, and sticking threads and messages.

    + +

    Creating Forums

    +

    To create a new forum, use the Create Forum link in the Forums section of the Manage area and enter a title and optional description. New forums will be accessible to all course students, instructors, and alumni. Forums can also be created for access by Groups only.

    + +

    Editing & Deleting Forums

    +

    To edit an existing forum, select the forum in the Forums manager and use the Edit button. Make the necessary changes and then use Save to return to the Forum Manager, or Cancel to return to the Forum Manager without saving any changes.

    + +

    To delete an existing forum, select the forum in the Forums manager and use the Delete button. The forum and all threads and messages within it will be deleted.

    + +

    Shared Forums

    +

    Shared forums are special forums where members of different courses can engage in discussions with one another. The instructors, and students with forum privileges, of each participating course can manage and moderate the shared forum. Shared forums can only be created by an administrator.

    + + \ No newline at end of file diff --git a/documentation/instructor/fr/index.php b/documentation/instructor/fr/index.php new file mode 100644 index 000000000..83b4909db --- /dev/null +++ b/documentation/instructor/fr/index.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/documentation/instructor/glossary.php b/documentation/instructor/glossary.php new file mode 100644 index 000000000..617bd8dbd --- /dev/null +++ b/documentation/instructor/glossary.php @@ -0,0 +1,8 @@ + + +

    Glossary

    +

    The Glossary lists all course terms, their definitions and related terms. Terms can be added to the glossary by using the Add Glossary Term link in the Glossary Manager, or by adding terms directly into content while it is being created using the Content Editor.

    + +

    The Glossary is a Course Tool and can therefore be enabled or disabled, linked from the main menu, linked from the home page, or displayed as a module in the side menu.

    + + diff --git a/documentation/instructor/glossary_terms.php b/documentation/instructor/glossary_terms.php new file mode 100644 index 000000000..feee3bb6f --- /dev/null +++ b/documentation/instructor/glossary_terms.php @@ -0,0 +1,8 @@ + + +

    Glossary Terms

    +

    If Terms were specified in the Content tab, they may be defined under the Glossary tab. Enter the definition or explanation for each term specified in the Content. See the Terms section in Entering Content for details on how to add glossary terms to your content.

    + +

    It is also possible to relate terms to one another within the glossary by specifying a related term. This will add a link to the related term beside the glossary item.

    + + \ No newline at end of file diff --git a/documentation/instructor/gradebook.php b/documentation/instructor/gradebook.php new file mode 100644 index 000000000..b8d324d24 --- /dev/null +++ b/documentation/instructor/gradebook.php @@ -0,0 +1,20 @@ + + +

    Gradebook

    +

    The Gradebook can be used to manage students' marks from ATutor generated tests, from ATutor assignments, or from external tests and assignments. Multiple Tests can be combined into a single Gradebook entry. Custom scales can be defined and used across courses. Gradebook data can be exported for reporting and data analysis.

    + + diff --git a/documentation/instructor/gradebook_add.php b/documentation/instructor/gradebook_add.php new file mode 100644 index 000000000..625efbe1f --- /dev/null +++ b/documentation/instructor/gradebook_add.php @@ -0,0 +1,28 @@ + + +

    Add ATutor Test/Assignment to Gradebook

    + +
    +
    Add ATutor Assignment
    +
    Assignments that have been created using the ATutor Assignment Manager can be added to the gradebook. Select the Title of the assignment from those available, then select the scale to be used. Once an assignment has been added to the gradebook, marks are entered as External Marks. Marks may be entered either as a percentage mark or a scale mark. If percentage is used, the gradebook will attempt to convert those percentage marks to a scale mark
    + +
    Add ATutor Test
    +
    Tests that are created using the ATutor Test & Survey Manager can be added to the gradebook if the test's Attempts Allowed property has been set to 1 attempt. Select the test Title from those available, then select the Grade Scale to be used for the test. Additional scales can be created by using the Grade Scales tool. Note that Surveys will not be available to add to the Gradebook. Surveys are technically tests in the Test & Survey Manager that do not have any "weight" or mark assigned to questions.
    +
    + + + diff --git a/documentation/instructor/gradebook_edit_marks.php b/documentation/instructor/gradebook_edit_marks.php new file mode 100644 index 000000000..2606b6bdf --- /dev/null +++ b/documentation/instructor/gradebook_edit_marks.php @@ -0,0 +1,20 @@ + + +

    Edit Marks

    +

    Use the Search feature to narrow the data displayed in the table below. The table below can be printed as a report, or the data displayed in it can be exported as a CSV file to be reimported into a spreadsheet or database for archiving or for additional analysis. External marks displayed in the table can be edited directly from within the table by choosing the appropriate edit link, editing either marks for a single student, or editing marks for a single test. To edit marks from ATutor based tests, use the Test & Surveys Manager. When editing ATutor based marks, be sure to run Update ATutor Marks to have the changes take affect in the Gradebook..

    + + diff --git a/documentation/instructor/gradebook_external_marks.php b/documentation/instructor/gradebook_external_marks.php new file mode 100644 index 000000000..75fe3a257 --- /dev/null +++ b/documentation/instructor/gradebook_external_marks.php @@ -0,0 +1,28 @@ + + +

    External Marks

    + +
    +
    Export
    +
    The Export tool is used to export a course list in a CSV form into which marks can be entered manually, then reimported back into the Gradebook. It can also be used to export marks from the Gradebook to import those marks back into another application such as a spreadsheet, or another database.
    + +
    Import
    +
    Marks from an external assignment or test can be imported in a Comma Separated Values (CSV) file in the form "firstname", "lastname", "email", "grade" with one student per line. The mark can either be a scale mark such "A" or "Pass", or a percentage mark such as 78%. Select the test or assignment previously defined through Add Tests/Assignment The first line of the imported file should contain the field names "First Name, Last Name, Email, Grade" If it is not included the first line will be removed when the marks are imported.
    +
    + + + diff --git a/documentation/instructor/gradebook_scales.php b/documentation/instructor/gradebook_scales.php new file mode 100644 index 000000000..6c923bd0f --- /dev/null +++ b/documentation/instructor/gradebook_scales.php @@ -0,0 +1,20 @@ + + +

    Gradebook Scales

    +

    ATutor includes a number of predefined, commonly used grade scales, used to convert number grades into some other form, such as a letter grade, or a Pass/Fail grade. In the Grade Scales submenu choose Add Grade Scale to create your own custom scale(s). You may choose to populate the Grade Scale form with an existing scale, then edit the scale to create a new one. Or, you may create a brand new scale. Once created, new scales become available to you in any of the course you own.

    + + diff --git a/documentation/instructor/gradebook_update.php b/documentation/instructor/gradebook_update.php new file mode 100644 index 000000000..ef99977d9 --- /dev/null +++ b/documentation/instructor/gradebook_update.php @@ -0,0 +1,29 @@ + + +

    Update ATutor Marks

    + + +
    +
    Update ATutor Marks
    +
    Marks are imported from ATutor tests, rather than displaying them live from the Test & Surveys Manager. Therefore, when marks are updated in the Test & Surveys Manager, the Gradebook needs to be updated to reimport the modified marks. You may choose to update all ATutor tests at once, or choose only to update a single test at a time. Or, you may choose to update only marks for a single student, on all test or a single test. Note that instructors' grades, produced when an instructor takes a test, are not included when marks are updated.
    + +
    Combine ATutor Tests
    +
    Different ATutor generated tests can be combined into a single gradebook entry, if for instance you needed to combine marks from a term test, and marks from a make up test for students who happened to miss the term test. As many tests as required can be combined into a single parent test listed in the Combine Into menu. Select the test to be combined from the Combined From select menu, then press the Combine button to import the marks from that test. Be sure to run Update ATutor Marks on the Combined Into test at least once before combining marks from other tests. When combining marks from multiple tests, should you encounter a conflict such as a mark that already exists for a particular student, you will be given the option to overwrite the old mark with the new one, use the old mark, use the higher mark, or use the lower mark.
    +
    + + + diff --git a/documentation/instructor/groups.php b/documentation/instructor/groups.php new file mode 100644 index 000000000..e54c3381a --- /dev/null +++ b/documentation/instructor/groups.php @@ -0,0 +1,25 @@ + + +

    Groups

    +

    The group area allows an instructor or assistant with Group privileges to create and manage groups of enrolled students within various projects. This may be useful for assigning group-specific tests or assignments, brainstorming, collaborative projects, online discussions and case studies, peer editing or responses, and so on.

    + +

    There are two ways to create groups: manually or by using automated tools to generate the groups. To facilitate the creation of multiple sets of groups, groups are collected together by type. An example of a group type may be "Tutorials" or "Project A". This feature lets students belong to different groups across multiple projects.

    + +

    Create multiple groups automatically

    +

    Creating groups automatically allows an instructor to specify the number of groups to create, or the number of students per group, and populate groups accordingly.

    + +

    Enter the group type, group prefix (such as "Tutorial" - the groups will then be named "Tutorial 1", "Tutorial 2",...), and a default description that will be applied to each group. To determine the number of groups that will be created, enter the number of students per group, or the number of groups. Groups will automatically be created accordingly. Check the box beside "Fill groups randomly upon creation" to populate groups evenly at random. Uncheck this box to create the groups, but populate them manually at a later time. Finally, choose which tools will be made avialable to this groups (File Storage, Forum, Blog, Links, etc.) then use the Create button.

    + + +

    Create a single group manually

    +

    It is also possible to create groups manually, one by one. This is a good way of creating groups if you have particular needs in terms of which students work together, and a relatively small class. This method is also good for adding groups to existing projects or types of groups. For example, if groups were created and populated automatically, and then several new students enroll, it's possible to manually add the new students to a new group within an existing project or type. A single group might be created to which students who missed a quiz could be assigned, so they could take a makeup test. A single group might be created with the blog tool enabled, so all students in the class can post to the same blog.

    + +

    Enter the type of project, title, and description. Also choose which tools will be made avialable to this groups (File Storage, Forums, Blog, etc.) and use the Create button.

    + +

    Managing Groups

    + +

    From the groups page, it is possible to Edit a group's title, description and tools, or Delete a group entirely.

    + +

    To manage the members of a group, select the group and use the Members button. The group members screen displays the course list. All unassigned students can automatically be added to the group by using the Assign Unassigned button, or picked manually and saved using the drop down menus, and then the using Save button.

    + + diff --git a/documentation/instructor/index.php b/documentation/instructor/index.php new file mode 100644 index 000000000..9731078a7 --- /dev/null +++ b/documentation/instructor/index.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/documentation/instructor/introduction.php b/documentation/instructor/introduction.php new file mode 100644 index 000000000..7fa0b5df3 --- /dev/null +++ b/documentation/instructor/introduction.php @@ -0,0 +1,9 @@ + + +

    Introduction

    +

    Welcome to the ATutor Instructor Documentation!

    + +

    Most of the course management tools are found in the Manage section and are available to instructors and students with assigned privileges (assistants).

    + + + diff --git a/documentation/instructor/links.php b/documentation/instructor/links.php new file mode 100644 index 000000000..7b8e91cb7 --- /dev/null +++ b/documentation/instructor/links.php @@ -0,0 +1,14 @@ + + +

    Links

    + +

    Links to external websites can be added to the course Links area, allowing course members to visit course related information elsewhere on the Web. Both students and instructors can add links by using Suggest Link. Student-submitted links must be approved by the instructor in the Links Manager. Instructors can also add links by using Add Link in the Links Manager.

    + +

    The Links section is a Course Tool and can therefore be enabled or disabled, linked from the main menu or linked from the home page, and assigned as student privilege.

    + +

    The Links section can be selected for Groups. Groups members can add links, without them having to be approved by the Instructor, and either keep them private to the group, and only its members, or make them public, so others not in the group can browse through them.

    + +

    Link Categories

    +

    It is necessary to create at least one category before adding any links. Use Create Category in the Links Manager to create categories for organising course links. To edit or delete existing categories use the Categories link in the Links Manager. Note that categories that have links associated with them may not be deleted until those links are removed from the category.

    + + \ No newline at end of file diff --git a/documentation/instructor/managing_files_folders.php b/documentation/instructor/managing_files_folders.php new file mode 100644 index 000000000..e75443ed6 --- /dev/null +++ b/documentation/instructor/managing_files_folders.php @@ -0,0 +1,13 @@ + + +

    Managing Files & Folders

    + +

    It is possible to Rename, Delete, and Move files and folders within the File Manager. Choose a file (or files if mass-deleting or moving to one location) from the File Manager list, then use the appropriate button for the action.

    + +

    Renaming a file will change the name of the file.

    + +

    Deleting a file deletes all selected files and folders from the ATutor system. If a folder is being deleted, the files within it will also be deleted. Once the deletion is confirmed, the files can not be undeleted.

    + +

    To Move files to a different location, choose a new location from the File Manager tree and use the Move button. Once the move is confirmed, all selected files will be found in the new directory.

    + + \ No newline at end of file diff --git a/documentation/instructor/managing_posts.php b/documentation/instructor/managing_posts.php new file mode 100644 index 000000000..f77829c57 --- /dev/null +++ b/documentation/instructor/managing_posts.php @@ -0,0 +1,29 @@ + + +

    8.4.1 Managing Posts

    +

    The course instructor and assistants with forum privileges can edit and delete posts. Access to these tools are available when viewing a thread message.

    + +
    +
    Edit
    +
    Use the Edit link to edit the title and the body of a post.
    + +
    Delete
    +
    Use the Delete link to delete a post. Deleting the first post from a thread will delete the entire thread including all replies. A confirmation will be asked prior to each deletion.
    +
    + + + diff --git a/documentation/instructor/managing_threads.php b/documentation/instructor/managing_threads.php new file mode 100644 index 000000000..928caf419 --- /dev/null +++ b/documentation/instructor/managing_threads.php @@ -0,0 +1,41 @@ + + +

    8.4 Managing Threads

    + +

    As an instructor, it is wise to become familiar with the forum management tools. To access these tools, browse a forum while logged in as an instructor or as an assistant with forum management privileges.

    + +

    For each thread in a forum, the following actions are available:

    +
    +
    Stick Thread
    +

    Use the exclamation point icon next to a thread to stick it. This keeps the specified thread at the top of the forum's thread list and is useful for keeping important information visible to forum users.

    +

    To unstick a thread, just use the Sticky Thread icon again.

    +

    Some possible uses of a sticky thread include: course dates, forum rules, contact information, or important course material.

    +
    + +
    Lock Thread
    +

    Use the Lock icon next to the thread title to lock a thread. There are two options for locking a thread - lock posting and reading, and lock posting only. Lock posting and reading closes the thread so that no one can read the contents or post replies. But note that the title of the thread will remain listed in the forum. Lock posting only will let users read the entire thread but not post any replies to it.

    + +

    To change the lock preferences or unlock a thread, use the Unlock Thread icon.

    +
    + +
    Move Thread
    +
    To move a thread, use the Move Thread icon next to the thread title. This will move all posts within the thread to the target forum.
    + +
    Delete Thread
    +
    To delete a thread, use the Delete Thread icon next to the thread title. This will delete all posts within the thread and cannot be undeleted.
    +
    + + diff --git a/documentation/instructor/pages.inc.php b/documentation/instructor/pages.inc.php new file mode 100644 index 000000000..d7cf433a0 --- /dev/null +++ b/documentation/instructor/pages.inc.php @@ -0,0 +1,71 @@ + \ No newline at end of file diff --git a/documentation/instructor/polls.php b/documentation/instructor/polls.php new file mode 100644 index 000000000..7b39bbc2b --- /dev/null +++ b/documentation/instructor/polls.php @@ -0,0 +1,6 @@ + + +

    Polls

    +

    Polls are useful for quickly gathering course member opinions. Instructors and students with poll privileges can post a question with up to seven choices for answers. Unlike Tests and Surveys, Polls are not graded. Because Polls is a Course Tool, it can be enabled, disabled, and positioned according to the Course Tools preferences.

    + + \ No newline at end of file diff --git a/documentation/instructor/preview.php b/documentation/instructor/preview.php new file mode 100644 index 000000000..d8151de21 --- /dev/null +++ b/documentation/instructor/preview.php @@ -0,0 +1,7 @@ + + +

    Previewing Tests

    + +

    To preview the questions of a test or survey, choose the test from the Tests/Survey Manager and use the Preview button. The screen displayed shows what the student will see when he/she takes a test. Though the exception is previewing randomized tests, which displays all of the questions assigned to that test rather than showing a random number of them (what the student will see).

    + + diff --git a/documentation/instructor/properties.php b/documentation/instructor/properties.php new file mode 100644 index 000000000..a707c65ef --- /dev/null +++ b/documentation/instructor/properties.php @@ -0,0 +1,45 @@ + + +

    Properties

    +

    The Properties Manager allows instructors to adjust the visual, functional, and technical details of a course. Properties set during installation can be changed with the Properties Manager. The Properties Manager is also where you delete a course. Additional properties are managed by the ATutor system administrator, including upload file size limitations and space limitations for a course. Contact an ATutor administrator if these properties need to be changed.

    + +
    +
    Title
    +
    The course name.
    + +
    Primary Language
    +
    If a user has not yet chosen a preferred language, ATutor will display in the language selected here.
    + +
    Description
    +
    An short text description of the course, to display in the Browse Course listing for the course.
    + +
    Course Directory
    +
    If the ATutor administrator has enabled the "Pretty URL" feature, instructors will see a field to enter a name for the course directory, which gets added to a url while in a course. The course directory may contain numbers, letters, underscores, or dashes. No spaces are allowed. If no course directory is defined, the course ID is used in its place. The Pretty URL feature is enabled to turn conventional URL variables an there values (e.g course=21&user=13) into something more readable (e.g. course/21/user/13)
    + + +
    Export Content
    +
    If enabled, students can export course materials as content packages that can be viewed offline. If set to be available only for top level pages, exporting a top level page also exports all its sub-pages.
    + +
    Syndicated Announcements
    +
    If enabled, the course's announcements become available as an RSS feed.
    + +
    Access
    +
    Whether students need to login, and/or enroll, to gain access to a course .
    + +
    Release Date
    +
    The date the course can be accessed by students.
    + +
    Banner
    +
    HTML formatted content that appears at the top of the course home page. Create splash screen, or a customized course front page. It is also possible to create a file called banner.txt, and place it in the top directory of a course file manager, that contains HTML to modify the top header area.
    + +
    Copyright Notice
    +
    Appears in addition to the ATutor copyright notice, to signify the copyright of the content being displayed. Use & copy; (without the space) to create a copyright symbol
    + +
    Icon
    +
    An 80 pixel by 80 pixel icon displayed with the course listing in MyCourses.
    + + +
    + + + diff --git a/documentation/instructor/question_categories.php b/documentation/instructor/question_categories.php new file mode 100644 index 000000000..36f2419ce --- /dev/null +++ b/documentation/instructor/question_categories.php @@ -0,0 +1,6 @@ + + +

    Question Categories

    +

    Question categories are useful for organizing questions by topic, but also to make it easier to add questions into tests. When adding questions, it is possible to simply check the box beside the category name which will add all of the questions within it, instead of having to check each question separately. Use the Create Category link to create a category. It will then appear in the Question Categories manager where it can be Edited and Deleted.

    + + diff --git a/documentation/instructor/question_database.php b/documentation/instructor/question_database.php new file mode 100644 index 000000000..389cc5a25 --- /dev/null +++ b/documentation/instructor/question_database.php @@ -0,0 +1,12 @@ + + +

    Question Bank

    +

    The Question Bank is where course test and survey questions are stored. Questions are created separately so that they may be reused in different tests and surveys.

    + +
    +
    Import Questions
    +
    Individual questions, or collections of questions that are not yet part of a test, can be imported from IMS QTI 1.2 packages using the Import Questions feature at the top of the Question Bank. Also see Tests & Surveys for details on importing complete tests.
    +
    Export Questions
    +
    Select the checkboxes next to the questions you wish to export, then select the format for the questions (IMS QTI 1.2.1, or IMS QTI 2.1) from the menu below, then click on the Export button to package the questions in an IMS QTI 1.2 question package. These packages can be imported back into ATutor, or into other QTI conformant systems. See Test & Surveys for information about exporting questions with their associated test definition as complete tests. Note that the IMS QTI 2.1 export is experimental only, since there are no tools yet that support QTI 2.1 import. Unless you are a developer, you probably want to export as a QTI 1.2 package.
    +
    + diff --git a/documentation/instructor/reading_list.php b/documentation/instructor/reading_list.php new file mode 100644 index 000000000..8e745fa72 --- /dev/null +++ b/documentation/instructor/reading_list.php @@ -0,0 +1,12 @@ + + +

    Reading List

    +

    The reading list area allows instructors and assistants with Reading List privileges to list course resources and schedule when they should be read.

    + +

    Managing Reading List

    +

    A new reading list entry can be added by selecting book, URL, handout, AV, or file from the "Type of Reading" dropdown at the top and using the Create button. If a resource of that type does not already exist, the instructor is prompted to add one. After this is complete, or if resources of that type do exist, the add reading list entry page is displayed. Choose the name of the resource from the dropdown, or follow the Create New link to add a new one. Specify if it is required or optional reading, and add a comment if necessary. It is also possible to specify a "read by" date by entering the start and end reading dates. Use the Save button to create the entry.

    + +

    Managing Resources

    +

    To manage resources, follow the Resources link. Create a new resource by selecting its type from the dropdown at the top and using the Create button. Enter the title, author, year, publisher, ISBN, and comment and use the Save button. To Edit or Delete an entry, choose it from the list and use the appropriate button.

    + + \ No newline at end of file diff --git a/documentation/instructor/scorm_packages.php b/documentation/instructor/scorm_packages.php new file mode 100644 index 000000000..a8ffdafcf --- /dev/null +++ b/documentation/instructor/scorm_packages.php @@ -0,0 +1,24 @@ + + +

    SCORM Packages

    +

    The Packages tool, when enabled, allows instructors to include SCORM 1.2 Sharable Content Objects (SCOs) as part of their courses. SCOs remain separated from the course content as complete learning units. SCOs should not be confused with content packages which are loaded into ATutor using the Import/Export tool in the Content Manager.

    +

    Note: The ATutor SCORM Run-Time Environment (RTE) that plays SCOs requires users to have Java 1.5 (i.e. JRE 1.5) installed on their computer.

    + +

    Use the Packages link from the Manage area to access the following:

    + +
    +
    Import Package
    +

    Upload a SCO from your computer, or enter the URL to a SCO located on the Web to import it into your course.

    + +
    Delete Package
    +

    Removes a SCO from a course, and deletes all associated files.

    + +
    Package Setting (DISABLED IN THIS VERSION OF ATUTOR)
    +
    +

    Credit Mode sets the package to credit or no credit.

    + +

    Lesson Mode is set to browse if the package is to be available for evaluation, or set to normal as a lesson..

    +
    +
    + + \ No newline at end of file diff --git a/documentation/instructor/side_menu.php b/documentation/instructor/side_menu.php new file mode 100644 index 000000000..a925e81e8 --- /dev/null +++ b/documentation/instructor/side_menu.php @@ -0,0 +1,6 @@ + + +

    Side Menu

    +

    Some of the course tools can be accessed through the side menu. Which side menu boxes, and the order in which they will appear, can be controlled by selecting the tool name from the dropdowns, and arrange them in order of preference. Those more likely to be used can be placed at the top of the menu. To remove a menu box, choose the blank option from the selection menus in the side menu editor

    + + \ No newline at end of file diff --git a/documentation/instructor/statistics.php b/documentation/instructor/statistics.php new file mode 100644 index 000000000..85238c195 --- /dev/null +++ b/documentation/instructor/statistics.php @@ -0,0 +1,6 @@ + + +

    Statistics

    +

    The statistics page displays the number of Members (registered users) and Guests (unregistered users) who have logged into the course. Use the Properties manager to control guest access to the course.

    + + \ No newline at end of file diff --git a/documentation/instructor/student_submissions.php b/documentation/instructor/student_submissions.php new file mode 100644 index 000000000..e18ef8035 --- /dev/null +++ b/documentation/instructor/student_submissions.php @@ -0,0 +1,10 @@ + + +

    Test Submissions

    +

    To view the submissions of a test, choose a test from the Test/Survey Manager and use the Submissions button. The list of student submissions will be listed, and can be filtered to show all, marked or unmarked tests.

    + +

    Unmarked tests are those requiring instructor input, or those with open-ended questions. Multiple-choice and true-false questions are automatically marked by the Atutor system and Likert questions do not require marking.

    + +

    To view and/or mark test submissions, choose a submission from the list and use the View & Mark Test button. The test will be displayed with a box beside each question for entering or editing the mark. Multiple-choice and true-false answers show a red "X" icon beside an answer if the student answered incorrectly, or a green checkmark if he/she was right. If an answer is incorrect, the correct answer will be shown with a green checkmark after it in brackets. Use Save to enter the marks into the system and return to the submission manager.

    + + \ No newline at end of file diff --git a/documentation/instructor/student_tools.php b/documentation/instructor/student_tools.php new file mode 100644 index 000000000..6becb4002 --- /dev/null +++ b/documentation/instructor/student_tools.php @@ -0,0 +1,19 @@ + + +

    Course Tools Setup

    +

    Once the course has been created, the Course Tools can be configured. Course Tools is a collection of course features that may be useful for various types of learning activities. It is found under the instructor's Manage tab. Tools include: Forums, Glossary, Site-map, Links, Polls, TILE Repository Search, Tests & Surveys, My Tracker, Export Content, Chat, Directory, Inbox, and Packages. Instructors can decide where these tools will be accessed from - the Main Navigation, and/or Course Home. Instructors can also set the Course Tools to display as just tool icons, or as tool icons plus details from each tool, by clicking the toggle image ( ). Various additional information is included with the detailed view, such as links to the latest posts for forums, random words and phrases from the glossary, or the latest links added to the link database, for example. Also see Student Tools for details on moving tools to their own separate screen, removing them from the course home page.

    +
    +
    Main Navigation
    +
    In the default Atutor theme, the Main Navigation appears as tabs below the title of the course. Course Tools specified to appear here will each have their own tab. Note that other themes may choose to display the Main Navigation differently.
    + +
    Course Home
    +
    Course Home is the first page viewed when a student enters a course. Course Tools that are selected to appear on the Home page will be displayed with an icon and a label above the course announcements.
    +
    + +

    If neither Main Navigation or Course Home options are selected, then that Course Tool be "turned off", or not accessible to students. An instructor might choose to turn on Course Tools as they are needed, for example turning on the Tests & Surveys tool when a test is available, or turning on the Chat when an online meeting is going to take place.

    + +

    Using the arrows in the Order column will change the ordering of the student tool as it appears in the Main Navigation and on the Course Home page.

    + +

    It is also possible to install the Course Tools Module, and use it as a place to locate links to the various ATutor tools, leaving the course home page as a splash screen, or as a place to display course announcments.

    + + \ No newline at end of file diff --git a/documentation/instructor/test_statistics.php b/documentation/instructor/test_statistics.php new file mode 100644 index 000000000..dc7f6a9d3 --- /dev/null +++ b/documentation/instructor/test_statistics.php @@ -0,0 +1,9 @@ + + +

    Test Statistics

    + +

    To view a test's statistics, choose the test from the Test/Survey Manager and use the Statistics button. There are two sets of statistics available for tests. The first is Question Statistics which shows each question of the test and the number/percentage of students who chose each of the answers. Submission Statistics shows each submission's overall mark and marks given for each test question. The overall test average is also calculated.

    + +

    Submission Statistics shows each submission's overall mark and marks given for each test question. The overall test average is also calculated. The statistics data can be filtered by test's date of taken, all or passed or failed students. The filtered data can be downloaded as CSV file.

    + + diff --git a/documentation/instructor/tests_surveys.php b/documentation/instructor/tests_surveys.php new file mode 100644 index 000000000..7d8772e1d --- /dev/null +++ b/documentation/instructor/tests_surveys.php @@ -0,0 +1,14 @@ + + +

    Tests and Surveys

    +

    The instructor, and assistants with test privileges, can create tests and surveys to be administered to enrolled students. There are a variety of options for defining tests like setting the release date, and using randomized questions or group-specific tests. Once a test or survey has been created, add questions to the Question Bank, and then add these questions to the new test.

    + +
    +
    Import Tests & Surveys
    +
    Complete tests including the test definition, as well as their questions, or just the questions without their test definition, can be imported from IMS QTI 1.2 test packages using the Import Test feature at the top of the Tests & Surveys Manager. Note that if the test is included as part of a content package, then it should be imported using the Content Import/Export utility.
    +
    Export Tests & Surveys
    +
    Choose a test from the Tests & Survey Manager, then click on the Export button to package that test in an IMS QTI 1.2 test package. These packages can be imported back into ATutor, or into other QTI conformant systems. See the Question Banke for information about exporting questions without the associated test definition.
    +
    + + + diff --git a/documentation/instructor/tile_repository.php b/documentation/instructor/tile_repository.php new file mode 100644 index 000000000..5089237d6 --- /dev/null +++ b/documentation/instructor/tile_repository.php @@ -0,0 +1,8 @@ + + +

    Transformable Repository (EDIT THIS WHEN TRANSFORMABLE IS FINISHED)

    +

    Content created in ATutor can be exported to the Transformable Repository using the Export tool on the Import/Export Content screen. Content can also be imported from the TILE repository by entering a search term into the TILE Repository Search, then using Import next to a listing in the search results. Use the Preview link next to a search result to open the TILE content browser, or use Download to retrieve the content package from TILE repository. Once downloaded, the retrieved package can be imported into ATutor using the Import/Export Content import tool.

    + +

    Visit the TILE web site for more information about using the repository, and to set up an account on the system.

    + + \ No newline at end of file diff --git a/documentation/instructor/web_search.php b/documentation/instructor/web_search.php new file mode 100644 index 000000000..948f5624c --- /dev/null +++ b/documentation/instructor/web_search.php @@ -0,0 +1,8 @@ + + +

    Web Search

    +

    Since version 1.5.2, course members can use Google to search the web from within an ATutor course.

    + +

    The Web Search is a Course Tool and can therefore be enabled or disabled, linked from the main menu, linked from the home page, or displayed as a side menu box.

    + + diff --git a/documentation/link-out.gif b/documentation/link-out.gif new file mode 100644 index 0000000000000000000000000000000000000000..ecef0947aa06e62f442a1aa33b6830322fb78c64 GIT binary patch literal 52 zcmZ?wbhEHbWM^PwXkcLY|Nnn;bF<=47Dfgj&;b!383rb9mZrpM)jO^vu3N{%U=09c Cehr@h literal 0 HcmV?d00001 diff --git a/documentation/styles.css b/documentation/styles.css new file mode 100644 index 000000000..3322dcf71 --- /dev/null +++ b/documentation/styles.css @@ -0,0 +1,137 @@ +pre { + font-family: Arial, sans-serif; +} + +body{ + background-color: #FFF; + font-family: Verdana,Arial,sans-serif; +} +h1,h2,h3,p, table, ul { + margin: 0 10px +} +h1 { + color: #FFF; + border-bottom: 1px dashed #FFF; + margin-left: -5px; + padding-left: 15px; + margin-right: -5px; + padding-right: 15px; +} +h2{color: #FFF; + border-bottom: 1px dashed #FFF; + margin-left: -5px; + padding-left: 15px; + margin-right: -5px; + padding-right: 15px; +} +h3{color: #f0f0f0} +p{padding-bottom:1em} +h2{padding-top: 0.3em} + +/* for the curves */ +div.nifty { margin: 10px 2%; background: #9BD1FA; } +b.rtop, b.rbottom{display:block;background: #FFF} +b.rtop b, b.rbottom b{display:block;height: 1px; overflow: hidden; background: #9BD1FA} +b.r1{margin: 0 5px} +b.r2{margin: 0 3px} +b.r3{margin: 0 2px} +b.rtop b.r4, b.rbottom b.r4{margin: 0 1px;height: 2px} + + +a { + text-decoration: none; + border-bottom: 1px solid white; + color: white; + font-weight: bold; +} +a:hover { + border-bottom: 0px; +} +td,th { + font-size: 85%; +} + +kbd { + padding: 0px 1px 0px 1px; + border-width: 1px 2px 2px 1px; + border-style: solid; + border-color: #edd #baa #baa #eed; + white-space: pre; +} + +code { + font-family: Verdana,Arial,sans-serif; + background-color: #efefef; + padding: 0px 4px 0px 4px; + border-width: 1px 1px 1px 1px; + border-style: solid; + border-color: #edd #baa #baa #eed; +} + + +div#toc { + color: #f0f0f0; + padding-bottom: 15px; +} + +div#toc ul { + list-style: none; +} +div#toc li { + padding-top: 2px; + padding-bottom: 0px; +} + +ol { + margin-top: 0px; +} + +ol li { + padding-bottom: 3px; +} + +dl { + margin: 0 10px +} +dl dd { + padding-top: 0px; + padding-left: 5px; + margin-left: 5%; + border-left: 1px solid #f0f0f0; + margin-bottom: 10px; +} + +acronym { + cursor: help; +} + +a[href*="http"] { + padding-right: 8px; + background-image: url('link-out.gif'); + background-repeat: no-repeat; + background-position: right 4px; + margin-right: 2px; +} + +div#nav-links { + margin-left: 20px; + margin-right: 20px; + margin-bottom: 20px; +} + +div#nav-links a { + color: black; + text-decoration: none; + border-bottom: 1px solid; +} + +pre { + font-family: Courier, monospace; + background-color: #EEEEFF; + padding: 5px; + margin-left: 20px; + color:#761596; + margin-top: 0px; + width: 50%; + font-size: smaller; +} \ No newline at end of file diff --git a/enroll.php b/enroll.php new file mode 100644 index 000000000..284bbb588 --- /dev/null +++ b/enroll.php @@ -0,0 +1,165 @@ +From = $_config['contact_email']; + $mail->AddAddress($to_email); + $mail->Subject = _AT('course_enrolment'); + $mail->Body = $tmp_message; + + if(!$mail->Send()) { + //echo 'There was an error sending the message'; + $msg->printErrors('SENDING_ERROR'); + exit; + } + unset($mail); + } + } + } else { + // public or protected + $sql = "INSERT INTO ".TABLE_PREFIX."course_enrollment VALUES ($_SESSION[member_id], $_POST[form_course_id], 'y', 0, '"._AT('student')."', 0)"; + $result = mysql_query($sql, $db); + } +} + +if ($_SESSION['valid_user']) { + + $sql = "SELECT * FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$_SESSION[member_id] AND course_id=$course"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_array($result); + + if (($course_info[0] == 'public') || ($course_info[0] == 'protected')) { + if ($row != '') { + + $feedback = array('NOW_ENROLLED', $system_courses[$course]['title']); + $msg->addFeedback($feedback); + header("Location:index.php"); + } else if ($course_info[1] != $_SESSION['member_id']) { + + require(AT_INCLUDE_PATH.'header.inc.php'); ?> + +
    + +
    +
    +

    +
    + +
    + +
    +
    +
    +printErrors('ALREADY_OWNED'); + } + } else { // private + + require(AT_INCLUDE_PATH.'header.inc.php'); + + if ((!$_POST['submit']) && ($row == '')) { + + $msg->printInfos('PRIVATE_ENROL'); ?> + +
    + + +
    +printFeedbacks('APPROVAL_PENDING'); + } else if ($course_info[1] != $_SESSION['member_id'] ){ + // request has already been made + $msg->printErrors('ALREADY_ENROLED'); + } else { + $msg->printErrors('ALREADY_OWNED'); + } + } + +} else { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('LOGIN_ENROL'); + ?> +
    +
    +

    +

    +
    +
    + +
    +
    +
    +

    +

    +
    +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/exestyles.css b/exestyles.css new file mode 100644 index 000000000..1b033f68a --- /dev/null +++ b/exestyles.css @@ -0,0 +1,163 @@ +/* +=========================================================================== +eXe +Copyright 2007. eXelearning. +style sheet for default theme +see: http://exelearning.org/Creating_a_new_Style for more info on how +to modify this style for your needs. +=========================================================================== +*/ + +#content-text { + margin: 10px 20px 10px 20px; + padding: 10px; + color:#4d4d4d; + background: #E0DFD8; + border:thin solid grey; +} + + +a { + color: #FF6600; +} + +a:hover { + color: #B34700; +} + +/* --- Styles for the node/page titles --- */ + +p#nodeTitle { + color: #4d4d4d; + font-size: 32px; + letter-spacing: -1px; + text-align:left; + padding-right: 30px; +} + +div#nodeDecoration { + padding: 2px; + border-bottom:0px; + text-align:right; +} + + +/* --- iDevice Styles -- */ + + +.emphasis0 { + padding-bottom: 20px; +} + + +.emphasis1 { + background-color: #FFF; + border-bottom: 1px solid #9d9d9d; + background-image:url(top_left_corner.gif); /* The left curve */ + background-position:top left; + background-repeat:no-repeat; + margin-bottom: 30px; + text-align:left; +} + + +.iDevice p, .iDevice div.block { + padding-left: 0px; + line-height: 115% + text-align:left; +} + + +.iDevice li { + list-style-position: outside; +} + +.iDeviceTitle { + color: #568D14; + font-size: 24px; + font-weight:bold; + letter-spacing:-1px; + background: #FFF; + padding-bottom: 0px; + padding-right: 10px; + margin-bottom: 0px; + background-image:url(top_right_corner.gif); + background-position:top right; + background-repeat:no-repeat; + top: 0; +} + +.emphasis0 .iDeviceTitle { + color: #568D14; + font-size: 24px; + font-weight:bold; + letter-spacing:-1px; + vertical-align: top; + background: transparent; + padding-bottom: 4px; + padding-right: 10px; + margin-bottom: 0px; + background-image:none; + background-position:top right; + background-repeat:no-repeat; + } + +.iDevice_icon { + margin-top: 0px; + margin-left: 10px; + padding-right: 10px; + top: 0; +} + +.iDevice_inner { + color:#4d4d4d; + background-color:#f2f2ef; + padding: 10px; + margin: 10px; + margin-top: 12px; + padding-top: 10px; + background-image:none; +} + +.emphasis0 .iDevice_inner { + color:#4d4d4d; + background-color:transparent; + padding: 10px; + margin: 10px; + margin-top: 12px; + padding-top: 10px; +} + +/* styles for iDevices with Feedback and form buttons */ + +input.feedbackbutton { + background: #DBE992; +} + +p.reading_feedback { + border: 1px dashed #ADADAD; + background-color: #FFF; +} + + +/* styles for image with text iDevice */ + +.image_text { + border: 1px solid #9D9D9D; + background: transparent; + padding: 4px; + margin: 0px 20px 4px 4px; +} + +/* override definition on "fieldset#shortcuts" in themes style.css */ + +fieldset#shortcuts { + float: right; + background-color: #FEFDEF; + border: 1pt solid #B8AE9C; + margin: 0pt 20pt 5pt 5pt; + padding-right: 10pt; + padding-bottom: 5pt; + padding-left: 10pt; +} + diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..205d1197435ba12d38706e49777f08672227b232 GIT binary patch literal 3249 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 0) && !empty($_SERVER['ORIG_PATH_INFO'])){ + //http://www.atutor.ca/atutor/mantis/view.php?id=3436 + $current_file = $_SERVER['ORIG_PATH_INFO']; + } else if (!empty($_SERVER['PATH_INFO'])) { + $current_file = $_SERVER['PATH_INFO']; + } else if (!empty($_SERVER['REQUEST_URI'])) { + $current_file = $_SERVER['REQUEST_URI']; + } else if (!empty($_SERVER['PHP_SELF'])) { + if (!empty($_SERVER['QUERY_STRING'])) { + $current_file = $_SERVER['PHP_SELF'] . '?' . $_SERVER['QUERY_STRING']; + } else { + $current_file = $_SERVER['PHP_SELF']; + } + } else if (!empty($_SERVER['SCRIPT_NAME'])) { + if (!empty($_SERVER['QUERY_STRING'])) { + $current_file = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING']; + } else { + $current_file = $_SERVER['SCRIPT_NAME']; + } + } else if (!empty($_SERVER['URL'])) { + if (!empty($_SERVER['QUERY_STRING'])) { + $current_file = $_SERVER['URL'] . '?' . $_SERVER['QUERY_STRING']; + } + $current_file = $_SERVER['URL']; + } + + if (($pos = strpos($current_file, '/get.php')) !== FALSE) { + $current_file = substr($current_file, $pos + strlen('/get.php')); + } + if (substr($current_file, 0, 2) == '/@') { + $force_download = true; + $current_file = substr($current_file, 2); + } + +} else { + $current_file = $_GET['f']; + + if (substr($current_file, 0, 2) == '/@') { + $force_download = true; + $current_file = substr($current_file, 2); + } +} + +$file_name = pathinfo($current_file); +$file_name = $file_name['basename']; + +if (substr($file_name, 0, 4) == 'b64:') { + $base64_file_name = substr($file_name, 4); + $file_name = base64_decode($base64_file_name); + $current_file = '/'.$file_name; +} + + +$file = AT_CONTENT_DIR . $_SESSION['course_id'] . $current_file; + +//send header mime type +$pathinfo = pathinfo($file); +$ext = $pathinfo['extension']; +if ($ext == '') { + $ext = 'application/octet-stream'; +} else { + $ext = $mime[$ext][0]; +} + +//check that this file is within the content directory & exists + +// NOTE!! for some reason realpath() is not returning FALSE when the file doesn't exist! +$real = realpath($file); + +if (file_exists($real) && (substr($real, 0, strlen(AT_CONTENT_DIR)) == AT_CONTENT_DIR)) { + if ($force_download) { + header('Content-Type: application/force-download'); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="'.$pathinfo['basename'].'"'); + } else { + header('Content-Disposition: filename="'.$pathinfo['basename'].'"'); + } + + /** + * although we can check if mod_xsendfile is installed in apache2 + * we can't actually check if it's enabled. also, we can't check if + * it's enabled and installed in lighty, so instead we send the + * header anyway, if it works then the line after it will not + * execute. if it doesn't work, then the line after it will replace + * it so that the full server path is not exposed. + * + * x-sendfile is supported in apache2 and lighttpd 1.5+ (previously + * named x-send-file in lighttpd 1.4) + */ + header('x-Sendfile: '.$real); + header('x-Sendfile: ', TRUE); // if we get here then it didn't work + + header('Content-Type: '.$ext); + + @readfile($real); + exit; +} else { + header('HTTP/1.1 404 Not Found', TRUE); + exit; +} + +?> \ No newline at end of file diff --git a/get_acheck.php b/get_acheck.php new file mode 100644 index 000000000..74d0071db --- /dev/null +++ b/get_acheck.php @@ -0,0 +1,43 @@ + \ No newline at end of file diff --git a/get_course_icon.php b/get_course_icon.php new file mode 100644 index 000000000..fff9fca58 --- /dev/null +++ b/get_course_icon.php @@ -0,0 +1,71 @@ + \ No newline at end of file diff --git a/get_noid.php b/get_noid.php new file mode 100644 index 000000000..03464e912 --- /dev/null +++ b/get_noid.php @@ -0,0 +1,133 @@ + \ No newline at end of file diff --git a/get_profile_img.php b/get_profile_img.php new file mode 100644 index 000000000..8b1f16058 --- /dev/null +++ b/get_profile_img.php @@ -0,0 +1,84 @@ + \ No newline at end of file diff --git a/get_rss.php b/get_rss.php new file mode 100644 index 000000000..f68ddda6c --- /dev/null +++ b/get_rss.php @@ -0,0 +1,101 @@ +useCached(); + $rss->title = $system_courses[$course]['title']; + $rss->description = $system_courses[$course]['description']; + $rss->link = AT_BASE_HREF; + $rss->syndicationURL = AT_BASE_HREF; + + $image = new FeedImage(); + $image->title = 'ATutor Logo'; + $image->url = AT_BASE_HREF . 'images/at-logo.v.3.gif'; + $image->link = AT_BASE_HREF; + $rss->image = $image; + + $sql = "SELECT A.*, M.login from ".TABLE_PREFIX."news A, ".TABLE_PREFIX."members M WHERE A.course_id = ".$course." AND A.member_id=M.member_id ORDER BY A.date DESC LIMIT 5"; + + $res = mysql_query($sql, $db); + + while ($data = mysql_fetch_assoc($res)) { + $item = new FeedItem(); + + $item->title = $data['title']; + $item->link = AT_BASE_HREF . 'index.php'; + $item->description = $data['body']; + $item->date = strtotime($data['date']); + $item->source = AT_BASE_HREF; + $item->author = $data['login']; + + $rss->addItem($item); + } + + header('Content-Type: text/xml'); + $rss->saveFeed('RSS'.$version.'.0', AT_CONTENT_DIR . 'feeds/' . $course . '/RSS' . $version . '.0.xml', false); + + echo file_get_contents(AT_CONTENT_DIR . 'feeds/' . $course . '/RSS'.$version.'.0.xml'); + + exit; +} // else: this course didn't enable rss + +header('HTTP/1.1 404 Not Found'); +exit; + + +?> \ No newline at end of file diff --git a/go.php b/go.php new file mode 100644 index 000000000..b7ba6454d --- /dev/null +++ b/go.php @@ -0,0 +1,115 @@ +getPathArray(); +$_pretty_url_course_id = $path_array[0]; +$obj = $path_array[1]; + +if (!$obj->isEmpty()){ + /* + * Addresses the issue for relative uri + * @refer to constants.inc.php $_rel_link + */ + $_rel_url = $obj->redirect(); + $var_query = $obj->parsePrettyQuery(); + save2Get($var_query); //remake all the _GET and _REQUEST variables so that the vitals can use it + + $_user_location = ''; //reset user_location so that the vital file in each page would validate + $pretty_current_page = $obj->getPage(); + //If page not found, forward back to index.php + //ignore error, cause it will give a warning: No such file error, and can't send header. + if (!@include($pretty_current_page)){ + header('Location: '.AT_BASE_HREF.'index.php'); + exit; + } +} elseif ($_pretty_url_course_id==0) { + //TODO: $_SESSION[course_id] seems to be resetted to 0, causing vitals.inc.php line 273 to redirect incorrectly. + // Need to find out where exactly the course_id is being resetted. + return; +// header('location: '.AT_BASE_HREF.'bounce.php?course=0'); +// exit; +} elseif ($_pretty_url_course_id != ''){ + header('location: '.AT_BASE_HREF.'bounce.php?course='.$_pretty_url_course_id); + exit; +} + + +/** + * This function will reconstruct all the $_GET variables. + * @param array consist of all the pathinfo variables in querystring format + */ +function save2Get($var_query){ + if (empty($var_query) || !is_array($var_query)) + return; + foreach($var_query as $k=>$v){ + if ($k=='page_to_load'){ + continue; + } + + //If mod_rewrite is on, the page# will be shown as .html. + //in this case, parse the page number out. + if ($k=='page'){ + if (preg_match('/(.*)\.html$/', $v, $matches)==1){ + $v = $matches[1]; + } + } + $_GET[$k] = $v; + $_REQUEST[$k] = $v; + } +} + +/** + * Get path info + * @return the path info string + */ +function getPathInfo(){ + //Handles path_info in diff versions of PHP + if(isset($_SERVER['ORIG_PATH_INFO']) && $_SERVER['ORIG_PATH_INFO']!=''){ + //if both PATH_INFO and ORIG_PATH_INFO are set, then decide which to use by str ops. + if (isset($_SERVER['PATH_INFO'])){ + $pathpos = strpos($_SERVER['ORIG_PATH_INFO'], $_SERVER['SCRIPT_NAME']); + $pathlen = strlen($_SERVER['SCRIPT_NAME']); + if (substr($_SERVER['ORIG_PATH_INFO'], $pathpos + $pathlen) == $_SERVER['PATH_INFO']){ + return $_SERVER['PATH_INFO']; + } + } + return $_SERVER['ORIG_PATH_INFO']; + } else { + return $_SERVER['PATH_INFO']; + } +} +?> \ No newline at end of file diff --git a/headstuff.php b/headstuff.php new file mode 100644 index 000000000..b8d644fe9 --- /dev/null +++ b/headstuff.php @@ -0,0 +1,122 @@ +', $styleStartPos) + 1; + $styleEndPos = strPos ($headText, '', $linkStartPos); + + // ensure this is a 'stylesheet' link + $stylesheetPos = strpos ($headText, "\"text/css\"", $linkStartPos); + if (($stylesheetPos !== false) && ($stylesheetPos < $linkEndPos)){ + + // get the 'href' attribute value + $hrefPos = strpos ($headText, 'href', $linkStartPos); + if ($hrefPos !== false){ + $hrefPos = strpos ($headText, '=', $hrefPos); + if ($hrefPos !== false){ + $hrefPos += '1'; + + // get first character in attribute value + $index = $hrefPos; + + // find first attribute character + while (($headText[$index] == " ") || ($headText[$index] == '\'') || ($headText[$index] == "\"")){ + $index++; + if ($index > strlen($headText)){ + break; + } + } + $indexStart = $index; + + // find end of attribute character + $indexEnd = $indexStart; + while (($headText[$indexEnd] != " ") && ($headText[$indexEnd] != '\'') && ($headText[$indexEnd] != "\"")){ + $indexEnd++; + if ($index > strlen($headText)){ + break; + } + } + } + } + + // convert the href attribute value to an "import url" statement + $importStatement = '@import url('.$path.substr ($headText, $indexStart, ($indexEnd - $indexStart)).');'; + $styleText = $styleText."\n".$importStatement; + } + + // look for another 'link' element + $linkStartPos = strpos ($headText, ' \ No newline at end of file diff --git a/help/accessibility.php b/help/accessibility.php new file mode 100644 index 000000000..135689a10 --- /dev/null +++ b/help/accessibility.php @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/help/contact_support.php b/help/contact_support.php new file mode 100644 index 000000000..4c603610a --- /dev/null +++ b/help/contact_support.php @@ -0,0 +1,130 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} + +$onload = 'document.form.from.focus();'; +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($_SESSION['member_id']) { + $sql = "SELECT first_name, last_name, email FROM ".TABLE_PREFIX."members WHERE member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_array($result)) { + $student_name = AT_print($row['last_name'], 'members.last_name'); + $student_name .= (AT_print($row['first_name'], 'members.first_name') ? ', '.AT_print($row['first_name'], 'members.first_name') : ''); + + $student_email = AT_print($row['email'], 'members.email'); + } +} + +if (!$_config['contact_email']) { + $msg->printErrors('CONTACT_INFO_NOT_FOUND'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +if (isset($_POST['submit'])) { + $missing_fields = array(); + + $_POST['subject'] = trim($_POST['subject']); + $_POST['body'] = trim($_POST['body']); + + if ($_POST['from'] == '') { + $missing_fields[] = _AT('from_name'); + } + + if ($_POST['from_email'] == '') { + $missing_fields[] = _AT('from_email'); + } else if (!preg_match("/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,6}$/i", $_POST['from_email'])) { + $msg->addError('EMAIL_INVALID'); + } + + if ($_POST['subject'] == '') { + $missing_fields[] = _AT('subject'); + } + + if ($_POST['body'] == '') { + $missing_fields[] = _AT('body'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + $mail = new ATutorMailer; + + $mail->From = $_POST['from_email']; + $mail->FromName = $stripslashes($_POST['from']); + $mail->AddAddress($_config['contact_email']); + $mail->Subject = $stripslashes($_POST['subject']); + $mail->Body = $stripslashes($_POST['body']); + + if(!$mail->Send()) { + $msg->printErrors('SENDING_ERROR'); + exit; + } + unset($mail); + + $msg->printFeedbacks('ACTION_COMPLETED_SUCCESSFULLY'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } +} + +$msg->printErrors(); +?> +
    +
    +
    + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/help/index.php b/help/index.php new file mode 100644 index 000000000..bbee5201d --- /dev/null +++ b/help/index.php @@ -0,0 +1,44 @@ + +
      +

    • +
    • + +
    • +
    • + +
    • +
    + +

    +
      +
    • +
    • + +
    • +
    • + +
    • +
    • +
    + + \ No newline at end of file diff --git a/images/AT_Logo_1.png b/images/AT_Logo_1.png new file mode 100644 index 0000000000000000000000000000000000000000..21cc70871718916be1426f92bd4cc8d349e815da GIT binary patch literal 14919 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 literal 0 HcmV?d00001 diff --git a/images/AT_Logo_1.psd b/images/AT_Logo_1.psd new file mode 100644 index 0000000000000000000000000000000000000000..1ff684d35380bbbe3dfa3746a21f38136c42c790 GIT binary patch literal 322752 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%0A?EG@d1x zT^87FC!S&-aW2r$pI3GybL{eox{OasP>-DA$&up8rySdu=!2{IUVB+=2hCj<fvS|Eat8T^hS63+%d87p5(*9pTt^z{qRMn{HIU z)YMV&w&vF6oJ{IQ%DZS+YFFabCjX|1)$}V^$3(}J_OM5xRm_%rIODV3bI|j5pc2ZA z%4rDeV+qo9jj$a25;@q0ObRr}d|Hq~>*z z`*86=DPRA?gBirETYFmS`;Vi|phx93TKtcK5+;Famj;v0tA~;E8IBjT$pu*jp(*GO}pLQ%<$zRMtw+tJ(WEz*rJZ&t+2 zRL{y=*0cIv?Ov-ZnN!&n`Hi?mX|t#G<@5D$7ueh4%kS&Tw`eg@vrp;Qsrd3QQu9)$ zVsT@S3q_bJ^0RicEQMrGrB720WTd=de;1FJX1?csFUh&c$ryMOQh_*v z9g{b%jSY>_^iKu|@L68HemEU`yZ)B?yle|!8y-Rz&i3qd*L76(#rb z(N=(a?caNo&Q?WF699sk0RS2W0C&&-@DKp}zyNS$4FF=f06^yP-fC0{0BAK;6=V$p zR!<57KN>7%jk_M2b|1Ps{X)Z$lXoK2C2&L4OfJ<$BOdokR>jCD&H(M&t(UTteFrIO zEN~4wm8uHnC&a2QfLQs)6k45?;z*>bEF-eGW-AB z=nH?EiJ)3Uzx_(R;OnDG>B#?x?^JB~5!4zJPuVd~_5rU-YwB50%`)zoe& zW48JDTL_JaeZZ-ZWl+FZGYQ)zb&k^@cWjwMpoNVS7@(1PG=N z+m?Ur*M|#<_|;qF8?E}0@hLsK(JWYG{a4TKYYrQtSC6+7{ozavP8*FU-4xc?p}56^ zqb)O>^?gP*;0GJQ%fFVpl|XXp@--7M)FY1wyR(f!;zE}R8Mqbc5za+NDHaTq8z7EZ zn?zowA3N+TJhvgJ8~9^AWQh517lqyTl12g?lE1wy>?HxZ67I!24LbFnZea#$ZnNrc zUl{@lLnj*0rR~3j{6k zkP3oy);`su@lD5ZV+gE4ny}?KylwowE|{ix1(Q)+q&F-NERGADb`jEHuU-p7Ao-Pe7E3dn>;0nv_ef_V$+ZZplkS9@ z4tLH+n(B_BpZ?8V-(!j`d^lxdIYX`&ZCl-ErKUtM#CW)n*U8L_rKBzWHWs$um3dXy z6n_tE>r+vnZan#vYUnj%+bmYleyqv6c8$1QxgIx4O%2l;`7Moj3#S?G42iS|r;TjN z`ueF|zy=b)M7GnsEpE{x2R2(od; zM(9Vb7scGu%tjj`VHA2MP%CWr0j0D>f+`TVUs^7qzq%g~F%RUD-y2@dv>l5t`&r=1 zTR?ZTLyCU~dkf`LBITA9c|D&6UH8>mHXUP`A2gL$b6sb`gQpt2k*7Y0QOLLC`R5%F zV4WWX=admNhHNxgF_Ye}79rW8hHVMCga-$@Una#IS9*E5+#7p9GU&(l823$P?(5Ni z`pJiGr64B0H~S;z9Mk5Rk9-E~E-QEV$Jq1r@pD)#X95L**rgPR`NZhHmH5~Let(Cf zQ&%_sJ~0!^Q3k>ImeQxn+ce4FH-_E5~!f7juIIUf#!tIu}mHSMaBGWx3p0+!g03 z`8f)7>afX}!(=+B`)+pm8UhW)jjgzSIJ)%+qqu`?M74DJ+S&|v zc~-B^I6xY3!I{j_R9ZWis=eW&$8A(&;keDZr>|UsX%V3G%*$V7MSaZtUnZyDqNxjd z2wxz}ia2NA_X~Jp@>j4~z*%8}0;m$Fp!sz+s^HS7QCUuIBoyKDwxkNf5kJ0##II%{O6A zc8J`V;>960Gu>!aaF!_BlOSq4a?B;7ec~^VzVtcMU^@+(*(}Qx2_QNW@&L3GU>QdJ zoRMkXw{FhqFK_o~a!;xzt8Y5C1^UZKQ2^v`z=iLag(}Xa3-p9bBEr#zjSN<|Gf+;u zfD4X7+qYuP4J|^!_|Xz3VEvK!P&@5s`z!5<@;|26mp45pK|JT;q%f=Bs?cc7S~N(N zPoM`n;_;OEA4S^=)4@QmOChIiq2v}shfn zdKS{F)+3R%ZK>KijbC~Ty2SWGP0L7&(R6UQe!D3%cxP%Rn*t3K08Vi{3s%|G8CSC+ z0&8)!`_W8d7BkLO8vEzY{q(28h+czPeGA)+pI677rOuEJA8YGzjQR3s;vX-wJD5`f z*OQPwq7i>YzQhvHCXJPK?0<*n*C*y!d%B1c(zS0x3?u8A=vZBqzw4|#fMwKehlml=bm>4Sc7zxnS3l_&#q*^=Lc=M5zQpbKq2i%op&1@bmz!Ahcna&g6=RfCfcjeUv@B)t8M^`%>Z6&{h z56`9G?29!s!wX3OKh7T}kY3s>P24)K4{)<7o@NAqr-JcI(d)q*4v?zgc=onp-fVeZ zqG@gFl1MRV0P^uRZ;NQzpF#?t6&Vc#6PTmQ_aMjl!G86CwC{(lF!BsSz+1H?2th6Q zU5&8f7*%WDp1%Z0Ep!Inh*WIp4%*QaIyN*ObyOr*W#H{gQ87Wn2)rSgFD9M9>4VuA z`9$K!(-U+jfzstVZDg!Qzv^txXaB3;K${-hI~SiOlD(9LU0(>3|Q@3Sp3|`$AfZLBO6v*TEqHN)UaTD6wvu_cOJ`5 z3HpqY&;KUGyVN4BNpN3ODRM; z)U3ES^eR%Q{O=f?r)$GfjxI#iQLqFe?lwkmQHXqGJ?DWGrFx?(Sq~J42BFp&_#yQ} z`F;Nz;1k}wDiV%vu36HN9rMaeQ;$(;&wPTJZq_L4;{XELK9-==x^{_Q{ zUzE}>@rb^UPj~r8inhV`>_>J7_5{;2Jlo@Cm^(fOwep}Z1gp=u36E=B7S%?XaBFia z_&7cQe3W>e&T27LNL6G zbWK@~Va2!gL%Yk@ffMj#FbTS#MhNA;(}g5^Oorlh!G?00EycX+2q`AQTEJ0UmC{MH zMz(2u{bXZ5%hRGTpmV$l1G>Pl93g49ye*7v^ zt%*rwC8)u&zbq6Q6^NZ)WXH!#aha|p>$`G&Kltr*Kk^ZE@-2#vY`=%KtPn_6$(%W% zT2jA!D`lfE^vZ)Mmi=8)j+E8Aygl1AY~27_(r|RBzHDSI*?#6o+F0&6NzvX!?tGh7 z$mjd67qL5cFLn`_TXDL1ou)J^S*JP0%@(1NfzQ!Cz<_7S)5Evj5L}rjqG>7IpFgJd z(V>|n_|U-l7~QY5-4-Z0=j8Bn%3p_Q7f-^)TLVfd2=KLR?jLsvdfL0tw|tvqs>yvh zfjA|%_MMr|D|OosO26MA^LsTn3>jX)6P)`7p>4P|u-i1=9}$q-pS9|ya>(%13?0)f z$4hH0z9?|Ejd~vX&9St3#l`Dz5mwV8ONLDVlH-v9NCR6DCiwUH0gd} zsw|K1RmTe!iYGP%vN2kbKCUV$>=M30;^GVH?`u)-)AIY?>ix^gTpcvtBEE1g9kq^b z?Lf-6EdrP-<9L%JLf?6rN7x)i+i2~b3oP4Vg01+VZQ57y1W|vC2?Hw%1QB;HoL{av zAE@Enypiar&$y;143WAEvH*Su0iGG2sKq=gH zEyg^!ikcVz7(WQ&#G=Dr9JW;AG{OdtfBkwhE~yX+q>-K>p{zimo5`ioj*`Ty+|0kz zlE~01ekke_faAUN+HYfI^m{<68^bST5nkMlv*r`;U=@tbad$fcI{p-W^2D*f$qa|A z?>dcXgt2c3j`Mv#iWQNY8%E8(AMexwi6T0W-}*~FxL=`ko&7A2i?fip_~euK8wu)0 zGu@^J@c|a;WEOGD5@A((BMM(Hu~PNTX9agPi;G%TJgNEAr8TU1Sh2o<%gv?&X<-&O z!HawNbkC#+s`8EK&c}nAh9<{hGgP}IFOwfwi?cop_Q%yO1R}#j*L0oKIzMP~*XeLZ z*O}s>*8IAGGj72(y76?U*^F#m8)*k)XcOKEm5Py-C?lD^x!Okq#v(QpL{kLTG~0Ro zO6b7Z@Mzvubd3`QP||2%D>B2-L1B<_+a85^YsLQEXElo4sZs>0L`f5sX>mx$lgbXn zek}JAddI*w^8#;wK~*Z_oZe7ZFgKrx#Y`J_W0MLrA}GwdI+8)4mb$H_?pAB)_l^`c z%``FaBK9>NU97V z-Z3??c-}=uV}yVwu^{n1m^KsK+~klCQY*}(-yS#(v^C{7(P62}T#KWh3&WGhSMG~M zBlA;A1qRoc*%D#+GMeJKP%Sj6Z#8J6l3ThKpC!Y2){9&Sq$h&Eh_;eL-i{2oyN#V{ zNPMNF%x@S=(L;4c_fi!AqqTb2A<_hM4%tS4hB89^7ew1bRRK>+Vy) z7yrJQc6e*>p{ym4J|A-!#{~4bcChooMuP?MzIPdG%qVtKtzBm3+OfZ-mTK3ozij7z zuE8t)zRwH2rM2l7tyIHrA9mGvdk7BO9Wc0e#|6*pEMzz5l8^j`J^HyNjYy)OJryGG zXVJLWn?;PxU?9kCtVmq~;j(>D`uB;PRCrQORn8hs5&R;c0mPEF&415ij5Uv~{tXoS zUMX$9x5GH?*@vGe^6^>5D-z5|EUVsXd8lld^F^Nt>CR61g9`Sn_K==qF21VzB-Xri zrDsprIFBpzPlb)0vw6K>@_axsILchzG$L+zewN#j#&hG{gJF&&S?~yy8^ONCs?xNat^<2|(o!)VfRC8>hjPgAB zY<5{ef`$x8+d~>qCg5j5&6(oL!uYIYHvTL)t+Y9cyM2?kRD;LKM`HLLOvP|r5+AHc zqgf%oK0rYUV(Lx&rcnIR!+JACIl?L~-Hp+K-?i{auA;q$bm=~1`)Pkp!FuOS%fsYN z0ogvsAt}Xine>sv-Ji?@)tmoOCBfBDBkjA))zG%8P5t;HC$88+4J{z ztPc@n79|m*U3_D+jZ(yGRu1$am0j|r_sM&V!lR=+G+Y3t&Z&}jhCJx3DHoUDM_{`m z%!M?Jh^Bh>UcZD6Ok4|RdB4Vgp>C6){~s?JiolbmY#5-Ml4$}4Ywlx07iQVth=uv| z-Us4h38&vMTTP|iiV}J6ZMM@A^H1)SZiI+k!Y;ihq?nc6tvSe{tEoG@sKsb225H2c z*-<6<)=!RwB(LHct zhqu*Kb=S=sGE%W622o~*F#~~I*?%0&^C~fs+_oj#%IzPC6Wfd-P9W&4m%tmMPsXe~ z#4@Dv05L{+e~q9_n0}g7%Fxf6d>}%pVC*HInt9c`PQB+D_mzYZyrmM)R6s@(Tu)&h zGv7{(OuxT`4GTe18lZ(0GF}2rm@BUgA_#?L>`H*bN zinM@k5@$9UoC|&YF)RaDkjIh#aU1#Nvmac;|FS}``OLd7bKSB8DZqIJk+Jdb*91|@ zGpf8ge?K{RFbGGE?Edwj2xJ7gwQ#sEnJ{fq(RE3E|Gr*cnzhHq10b;PB=CkS`JYCY z{4(ZH$E!$WAX%ZgL5@~E`Vt~@lU4(@;nF9VVs{1&BeFj44XGrd-l2nx(9m8PS9C*6 zy5gH5nb@dMd!!pu2+tN%>QTaj!9JmbWqVq9i)OTXC@!*xPo4^RgTh z?AhnIG@^2j#c_WtVS zcf&dqo{IW_5|uowvZb$RvenKh^SywJI67Zb)+N4rwd_R*Q{c;UwCpmjal8+G@hq)3*Y zXy<6_iU*xv6}8tqGByy(mGEMnll*IEaUY=U%7{H>xY+EBXXG8d_gSRDy?=YRd$IW2 zo?QsKmk@$+y4cDb9ywNJZp$d^iYg>qrx<1POJ6;SJ9*?gh3=*a~$!b6Y>ahiFG!Wa5RC{{%u zH=FhPQGLVwV3}&#oQ7Jj#+=mu#y;$_}_}qBYlAc3R`#g+nF&VkVnT*#g|8}us$QXzDVW^wLD&E{= zJ2X?Mug|1F_*$psT?Q!T)dd=3?J^r_%dVAVHAKzEyMG}QVEFR(_|uDiPLi7lwIL@l zMNyga-Y#4J+wJ4qkVvJcjf`pn7@tjkDNQ2>U?gORuFU<=8*ae7f%|>(ACkm>smM?C z;G=oK)|uq3h|~o@{GyS}%^M*+jWLd;eEm{5#i-l3U8^BF{~A&5_Z?_5N~6r^7=7Lf z7%1r#p{^U>WFAt`TOe>oaQ(eOB;uNHY4AV ziv6qgQfU^Liz?eTyRtsy#AMs$rhF@dt_ z7s5^xj0sF|5;5ox7etrHU~9Mmmh?!qCJZ4f@O~ANZk$R>X3L>*Y)s-MO8S;YIgrVC zGo`jKZ=*>Q!@Ab3tP!*Z`lpLt%y2|nG1}S5HOs?WRV>w1!i>pcsG%<76G_gy97iaP zvK1vK-m__KK5lXB(jQ6&-QY@UD|n4iJu|mmeDBxunMp?gQzN*HAt6eNtNq+O>#p49 z0}5Q3*{Ly3evmNXo%Q6kfCb=_WELBt!pP)3LJDWmw16VJ^cN-nwxIg3p<1XVz*nVZ zK8>2Wn)c8wHs&9I2Bk}AlkAkM@2yN|js0qOLkktft<$sMu3fNhp=xL8q-16JK6ti8 zGspQsgke6i_M4)obVMzr&PQ>c+nv2HIW2vX8z%}Vf#W-OD(pGdByx9{P524vwm;0f z6`Ae&?$1LHC;hLGAYM`6FKW3NkAEXhNUvKtS;;6r6mfZ3Vj#0(&iJSCqz#SM)3HF* zaAIde0mt`@!tWjT1Lt1qc68c5Lb(znhpcgOmx0r}yYMa)%M=4U%ukM{cb0A;7Hq0i z8pEImFRu)^eAXa_B+&f0s+nzUhz1onXZNy%{0C^>0yIzPQ z5bWoQ_a@MAZUj#vNl&ulPM#!==&mQZTFpw)%FmeKMO+q2A=rhkw8w{fxg~Or{WUs`5&5 zctsU;oT|FKvWB{bvZ5@`icG?p5!`S}S~z7zMGc&?vbK_nwyFwl^pe!)g)v=nGFr_EyMWgv?D<}j71<41g$df6a3QAg9S_+EF3d+iIkV1|cLZZ0_%aN#33?WQ8 zCIl*;LiD2%VbO?OS2uD1O;1u1DQN6sRL&hgR*qi)#Ro0L9j`#}A@~wVG^&DS~XVbKn) zIwJLl{C}l&u&dvg+9c{|$#}A_0z^QS6(_0;GqsH=1XmiFVoxUf=rbD4hN&pd*iHp! z<%%bg5XE&78579It)@GMBAB?+2>Qy3N{Vud>T)WI_DV|HYFgTA8gh!N+KP%SLKu`k z$gm>26FowHM+OV!k20Vuy1UX`e@hB0?f*h!SXupjbNKoy{L1;LG+V;BpZ~HUsrDg$ z1O;0Hl^j696CkuEI~Ox%0}vHYDO9i(-|6T zm>8<6sGGvHiis9eEDPVb*fG0~*ONI|j5n4wg$T!WSDzmn?O&J7XzX9`vuMDeSw^J7 zaU+DKaHM=>ys{(sPt0W^e2Bl&w@+7$HD=;m_%=nRoG4=Qm|07$>@PE0*l(kj0CR^;^*~flk#QeYN zWB;HQWAwa-nIXNmlgjb?>MUiiAd8|9xd*N>>s&S!iohFOo*NY zD&$d9msM8NfFDix(UMhGSC&=QP=z0ur>QEdrlJTxN-z%dHC1GlwbW%*6qRIElr-T- zOIA%8epHlU9+ab@2tSZk14*kvJp8DrswnH28EKj-YZx0Es%x4U8)+$-Xenx{s3@wc z7{O4>#AHWKLF6Q_)rUC5FYy zEiI%(EmWo!q6xfl zQ7Mey$NmfZ&R!K!xNmUlQF(gvbkcB=E@8gwWPkn3<|;su-%M8!2iUDw}DHHs*vD`*%C}*gf~J zZ-K_pKfC)FN*nbyc(+N=hra%+Y!)9JM_U*h+fc|JL?41a)z;hyXJKlruB4@|E~hLH z-T0SuzmzjN5hyU1XTR?Mj9mDX1U{uv7<){^ zk1X&<#FO4NX|6xAz#kD$de@}6{>TDyIq(N5qrfHEFIt zvcMk^PkPs+x&Fuke?&a#U6bbeBMbZy@uYW6n(L1&@JGax-Zg2iKeE6d5l?#8q`Cgc z0)Iq2>0OiN`XdYc5%HvVO`7YEEbvFflioFHu0OKC9})kpckzxNd_W+<(++~*u?EPw z2y89Lxd`Goi1zit1rjL8%Po46@O^nn9D#&~@1&DF^&}na&Ez!EgAnGKSR31~wK2uP z_x!0eoQ;E#rG+t0Qcgj^5Wf0PP*5>4)his$lzvibAky9b$@r*M8m^?i2MO-M} zlcl&#WH$ya@_H$X*kddj>U^)k1_orG4}@;9Fm4 z1d;o<8o^i3X#_JLSI;qxAU!l9!IMDIhp(zJqo{%kObvAvm}0|6|E%%o z7nK@og#XCVh^g=op7Y;7BK<|h##-Y)q)Si(-0T_ite-2K_g#o253&n$HvQEsZDVC) zV`78Q8pGERSGxKVn8O##%=stt$nQ?*e>;EvJfrjIGUrCbcC=a zlB=B0e}K7vY}g5_Jwx|R0aO~n9m%vM;A#Jjp8r3qAkt7Q{RyU+>!}}|`tMPUWwDAX z>v{lkpxJ5~@HPDLWdduT_Hp{5^pWp$|6c%uLgm<(5)uZvxuKjlvLkvGU9sb7-(c}6%kX-+fWAty;@aL}i z|J@+{YdgW*f6gfVdsY0|7yiE+rv3cf|G7i;UsaA}xOT9^{U_YO{5ai=-YOZ|SioCk zS?(WC&sJ;jwog=dn&ZG{lVdCk*M%p4dB0DC|Iu7Cunzz;$SAiy{tgpA7QKq;Yg zIvtGx;ErL)P(#zgFob7D|Ad{5V$rjrQ7r|=(!pqY##nj|TEiYR&4Y$87%hVrO+)zz z%_1Q*#XELJH3sGEMRS({1rHC1@qr=dC>J9qKKjg9jxxkxz&aQ*X^>iAfZTWvD)Spm z185IeuU}nkpAht~Fm(F=4Gt-ralAG>S{sX|kR!GM;6_6jpm~aDiX8(lG>vGF)Cf7d zn-9%l&?EMOdDx|l&nP$2>Hv(l%uFQ7W(5O=O^_t>>~8@0fg$t|7hnm;uwW8v$GgW!L={%r7waeLD>_OPf?<08hNm}5otBuvQRboHUxyLwK@ z6T}lBA0e1#L0x8VW$6Zw^+p78GqWcU^LUXdp@u$0Po|}&Fe(ps4jI{ylgpjp;TqsW zgHfJ9`27AC@Z)G|vViHwEr?@OXl zkR#jHtX+rWdJ5g22lhtj=&pFGpOu{%(nbIt=Y~Vhbz?|ELulr{8b<3?$t|I(F;~==;0ik< zJ7Tl>Gy>iW#uZ_lpJH!o4CA`ctN1-x;%+Q)8uUO&3yjHrA!wW67UQMiN&i}!xMhSO z9H>v@HYQvkGl9j9BS- z-Z9T@9-gNeK*7N^2%dnG`_YO2$BZm$vFITS&!s0I zYaIg45qcTic!kYPawj53!{Ugf38x~mNcbPJu}DCo48MjEk&7ch)I}Z$m5Tzlwo3@`^sfyHn=qYN~F4p<6I!E#^?XYjRP16HP4m<1Raj50Jo0X9B1F}8VZ za%`Gx#%$JX>)1TlC~RBV_OqR0yTF#tcAKr3t%|LI?F-v?c20H?b{xALyAJy@b_aGm zyFdF@_Jiza*pu0_*dMZ2u{W}JvX5{GaLnS6;n3!=;8?@q$q~e{o8uJ6C61dMg&fr! zA2|9rIXT5R7jbHEF5_IwxruW#=RwYMoL4#Tb3Ws2;_T((;+nxF!==Y%&E?8P$pkWJGf7AU*^8YUCG_dJ;)=(vw%mN$C}5TCxqt+ z&qbcwJmowecm{cec^C2O@!Iol;@!@Bnm3)dkoPrj4<8?&B%c=FDn26LHont**Z3as zHS&GupUS_4-gbOl=Zj6Oj@z7Qu_`6uBr;DDpv+Q*@E2 zh3F>H1EOi7PenVYO`WDX&2d`rw3unR(;CFs#1@KKi1~;e5xXu{BQ`L7_H@JPgz5XH zr%$h(-Y+g8zEqqbzF+*Rc(wSz4BQOU8QwFF&B&hdW+wa0B{Nse44N4`vvB4Y2~i0h z3B1I9iR%)tW?^S7nPod`^Q?qfrL%fx&zWsL+i!OC><6>I%n_Tjbk3$ZC+6In^Lehw zT>ZJkxhLl4&27O=!x`dyaHnw(ah>yK&097vU|#&Zr}G9S7fCuu?vT7H`9_LYN>j>1 z>V#B*ROkG;^R4D@nV&lU)dHRcnhU%ZL@g*@(7$le!qp4+EX-c`X_5FMi$$S}QWw2m zEU?&MvH#)=i=Rt#N^44cOP`ajSb|-mw!~{m%#v~$tcLUt81uJ)YH{JYs}Z!sBu=KR#Q~d zM)RQNBP|XsBdu_)yISA1)wOBb*R?xz9v z#XHL-mK4j|R#>a$R>!SguUxQ_v@+WoSX)>hw|--@$j0C1&MMATHmhP*eY91w-C|p8 zCt~MfciFDb-pKxleVxN%hX98H$0?4^jwz1)t4&r%u5MVPuqJFx$=aD~iED4I<67sq z?$WwmCsU`BP9L1roOe6dtY5S~c>UuIGdK8b$aR_G;_8yQkz?cPjh8nLxmvqkaP4)o za69YPi8sba;alC8x}R|WOwc1l5}G~qJR&_ldFpu{_x$W-;C0ID3(=T(hS;@f*`~Nn z{odBzN#1my)jn5!xqUbK=8&e6yh#tqbI8HuXMQq%yZsvcwf#@}cT!eRE>SVm_0(H5 zQMi~Y4OkqoE1)q@KQJcnTaaT=X0UKDDY!IbNyxsC=1{ZH#LeuR@tX^_%-gbkOJkT} zSo~JZR=2GM;Zord;UBh{Z%f|JyWM+x*$%}WkvqQbT)p#l#O#P|5ly?ycctzY+)deC zyGLhF++Oy*#Jx}VsqBm1H?rS-|C0lX2TmOrI*32`?eP{d6xu2_yT^gGnH#_b?+`xI_`4{o#@mUuZUO1J&nGl%p@uK6!;zZ5F zluI)&9k?`{L`rH%woQJRqLGqvdDi74so2zj)X!I(uT-R&q}@uFOOL-QcJ;tDa4q0k zOUA~Gn(HgB7i4N?rr%g_YDIPt?#MthdxAn6!>_md2VycC*@CfKbw87XmM)!(CXJZ_+@vSaNF7T#qBpb zmUfhOuIc>HMd_kfYeBC~@7q4#zQO+e-)4ME{;v7GcwqHF^I*^r*U;%< z>EYWWD@I<^ed%=cUPBaJ4IqmFel(4&paIVe7$EBjxMoD|#4)7+*e1g@^e_+y82Sr( z0}xtnW?=>}SOAv6KR|B-{OB(-xfokPigqA~rhxl4C;>yS0}+6qo12@5o1ce=Uzm@V zPk7oCe*P)b#6?7=iHL{`^P|@&U?hGFF#>#i0)hfUf`US#f`WpgNFXT6U=jWu0rdMo zm>X6nnhhffu)-KNVGR8Zd?LV0e~2-FT5=!{Mlj4}6dSA@I|nBhHxDl#d~`Mui2+zP zCQ=As*fDHab}R=MCpQNhzY0VOv$4;cs>ETq%2hp9 zZx&X6=ymi6+q&>wxEMU7S=cBC-+`)>Iv2Nn4`8M=DJsy<&7PI z9Yl{|gCI)hz0_Xw)UQP@glL7Z;5K;$%zg2Z%H>;kSZt}T3?euC&IrFO}mTkUw%;3|j$T%kp#nU2BXeTZr`p%EESZ*;t(l*k9 zP~RID-}Hu{PFRtm10QW%FLE@m^sTimkxncdq63G|($6i7Vm?1giCTsCy?lRGiMa8Z zhG435z4wVqJ9x!Fk&QjQ&^4dQ! zX(Vr`yjRctL}zZ$<(IuBtzpq}QM4g9nIhMQnyN2V{nvelW$578u!;PRI_=f94~}@= ze4F~p{CJ+_wDRN}gVl;IP6LO1?elwO-x#dOq!9;rvum~{R*~sITq^nNow}`Z<>wE~ zP|}gPkl!va{QSgV?n=S5^e&8lT$A^by9O!!O6S$SM>#B&irre#F(BT0<#X1+E!Dij z_>j17o#nNOUq8lu%@Lc@vnh!V%r9P=`|fy>T2swt_o?fz7L$j2gW{a8Z~C0)5PHdJ zop@i9-a)@FgIw`0BRt-Y8sDzpA2e7YC@EM`e@kX=lX7{tyqMIDRGs9eRsOm@Q5!r5 z?bQ}&+`ANdlD1{b)Hi7lF5SK-@4Yl+q^d@v`gN=PKy7WN%v5D6C(hvUgN;db!=AY% zL6_W{zCPU(zUqk|K~1f8P1B`A5_zBHHxD_#d(*d0{*iD%{&)5Ajt%|JQ--rX&QFqQ z?XQ+UvX3<5VF6yDMJvtwOTDW<@lD{0+&-OrpRf&|)|?a03bPhT{9B3TTNUu`@^k}*B={e4L;@)!~8P78Q5s=yRykYS7nY^&JBZ+ zUHhb)uPIvBT#kNzyPEX$Nh#;+Evxm)5@tq;d{VxAnL4v|y<_dY{=w>wl$@966w(8% zQ$xh`Ut4Na`iGfMKT}vekY4P2Bx)dFFi`HuW$|8oLkBx=3w}*)AV1zXWcW>*GaXc8 zL!(xgw!F?v_IMK@*cHF$%Qrgs-u75}O3f@?{X4yN+E?xdeyFX==<3>(R+xXGBD~eU2&DRvogO(iyZvuL3;7+>`DoBCxW_u*}b@rdiI}()!TEPXWmX+ zJHtUoLt|cV*qLT~y@fgz@%0HqeP0Ha*FQhF$aCP!BmBAQ&o{rlF;?!sSLf^b{^a zgL#`mrTEC7?^R_#+b0p`;`G(Unq28@U|u+IRy#kFPi6Mo4&ODU`dsp$)5AwBD0$9X(_Z#^C%a{PG^-yWX$8g8F)ul!kyFh_yIkt=%Br>E2Igl@G9QtJG8)U|AL0O4TJ$K1=s zR-)Ow@y6LTGu7{`G6=m98K>h}ty?+c!1L=W+NbmcIj=ZfnX_GPKrr`Y#^F5jkW>`m z*==fFZfr zUi0};#APoGlm#kw8VX&x2umS9FTLWSbEz)s+_9>K5)pC&X=QOA)5Cmx%agv(xzM7m zVWwG~Q+VNsXOf)^;C;ne6N>-NM>hE^{c$99cmvt=;JWOYO0U-1#Yr5D?ANL@c50^{ zU)8(iYsZzG_0~dns#@f|JFXNJcFgR2?< z9FVmQvR;@1H#^+nCWjl`1jE5cGhQ%GgC8my^Me#3|AGPsjDa0cK?E{Nkc3fyk+-q} zOJIZ>aABsRzo!b9A~1g@d~$=30^o*`7>jShEFPtS%QB1-x*3A(+E6gcXdGGV9o<7j zH$f2Ii||tc z`#GE#_)rFxi$Qjc#_NHZGhPqOLe_dleFE@FZT!;-3Ds+Qq zYr-@1DKZrD>#ZRs7y97Mj5=Wt2$oG@ZaE=Bff?0|!YmuvK4MC@G$yg=h}6rLjBb2! zqiirhZ4i&9X8C$h;1hDR?OE!K+6!jO%txtUfbdw+l+=+l61Suv@g@1F<(Whb$d2yV zLjQsn7L^2d9FaJ}M}n_&!PN&$ViXYqcr9k^@giJ*{}I$xuyhR}P;lrDnLetAPXGzg z9|u2zKo=;$-9%To!%TqBB5`n6kpjC|0Niaw-;0Ak@=Jkq>^^W$2GPYQgy@c>;Rn65 z@uA|8_CeBYD4y!)F`8zSh4(@DC)knd<%`G|^DR95dM`{Pmn!^>+msvmNnzx7TwyK- zet3M4s@TKdo^mG#xEWUQW>~u!RHNm11XKJ*c_El^A!Wl}0i@w43ZRx2Dnlt2qEZ*4 zmR>k+X=G^NutzEWz|vd{J1e4A@Ca6hjmNCjc#W9eiuMdUybsQSL^G4Jq2QN>@?M5l zDlApd*A;%%Ai|H zZeV1<(Ap1A8>^4){jvN2Pan@Q)!^H9J}8kP$#dz3F&bb(rI8rS!9GjJvw^X<@2JpV zAC_$3<`Y2q75z#wmB3VjmEMp6XU+T9+^p7Rai*uwX7EjrS$lA#p?z*pZMdcd9$w@v(($lcxEXY?l?&UH{ zg}>5G04))$Y6-z;d?_!eUQ-?O?#8P$*gIg|EHtzF5K=F1*jf>Ga4hRj$gkq?;&4^ z!QfzkT#FeN{rfo&q2aT(G{L!2XhCF(H)=`7oHq-~;DbAGmcRty&^}Fr`)_18=XsB| z!1(-zes^bde&dIHXbbzfQmLrC|EHGCa3Bw@{kL0I6IM#-H4KpJw=K(M=LO$G!zm$F zup=8I{>Gen5x0cb=;{wm{oZWD;A4c$*@iWbIomMv&=&ZwpJSP|LE!Jy8ljso$FACj zWTDre&#}m|X!QE6IhKWfa*jpD=0BWc(d891I5J=}nCg8B3sXHFX?^*H_#DaUr3W3}L z{huHMBOdVx#jrw#w5d@qP-(1?f#DUHiegwH7RY5@1&gON{R9~pc}g>iVTBB7>M`D^ zG*$?$G;v)sR$DA^m=gk|=U@5>VnK4nDj@MZ{U^x4$WpaX3@b$L0sqRPCPN2-d)o)_ zBgCRPjt^6Rf{glDEet^G!wMM~Q>}L>h7~fTt$eW$mBtDgn58a5D25d>ENWOwK`qJ( z85oaM`%nxkgm=b&S<~|)U%+O^X268ER#=0Ou0cru@RctJ5TAxZQu#&{ZrUFYF?SX2 z147GZkj&+Qy|ZE zyMrKT>S7S5`Wgf)?-_#(4Yt^EqZ+V61}6NB9g1Ovf9E^+gBHWb4O z|GK6auubX}xN&qdIQ&o?Wacjdmr}LBbuW9@AS1d4iY;3ruc4#(g=hMkYrK|ytOq!;?c#xFHaUE zG~*y9*hLp4OfN;Xj<3-HXZwdhJl8#lx$rm#oG&Ory%&KuZf2or=)I(BkZ@`@3NLNC zhr;BhPRMg>{vnXcXN7w4mAp=HrDQ5dClVnhQ8pA@)wD#*xfYrSwne@G8Our_CN$p= zWE@ri0pAax(5u=Qh43595c{jS1OtxrJpmQ(I>B+zYEVLbKL#0|STWZG^+Z<4z?AMO zL@}%o3r;LN2p-KN{{$JB`>B;Eh7~fT<>qUl(pVt_b9*-zieZHe@4erphI%h6WME3V z9-$Z}ti&QCKcO}jRDI9`6tUwd+y8!|bX$br{yaP&eQ=A3fHDOR`2#nYni>5IkGExJjK_J3| zKtu+Erq|U8;pvodUjag#82=m=b{E^3|t$@vj7Fkj;Lpcp1>zxNf^)O~*k==5&|-UjL@^p{9QAqLQ1 zt^pnE4uL@XNYK`}VGJ@f=zLI$YQPHr&U@W8YCzk4O@P2JZvWZOn=&-ttvc{~Pes_4?R&VINAtOp1if6`JMzn2ebl}O3Ycb*ydK@2yB#6n%|TB?>O<%qT3q1X>n)8vrVOn2^ysfu{8qH2wq;6bFW<6LM?UM9&hcoy z_93##<<5YPQ(=RDZ(hcm@Z85=I#twy-o>zg;o7WFZ*3JVNcTQG&8401#yjV{_G8-% zlMFqqw^tl|*H3>nYx#pY{O4zc*+tzWW@=J=%dYtPuS-%5&*H4Ub;+i9!Df@bl36sX z#6$UtsUOn5->!MQ(xZIg$f?0Cf}8KIsk6w*Tu=Awp&M2C-dg{{z5UgTcl*BT?Y_F- z)4Orb*65q5=O0de=0OSeyY^7FfA&F-cM;nF)@r~0qes&;LN$4Jin(6M^jM0)v3EX8 z*?4zx=8VmQd$-rWx=OD8*7W4z%?Br4E{7C;J<7##Aa>In{_b);|LH+l+Q)SGjyr7d z3Kpr{P-y*aXz6y<(1(}0Ij2%1ilnrz5|sv=G>yD&N8B(8?z5d4Y2GYfd(JF=JAr$5 zcT%8ZfTjCT#}?JmR(q$?OXYkK_z~T zPhVB9+WYR_&P_l6$XEMJ>^nP1fs?~ZG$5-G!5x2{pzdg#%5%Hhl#29-PH z2322%4AQ>z+wND~%JJTNbFh619E2GrhVC1c;k6{>IJ)e6$A4YweRyKc(BMrzhpYJ(7u2hmJ&$EpJsX z`fPIh?D@znnG?D9U$P$wID9L+=YvR28{T|8e>4Bym3o9AY5wXx@{M04b! zTC0o8Yn|2g29Ko`IVFqc-(7Mx|E^jHSBvlS#&F7t4{TC((id>q-)G!w`Shvs{<>t@ zzz@6UL}g}}J|7^)3^}`;unqFxX^?b`?BORig-o8aVEx^d0sHPg3O^yap8WAlMaw1U z*l#uS9Hgl#&C@C(t{*TP-b+ed?n8O7&_ObH$Rw4cd3r+bWzpjO^MscVyp-EEn?Hc} zj-YFV04D9r-uGVCQ;zV0Ex{MvpQ(HrD$P8bZj@B%>5);8z8`;jNoS0ZZ(8D18_lB% zyhr!<$8O*P%bP;vd9#ns*sxmVG4sk>?!Y`>KlK(-Zb$(zvKO+ z{+zDfC1=$o9Qe$acA!jEQ(P`}#oaot)j=Mv`7!{vSJ^^l5H6VlFA_JR^Uk!**Yt&oUv8JXNaC+jRod zVm8K|YNO39<~!VFU*U0|Jngi_v$^*OQnN6?nlw}XyL-fPHm-fxwNW-c@=0^oX`LL@g1$u@EJt|d!ilAZZU1@RtZueslL7K-|#YVRqqS%StI*X zOSt%lhqh5$PCR7GTaqkSbEt6T74f2$=r?AYI}Joub&(1(d+s(hHC(ZL(RAV(bn%C2^tEgcH`!YjkGYknEAwRPP$j{^J& zf8yLBBS}8p^pua3#;OobiGc#TtQ0_eF*VLLF@ zdo53jc?sLsMbAj59u+mVmHKLEY**FEIh(GfQ@-o2i(JF$NL>L-X}-5nwk^HQ?u}H{ z`7Qik_IRzIy(vNPqV%qN--c?Y9_cu{<_i~5d!XN--tys*z0IZEUlSv(ZkckGo_}*P zUi`&@?e@DOy0|)LIyk)D(H8kc(FLGJ7(bl<&pxx>UhAb$gSRuX5aX_ zPg*tlSe9yT0g<+nZ9D}VPw_`TcA2?)<6)ioaqb7Pc`ah)X*I^-O}Tr!7m;qbJ{Q8u zZR2{zzU#n^jA?t@Lz<@xc1@m*j3r|Jb^w+L+>G zRi?hok;ipPx$=?&JA6|TFGyYdu7Aj(S(Pt4tM}Wv`frUAt@oAB6<)m~{_*L1v(-)} zdNLv1!$xn5R!>`1a{L%~qoC)a%cUe zH%+!BoeNiZ7}WRKD66>-yLz2uKO)6*Htf;!tUayP`PwCc%Q(e_+n07nCQaWWaZ>E< z7g4JddpfX$XCGI1zR@gykR$Bi8#iy}Tis;Z)5FuJxV;8fkDX2UWZDzr*b)*3hLqzk zT@K9S#}~T4OBGDVw1#z*JJspfz6d$?eko>|nJ#yYhxy^!qEE)%1q~S;%~uYn-jOS8 z)X^eIIz=4k!e;kK9BLHq8fkCFMRyHS$)BI zW3TWHnxZZr^($+dgm3O%(L;N>yZ)>UZJ~19$#nbbUOnzvTL#Zq&5nsantn8OuUY09 zN>j~B_5AA%&O?DW`ffdN>2U>7JI_^%%sj7tz!QH!!;X8)H_FPH5g%73&(2pqkyRh{ zG=+_NHa6P(-ql<6`%j)U!6f(&5*#C*XSw&q9WOj_*Klo%3o&4QMa$Og>Gc{GSGVOG>4}Dw_Typ&GI(~72{y*u@$aGo zhEFb;cI8k^?hWzbTh3Ptwq8-y6LVh~bEjsdSpOYj&F6 zY3WLJRJrRAcnU&04(G4F;F?i5r!%eY#sX;$a`P>DEw))FBVDg=aC(%loccUCUX`oU zul|^NE@}|`}A{fIeL7o?>c(ZKaeN(Syj0Gu4_ZSS^iqLRIIEXTVC5y ze)RK=A^E%Om(*hRv)R+v3ww5z#=dS6tJs^OGy7e}uxwOhh{%wcxmh(o$$rM+xP-$S z61Vk4nhux#O^M-IYmRC`qL`8C`6qz6XkZ#+Z7`go2VA+1|O~khy zFC`4~?d5m0TwSv#YbAc3%LcBMrVYM&kxjFNzh1)o8>{e@Jlk^JTaIM>=$&Qy^i$C} z*#&DtW&>KDVxHZ{19O!=UdZNqK9&4Y)Z@VO6BMt)kxZ$X#aG{L>N2|(ny!^qa7m!l zR-{@*+2?+!=ltS%(sDDN#Vc zZh4bvknFo-1%)**u%GAfPI(L2YpN`)dbp zkR7u*Zj}^#{_<8LLd`kcZBXES{F$69i^6ES&AuVmr*Cr+dizSr?}Y95cN?<$logdV zy!~T}lsnnV7c@lA7NBiCeso#;kuHksNpHa&w-;;MlFjz&s$IKyFl2guim_m2aF*g0 z=`*X9FVuV&@!i=fShoGuqQ?s}bq<;2rlwt`XRnNijYu`<}d+nxyklbmN<@&by+9}eOX+%}Aax3+6 z(bLm#r;|dKr`$7dFf3Ws(@VQ)T<&ii|2UKLtG<&tp=0N((v^MoUE<9bgYJ9tIQci+ zV~-Mw*j!dNM>_{poVoM5%XCrn)04d`lk;4vXt&}NPu55d>v(@yH69u&Z_6q#* zPj}?KGreEF;1%TA*%)T{yZF_Z= zJ8*H{kuAAv8f62_Q=8{W)#R9mzF&7IWKl(sdAab4s&@ zO|_&T8HcmDjTN^0QUt9Iwof;1a~j$4vZL-19XEB?>lu{1z9$Mh`&Tbap;ftAoN1qR zSY}5=qTt;0jN-i_QQ0KJpvWXb@oe>Dsyot)jRlV;A~#0dLXA&t^kuBA7!#eDTz`oD{9_7G}7hNOqX}v zdAUd6>5)xFUGdMh9*e3M z#7=jm_D&zL81ARPnJD9*l*-5$w?(d6gFt7{;B40V)zyvT|5Z$XYbEAQSi9Z#R(dY0 zjDJ#mXmAm{6JuVXd+*f0mHFCBe=uIhWaLfF-@H~t#P{Bgdn5AIfgUq1;hz;db&S;* z7F*AHmOf~=8W|j~Wy#gU*`bHmf3Y}kK+!*l{IR7ocm%dG(&M4CYPe8BeC5looPVm1 z_4G(g_!gU-;+|dia+-^Wt;a1kJmb^L0?#8ye0A5)ayD}|vpdDK@th5|$_@+HymHb; z@{vE+gTW0KJgbg(Kl`VSV_PZA+B2ZU(AKRt7rbK|_&jd0A&t+wit}TGc76!<3f2zR zw!g*Xk$n%3`d#AVLA#ISBY#_09uLXB^R~Hfoc%v0b|Z838f3O#VHq9W*ZV(H>wOd7 z5To{@@LW^m52FiUB@Wegu77nbzxcY<6)EJ;aX*6=00a9CkwNLl%M(X2 zSEQ~zzL!_S)#fm0H_rc`5NmGM)mXJ<>~W{WI-|wv4(9 z?6>e@#IWrCk_o$NR`o6zmo&2WTXx5^_xW!9qiA;R+A@FZA`-r4;@gC%qfL(b43-#6|TJqo=t-!g{iksZpxN`8t<6?V)y z2+ywYQ&4C88C`4_?q2n+WSH3Zc=HxEIL1oP+VEWMEcPip45Oy^IIQ8A_aYLpUlm^| zyjHM*+?srnh+ggm-}Q#)(e=i(hY_N)$*I2eb;AgLnvIoaUg0CxO`8_ZY+BoMXGr1q zXZ4k;hy>Vz?D&zjAF=3Z2JrObfk=ZVI_+)rU$z#V-rUUK0$|<^XZ!Gmtnhcw24z_F zqtiu;urF#2>Q^+YeAOBDXW(8u13)>rF`z!C<#T`8(BrRzAEm3?N4fX) zeNy=W>(mR_?sL@=JKqdswk1}}+>X0! zu5tsunECc3-)Xj3jtdDFcct$uC~OFdhyg%Pzwu2?T7gxje`Y5a26a=^m+BZDYX;$g)ps!%n|F7ce$Sc0^nqJ}d!evQN=i;x7^kVLvggZGO zR}6jlHD_2KgASfC(&@GE_{}GGI(Xj_1xU)`K8i2d^T<LYEpdS zisbB6!$b)#IOANdde+#sen=XTNrJZCd^f)BXxvGw3p~jN$r?#IK9At;$EBOkY6zXx z!}9lu`P7bYh$>%DTh z^T$oIjv1PvZ-4_ZlRZo<)1sk#!0Ffg%VF<8E)33gKKWYQimLTJ@e^m9uy^qN-G~H< z^MryupSBMNG34k&%bXLAyP7nwl-}`SsH>Ol%g<#vCAcJ~=C~B~ml`}w6}k+-3F@tkH~oFz>99TF8x-KIO0@UM`c$Wa zfKsWFp2s(RUjSgTpm@y6y&zn` z7FL8e|O3_k$-d^t-Zbhx0*~w%=E)6K;`fFZ-Z{_2U+c zN=h=U;Of$%Ck+j97jZq!ymJea37S7B)M)J0-u zdDp}BkKo}6WxK*O#j`HY&7l@Yt=K`ScobE#y>VaP=vd3F!O!sc*jQxi?mXZ7{z01CuBcB^iHLq0a zZey(dk#S#cK$K5sAL&%Y@+^2v_rkIENd|J#0PMQr1(fY&@< zQ(>>ssDtObNJX4!irZ66Y~!H&BICZYY`WaJuA7@*ANr<+=x|&CmSU3Df5tz(uGYFY zfuYxTg~i%I*rG#aI{_%Ky>@fSVK@6*!ifgF9!1kjh>zpzdA<@Elz$a=c1#_uwp5nO zr;WDZJFX7CUU4UiX^XW_qA}$fUB~D>KTAm8#iUX?>RPjJdw$sGtHUfSD8^ih8@jgZ z+@0(7uQ-@%e`MUZf19ilcQv=}{Q~A}K&RWlK7Is$o_MkD2}fH$!yo&4v7Nk;w-&?} zCsmhJwUrM?BfUPYAHH|)5i1j$SE$kYl1@Oz{k0xRhH=ulN$*v@6+U>d4z9ulSEhKT zDV*H{%f|P0jauwM(iDExr249i9r$vYtJ{yg@!y+qV3EDQN1e|IV73YkKDB@kbxvVUxdD+l93-sf@5*fhXij_&Ss(j{C@5 zYT=b)9P4m9sqxrAG^2E6+z)ZSkaxkLaA!-}h&fZI*_!}JzZsjDD*P<7JD%Z>@SQQB z`Yt^Ye!KC(%dWm8`b~s7tO5Yyso1>uxy3u%4Cx8paNPg#QrdE7xS%lM`1wV-~2%xiyY{uPmO66mgZQHPOGZtk;>Ejl=g%+@tGO z=_ebsT^H||a=q;>x!>Vig23qPscgUZWqxDJyb;fFE@o``8itlweLGF^$GDiP<2`pK zj{Do7FOVt{!p{d41biYh`P3X|l@I66MiNACxawcVT(89A{A*Rx;7^gU*j za#jlf$MdjHlJC-+-VE+z;F*&|k+hhBAD@z)c@Zj{$n7ZRi`^op5YzN&0daoNLw=bu|Q z4>mLF_ba$@Iit>)^fj-Y9Lb~@nUb>h>BDk^RZHL;hg_0E+v{@fd|(?|^)tUH{K_zL zO`W>NIjALdUtMB<@kA;sH`vcO&PycnJh5n(WArmRUGO4@=Jk@;#I)AuabIBzPT3-BKPgZM+()pm|eGjDHmGmdkSC48I(_i*cs)DOqu(|Eb7|LK zBBLX^-angEzSnb4(CLgz4`w^Uqxx)4Sf7o#viTwYtwE18>l$n5d$(Q#;c3Uiu!qD) zjpg+f5+!yIEb<$l=+w2fR}GreN4=Y=7b{#L%cwDX!@9*zs{Wf|=RS4n*gSk3(&+iX z`xWHkGzwUlwDZMaUkEEF5=Yjij7xdH+thtc`Hi1jhlg3IzJJGloMMs@6zi0!Fh1^| zg5&<`z+~4rmt3``BxXN@$ip@ia!4#dyBcz8G}-)CYyRb!nh>wCOeZGps*wJGB=8u7YvVrPj{hYNDz_W0EUM<$U=DfHN& z+Xou)%yD1gBRrt7>_TpQ^~1&%m67R8`sur&KN`nsUCw-Vp!*QRAL;Z3aCLIZ?p)dp z!#ZE)T2B*7IJW9S?)iBIuAiKS30}y0Uoh7E6weiEQB38k(=*^nI=3U0Zk~HHuTKDa z>yOXOr3Q1qhP51P=z677GhB1?rN-Ol=HBkv1ODUppG>XB;n1L>D6__Y4_Dnfd9w6D zrv<%soEwRmY-I3Zb?r?2B;&qUz3FNCVGZ&aIRR*YQ*7J);^uJ1*c$=YL2?K0(qms< z`d+rEqn;kXkcy7`2QyUQIz;K^S~w?;Ugl;1c*mX#$_Z)6XWV%pcPNXO=MrLr675Qy z+B4`K^KFFlAYS2|MF;maYb3- zr!&--W22D~IDpsWPUejVanG-3HuOR}ZxednxZKaqEK68(Z$T?B-71R`007>|aF)WG z@2y55E%fwhHayd#47Qus;>vQO>-z^Tj=g3rC%E6*lOUAiRBtm74dxFD*hBNxEbQ9u z`XWdl{c(oT**Hy&rW~i(QB}beG)wDO;rn;d3Cod=%+FGaq9^!^J37};?oMryF83v4 zoof!~-#+ku=jisS6j1s?g9kqW0E8-?<4#HJUZxDmP1f40cgw1h%PvpArBo+m6)9T@MX%A%MthWTRXmAuXzY* zY~j}`5*8J#ZeBhNt#l*nE?)rv;j_t{l@GsMr}83w{D0z5=-Gh7;?m`Q1_VRw${Uuk zwz&?m4%P5wFOSiIDa1Svg%sq!=Nx5_QFOVly31WHRHAg@C!uL$E9x#P_^3ZI7iq0o z2b9i#Y?Hk@o@BuA)u{cz^g9(O~} z8yt*=z22OE@;JKt5F(Q%>HZlQY`lBZCU`@Dy-#j@ewF5P)zYQeU_xr@ZZ$OX=TOaCgFYw$VQwDL${jM?`}=gzrFpf_P23M2re4C z-gUWlPtIW6^Mt{y?YUhy3Ns!jD`jRD$G;WpeM?ykf@7H-56T~8u;uta=j|1PrPg<= zch3q9lQgqmJ&)*JiD)X5o&9E_rI~3A=9b;p?x~}_&scbJ`e6Q=JM$SYnk@qdce%Jd z&*S?ZPwj{g(#2_E4xfEmU{JPelhQET)N(jY)gFv!yIHyE=9AP-$->F!QvB1vn`_Fi zHGS`ufp!9?tYbrWTMZvBy<1gX_@QWI_mq}|hxc{tc(NcbKCL6F_w?LPo3^vE7Naz{B0+!u~8O$$$!>$`@lWJ`rADeYSxtY{zcMp**L4v zADtdMxeeJTGxKpTt6DqS*%>cy$aaH+#l~~Zj}LTTqJK?5)4Jgx6}6uAh|ulU_t!^{ z&M=rNip1c)j>QdoDjxh*RkXgOqB5(#>z{r7hoIe92Dvzr)wRC)T;sO)%lkR$(hX{H zh$=I*{gcG&L)ERNuS%NBkJemyE!FXMVE5Pt*z%!B^sQ(sYV7}8=4;mIgcSuY`S!g- z=GBu|tN!W#CQN-j!^mcx_3RnKE4PllwbPbBg|u^ZLOmWMZ^tlF+)v=QPHNoyCq|BISv(^Qt$Nk;cr^$W#kcy}skah1r@?i8OKX|DX}nER%_2cL-hJrl<%GLCdhFjhi|!4~{QjzbVAV|TioOXqE=}9I%iDIp zllwv2eD>Bs`qu?!bWRbh$r^;rtJfjQV$GmM@NG+LgHJ zpucI1>ch;Bd!Sv=88qJEj(42-JA>D5`Ls15L* z|E;YpH{qS){{aAgdx#^t9o>~SX}xFGp1u|T@xEq@$Dj@L#lZ|=H2LFDH6?xXnFSFS zQdT}%^`Rbm>o*Zb?sm#lE=|1XA>6}z7wXj|A&ocirmZ>{&HfG)ksUYGl-xquU0b z_t8kI2J1hWw0de6E1q@{iMF2$rMF2$rMF2$rMF2(Mza9b9_w9FoBGmWo_YgyU z-+m7-)c5W8@B%^9_wDx(Lw(aAZ>idTJzR|v{Kz-jBmdA-3)b|bbeM5cUP~SJy_YL)ZLw(<7S|Mq9sP7x< z`-b|yfgtMphWfrip5&iafzy+nQArlK*MH0t{{8C|p}ubj60?m_-?!N!h0vkCZwL~zjZxpX*&>C|p}ubj5@?Jf zfFgh*fFgh*fFgh*fFkgxAb|S5{q8S``o8@hVyN%i@8N~|zWp9vsPEhF;)VLY{Vrmt z@7wPohWfstzHjKcZ|J#i=(%s`xo^6rXF2lajXH9pfzQQx=enEW~i z^?my_KGRE4-?!&(@Rm`x9ONn&p~z}&=^GkMF2$rMF2$rMF2$rMc{t{ z0d1X{$FwWy%FLsUx&W8Y<}WwT%x(Nxxn-@e4*a#7GmtGDAA|8?+~Ig@9BS&A0O^^c3CXTW%8%aF%FJ)%2dQ& zJPX`wXd`_HLx+`BV4 z%O+2%fu)DuB2JDwNyf)OYfY|A6Ya|MM-VH?y0R_A%3IlSqQs_Yt%l*Ug~Pl+^S65k?g~gBU0RXKf|+h5qT1K5?liP^$J?^n(o7By@Xo5r!D zV~~t(cz;y(PIayXLsC<=%S*g4N$OGDKMA zij}F3q%Rji@Nf~YAoB^#e)!j1tOkO$cG83;ky->Tl4gyj>`sH>n;^K(mdBFW7Be{~ ziNq0{thk`LmM91seIW^!vt%4y7Grd*+28w{2$g*Rn=xf}Ffzh$r>BQ-Ua&Yx>C)=% zc=*0zlmAt{SiS*_DFgZB&1yfS-qWoKn*y2)YUb|6icS_o2~lWd&HJ=`41JVZ_-pVJ znyh0i2&Pu)k1ZXRi{h4KGABC9*R60{cc*4G2u4V38Q3&N9$D|~wAU&?Uq>utpC<^G zJ`j~$Xsw@X9!@f~4Givpexp4Ka{L7>;;`=HmI1Z+aV5$5y!%F%0H+&{Z4O-70w9>7 zDIdTm%k%{ZgGWX3dC19UB2P3?h7fC6;eDpLtWdvFKQy5?OH8p`u!fZf$eTL;pQWq* zfjRF~ieHdeAHw~L9ln1xv_IC^L?{n8Z|0goYzSh-+|jSS&D4!~z5$6-X{`=}V7`aN z2iyC)Z-mTn!*E%+J_sItP4cl}Rx#&wXObpb%Zo3Uw08S)3j`0HJy`5NvhjqIik-z8 zXYC9bv(8@191Yne&a1-gT^#f5Oi9I#?ZeQDvra)#l$eBTptX4h!ur^97vgreExGHz z*_GH;+Lxcx1i=aiGsT6m-11m+xaXbB2}i{nG-FH+K=9OGSGRYw?DaGl&<;HIv2=xY zlT0Iz88!)~{MBDOdxY#( z+9zQT-zM4BSG8`ahAobfcxUGeg7>nN-7Qj_6YWSQiQ25HCAy^Ubs)GkY^8-HD;qNa z{hk$2iyk!_&u<7zV`iS2S9&fp^V^|uw!yZ?2{MUJK|?mS=RDw?0qv0I z>wKigScB;FQ&XM$V7jjN=?_D!NX62jtP{T_LX{7#z&kSBpc$inmG==aJ|7?{y=r*T zYCf{L)xaVX_5xrSm%%E{al*y~1phkas^_J$#$Dc$PldahvyLz?W)PZpDyCOmLmUn+ zt(N}E|AJR#-XluH&_^$O`(mYGu1A194nL$lyu{qtgKF2Q!6h`zJ^1j9aW zY%(vEPGhZsN3^usB){*X5(w)UmIf=0DPm}vEZd&G&P0WZ=o-C6!4pKqzoP<-HtE9fKKsGWTpR9+T zzk?2WWYLwOQs3A~&{I<>-2j43`!6&1>X;q@Y9ahpCw^e<7R0jE&K*q4FKRe^o;w)xkfojtB(*_;rYl$ zpl}avTlt2pWiPL$Kvc$Vq&@Z4tRQ&jBDs#dOsA4h7Ko+E{j`hr#cmBo_QEwRI(mAk zkQUy1(>BJs)*}$=JC2QO0x>=Np8m)7;tCcA;*{$w5Bf-k@3z?0m=%6ex=wvWyj_bh{vagsO-VeRAsywE>=| zjB=m0mKH?Y-X~vOVK3^#v=Q!|CihPyuV1#lL}x$0HRcX7rnUS_;aMZaj2k&?O9k@|JPN~HtZCWer66TQD*7T1YBC9Udj+L3Tx?kejE1 zH-|B!+&=`(g)>Udhh0PTLj@v{=d8F01UI?4>Da$e8R3}+OxOc7-L7@3g$cQ2jj60B zn+DvA$0HETbcLSD$mM{DBt8Jn&A$zT{s1}1Q9(73yAP18&H}+A|5DutYx8ug$RPOC zP;aADsdgfXM1EuwsGP(52I(7<%k3w_V-sHbnR5C0!>aDyQ?H{vJC$=(8nsk(Ll@2E zJBAlT_?s!doDcI%wMv&t$&5VXS*NDL+lhUFSEz^9I9h1#J}7NsJZ}t_Grj)j?J!MO zS9)>?KPf=kkyVS4DmNZ_&PH6@9lxm;@Vs<9*ymV|Kz3?jBe&=yWIySkplB5^U<2XMq_f^++ZfEsb zM(QL*Lb`m1A;&5{+o!G}b^&?~^VL}GfsBcfd9tbqH7O12bm}L#VLf9s&My{UVxets zK+DQnhA;>h9q$qR-`}%M&t8?WASU1#T%HX zS%x&-6b>!xlh+*zGyNb^i_e|fJ^=i45_-;}$mS%k#dVzX;OC6Ci(9|YGKTD9<|~!W z(uUNa&BqI=PA#t1(0sgySO<3~&{|6+r`Y$Y_;KHWZ5RnjXf{Ty4R^O*BTJKxrNA4ti~wAb9%v)*Bw4Hpg`1BuY3Mrs`NYX3kQv*d;u=LAp_zqRQe*?P}S2 z1qPz(<?+Oqx5S`11#ZIMSW~K=B6k3FkQ_O4Wa(^=<_cKn~CAcLT zRg2go&lx25lRzPWSIGQ%i-OdP zwW-<-+NIj(HJg+qCA@eenPV}(cqUgsG8GPeyWehYTv^pxAdtE_ZvDlwix)3uK3R&C z#ylpcEEeZDKNSmFnx>n=p$HRgvvt2(3Qr<%b}AJIB$>WA*aGB`6}v>^5V>C~1cKXD z)*HFV>x%N%U^B$cJK3D=ZoRr#`YfXBRX{EH%OJ|i6eI?h?SWs9mFcL5+ruT4u8UWV&Y%CZY1 zn%$TcBsoNW&_Oi1r636{LMATN57B!F_ z@>DhrS`#P2ox31-p6niCaze}iwmfJS%By^%SaOJCwpt3W1<+J|9Rywd+*zHCwQGfj zIRRnE&i5dAgm>kbSO&8t(h@^#h81O&yQXfY_c4l#@4(ZokHf zURtu-FHXC9jwSGPvhsN1^G=wiYcTy}@JkYpv>+>kE(Q@frwD^!UHZm*ddB=d7zG@D zyYCqI_3ldBwFVxFmbMGIpq;k~J#Sp@+t})nb5}Hqex+Mwn>#`9>-HVP#ug$uct{KN z^ZPbBqHXuh!rDX6%1fSueIrhGNOv&a`~>-fRf>EzyBAhKYlF;zBo9)q~(?S7&D(1t%3Z-{qyEg3OkxI*`V;!&-}6w<=Ha zJ`1$cF3Uf+W@=yJd2RQzNA7X1eP57WkG#ACE-Zt28aq_uYKq<))PwoRV< zC(<(IW4w{r3HulT@*z!nr@fnwQ=EndQ3Pm0xZ}X8bQaoCRpGG=>`VE)fS}@MMR1oU zjV&oBN@9aNazQX1Wosn`u-k*65em0Axo;HT|a|)hwMrEJCYfX8vMLqC4xl zU5Y_;xeov=XT@q)6nM2CXAeJWyI-E9NYuSyxy7Z^(R8W3=zi8nAXcQ<*k?7t1;0K} zyPB_LT+=?t;cf05(6D2dRis=E0kYJQUjA)-`ZB_8)rE|*)P+%oCpnK?I#kVgwL)4A zEsS=Dt#>Su3n7?J?XNHyE*(qFPK~(anzZO0`v@YF=yXgQ1==*7ZT&nNT(xY{EKIC? z9An({9P|tmq}n-JARBQqu$oI&o1AwjHI0;h#5QgT!9_SrqMOnQD}sk2nLvtMLtJZY zr{-L1YisB({fs@aq;>}8#zT^t~-dPGTGU0 zCL|;xWM>%Jth1Dr5ng*9re90B z5d6}?d(j8(I3{TvmGRm<51}lN*d?kf%_m6znt*fyDXTdezuVtY-SZkmX;gwDRrJOMeSRSkv^lm7YasOkSiVsM{jNroX3OGefC@`@* zI8k2@_=I_cm4~eKr-0&QoAww{s6A^;E`)i_|^hbybE`bX%y&RrD5UNc?p z&tf?L10?uR0bAV{UL&VmV~&L0-c+MwJNE>%3p#_w%OWV&XBHNC;{yMA@@`X8AJI_e zN$r_QRJg`iXP3g+!V4Gna+sf&uz+`p{|5m0?IALi3-V}YJ8=2j@H2OxyS!2&j*ty~ zaWF%`JI`fR$J$5vH2Q9Fx}#knBoDpyn+QxM0&g@+9Jg#Q^(nXaR(r(Xf@jb@5V}7h z#uac8#pWv6YYk{MDk+P;CA@;P^Od=c_3S zYFTUaEFuYU5jY@u@rMRX8dDH^6i@6NPvUv5F*bQz`%m?|Fq|c;IhPmFoY==*Krq55 gU?w#(i~{~FL6JLy_=PCXCC$i6_vcS zRiy$_g#ZcKN<_RActD7Eo+|z*60{N$P*71-Ky9E8r6p2ZL=zf0E;X_-sbhO-BnfXYS=;W_D-Se?TQ~9Q%4^c4zMQ-S2$&%rX4Ghr};F`L!21t5kj{;vR7#+1rZb z+!l^YCSlIM{nN*8Y$MQr{)M)dYGv%qiSC{!A3mM<*pV($NpoAUJ>N3xl0Z~b;F;Zl8n{n;0mYYI4#Z2z$yM&Dn+XdM8@ zNJvORNdVZWLO6$ujxhq(VO2n2X>F5o#p7^KTQ$E}ItVN`BsrPT_!hKQXr<~TVyn+) zl05dnDegR&CSocy5wH%cEY=Fv+PZRKsAd92E3{S^t%Gx;F-Bpu!WdNpjbh+< z4+*0&I}NSTCYVcyBh26K1at@#Ct-Ab!pXVShWjVy)-Xl~B%=Fbgd^}^9`&CU(6I-a zNob?_`77`7_xBe87@NMut5&Secf4dZ7H(pB-u;? z=inPZdL7@lh=isSiywkY__I3!y%T}pymx;qxwaOr&#tnxR^sH5{S-DUtZtN*S z^me4_XiGDBv%sfCIDRO{=;W=?8wzFJHU``3fQYKU`9cy4t0nrnT1jXfGpD9l_G|mS zBiq7P&z)p{dkb^Rn|QwEuD%>S9humREi|L-K|rZ64YgiCF*>zO(rC`~_tsQsDA7i9 zw!fFnvd7fI2F^i#smM_OL7w{Dy`)m6v5(j*1wvEfA+S-Z^7?R|2ktwJi16;@a?Kw} zqZz!ri+euO$sexFK(&j7Hb`z@8kJCz74LebJ$vU z9D4>wM>a)5Yo=~)B<6=-E(4}Uz*+CbXD_~6{nEL9299-OjKtdOdXpJ+xEAZXT8=$0 zB;niI_s-UzQvyeO+j;WA)BNhye>mrr#y=SOy#?z3{B+&>@S(vco}2&fGjCM?m0xj{ zs_$-Bc)oS{g@XI>i&yR7`A{La ziY*teO<(@a(~IM8{1aFKW}D5~0_1=UkYtBK^*@9qu!av5fLnmK+wRZ=J7l--KWzBn ch~Cfq8$nF?fikgHpa1{>07*qoM6N<$f@_f+=Kufz literal 0 HcmV?d00001 diff --git a/images/achecker_disabled.png b/images/achecker_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..eb3cd8bd52853c0b08af197a4a008a9221cb8cc1 GIT binary patch literal 878 zcmV-!1CjiRP)kdg00001b5ch_0Itp) z=>Px#0%A)?L;(MXkIcUS000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igS#4k0)T zCp7>700Qz!L_t(Y$HkR7Xca*m#(#6{!@T5?LlUn*Fi{L@Py<$WHX;@pM9@NmmV&*7 zy^R4ITR|*Di6W#BE72kf8WF`KY9z+oV!ZP5@{XOc*x7yaUJlYZ1G~%2{=fPD*EjIL zhlpOiP4=odMrLhBc9BQ={PocU;B1}wu(PEnXd|@-^N?{KhJ#tUZj6Bj2>aTb&jmMm zPZ5DcB-1-JnUbxXRyH$uivx~9@1KjA0p1}INDmEJU8 znV3swz7)V9G5jLWhT`ld8~_0Zh4L8Ga)`|g^BCg+MCJlXfprM!VgR5#1`^2$_5dBg z6OV&&E|`R!ECWas3WY|Y*yh%^6QI#l3@O3CoR;GCXM9^p$lH&Y;|Z`!C~o+%c{5+E>`$|kA?h@Klc#Sqi{qK&&r4G1)b&@*1*f^>c!};aqJ~2LKlrZ_gDZdjbxZ&A6xi>bvdf4gSZPNKWTOuQRLBRG900vIYL@# zbcv?A5=GKTqiA*~5DG;Xd1kPDr;C%MDx}79A)Yyg*pJ`~5{0Id4xR#%F|v$RX_cM?fQrmrNnC3{#>+X(a&ReZ6O#b{Z%!!UQ=_;HtIK0h9=;v^U2G*E;2- zS#Li@P88!HioX-IdfUEyhJ;RotT!`gri0gcv;;8y_lt+whN>_%80T||(8*cU6Pq~7 z16vw?W878LwYhitTJf7L+O>?(&e+@LOt$?vois)9sM*oceK~cI^cpY3LgCemzE2adS3wL6)&S}47JL8z literal 0 HcmV?d00001 diff --git a/images/application_get.png b/images/application_get.png new file mode 100644 index 0000000000000000000000000000000000000000..94e9b851413d3b6d843e7d78e59b5b85ee72707a GIT binary patch literal 581 zcmV-L0=oT)P)kVTZbODA7H%hmKi9q}L8YS>5#DK_mo&>|icyi1@Q?v295z*r8h# zV!Qk8_t(sPyIUPJcVL*A_vLx!=bhI)n!5HFK!VkxJ{h}rbuwuKKS-Qux!7o6>OyC> z<|S#;`krS6Y)pLwf)i`Wap?U{3iS%()LKqVcY2;6iUNeHe(ZVZ_|tVA9xSkkLRu*YddJ`PMkWa znXAH?F*CZYyKmEzyY2s5+pb$p(eAIGwYVsQugIakFb{exx4f13`zu$S-vSH(-)Zb@ TYxb>N00000NkvXXu0mjf&j1T} literal 0 HcmV?d00001 diff --git a/images/archive.gif b/images/archive.gif new file mode 100644 index 0000000000000000000000000000000000000000..06c4daa56bca6d7147d73601daaaf95e7dff5c70 GIT binary patch literal 140 zcmZ?wbhEHbOaGoGiUz)|37o)Ooo{Z3=IDnz?9-o7DgbCK?g*D)H*Pm zb8w|@GjMaCE2Wt3bjTv$vj+n!?~G+~Gal@d+tCr2*!?W1S7`r&0}2Trw7BdvL!U8Q mSj_i#U}CUtVAS@UeA17hrNoNWg@M6ZrKw>>)Y=vf25SI$y)88W literal 0 HcmV?d00001 diff --git a/images/arrow-mini-down.png b/images/arrow-mini-down.png new file mode 100644 index 0000000000000000000000000000000000000000..5bd8a57ebf4d5c9d99055b0b49b68a8ef06c77e0 GIT binary patch literal 408 zcmV;J0cZY+P)>MK z3QC$3=iTkj?B1RmgBbMCLmXIk8UAnnVfleSmh-id=Q#$~z4fY7y>a0pktta7#@XJ* zt>b*kXNsmp6d|n4V{35-b*GL|Jo=_71T|?WrJ#%gvjPb&2ePyfPe39DB~%Wi)QZ`< z8V!;R5tju8StkN#>iw3fEC-5M<}7Kls&$lQS2c_pa|X5#DJ0_8B>5F=pY9&`v)*y& z1(1s1x;4yIy#89Rx&I@mGvnIt?~Jpda}Ce;feZscjxz4)5$>*I6keTl}@X_`rJHR?tb+CrQQHQXrNNVIjP?O0000kP5fC&*24HIwpJ<@mfG6H*oopxh^EzGVWC(EA|lvWOR%ztPoPPR zMI_Ok-MKTnv(AiIq|sV0+&Ob$4&U4Z{QqM1$g<20Ta8LJ$8vmc_VDCK6Jzkchn@S4 z#XQR&5Yhh9>D-;CJ)bxsHZ8b7915d^@zDwUyy=MBjrG-%7u3sznc`d2Lq8foQUWQA zi6qD<38O@4J+<*rdt{EtG^+(8L2-JljG!=uw4|Z5g0!6~cUL+=k||s~U%DSUqf~OU ztvdBY=^%_ky!5(=Mf^pvG)pC8mI{OZ`*1jGS1Svd#El|#-tE2a@((Trm(1>GccaLd yV+r4%MC+{697=WhXuhITt*)!L$G@fehw2k2fvQ%rAqT|(0000}QNhkm z7ec&&g$u?^Hve39<4h!W3RV^#IP){Ze9Yl6z&}xzWj}i1Xm)w-aAu>}TQR%rT&~r* zilLQ;A`MC+?rT7NV5P@_otzI3M#kGr?l=WkRoXFT z6eglI*(pq90cGO*ehrSp5Cq|yIoq~O4oWSjUU&=Cksxk>#F~(D%DS@Wc}{sxl3Yx9 zXDMjhpRoq}ET?GdJB_W3teQJ+c=C9hH_F&we}v!Y17T{t_2w+ttN;K207*qoM6N<$ Ef_`47!~g&Q literal 0 HcmV?d00001 diff --git a/images/arrow-mini-up.png b/images/arrow-mini-up.png new file mode 100644 index 0000000000000000000000000000000000000000..d319ebc9a5a0fb541d0c9b5e22caf76d467d7b06 GIT binary patch literal 405 zcmV;G0c!q nm4HXrm6WuMs!j86A6jg4`COLwPSxCpF&Rmyj$ar)so{3qF18LR=j$`rW( literal 0 HcmV?d00001 diff --git a/images/at-logo.gif b/images/at-logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..c15d18d5969e94c70f0c228d1c320d24f1e3e6fe GIT binary patch literal 1627 zcmeH`>o=PR0LEXH2CbIXw3S-LR;6Zbrc`aZy41C21WVq^RI3+iwGl)qB8h~ir9xA| zL`a$YT}jjqk%Xj7U7{IvzXVy@gf^!qnZwro6Z^8~!|&^J&hI&ox3AYZ=Wrwd13myB zv$L}knUW<}Zo<wR+DJjfMD#`B<}ZZwm3nbqep)n&*NJ(I|IN!k?IZxgeidRS8F> zeZ!LRxs@fQn!zlSOwEkEnjDr$H$G_RR@UqYHU0%$AADg1yCC2_j>0cZsbS%w#i;FzKZuN;cgm0 z4G)vTY!0q|2UuO8*j|Ap==w8})&-%HB*H{pm_mfjkkv|?!oGCLfjx=zv$|%0TnJv> zM{SQaC^>QoT@^yN9P?X`x89%0iTY^-r!O2Nb{Q#)9d>;nSh!#@&8o4=hnY>^BV%i( zDdKCHi2av66-X?h2RwZh$M*IoIK|8VFxyZEuv`-3<}{&IkHE2@V6Dp5GaEdX($STy zt2ey6$!J?qq`PWZY=2rqKj{(S19v=+ciL$W&Jr| zrkQ$JB=%zGQG3^8Q4r#N8hRfQVu`_=vXcl=(9mb(kbvd86V#}rN}UI`#h>(p?%ABh z+r;@;lrE8jAcBSn*QF=uKw}?0(6kwu5uB)UgGZonrn}NrZh8r+X19aFz)weH=D-66 zAGH^8eAYeO{x}bH62dM=UAGsEn>b;tcJHyRSD6wUi;h#W8j32cS@>2CjgAiF(hh-G z<0X_K84TeU4Z(KH3>vyO0jF=)A9LCrZt0}7ZJM^EU6mrh?9+Ovh0 zJ?p1dpgK*X@}X%r(>-VUi-rTMoQXc)-Fe^}U*G6dCIcHMuw9b{+Ux5I7Kd z#rxdFpxOA6>3(ypOZ35c(xBVo(Kvm7&O#EOcXy7;mAzl_uOn+#vSRjYd>;pY8|Yra ND5b3-c0+(~{stYNfkprT literal 0 HcmV?d00001 diff --git a/images/at-logo.v.3.gif b/images/at-logo.v.3.gif new file mode 100644 index 0000000000000000000000000000000000000000..c15d18d5969e94c70f0c228d1c320d24f1e3e6fe GIT binary patch literal 1627 zcmeH`>o=PR0LEXH2CbIXw3S-LR;6Zbrc`aZy41C21WVq^RI3+iwGl)qB8h~ir9xA| zL`a$YT}jjqk%Xj7U7{IvzXVy@gf^!qnZwro6Z^8~!|&^J&hI&ox3AYZ=Wrwd13myB zv$L}knUW<}Zo<wR+DJjfMD#`B<}ZZwm3nbqep)n&*NJ(I|IN!k?IZxgeidRS8F> zeZ!LRxs@fQn!zlSOwEkEnjDr$H$G_RR@UqYHU0%$AADg1yCC2_j>0cZsbS%w#i;FzKZuN;cgm0 z4G)vTY!0q|2UuO8*j|Ap==w8})&-%HB*H{pm_mfjkkv|?!oGCLfjx=zv$|%0TnJv> zM{SQaC^>QoT@^yN9P?X`x89%0iTY^-r!O2Nb{Q#)9d>;nSh!#@&8o4=hnY>^BV%i( zDdKCHi2av66-X?h2RwZh$M*IoIK|8VFxyZEuv`-3<}{&IkHE2@V6Dp5GaEdX($STy zt2ey6$!J?qq`PWZY=2rqKj{(S19v=+ciL$W&Jr| zrkQ$JB=%zGQG3^8Q4r#N8hRfQVu`_=vXcl=(9mb(kbvd86V#}rN}UI`#h>(p?%ABh z+r;@;lrE8jAcBSn*QF=uKw}?0(6kwu5uB)UgGZonrn}NrZh8r+X19aFz)weH=D-66 zAGH^8eAYeO{x}bH62dM=UAGsEn>b;tcJHyRSD6wUi;h#W8j32cS@>2CjgAiF(hh-G z<0X_K84TeU4Z(KH3>vyO0jF=)A9LCrZt0}7ZJM^EU6mrh?9+Ovh0 zJ?p1dpgK*X@}X%r(>-VUi-rTMoQXc)-Fe^}U*G6dCIcHMuw9b{+Ux5I7Kd z#rxdFpxOA6>3(ypOZ35c(xBVo(Kvm7&O#EOcXy7;mAzl_uOn+#vSRjYd>;pY8|Yra ND5b3-c0+(~{stYNfkprT literal 0 HcmV?d00001 diff --git a/images/atutor_ico.png b/images/atutor_ico.png new file mode 100644 index 0000000000000000000000000000000000000000..205d1197435ba12d38706e49777f08672227b232 GIT binary patch literal 3249 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 literal 0 HcmV?d00001 diff --git a/images/audio_alternative.png b/images/audio_alternative.png new file mode 100644 index 0000000000000000000000000000000000000000..ead03707339217d81793b9ca813ae652207c2038 GIT binary patch literal 3474 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 A3jhEB literal 0 HcmV?d00001 diff --git a/images/bad.gif b/images/bad.gif new file mode 100644 index 0000000000000000000000000000000000000000..80a2e7b6fa8a49e725aa477e393342560d7b568d GIT binary patch literal 174 zcmV;f08#%(Nk%w1VGaNd0J9GOArlj*C@3W!9)~6-IVB}FA|fs$BRe}g|4vR%6BGYU zOlLGBV6DG5`PnA^8LW000jFEC2ui01f~R000DA@I7#(eFs<&M!CcgXu#kw zo>UBsGf+w6C<75HPLotdJl8m#!P1j4TEfet=*SU<%BMm`BPb25oN)lf3=qzR3^Ax2 czPUxAAa;-|CC5S_5iDGqI-t7*&>s;1JHM(qx&QzG literal 0 HcmV?d00001 diff --git a/images/before.gif b/images/before.gif new file mode 100644 index 0000000000000000000000000000000000000000..a1ba5597ffc6f84f9620ee89a310966d27b05a78 GIT binary patch literal 71 zcmZ?wbhEHb6k_0In8?8J=H(3r28RFt{{ul41&^6d=)qSOuEKd&-`?A4+rbSOmAbKZ$ef`~J^gKfZl^ zd#CsMqkm^4ODjbxm(h$o+EGTOW{qWQtyraXH7l=nl~tLHCUUe8%ACEvUhSiz1tm zMjJ7vd&S$tSvz*I$5t-uU1wd~0zoa_IB|&sN`~vL)AL6hP%=y&1ONpVfkq2zFzE$^ z%}Aq-Sg-di@dGEjB}eSBl}k3+5;Xq7qPv@3kOYl?u;?af?2;vMK*=z90EJ^L0*w~b zjKT#7n~_GlH+tDo=Lb%CLXyQW9F+UK*nJLX_<3Psz88I7>98(%++Va8$CDPw_uh}= qgFX`|9p;7gY%YiIH7$;Dm1zCcR5#5JPCxhOTUBr`uxAtW<5 zl_94zPr*nbH?c^;$iTo-!O+qQh>R^1v<(am3{5s435h7k>0O-< z6yhxKh%9DckUI#%jE6Hrf`Ni8>5jgR3=A9lx&I`x0=cI=T^vIyZoR#|H($C`;=o7i z{VKjL6TJk37OUt=OLJTcCykL#?pG`yVdvS28Z((e13Lz`LV|zKX3f~CB?{6 z{PtzTyIc*AvxGKF7w=y8`s|Kx_19m2UAK1Kx_?{lpZr?S?_!~6>ifCujh%S$G9liE z$*Velnm(JIxBd6KTeog~udc7qSBQ>|K3)Fq&Q77-;>mK|FJ->P^Br(r$m_YrYhP4+ ze7*6u$3+`@U37x21pD=qz89T&Jhg)1Lw}L_tC%g1wo04l)tK1cXj9^=5t{8i^>!%3 zoAq}#Ms;<>?5q3xi~VimjY`vP^2cnV-hTFBVfd!Oe=Gm!o`^7o^+#-iI@uXEPYtsX z{2a!4;-!}j!;JXMV>g8+JWTwr${=HDdw&~~f~y0AhoAsM5DQC#2qWX*=T3=x<~!GK z`K#~0L$}{v`)XGnd&A;nqnRO(KkmqywM?ql?d|Jks{jWvaq-&)QyMtf4_pjCRQT%3 z%4$VPzV_n%grRTZ%cKQ5Jf}?eok=jrXf`^1CSBp_;c3&R9btYj zL;L!VqUkGjB)kIBW>y}%7ahhRlm9d-{+*Ssj?NjI>)SUl>^N`J%{COmjHHPD?V~n% z=JPW=Co)V{0rNqOJllbbVe7v7UJg0@u;AHFW9Ch}cSpZ{^JYu5Afv@TVA$zMy}P&f zww_Gd%d?*sE_J(o`}WmGkCLvw|GwI9dGOWJyL=4p;ho1yt_D6bJbx-VYh8m6FI#h& zmxGtbuDkE{J1J=}@Na)PcgZH*)jgROM&&cS9K`s3E&Jr{?ZIHNvFx<*gxiL4c?<^Y zFZJB_cChRIxmrk&Ax(Sjjjqc5#Sag)_P^fs&nk{bOOWxwK`-ThJwJ_ZeRTePrP^p_ ziJ6(%x?}ISuQJN7VJ=XbQt{?U)8o&dpP%0!$G7D_yAb=a)2C1CAOCi_;%D@kH~I|B z1wkf;_Qzu)fq62y>chVGcaM2JnHe#0k1#Obo;y~;|68B^LBN}1ce;)q-Rige^F3e& z@2{@^aiIRrjXQU0Hf7G5p7i}D$EmEG^GCk?e{ahmllj!_s>ZYaj$>{)``>>59UdMQ fR(JFLll_b*GdN0w)*o&MmU0Z9u6{1-oD!M<;S37t literal 0 HcmV?d00001 diff --git a/images/calendar.gif b/images/calendar.gif new file mode 100644 index 0000000000000000000000000000000000000000..dc1f31c08456df62365dc7404fc11ffcebe8295f GIT binary patch literal 392 zcmZ?wbhEHb6lM@+xXQqwGpMeli{K*1Vq5~p9eqvx-dSGUO zhYlAr=i*5n96Iw2Zx&1DUR-x0r|jq|p#;q+MzaM66S$aXyk_k_nYP91=(NbKKeG33 z4dBe+(GoDx(h}vZtBy}hPEF6ujx7_^U^n5{;4{(C;FXwKU1`Fm!mv!XddjpJvosWz zDNACH8ic!vD=%&nkr_s99|7p4!T&a_sQFCg1q~|Nk@K0E$0Z7#SGE8FWDM zAUhdY10JaMrFb$m337I6GC9iVw_6H21|Dg-*eB}9!*o+oqvw*+GGzs}DJ@1DRCF|^ qv`tY_u$bTM<{UK3fjzNH`J;jCA>L!R9_`Xl;cH(K`@Y+W!5RRCnn2zF literal 0 HcmV?d00001 diff --git a/images/check.gif b/images/check.gif new file mode 100644 index 0000000000000000000000000000000000000000..14834e9b74827cc10405a8134fd99f604cc61a50 GIT binary patch literal 57 zcmZ?wbhEHb0r5dH3`_z&{gMk#S2vgh+ocDGZC}X5 GU=09i{SB-D literal 0 HcmV?d00001 diff --git a/images/checkbox_check.gif b/images/checkbox_check.gif new file mode 100644 index 0000000000000000000000000000000000000000..d374e92850aab88127b215bacad10b2631e4332c GIT binary patch literal 100 zcmZ?wbhEHb@~ literal 0 HcmV?d00001 diff --git a/images/checkbox_empty.gif b/images/checkbox_empty.gif new file mode 100644 index 0000000000000000000000000000000000000000..b14f6e36389d6c09ab7aa4f8e97ee65ef645e2d1 GIT binary patch literal 861 zcmXAov1^lI5XNr^5+Oz7&?$p$=@3y6ZZTF63f4MQg3wJUBHZL62z4m<;Sj_@ghJuA zQd-f$C4WH|1M&JT4b`MhVrr&t8m37bHCJYcCzEXQVi#mL$YSkjCi7=U6VLK-YsLla&QVKUHQ)5=|nWVul_8jWVN*=n`g?e_Ha zbf?pqot^D=yS-j-adB~FWo31Bb!}~JeSLi}7;J5A4Tr2P zT)Fo6~C3nw13mdX}ixazIgAz?WgCSy}SGN)3g5=K=GfT zb5UwyNotBhd1gt5g1e`00E6OB7Dg@xc?KN@AOJapfz{@LdS6QByo^=T{5E&-2J8`K z*pcPNG1*ge#>AuqOHl^yiKzvSwmgkzo-0JEDoi`9-JD<&Kko`wzf7Rq^qsnmlj?N@5M`7GQNG)tS&G1 z)I>dDT^r;T*jr@2R#7Wcs5C`a*{eY=L_6_Ots3oPO!PM`EG$SG z)7}eh`DLNZG~E2Of_1~`h5R8%kA?WO&kNJz(nfwp!V9+Z+X-o9_7!%H`~53+<5JcD zH($=lQFN7x8Oidt+*d=~xk>5MhnW&q>bPgHh>@i9JKxukGTPZIW?p%P^AX&;EUHhK zot=Hk&XRGcYSkFi>*8a2T&OQeCAuqP1dR6GPmaiLAv@2bT|UmLd~sw>S!^HkYD2oC zvbVaYE?yK7%`NclpkGyjHJZ7}GhR7uMM(o~xr_5tBf`3wQK5uaDq^O+?DYca7dq~x z@`_H$S}8**l!5k3ea%5HGV#bxB_LQ+chWtRFq%l>2 z^BV?hWDm2HVEgl~vS%G7ot2>h2eMl5Q3XLnUS(YW1JYh}RzZ5&^PWnT_}-|vo|Eqd zHYSXKwX4e-kRI3HLK=~`WL{3Wn|Fy-}DlUrUXnm&x^ev{`qq>LbEVTAzm52#iVPxHB4zws4DEG#-MmF4~5Ks-Nhn zuySSFNgnF38LxtbFmi*UCgj;CEd2)GC!YaUs3X<_*xAak?wR4HGS(^5!4cG; zq74l%rp_Nr0Qq~`xP!2pehGY$t1-gid9!FNF;c(hcgN3KIrdhW`lYOm9PrV+ZoiXb z6M1heb`tK8IQ3y-#SZJ}Va_CAA_Q>CyM&bIlPh_Fx_Jd_;E9ti5#&R(#~)~o$yd8= zo$ZW%y8-t(Xh3p=U!eQl(Ta9jdP!K^z2{uuwsab>1OBz}wjp-=KB7Z)`r=ENgESI- zo#2YlF@skS2^(_F_trUBpb{yUHf@>1Pzk938NbP~*abexA3-ptezzZ`;h`iqbF2G617rfn z3U0fu7hiTWsR;sYo9(BkJp9cX(jc9EO0_uE5Gp*ss|?@iZOh^glOE{}&-v?943fYb zAwQ9NbrX>gioB6Wz1DuNC7(l(%WvgxPIY*2PLe`U4n zV(@>3d**KjP$B)hSdFev3Mhsg4~gUM1brA*w!pWL_U4VoC00e#yyZ`_I literal 0 HcmV?d00001 diff --git a/images/courses/3dgraph.jpg b/images/courses/3dgraph.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8aa608cdd638916e9907d5551d48d27c58d37d81 GIT binary patch literal 1910 zcmb7=c{J1u8^?b?vyUvpG|IjV#x}{;a7(7iWE&Y{uDM7dYpIKg@+P@vN|G&3GRij7 z7+YcJQbLx5NL(XJi=BulmAbh)?|aUB&imi{eg6D>pYQWM&*t#v8$jNP>PQ7Z5Cj~y z2G|@0C;%J^gTtV3I1G+Jz>#QqG#Z6MV`b&G$tz)Z>`=nuaLQ`B8piw>Fyr3=%jD9EO0}fD{IVV8G2b zKn(x@#Lu?>6(|LP!jT9VY73L(0ZeV?;~R;r`-&6d?l|( z6lt1_Kw>;fsSeyrxBgD;lIsi7FPxo)#@#>Cv|FcZUvD~(V4Nqh zniZd8yHIpJLSeET8@mS7O#EW(By%i0eCd1p`ky4$!AL9WC(~iF;iJp;vTlkEjj`*A znrd&wnVIDySC>zE$tc z5#)7JIRNdC)Tssb6*eDq&9b@PLKQOZU5S7**8@X(_M5#sSv`t!AB!LxICNR!=d@P2 zCbJ3yMiH&qNJsZHyFDrS+z&@PdPME@!MN&sr+auK#M@?Pr^!?e5#98`LdoH0)%9mw zYKx7z(gbeYqOjZYZ~6SDnRLSU|pC*CZ3A-PY`Ujg*qAxu&uF!#?a$ zeV(Pe^9gqe{&Vh;wa?dkG~TN|dDKRYvgqhsOVjI9)zDepAO5}asM5raiDPF-kvLJ& zWCPPqSI)S*AqGAA=_*B5>$yNIuE4*~_BpAe!uX)Q{Y0r`Mkd84gf$XS_FnbI?7IX7@15GN% zNv@EhyWP%d2Lf((cb~7(6F84f;}jzk9(1Zc`7HO!n585;6j4E}F`}wE=ALhATX9`J z_eZ4cx}i=(c(36?PSsL6jh%jap*pyE*UcLHnuPIRh5Q9^uxf8B2mwVsp{>;Ev=-X^PUaJ2n5mz?7bwA6w-5{%&cj+NUN zGCTtmf8|flXJhO5wZzLL?oMuUGWFs#UWRtJBq^+(wer|!)uzmE>IW{N{kU-OFUm@B zN5iY_`N1+1FLyo_;f+l>xFlZoT%M2#eswJfzlSNu2VpGrx|brPRPEekwH(|&IhB(a z?Mw$O=7VMqzUc4Z0essz*fF){sf=M3h2hb%n-LaqD&3oG=iu<-+wivwv*o{^JBOJf zS*OhJn_E3E2`n1=nCzQkwctZE^Y--+c(T5?8Vr1@TEDDmZ;4M;CKdJ+Cn?_8^9wt< zcVDyEIEwbgYq7xVxg0#O$*BDLSLSz>#2))QP*-a;mBir?PU(e^e`oTjtSPt4P2XhWT}31HqAcAopD1#)8ul(dcZEs_+J`uBx;~6g* zCs);&%*zy=NMr7`{nJIH$NUphSOXc>+rMc#~9o3?g5^~9Pp%Y>a!>Ib!yRdH%)G{!_5r-wH* zG*r?eSeWXYA2cx3{~ZDn5fOol!ezz8Wc5|hD*FGo1nmHFAK(tSLqMtk7zu(PL4qzo z9soeXdu9J4C;$->hJk;>!x8`(1Qvn_Lm^Puzb`;wh!7GGmQ;b_t)z$n*Y2ZK^<#1y zq)Fe@tOH|{hhgdlHWe>F?s;YQUhv8fN)i=Pe-JXiaXpQtyrzi`6Hid0WyVC26q#ic#! z&w0bx|E6OTdnx&X0BB=klH_bicS=T;DD^Kk+3$S<`Jr?&(stc&i1p%Yz$WKxGp(`p zuG^5}qjTbS!0$eI{`~Y*M>wEVjkev((2YL*u5D%%g>b`$2j7&dD@k+oEj)-gKfSS? zT@)DFL9NH0nua}D7F#iUbyGdaqA!jxA1Cy6((&4c^MmG^#bZlUL!tOw>EY`-qR9kg z+GrgPhXc2-N}McO?XP4V>CBR7jv7@> zPCZsFLFwU)jD~xPTD`qK)k`Cj6|g?DeB`NAKc-BpKPOZikh}@Y30k_$X|OyrfK~S# zG408<@1xzVUljnQo^fW{XV5C5cAk8PO8w;WN6d0rg{&Upi|J2Z4OzWO*nXF1^9!I5 zcE&V6AR2MCb^D&6k?? zaqv!jV7@Aeco^x5auQpn;7w){N?S7GyU&G-a?LqT`6t}CKT-qj1i)OXHkXCpOjjYZ zrzC3HicLdLxd$XZD^DURwhg4~y&lo5O6Qxfsj9m-_*k*?UK%GIN(ezV^!GbXU5QM5 zcU1nBLw782k0*GBQC0B)ulb4J_VPWPMb5lN&3uWDk;M9THmZ7ePV!c0;opAo0GG*_ zkBeYB+Z)@R@Jc(~LHFImt!oq0L6Hp}V7agdqt-jv_k`gEZ`@KQqrbuPXwHNn=Z z0+SGF#hF+~x6~#1_WU?Bso~2hmR29DrQ_*Ktd`}M7A@@k^6M{m|FnUguvz}q>S8@k(ExwxdXlE(8_1pQfazp;oR1Hy$}24Lo|%@c z7RD58;@_%;34n`^5odm|U+BPz9%IbsT#@_Z)eCt`nP> zCftZ$yEuEI%q4)-(pxYOy{>%t=;s#E+iF>HX>oS_>t2}kJ5o>FmuGVc>LL6iX1+m| zv_4y3SF~h@H7?cxb>Uuf((!iJ>w4$0oVE^rEmSb z+NQ#OU$kCYO)lPUgn1YcZrRNP#_~#*Ow|y>;0&YOM#Wy@)QsHFh{ZCTKP~>2%4< zW0j3w7P~%SD?8lPb{Ay%>@9OvA@Ybcgx)E=7E%!FioUc_ELK4E@tgH2OReTm!Sohv zEwlPEg|-8!8&pQ3*F}=mNlCXs8rAuN@Y9fRP^>F?Dp@IRE&GHfPaGNtEV7EyU-7 z29BoxaF28oyEckD&S|{>>BHez&Juo(`?iS3ZeM|6O-awRx50L{_~t{#B56C?&-Ui70GpI$gQvNY*A& zX>vw#WHyPdbQf^&+GZ~dB6XBAD=@hR*nK%+Zb*P00cq6RYgE41)Kpj z1OkbGt09p{b#=96C|ymIh6ZY-whmesZ)ilo8{%-r3ZjWF4uL!>jqk-7)t*{?~vYMLlp zg7Wpq)~p21v?_@O0TBK_wQxYyiU5%+(HsMSFcl#P=zj)-DiH&NYnyCf(%Cxk`87?i z5qiXfN(q2cSs)lF25O}HdP{)Lay;RS zW=JS^ZY@WHmek#w%Ah4Pzi3*&4_*23!!{Zfp1U$!FPm0SEO%%QjjGxK?%9 z?$vjb>IY2(OL#ccy!d>EiQDJqnZts`LlcW?);mnSv;r7*2lvX2LyJK|-e5qwY`|35 zBLUq;4-u-El_Qls(_Sh7O$N7Zkw=>$Z{H)ZCQWm;@?i(JFvm7O2 zXuGueSG?(xbH}}=nA;lr>>@LI!egJsls}rpw6%bpjkM(x4ysr9|Cwr5z;P`|T|+Ka z0%2pPMl8AGQdW=*rc-`LzdL$yers>usQ^=(N}6ymo3d@{!3_CQQ+b!~cOK8ObUZ^Hj-0LD;8HNOBXX#! zsIC^z!>#MnrF;k*vUde?kT2=9ogGcE1YmwbS}E#=}m8)hdQLl zoKWbe*z1_eX>wz&L@ao*%jd(i>DY&_J|}SRK4sfPGz-1DO39)%=DE4dKABnI+)>Wf zt!RfBr)sk|2u?_hy8xX2F~yIe%hm>%YQ}|B)uif!94=^g#R z!KLkBMoLrVro~b8_c@llOc3~xaqMG6zsTZj<%01^eVgk`MaZ(>nzYZ{`E&dgQGH*% zB+|44p9Su~X6BVpyxOAA>GixgGRN=P7*zX4!m2HzPIIutFKq(r&e3q-`+i8*4Z{fL z5l+17Jl@$m$p&}?Zdhl5Possm`GP_TER$Z6VXwVIl@WiGbsa6QUs)u~bVFUmE*+v;z0D8T@NZWLlm4tzwD(2P z&@E}BUu)h|w;D_2-)?3VKUtl9U%XiKJJkKQ$KwkfE&4&bJR!8k!-k69wR|6r)dBPj z>bJRsV|9zdA=VShMaKPl@0R&`Y~MTyH3CNh^M4NDx)8e`w_iDW_EXXc{oQ{7ZcT*p z#;k38E(!a*BDv`rW~crg$9rKt?a|>Stz^qGw%o^Y6ydqWyd(SKWRH;b+7;<8*vJ&x z+vGzmZ@rWgDC?cPyu(MkTQlvttkAZ-#*qav+np7^2k6a)RqVn}*rI0h@(|~G136=F zMvmgqcbJi(-{5Z9(%8uOS;_Z-7#nZoDx@W2Bm3o|&)w3K_{#R?xfLf_ieJWbr&(g5 z#o3)A1HXWlRdnH$9FUJ6>GAy~+TMD(G7+br_Ki6Y>Npoet7(uc7lR_RgI@|hNyv17!z0s9X`=dYmwS=tJatLJ2zZ!*OdML&JJpNiLXtM&MLmJ=||ZVq|^rhEP<)=I(smu{00qswN zpaDldRyXr_?2K@k`b%@1*%eA5LQP|T;=8El|C=+ zI~|L6i1RnOp@U0#@2|>edO@zo>T!u9B4d=&tO-4~(+VovS(9b-D9JhRmJy^bync z%@UQ)Tnz<9I4zT{Y(`$QerXT4qR zM@+9Na>d#slF*cI_%R*&E7U)}>3WbQ(8@xI4i;+;`{XYqI5-wh(RRvp=Q@6w&b(?) zm9mWxnut;R)G0YYX;1UoMa|_U_Inkk7ZfaR?sa*mHpkm7C~46}5f*?Ck$FVb^Oefs zB{{H^%UR8w(+$ml3u2>krku#{?-;eYgF{t&-Ec-b-8 z^UfICgL$$ueETDhyKzV_K8KSy_zSa;rz}EmQ@nZ_spGdD3;%L$9JQT$A-0Lg z_*Jt85%^XWrzlVP0im*78dNs~1RXN$0Ch1UFodAHy0Ryx1b) zma#0zz6@-xG>ZI>@W?-09btnW|!2J9vtGnvBs7 z7^p5~ujRt0Q+FGKEaP$xSYCnF533SR&_7iO3GJgx`OMHfck8)`j))2E_Cw;ZVNGLM zyyK;(7g|FilN|){w`4O7w(E~yxRmm1FIGQFbas1iQMU3#P_|+6Ge5S()~{F&R8Q{l z_9`geFOkBXqzETTcl}pEQFMp?Yi(EK-ZLvN2h^TLNw;X7 zWN+mZodG%E?>#if|GIg_Rw%U|@RQQiP1r`0chmBoReI}MbF~Q0M~W+=g%oBRv$tdE z6g4K`ET10Wi%Bb+cJPU9v7P$kq!X{Dvr1fmXUGAbUfO}_>r<{j=o=gjz4wgYbuGRj z*obqlcZRJTQ=DxyRGq~dHyeJQUXo3iROC|a1`UpgRo!@pU7iI4UM* zRG~;*(RQyL=SNWGusmIpk|`P`_BDC+^mrl^j wrT)uyg9tZvzVZ#rCg#x(}k!K&^)$y7e`H+L5GWdHyG literal 0 HcmV?d00001 diff --git a/images/courses/bar_graph.jpg b/images/courses/bar_graph.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5a2cca3e7fbd9c4b9d0dda734701d792206e09e2 GIT binary patch literal 1961 zcmb7^X*Ao38pi+s#1b??CYDk}5<*B+Y_*hHM}&}2l~6sY!H}V&Xf0hB6jRk!6H94p zNmQtvAleyi)7Gs$mTPahZIzCycC@;vac|E(bMD9czMtOnd(ZPcAKvqBPi{X2RJ=SW z9sme}fagvF+cSVGpdcp?lb2I~!C;Dt3Q7nKWdt0K*n?72)xhXtvHH4bv;o12Xn-dh zq0uBKGqR1XgM$N(=;GmQ=V4{++ZE^>1k< z+~XEry2s_Qv4Gq*!1v^>-1_SF!R>^kH?irHCY0F{j32OH1LR*8%;yjFV8ytOs@69! z%LG8alA2=g1mu4M=#gPSH<$SGk1r7WZy>{iQd{qlQpZO5x!Bk&QO8B>xhi?;~7)fxTS^Zg=dHn#o7D0aUw}oomXL< znE1o$wU1BA)NhW3d^nloq9x2Q!xLQ0!~A1DHL{a-)v(R@mf6z8l2_)z68lC(a(I_5 z2EcVnV-HyWcDNFBqWh4828a5s!>58I!4$d%Wshw!Xnc>jfGX$mjM6TVC<|(g_6&2r z1*&PdXH2Y%IAW3Wx{Xrn*vq?GL=x$dUj-dgk@#~$XI2M6F>8$5H{d8e94^^6oh5BE zQaSDBM$kD}+kcT7-Y&|Eb}!=08O~zWT=4vtnhV0RPnf_AZ(6@!aco&Ni&Jfa)p9D3uu^9?7LZ-fI_-NJ+4F;`;`* zv1PAN>MPd@O@e%ywJ#P$8vV&iB!`v>v)V#}Ta_m4{#8d{T7zF| z#CeRs6m#2dNR~U<|ex6xTHrX;lt54=<##&7>C=Ptmz}($pkOn(}E^s zePz6gbv^;&`{VD8oF}`BWlM~aj36$5)1y8+x3g)(z| z{1qO|`d;-|yvRlJ@YpIyv9qA-n6X@QC;P(8xmH3!gI!9crKGPVar)0uMTO*FjpAg^ zPdVc*7U#R&Z%t$>+~no{vn`osn$D@2yuJx%I$P-&GI9Vv3hvFMxuOrvm&PcVw|;&u ze~OkQ=#zPN9UZ@Ae{|g9N~FT6vml)t`J!!i;p0(~R;f~)ZP;W#?n~~{Vb= z4=Fe8GV@@XwkVHMu5WbqKtX0E-@+Pz^!((O68ZYhp05dwHFy2Xh)SI6o9FM~YnubA zjZG`YO6*#X_dNu}1r^6lY0L-Kmo{{4-?z*ys?Ap%`pVa1ur0G|mWSF4*Hn#&{HxSt z{!DM}0Uv=rK#Pe?%WKxP?`AWaR$->B#~SLMw4iPob;{CuMFpXrBB2VxusP3Twt>&e zH)7!ah3BNmbPixDcNDj%Y%-uSR0vv9#ZqOkW zoJ$Qg^h`5cMk^^#{r;wSxPJtvVc~F)w8(wdP#kYv{^JtS)J7oF&M`_aYMPdI25=vt7!uc*oRX+)sa z1VpkL&N=>zb0}qSt6Tl8XNe?>qgpd9-ur@vl@V@e?4MgyFeByuT%Ki`ao39=`uZdU z@$Hp-zn2+WJM_#q0XHj-5R|dQok>c_YwJjs5NB{zYYm-@ZylHYJVD)jGTpcVz*)h| z$LgtZ&M^j++_oxC$eQSk<^J})^9{Wzt5p!1Ftz1*uRChXI@`%H@(8j1)h{T1H?Jes zPtS;~S&g0}wZZEb?Zp%Cx_WG6GfZ2g;hn<`A$H)uKiC~yfrA3$m-Jqe3T3$dZ^>O?$s_@l$MC-=DsmF@SU%lT2p z6uF`pov*j9QdbxeWpUv!#R40>$}I)tb+&;IYBdqXSvbL0oUQ?-fGN zrvr3cW<}~iLp**!C9Nx`-LZ11`?l+uje@T>@9VbQSUiUJKlVZAu2WX`ltZ_t{sN}D BLG%Cs literal 0 HcmV?d00001 diff --git a/images/courses/books.jpg b/images/courses/books.jpg new file mode 100644 index 0000000000000000000000000000000000000000..594e3e6e362827431d0811090727a5e3720c1cfe GIT binary patch literal 1958 zcmb78i#yYc8~<*@!rIKD6v{2Rop8)$Vl+pV%QDBEVR9MPIWm_}ilZ{Qrnw}ha?K^2 z5IPx-CHFJKh}@D(N>(TxJL^2Z^9TGs@B2LO^M2m<^Lajk3BfENb=Deh4S+x(z(!br zU<$wiNZ5YFen}((jzCEx56EfC%N;x@XP~BjRMX7R(&Ch%DaOi<>||x*g~ymW(_Fj) zf~gm%r-?CFqeBwNp;XFu6Oc3tB_}JVFE6i8u{O1){NE6C0LXm+0U$s?$^aM%f*?VH zZr}(2K!jn1asPves2BtcfS|&ou@nFX1EL@y0~P)E0|bVM0HR1~B|R~erQ@{&%KCoc zSt_UNWqu@IWR1+pS`nQhN`%TopzpT-S$>~FB_JZaC?i1t7$gb^`+pGbNQltU5~Y0F z(eIjI5`aPe<&XdtsCY67YpT@bdt ztVbH$HSDp_d4!jG)}(HY0YPjQF@Le#=lDvpi;qL=FJTm;;0Ov~E>A0C?uY>Jf~egc z!zNtmN^jMo9u0$@+7uDfp%WZ7mJ!3Bj0eSBMFNQ)*5fy4?Qw|O(>o-UCA!U8ug~gof0CJq{}d=8b7iY5RCYnu{bUO^48mMe$T};zL+UL zQfv{eS+o`XW0>(d;>6qEPL1?BfAh}?wl?o7a4cfg$y5~heF44wHm{F)TKX8aIG)@d zl*us9gBsn}Pg!ku`Y~VJ^d|5q16g+BWP^^@0@j{gUx=7t$rtl2UWcdYZW1vAeR2c& z8dJp+OzB{5)9yRwPO%C-u*Rj|;~24(!d6+o7V$FwGQEG>P&=#S#N1X3rwFd5T@^w5I#&c;OwgUBQ)N~!6ELumpd`(I*eNycbp>RnA4$s!0 zww)>Ba6Zsq@-`ieK4`|1esFk>UcRiC5v=1{pJS`@@}s*%D~_yEHfDx~W(^`>u90My z-U`Qn_$B_3(hAI%zxieMX`@)*T83|5b|EJ1d{(3{Vu|x@L7uIPdUKD1B**X$olSj$ zytcvb;XTVWvDHfCo31-95f1x#l6?p^kDIQe3x3{53ByN?9A1Sl?b0CP7x3nB`4%_Z z@Jh;Qmv}?%{jvU|o3_yh!?JaPV_s#Y6GnIDrq8D3bjy~JH>x(BI|6q76hh##h%~lz zs{yh*E1k|n*hQxt+9h$9Y^eK!Ua@c%4hwva3^Ng=0M}OOHOZ3KLr^r68aM!XYTCA9 z2edqM1I2UBUv*+$Iw5F^efNU%fC*u-_4KREuY3tNgOZY28hKf_l4fi7Wa3%8lAvhu-kXT}uUT!@rKDyvJu2o3*rQI&Q^$gBAM5eKEUPg*OZ;sCscA zCxzhjhK_GMq3ram*@#|#qgKjeaPC9YxXqXgCNA|^@7JO{H3}(3=1E%sG)Mq&KCfB6 zs>Yih<@VIgM}N7dZTb!HCxT`#x#nZ#oUMws|Vd0ueqjM8)Z2sRFLw!Z5py_U+}zEvaBni4MQeRqH3Eh(E-}8ToGv7~I%7 zH-FuHptD%~r#(0JR?q9lIbhjnMK2ijb>;I0vgbArohbM4mw6>?h(>I*yJ8}g6v?os zna7d~dYl3kt$7EBymd+5Id{U*On6~cXkBH;OjjZ_^L(K3Zw&n9foIKEqRg>&L=bbB N#(otC7A+R={s9flem(#I literal 0 HcmV?d00001 diff --git a/images/courses/botany.jpg b/images/courses/botany.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5118b9fcd6a42921a5e614a2c5574cc71781a711 GIT binary patch literal 2048 zcmb7FdpOgJAO7xQ!xly^({h_^EyS!MMI&tDEF-z6CAn=ixt5YrDWfJ$(>S`gBn$nD zRYxe9ge8?32SrGv?4aCs^gF-jIe-7&&!6w-ec$JOp3n1q6jH??pyBRHbp=2m5I|WO zK+y|01IjQM9HyiUhr?A=lvRLxZ*Y7>tprCC|1N}9$nYc2BrH27*0FE*Em zbz)R&(;GWiBxn!-`Bna3{8ys1LYbgejDMv8kpHnDFaSYmT0@Ps$X@Il-xM+cxe^0L zK~R7rFf~K)5t4F61(eT6P3aXL)lU&HRm$Qa^jyW` z89sD*4EI68qo9{ge!hP&@dD3o+ZOEkX7;3eWpUJQ3F*F7$_9$u5+)zE9mS%{ex|g5 z$P&@J!8x_h`8mP8mvsEne>Re@PPGimWf^pm8go-wKLTr-t_}0G3=VD+ZV^E>O@?>-iozevH^N8NZG-r3{7ZWQ6QSS)ZfbV!|`v+lgl;@_xB7Pc+?l+n?`#bbh za-(ET9(StOY*p-LPI}_y{;GL{LQ~#kXYfbI&b{KyY$-&Ntz5Y^T<%d~-1jsf9 zx}>7I9`5{keEK?QxFYM!7~qgAeAga-qO3QnQ(FFlt(qRY1(`kmq6(&7G$(wrK;uUT zyURvow%lB*2}T!fI(S*70Y2lEY9`@kjHNQ1)jIg$dvBqyYdq&#*YRXa_V33!(waRrNKXabj>B|2A=9MGNJ-n$sr3zeF&oaGgM4=} zEQ$LfC$t`X@K$%+V$BiQ(Z9|#cQv~eEvPb!zurqW>iSS`3bLz@GJz}=0FEDEO=g0I2o|*;o-q)7MMI$VEkwM>9 zN8`^(X~1o!l>=fmfyTVMcz$82^U)6+zLhi_(-}{CXRfp7NXH+Jd5*`Z zKT6-~)NJ0&8?ZwBPyhj?>wZ%~Lk8x)gv3=`S$k~y4UX_1bB=x1(~yw*pC0;^o;`hf zJHLy~9BK}ng+P%*%SAeKb~xN7?bO0ME=@-~Y;JkR%zNHWkYmetWd7A+sOL38kX0j! zgRa!4w@4{=vm@z$EniTotKwQ!@eb%0$=CwqklwcBgr!fXBX|)ZxQ=dmDIB9e2IRi9 zNxNA~39jp_B_@TrohmN)ZQ@LWr0(GRho7pu88u}7Ex&?X1rWztu$^hgbWMbI2~%uu zDcd}H5_>{)FIlJ@zcF)b64AA0S4>H=O0x$mc=zby>Eq##z6kG@89U5+J3n&tF7+vI zb~XzA&^T-*zq>bxlUw>-bzJW;ecoC(GjWi_8^qfOg|4?#046Xv4)^{#-gAoPKY$Kl zOqyP7cw$(8_@Ee4n=&kSf9~p3s>Xlipb@e!s=%czT0iSqQzpwI2cx|=;UL5Hq;x?6 zBx?8ksrl;={4~gG8>eqCgmAJY^j%8?GWcOLi@`{c7>2h_L&A`#Q*P|S$QM3@v%6Q` zfjS|eJEQGVG>oaWrs87#Yt$Va?uY{Tgzx)vW*w)k-86>W;tM@zxc_YFR(aIazynsZ zO*Wf)!mPnMW7*6NA(axx1Nm{yg?=X4DD{hDL&w9Ald>HcLvL9+j<(8SetVSvqJK4p zRbGTg4*1U)?#52&aaZ8)4T->mi$3yaB{^w-8d5pxG()6yj4WqW7w#QBuNnjNaS toVI3?gR?{`;EqMdJ|LA82+92C_iQE8-Z`pQMzee5!ukDgy&V-je*?GYe@p-X literal 0 HcmV?d00001 diff --git a/images/courses/brain2.jpg b/images/courses/brain2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6effd6258522b94ccdd60976d986e4017f3e5350 GIT binary patch literal 1544 zcmb7=dpHw%7{`CxTq?9?BH3J%a5(PAjB*{Kxn<>6Sh-~hqj}s~E}heuxul339Tc%J zv2Lh@kxpu<=91g;h+!Nhm$S3ZKj-Ps^L?N9pZEK|pZ9rR(RhT0|$VRm4nO4AmBUUNO^>UvK~rVNlDpETSraL#=_3l z+5&6k=pO3h=o08^h4m#90#8Sf&XVlCDHqO1rH7s&MSh1s_LZNQphuT>uJ!!JsfHNeS4$4G<^{ zNFd}jjFEPlULleuvDExZ1$*zNC9Nle6Q+|w@sN`EB|q@KGl1AYA;je$cR`?HmYs2k zhzDe0ViN&FfJ5NR#pPkkxf_%cNzkL+ZjuV&Doj~4W}J<_m8M^>W4}#Od>=eSDNNIEdq7Z@U>HZg3eiX ze@)WC>+yuJtBUo=1gaI64d(6r zL+}PPspgaZb^~E?nI$Ych09%{R6fb4`*|p^lD)%$^H%VUua~sUHHua489nll9ax8( z8%mmd%wC_g-mbwt71I}AxVhU;YtElTK31*H3DSubu2%2AzO+oPCJqrK$zK=ok3OTd zF6%^a-f4LC^iSD4JrHW{e|e&=rL+QlGoz{;`qUziEnoWn-EFIR|H#gI>#yR4LHX?WSiZRe)Wye)s`s4 zxN7?bn`ZdU)2wqy{7lx2aoZRJv(GgAPGtgoKxLOlU;d-eI%c?4c~kbAmpd&N-AA8k zH`=xqF9cv?+IY=|il4tkjCn&H*eA(Rg8QFX{JJ5$Pt*R>tvTo&$fpfe^SC4tNO0qX zH(gmMbd9*6j$kAWzrGu-Q}DN$9O}9;sTJK36)HUHw`WO;F&Mn;Ox*C^ij48k>E%rd zN*5d|D^jz}cXL~(s=2S+WDjE2L;&k0PdCJ7tbX43kI1deK(x;-rF{>+Up6Im% zerj~c4mSu36*IHp)7~YAqGxXODz|QqzC4(aLBvAq=1(!om{Rd4ZhdIY{(v4y_jV{S z?Wqn-t4UXSXsjrsQG{ssR2%WtEak~%xW~m#5-c}-R=qppaH}l&LRh?NBl-47RPUc_ zyK~Q-RkW?oL@s0p0&LmPNUoNgm-@L|rwww}xj6Rc@^b*5n&QcxukjB_J~&2+QJst? zm)le6)^+b1<-?}5GcTvKofHP}74g5vG7_sKtm=76(zhW#D@>MCfAquTwg{u$amdE* zBZZ_QwX^v{JbzNO_Ft9_Y^r3Pl?J!Ly*ho{&$NdYFTbC#L@DHw5 BqdWir literal 0 HcmV?d00001 diff --git a/images/courses/business.jpg b/images/courses/business.jpg new file mode 100644 index 0000000000000000000000000000000000000000..94c35527b7dab93863a96d863297b920ba090c4c GIT binary patch literal 1428 zcmex=?)7ux-bac0Y3}FL zZ;SMaY$F-`KfoZ!!Qju}&&;UAz$D1XEXer(2!k{OBLfpN(6w;D#=y$N%+3Omv=m@q zWM*byW#?vLhNxgd??}^^bx34 z1ldT4*&tCtB#WS;w-|VUhBFB=3o_U<#Q3rKb1j&@WL1%4cScW|jm7FKqJ5LRs$AN1 zK8mh-()&((q5aI&)usDvw;4tB^)Mcdd1b5CG+ip>{oEO6)34s${d47}OY8Hccfa-2 z3Xr~;8NR*u?l+iFsPL9$k7h4!oz5hgo2s}ru!-^cvXAq9I?nE!d$N$xd9_l*ztdiO z_vF3b>Km(-f9_h(JKNaX{~5YG_$opc*o%9HTxpbgFLI>hL;Q+*JCgbp++J1c7Iv}50|~UzV0T|s=e|%mz0#< zJsmwg$ws8BDSXyL%h@HaCu<&RK42G>YmA%4y!5)M&yE=Kc+4>Cr8J9@+Cg4b-jOBpos1dtd7}=jy%Rk}odzE^NDTpp=6_$lvT$YJ_6j?84&$4O4eH zMzxD3Bm^Y#X$LIYF5BRoAt7mLQuk*4$1j_NSKL`@w4lyw$(5wPr!r)__Jm01-r2gd zO!=V4inqI`ZPz|%<{lBHHl)gL;>hKto}9nd}7 za_UaznI<8lsfPs%9rtUSa$V}x3&NJ^V(VlH9ni@5; zTee;7vCg(zckXRfRS>Oyoy)RV{9Ulao6tu~zMwlk++4$R$DtyvYfDtmq%7~3xvC?) za@plW+eLx#^jFH=LCde@l$%ec?k+14F8^!&J{@l~wflZK#kdP_*dHxbzvogWQ+c?> zY|`R)qGuOvdHU+hgf&y`o^AToE_~OqXLa(%+>2@Y3LaVqjx$))e7~&AJ8|14)jjW1 zv;42Wjy9Rc6~AG}6cyeEZU>nI^^vQ5*`}`4a|tT9n6Tyd@qga~OjZUhMRI%EC$n3j zoLlmPBE#GdhDg2?);lm??yIb7w7%h!zLGD0?Lw>hqKy|X{qdUHsr~edYgbjZJ-sf8 zOfqnkWb50z_|pTsm13UO3Ts~_)^#q}yx-_gw@q8J!rH414D6DU_uWjKllfl#F1jK) zarKIp^<{tF%l#8g%Dd%o@=6g)sYan<^7|-JyJy=EzuN2+ytwHc*S^XHVUL6!XE_}U zTHq)fdiL?6*i}DV%BHS(dzKSOPcD_-p^m|{`Wt_-5T-PHv$8;xv_k-dR$-2 z`gQM1{iA%|eabuh7l_Wi`f{;d^uD^e^56e6l(OFTkhAJN&^J|%Wlil10q<#NG+x&^ z9TJ$hXI|aHkSLqW+qrwXue3~AT9?de|M~LWKUcC28a?E*`2Tp%L<|0V!C2y#aN literal 0 HcmV?d00001 diff --git a/images/courses/business_service.jpg b/images/courses/business_service.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d31d35bf39775661ae7ca03ea13f1cfb83d780d1 GIT binary patch literal 1477 zcmb7=c{mh!7{`AzgAnGBaWe*sX5Eb#Sv#{!jN6RKeN|#zkA}!qrzJaH$$PXG-^h$F<%;wW(`Nwf^cKpCT;fFWt?sTtUsIXc*w zS(9l#u>myq2oJJ#(DmSmt4WzznNs!PkML?FFa`S2ZFQs4BmPEBuf(&?Dn?80JFo39xuB7@m`YQ_ zA87bB&peklA6L~l(HFHxOU9v;3&ghM_SFZil3cC^DUx0-4#C?xuXrXB`wdT=Pt;D; zXk5gPVC)|S! zp}VuWz}4{YViUB8e*ioCg~N$)O$dTv@*2TO>5PW}F|pEL$+= zq>_kdv)Q^i>RK6lCTk(}x#d(zvp9v_M`!ve^)%d)e!$^_<23w=nx55&7k3skx~}hG z>5J?oYK2@yK`G4BX=A#-0PP%L8`S5N{i3FF*g{FHpY9~&R5^XJXK2XcJObK@h8azkn?qdf7t7E#|rziMCYVY zdy@C2TMoabQBEIf;5t;v&=FLzl3zhPBCq@hDiyO?Te(1*!f*Vz3i9wX+=2_Ea#_=*FIrVNPfh2kiLNYunsRz znEz*&hD)xp7Ryg*SG&fXf{))ec)xW@<44WP2zm3ZyS0xzJ`fi!_;<~)S}sRAR*Z5K zD-rV+iF55o`=Tn=gy+-;qjm<~E~xb0=&3vfPm|Do!SR&Qo)~MOkA^lVc>FoRUSPa`got|UK6-J+qB#(q!nE}sWeDAKlL`m-(Pixb<+Vei@lrfoi%XN zMVMMhXIwCh>D-R`SfQNVI}lx3R1-?ge^0-`j1t{mrZs+N{JA(xQzzL+*2%kOh#}{5 z2X)JyV5y-dyFqdtPV=|Ng^i&MRcc_u9X8YO29~a(Bw73cq5_OMWb*mJ5Z2 ph7P@9t3B(?)EfvgK)eYcod$Us4BN}GEfO*&b#-;sVpq8D{slQIg|+|y literal 0 HcmV?d00001 diff --git a/images/courses/caduceus.jpg b/images/courses/caduceus.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5f545746641c242e5685e4b69f8f0c155cf4b956 GIT binary patch literal 1505 zcmb73GnkM9b-~< z@$N&+TEWWJi^^8^gK>ZVM91lBfc+I*k95F%nW?Lvq6*;n)1eH$`p#4@_u1qq=mtGLl_6;{| zR;^&c5=~4NmIX}QB?{)5I9eIjwl;8*2On3k;g38x(%A4(|K)^9M;!)3Q^a+IM9i&N z9V+N5ZOVWoL#!ZE4^;v?svrGQjA8heN>f}W#L62$`&z-v}M_eF=U1W3*c9TVf}^f&$%5pi+xb6ytWGU~q9 ziwE1igUL750lc0ropEm`qEVyJ0kB=xZ^>#>5s=5a`rr!OGxfnk6-kv>43BhkBgsO3 z#=P1Pi?Q=yn>hraFWy)=XI`PWX=RFb(cK?C@GQT-nlSp!l`5(lAvOIm^B24^#j2Cq zZT?m;2Qe)`Mz={to?q;Bw8wI2p{9ME?Rf58`Ah?ihL7ll);QT>XZMDw+5H?I2j?4F zUT`7iWrJ6mug=`zimUiH3gQvbRQa&61G0{T!>#9oUo_0FUR^-)cD66;A{Oavz z8v5**%_*0kil*vk>*_S!(rNdU+aIv?+;E0fJky2DggXX3o3a4`*pfbFM6g3RW`3n8 zY&v-EO6$y{A2Ba14F<=5m0#o5x++EiE?WU9^WuIl5ww)%(cyPpdH0+ z=!^5U)a-VB<{2%uUfHbRP|#sYQVeC(xr&3*r|k72di0sLIh>#Y4$ecCl#A@W6r+2e z89KMZ3h1gTcVhw5^qMazgo_zNg|5+yj7atMFy&HalH!TA*KbFI z@G07SrjcAyi019G(l{!E-~QEm#MS8JBRWqjvcsP3Cp#<81s%#&tw*ib8pWbzL9dl& zx@h@Ly)!BG6)C4I8Rwk3by5p|!CPjDTve^n#*miG;5CFz;ljj-xHK@Tfk{mwy%rXC zPskZ<=zQY?1yfaL8ogr-6MtCs(chs9Gqdho9-aPJW1SqP!97NQ;=3+5MRhR4I_wpB Ui_e%o3@>pe@dVB!wvA-$9|lXO1ONa4 literal 0 HcmV?d00001 diff --git a/images/courses/car_wireframe.jpg b/images/courses/car_wireframe.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c92954f8564004e8459283c251ece325d6132275 GIT binary patch literal 1235 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<Uuc19LfW+o1xtdRf% zBQrBIBO5aZGen+|iJ66gRgg_c(NWkiFtKnVyNHsJQ&Qu?jR!9(2R#Jp5kWQ!q6s7_ zh-3p)^cDjTGthQHWR-ODx>vX2O7WGo-^C;S>WT^uoUQgvduLe1pQ4E@?Pa8FYMk`^*I|H~bD- zyzMtPcjl2+Rp%YeaZ{OpLXR+lCvCuEg4-fp!s&rVR>8Pm{$iO(s z-|MVXoyyt5ZkIGc<;|(j56Lq;SdlQn*1|-psy$Bk>N?q82lXkd5+zT5aQNr{^l{8; zy|p`U3Ex=r=eEk!oJ#jP&y$Mr_p-Zgd)_c>+rhGo|C#JvtUDJuywQG&d z*u!?Ld;VsR#k>Cud2gy7?K0Os6Q*-X)mU@7h=3E947-*M|CO#4qB|PcFL54YJZblJ z-7l%c)~O633!5G7c4oc!R-J5iJJETa$qlFZ?;UKtYW#}Nm#+<*ZasODs_ygHpgR%P z(-u#vULECnVCBa1Dm=f#_HNUeefE(}#GAEGHIp?R&PzVI5L;#LXP%s<{UiL$sjg6E zEsJwO4#%fGvV9dFao};^tFuh2=9cQ*{CYYl)y-#y{)V|{t>>L}=kjDf@K{A|=IW>& zRnz_4t{6PMTM`^KRF?%q??N_wck#yfSZ}aTI!YH)$yfNM)lg2CU>8nfV%vR5qeV7s*)UYCLSz2S}=uQsH*Py(s-$4 zuiIbt$Q{!v-?za!l1o15{0E!WWoJ{H6SprCVRMp}S?NAi=f{e#rWY0jO#z|HJ8mtE zoxyV^!GiHr2&dPCnxKUz?50ZX%Sg{R43)VTy5TXON7wCj%Vo`bmn}0{(2{w-?ZO*# ji|?#4OFe7nOqeufDa!=6!k(`mSMJqxSPDXp|8D{SVAu8F literal 0 HcmV?d00001 diff --git a/images/courses/cell-anatomy.jpg b/images/courses/cell-anatomy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e009909ea6b217402b9eb3d2dd911ef87e697595 GIT binary patch literal 1667 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 literal 0 HcmV?d00001 diff --git a/images/courses/feather-pen.jpg b/images/courses/feather-pen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b333a6d6a5530f5604bd0729507209de1914c47c GIT binary patch literal 1470 zcmb7Bc{tR082`?Uk(Y@{ciNDpgbU|vvULQ?wubVvV^>f+?e*pzhoR}q+$v^0c;jE@{Krjv*y`u_%} z1)z9%%9sZP$E1?+>nh%y)jJN_H!N?sS7UhvcFpfORDPbG#rhUX3fv*UKfPi zj`D$Sw*jH+ck$lrV4CDH4*iEi(XTS=&PpMKdSA!pncWPHXQ9{P?m2a0JuTk#n9tA3Vp1Sq&|uXdU3vQK*+GN zLXp|XY^;`Pp!bF(Tj=46gp!#KG&Qe69U6NwB(p4OydYPdvb0SiS&%jqt%#-5hX*yL zdYD~&uYFRlzRWTFRPZL7*CTMfcf-^rneroyfMQ_hYf%M8@w75q>qyWdF8Pox9G)vM zG&$K>4dPn*pPZ6!i=8`pdC05BlqaE6R-HO|9xF9TmY(R>d$Qsb)m-^yZROUc;h53J zq-w=S0p$M3?!f8%KFKtV#4L=y48T z6h?7b3=)@mz($(|cq^h_^O(mVx9+#BblD7MMjSSikJrTs*zrPS&D_3rs`Ob(*QiED zB#%=na0?8wWeaN+tjb-jb(A&S$wTFa2<~ds-zM_WlFsU^r8`_|eT-}@S89+^&F}Bs z)Q2sZdnw^l`0pfm5Vb~Azd%W>P%GYb^v8E54!IOFS+44=& z1Qn;UqC>f!18uuCqx}zG#!n5tjSp4ef8JWluWDo3)M~6@GGP9+8l85u2Hbh6_p*9 zXO0Hc4TDVwUx*G4>`@E2bk3RsYB+#pgK;|N)@Fh*-%nkg+(hgwIJa_lNw_^yVFz_$ d7ydMr8>h1ZmVB=?JGbY(H_1y?w#nm+{{upyjJ*H= literal 0 HcmV?d00001 diff --git a/images/courses/fire_helmut.jpg b/images/courses/fire_helmut.jpg new file mode 100644 index 0000000000000000000000000000000000000000..131e2e69a7e1bd6ea9c3dfb5b4c0afa9c77bffbc GIT binary patch literal 1274 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 S$%f(2=dX)ii(jq(e-i*DJ1%$t literal 0 HcmV?d00001 diff --git a/images/courses/helix.jpg b/images/courses/helix.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e5d661a80e0adc990e765d738eab6e538bb2e9e6 GIT binary patch literal 1297 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<EUauy>_ACF0R~29 zW+oO^CRSF60!Aig76w*9Aw@P}!$2j+#KJ}qCoyH?i3>%Il9D$9wMYPc4)Qz_U}j`u z0m{e-GB7g&5t1Au6VN0>X2<`x7=Q#HOZt(^7vwQ%=^*X!Nw-(_@N)%}sS_RzN{hnC!4VsKM2&uqz(<{p{yRsY0i zrAE$n?o$26nR({xiT8edDqCzN^EMasJex9kiG@*}wshVe+ssELGpz#UDy|h?Ncs9> z-LvTF%k?(So9G>*>d?C}wrl2%Kvxk)$7R=E=c)&Hd$E_w1TDOFN7Cui!aFh-B=anL zHs|*1ZfVtQFK}ve6W#Wo;rROygM>~8#T9>^IWpcjZBf|sbIH7wYma`Pv|8rK{$E)c zOCIlh`RCC_vCh@g^P>x8dG9ep2) zJ9}g9_em7$PDx(p`KtJ%*A?|mInpNFMO%+0s8$?Xw8;Nm)}h(g`1s0}Yw3HlvM-es znR#o2T;;J<`uA>iYo65+VKosknj2Rs8ZLJ1(vqTx=nKyp=LN}W^7Xw4cYYgn&a8Vz z;j@LDK{t!Nn)W@Jw`lXkC5mk`uWaW@eX3cL7oomDZtClwYn#vKYs~(u6tc(VkF#NW zkmD0?kBIXNKd%s|C@U_%-M70dSaFg}a7RXB)t`@Huco|~*|EJu$))t)l=(j&$!gxN zNi{VqzBT8mhUym0>ZvThzOg)hv{}l$WG;7Ggs|PZly()nASS;Pp0XXXby9 z+*m9sWOR5Udk4Gp%e~%?&slceyniMnbNfrr7jG50U)UbCFcV>X-up5uJ>&E3{i3nH zM-Q@oTkM{hx|Mb7nkOB8!w>Jw zzM1;UNlm(a-=itTyy=f^!a;2|kwp&%+4N94P=l9uTM$yxgCbc<*PL@6J%+Jia zl56q9@>GfQil?hTB|T_oa`^4nS-6Ynym-ff|1&?z(PWf$FouW~;E@_FVwZu~I zgm)2 zEqrzhd9goleCV~>Kch=2`gXp9qxDO{)^ Q8y0nGFm-7xu>XG(0N|VmO8@`> literal 0 HcmV?d00001 diff --git a/images/courses/helmet.jpg b/images/courses/helmet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..054241fbe95479a8373b2d81b8b1628f6c1ebead GIT binary patch literal 1722 zcmb7^X*|@68pr?t`Olb{%wWW6Fk>v^TIyKFoMYyq$-ZTq(~z|o5oH-gGR|payT>WA zD^e2%SC*kD%Msa9cFC5=u_Z*wB{j#nFV5$4U*7NY^7lNy-|y8U8WYU`Qnr>hmH-HX z!0G(~M3aCSfD#i!iXl))Bod8A9l*$7G2-GFIq8FvGDLZjB2k_|P$FxqE2$h&B@i?W zG>_=&QK?jtI^CE?G1AtjQvNal(P%VA9D~PV@f2l(GUfk9^aQ}607t+P0#yJQ4uo)^ zs2xxM02uUF+rL4G!5}yi1olO`6aWJu7#u=~LFoT3fG`Mv;}FWy2lY(ERGfV{v1vl2 z429V`Xd2F!Rn<51O>dk1uzxBC0{?5f9|H#g#J;4217Q2?%YSErFdU?;XCi%2hT<%m z05A}^-w?t948XEs_m4Ix2Y6%<4(%RE>XoW|NI9yqp8 zY2BWrc4;*^-g&1z#>j2S_`$1L5%74@^WS$*HA!y#qce&zRPe@S$0d4hu;#@pHFw9@ zABg7V$1Q4zAD-%@&aZBC{P6TM+gBN8b+fcfg4uRY4(?r67jjFt&fN$#K z%ge(h=2=X1e~_GH{9f|Q593~+%RY904t8Sog&Ev8*v%5;^4HW@>Jbjbzr%74$z41b zteifW4I~dH1xn0p(cc@U29~T#Mp>JLWC)+BsyRAuRGQuU%3o@kKx^|6Bj%(|8A>XT zP8w|tNt=5u@p-^Fe{TfZOiPVY$-}=Q4@Hs^+o%A%wZh8bS}B<@u%aUq=*nd07smMI zgYG&_j)HL?^ZaFpR@JG6n|C7vT81+DfMO=@{_jha$1bGox*++!vIxITkBI~&Kg*IM zm);N!xupsB4_^-a7LccWxe&v+%WsUPikI3D*D^Gw80aoxLU^+?JUX-Z1n&J#8ovh{W*@w6%hs*KZ4aIVb6Hw=vI?gm=COFpuWz z?K|}!kHrm)7FWj(UQE}^gW9IQ&%K7qmB!B(n%(|6jP#yK` zME{S2_yF4S(9KWfiMbd2dGApo|Ayj!AW>284=^k_TmL8AaV$igDYhBJAqRMY505%Aijtf9tA zA|(l<-$Tn?eM%uJbwQJy+`^mUB%y>V;p2hr_)x3 z_*|~B201OKq^eJNTsvfKqVKgF*H8qky)0InwxxQYVLks4x0+;-eoa9$9=ZnmoL5_Py!5o)OeWbVhJ88$;n};?kdmt zrRY)a{J<$Hm+f6RhguU@B~KrFws-tOnwd_-bMDe3wc6)wf2~GR7NCE*8q4inuCCm+ o*}T*b9rHKj_(e|QtFp$39sUbx&F&eT;5Vb0J-+EDu879}1RNFOTmS$7 literal 0 HcmV?d00001 diff --git a/images/courses/light-bulb.jpg b/images/courses/light-bulb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..34424b3e5843b48f8be65486fcaf3a0a5e8cb000 GIT binary patch literal 1556 zcmb8qdpOg390&04Z=227rVU*fo3%>j)*9g`p<%)rbtsfMZez^ci^rKd6*6l&Zk=4} zP_C6yQLD(MT#hV~BDZpRh#n^;MLRmrdCp(wpY#6w`F@_)^ZqFM6(eAcvjfWkKoA6+ zRu3q?0b4Se6&@*wiU|xL2So|O$S`J~S|nW^Eh3Rj zHQqv|7;YpI^>=JFG~LEvFi2Z0cUsV`jkh!C-w_CnMq^Yl>#^ANbUmUT{eMT%0PxCy z4cKsq0$_Luj)xS@Knno)>c0^9H!wIt2||L^ZpRt`gWxb2QdtQB|GS4^aDc!Q^tRb5 zsdKm~QVq&Rcj3hvIhAf_--y)okB-S2{fP|Qw*Z@GwY$Jq9kC#ojVmrr%ohdW<3SbaCk*MVCFRh{7 zuD7(*#*Gsnn0=%^&2Wh`oX2@LNrLip7k)d6+m%rEws4F+Bpl4+EG|8#A>;Yy9LPG^ z=LLBU(+}}W`C^T(_$UVpbMNV}G$yPAsdIK0?w|?mOeztP*NzL)OB`L(U;27!oUKEQyEWjZ=R-vmUR~ak~?SXHc&-7ql|votpD$lM1?ro+$oQ^)2y0B)iv%_t(Rk(xq`?Xd%3}EHG()(r4kVw@nMiLF1!_3@G zMk$ac+G_s?3tjDSdm+h_e+iMWBIBf<31rbtBG~G0Jo>9W%6qsg@%QHk@=MOLFn;Yj zOA6)JqaWv@Vv_3a-C78QFH z9wb=2FrHK~7|0d-?RCPH{bU(Lq$UrG#wWerCDTZ02OK${zdpLW=Tz)8O6S$Ci7`h> zU+xK)lNnJZC(=#1BVpR97hFqEr_uPI=dX&qL`YWu0pBfz$4S#NIH}s8H`##n$)r!F zJrJ2sGcKz7^M(Sj+k`yE+81FFs#Xu8;xWQP?Iav?9W#d5*I9FTxO9q+5Ffh%S)!;l z)B+jJ{dwNg<_D9Y*^=yr%DcA0L`up@7qU)@ WgH5_P<)WKOr}ganXBO&;mwy3HNrGko literal 0 HcmV?d00001 diff --git a/images/courses/map.jpg b/images/courses/map.jpg new file mode 100644 index 0000000000000000000000000000000000000000..45761df768f69a2ae31424bbe46cfdb16476f183 GIT binary patch literal 1064 zcmex=E}IM4pj}ftf{+RY=iLSjjOkQN*ZFdE!E2r^by3fy%{!o<=o^fsu(BC?P2b zmO-|JQIJV7@c%6a9%e?MTbKnI>>09J)HxY)Ca|C3I}~=}{PE+i=^F&@?yPzjQfJ+< z|8}O`;_2Hz>jkdl6f<*5xwP&2zOc6&SL!shwB1>yxiRQ=sASu5bI0E&8(DH>4=8dt zoOm=pzIwy)yCq6*wwra=-)#FCDWvA6GakJ&-@I-vT$9@*>+-36ecdepgRk>orHeO}k%Oea6b)A$%>>$5F+*OpHy z%Gk8<>cz`dw+xH}1&qa1Hmb}IwvxP1k{0;$%IB4rj`S^=xv*U7&!e_mpCY<`P1WBw z`~CWiD|7C2pI?_-dOi2(I)Qznop~qq9*VHuP_E1UUD9)TzSm=hQz5-gCTZdq{_+JE zUy|#N(Ad!XiTUIEy}mEy+|Jp~a(F)FrN_I{Tb?czWyRqSr>*U+i&xIy{%e|2oay!C zTe?doIDYy1qt`<6>$8yPIBCs`VMaF_1G}TMjprMxOuD?vtK^xK?N*7+6>+9p<4Zk% zEfF%gYcBT6Id^5$_cAW?&*plIgZbMdCpPB3y?Eko)z>S_eu%AOO8p*Y+rHp?Z^RZo zGozs8iEP{7E1o^3+j8~s&q#l(6>t2$uk5_E@WB^7Q%7d8k0Mc=Z~xi%-^)1D6MoXt zY`4-*X;bE{7v8S#oAggC;?$b?ysuMVoVwTMrQ;+pLGxqPboq{msktF9x9+dpd2pkz zg!55`&GYIHe3@EY$mTA$Hs`k_v;5pD-<7LtVa{*;y{q^iF3oY* z_^R;T*uN=d>yD!lpGA|n>#9x`Rpi-jiCP(?y+1Cu^v;(#c1w=>l-_VCyZ0t-?F`*R b0yE{Jwv>mbvQB<$x2UQ`K>&v8|K9`vE3V66 literal 0 HcmV?d00001 diff --git a/images/courses/microchip.jpg b/images/courses/microchip.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3833b8b9d070882970b86d9bafdba9ea1d24d15e GIT binary patch literal 1611 zcmb7=eLT|%9LImZZH+FesnzHt+mh;ZF_w8IX5+>YE}N%n?2vgnbQj&$LRhm+jJ&QU zqli|sj;St{%R_EX(_$eHNvq>(bR4~Ix9K%oG;001yHqyha?U_XFbZ-z8mheB>ZT9~6g#kksFwr#_ZaQNM> z;ephHVS%9`F-e7)F?3dZNNBb=hsEWKszkK(dRblBwZgL^;fE%mxrGJB3gc&E<0m{6 zdPw-c(RTm{V;~p^hJt7Sf&ifiP~Qny000DPIO0E`s~|8q@IiB24?sXD6okWIaOgh; zAOs4)5Ub3r?KhykLk^{HWD9EG7zZC*=*jB?6UdB<1{ccklMiqC2!jpwVHVCutvH&{MmM8(u>WOKrJ^*u~l+TOml=SvHXdeENFj*pt^o%;A2M#V-9r)sK zlwI-Hk`UJsbi>!Huiwv3Y9OyM#yjAML9Nuqd6|V=XYHy zoA;b7a3a)G&SkZ90}mGGq$~AKpSSZ>(-$0K`;;W*-O&&&J3LiIN-L6AZD#RJgKwKD zHF=?X)Vddxo5hu7?VwT3l?uVIX2eNOLg;}?cK@ZSn(~05*dA1|8Y?UOrGQ$b2h5XA zx>Icu>&h1XsPatWHC)-UmgorfrkKZYww{o;yoEO)r-*iSd1A}^D5KsI0{%xoxq7P^ zKvre2RcI;YGL_o5t%AZMrgNvpWcIQBSy_-C`%)${n)u1G>fxD9Nc4Wk{vAZ@jAzo; z%;g5V>5B@hs;q4<-8>#2-aeT6)f$<`ew>i{E@rGzo4q3D*%v(6B7WW9Y-$;Vf0U4L ztgmuc`e=VImvkpZqPaX9yKgO5ls&p|F|l=EbhFn7@;-K|-TNs05%PgH$%yi#Dzex93CZUm9Nt9@_h{1m%qs0+=_!GlY@S7ARi z2S^z#?|F$|X|FuCXv%E=+6P|gqnOTOWyrCDF@~9MFF}-ArEEL#idU`l)Y;BF9=g;(YhwS( zeU98ZBUnTVF-Gtk>iOH##Vy}&%vn8;;U~5ITGO`9HhXsa7XGoFZUfGWDZ)mxJ`xXq z^jnk1vpWJe&rMAKc8@#5%%nz}-lN%0lU+7UdiPw}nY`+j6q{Udh|6Hl`Uei+%j$X< zjrrwBkr7M^`*vla{=o2kOI@_->rD?q9wlf={-s1bUYpk*R!ds!(slZB_nfm?7O1OJ)1b!c)6G6MF=+P}S%93$G9lon>QC7M~ zo&_W_p0qB@vzDee7?VUE?tn*ZOBbOA^izyt7rfb;+`5(Ghl zB)B}P-YZm8e7Fa zC@N1?kAA&gP5*4l?d}na=$LZ;{;Jyk3;yj203a~%Patxih5(R%X&^9?g2K}DBvSxl zp9do$NZ=^&SxSq-)2v#JZ6Y0UK??*o1UOZH)4(R@Jd69=V74aK&tyzMii3 zVoKibOPC!WEuR18=c{qMpM6rs`*_QhxAW&5?s$>Yyi$`=`G0?WW^(-Nm5eyi?0~=! zpHrYHAKZUzDXEj_jU@+e%}vc1?v(0pax_Y`#3c5^`(&TQ(RstQp_8Yq4ytmeO2XZL z&;JSx&L2pR;E;TWY-@f)t}Y!h9L%H9d!V@>6xZm4sJ|=^z13nR;TmD8Ap^YhwB<=?uBQ21?~B5nJ`q08X`clHS(& zo^2UhGJn3=MBNo(i+#Pm<_zn(cm{!33hT9Js?bg|!wQ?6J14mOmNB&Vs@_nD=Zez0 z#~(n;8?|_UpLwa|E87hB((^i`-5Fi(=K@FQ}M8mZXqFgSW1sWJIjX&j{H^%<@c6xhyagi5Sd=CaJnlH)$lY zr(veLX{(g@9MKif>u4X~FH5+@owXH4u5^=TtJ`wZtxKCl%5)rd_@-O7N+rNZDXi&C z;X7-EDDRz&nM9a!j#@AF>pw3PXSX#x;2sV1 zE4umYfHC$|RE)j^80gz`t$3xtyL!)_BiGsW#3=+$Exx4E!d$dLXO+#^i8yjMwCv&3 z{7|5{Y=Ox3A8C%Jzu8+3~ zH+M2TBdnvR-wwAw&a^w>dlF8tfd%Itidm`L=+6^=6YW(Ax!7V}Q8q?zT?J2m7`E&| zvE*DmGUn!NKQ8`E<#fpX+7WY;ebC-BaD3pD^C5ykkhAWc@RDm6+(lV!Yf1^R0Qc_l z37Y9&CWF_g{)UaEKd^isHhpYn7ENXL`@J}vu2hIh_=FaRAMX|g)?cD-@IFu@R^hzc zry0U>W)aMs((Q4)al882qU^>X{*ijzSd#X{oPo=#|2Z;vpl^m|*g-BUEEl8c(~PsK z4PS(fZC?C>mWHh0BK+`Q3I&f_S0sQpY0anRS|scGaE82aca2gv#Ay3mUsRI#pzV2j zRC9z16Rh~gc*cMi95y-?{Wd?gKww?Y{2qPc9Fzsrf}RV8S+yO5H+r(jX(tmGjF2M7 z8$JU@vp`MwqfR>v)&{vm zVO{nAmiPT`Zm%rn>gv5zE-A8(Eym3A?_MTOhU#@~_vd026`!s=?X3DSidJF4pc3@l zY11iQKds{h@9=lFt{0EH%Y8}fSJtoS-t@p%iL|bH_P7$5vSF~_be#2a*J^|J#$t|g zc-14ZZrRDWYFp7RC5RvabP8>hPx?J1K01KEQdE3DKEbU1{i65elHhK!GWo@v%dRn_ z8I=@D(`m-_-d>Jjm`+{9hq5~@axG_*D7eof7wZfH^j%v>S$B=;$s6^Fu(mNj*Cxf-`U_yOwlEP4aH(_I78K+{F0Cu?h(bp nA+n#0n2+#xnNT(XhRMntinm7zLhVtg)GPzc8Bk!gmt^ukO7aAr literal 0 HcmV?d00001 diff --git a/images/courses/molecule.jpg b/images/courses/molecule.jpg new file mode 100644 index 0000000000000000000000000000000000000000..85db73d5eed3f2da5a40bed0e40e953f31c17214 GIT binary patch literal 1708 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 EFDR`(C;$Ke literal 0 HcmV?d00001 diff --git a/images/courses/normal_surfaces.jpg b/images/courses/normal_surfaces.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e0137bf70d0c5fe015459704740d6606c268921b GIT binary patch literal 1871 zcmb7^XHb)g7KXnRl0c9UN+2Ks5|9!u5^698Lb)Npu5>BVOlVRC5mrPNK{O(vErPI= zm5#C^B5f6srCw>mVyHm`alsI!C?cZS+?~1m|DN;b%sg|Rd1ubd5zYvg0BHxhJskjn zKmbG3fN&nL0VLpXaX3su93hTGN=Txx(kLk@6b_>xgVj(wqDfFA;I;o{ZlJAeO2iWk zZ4FH+R2q$T#DMPjZwm)=OB(gR5)cxJL>)q@NK30wbqG4t|GR`w0W<<|0-PWqJOD<6 zAZU=V4^RdGh$yTm?ms04gF@gSun3w<17HvY41qzx2>9O|1cm@mw2Zo#ED46UaycC< zcRBAi!BF1F*qWKv+~XRT-}3v!mS`0V`rG@DR5XZ8P%#n0qXCfx3J75a6oHaiY>zHWBrf%$dt4U3Upmdcv|y zx;T&g{%BOvqoD1>U*27N-K#q4YP8^6vn`h6(RfFy2CMtk3>Q!|59?gGA45+HP$4Gu z;1enyK2C~Qflg1bv7oGKy3Ak|_-Q_Zyx#V4k-Kbr$@T1BiQf+X1a^$-3*mD+e1VA_ zS+Rn1@+UseOLmh2pmaqpqdK&m(m+}Jv_+T}0^zNXZock5Lpf6ua6@u2>q1O|e&P2n z!?OJxM>QOQ0F{xN^6e4hrt1-P8=wj*sIsH4WaPW^wUEW`$ISeE=!pHm0}eS--v-C` z^otp_D!kKL;U+R-KOper~Ywn%@6G3HF3(DJg@RA>w!PEx;9E^ z0QRKA1HQ#5_D}?qTh&F1`q3GQ7`19J2_f`-5E}k6!KW{F1$_8A>(3o?A6&ubfI< zpIjtpZ67rY$@-6(W;45b_n8;{)d+!!5CZVqYj*b*-FIh~erxwuJG*5td-;p1%c`%& zQrmUI18U!dv=OgEYkmiuG)b8kCu?pLG{WcVE<&OPb6)0oK$DNn%C{yb?{o(ymL zxPFQpPgkDBoRO~%hB6eG*O+%JfV1I|g0SZX2+!&zBh7K~C!OglWDb95f@sjPn(dRf zxF&^U)t*bAyeFv2B!*SB1J?Cm&>tsE8+KIV`T6wu*t=Nbblt7mQka_-B119lc?jm4 zO||MR;9$)@LV?=*P)6|!wGW$gKuJx0Kq|>uN;t4@IvK5ic{os%?^cU(P6~?dV=;{~ zKY$@%1XP7>EP5G|pyv|7@f(=m?|4$!Czr_!{pPQ~<1ssC%sx9=Q(Lwhr+3{J=j78) z-SE_MH4b%#z;PC#0fm^mg)A4hdJ6ShxD%VES@Ds*PUzcI%yWtVPjX*sLm?AcDe{Y zLRwHQ)9>6!olKG1n%)ah8k`Eu=`#M+j9;ua#HTG_wn>qroHVTA`TDN|7UejbI)}=O z9nn#R+oT%7SWwotV7C@j?aJJPSlsxQdh~>^PqF)l{007GvedD!4VSWZ#7*i~{cYnJ zjn$AftC#Smny4Wge$}yv0PXRiAA>!O)X3^Bi$hxk)<;E?m#N>$vi*JD!kxWoIM#0| zJ*ctVP5#V1j2A>M%;18?3gabY>b&oV8MpGWx#QE8)!=Qx=MZw7K@c>1_c%9?n_Js@O=}9d+eKtB1vOobRvy z>E<}6iYsSYniUGmQ!+fGco!z4mA;7{c)wCe8;*#sH7|1#DF1o0LLM5lFIW!W6CE;8 iD%De&!f1@bw_T1Ol*w=r0x7MF8H$D!)t@7Uvws0{;ULfe literal 0 HcmV?d00001 diff --git a/images/courses/pedal.jpg b/images/courses/pedal.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1baac078969a9bb6662386fa0a868d9b4531b433 GIT binary patch literal 1702 zcmb7=eLT~P9>;$_V>3?~E5l^A2{Vha4$g2CYaVLN%tJ*r4{NfSk#cpq6zV$BgD&N1 z7C98|$(TAM&*Sv4M4c9MJ=~&DE^@2Ij_yC_{(IkleP7?t=ll7*USG{i%>-Z^NDH6= z;08pH2mqROU>l8>7>79_;-#j>2{4>=jBjibCl0gkXi{oY%F!fYErvpJ){FwX09_~) z2G!Ap!C-K>uATwX&_G|`z}&=i6Vl4UdaIQM8jZn|>@Zj&4vn_|&VlIc;^yXNZRg?T zPNtGv-N@fUKsX$3pl^UOG(?eY&^F}%8%-yG&;=L(0|H_JEd&TbfSO*w5&$3o1O$OU z(bmy|Ktb4sNHqquKnSP}g~GL9|22SG5I`HDV`6IKg2ejnWFCRKCTCriY{ucqA)%b6 zXOt8_mgwxAuEmY(E#IE?XY9rV0$SfH5#RRz+X(^)8yBXpMDq$TfNYo$5Cq@@sMnB8 zwttFCf~T@gHvU7j+4JgFTQlzbl!x<-Pp$obV#9Y6?{t|4INriuD2mA>BrdC>_8c3U zGOk&fAM`A%s5;?}&Y*jT5=rc%b;GqXL}|m`xbXoj^ZCB0dti>@iLBJ4_`-?hi$GUI z#wYGfSO=|RFO#)xWOVO(WsFJ#l$vo{+%LaZ{93#cJ<2>A7)f!IQl&bV95|!xwF858 zRMAP{bhpGD(J{bQ67h^>=9{QT05^$V?(l1Ehk3;uRxq zZ|41XW;UYV_<{ZARrj8gV)9?El{_u~c{SUdxZauku(zCbZ21u%FMHY&09~2p<9@o^ zQ+U^hAh|g!dlf%Y|wKPWcR>(FzCJv#snu1FN@!&+s4Pd%TsSBNL~}2$|@>7-c*-gVr<46`6WtIO(Ol~n#7^b zq3=7*VPfvzF?JTgT9aoj!$P8vv2qMrNQF;cM4x<~7fAQ?Z&5WSUrA$R;V|mIecpGe zB7$ep*C^$6v|b_qmW<~S==)Y<>u?3Gp$=9%^y9L}d-UYCQ`JqEU#0!9bpp2j*ekf8 zZ|5`2Kfm}lB#4e*qZbLqJrOCu65fKMf64e_fDw`Q)=1LoHd!HxI1jrffj#JZsT*|*OdXN z0le`kp9zP$;P{n1cYH?nz1A!NTfeXQZ8O7I7r%DkFwn^@aE%CmT|QI1G*>^lV|B-( zi?-ZCf*asmeRll(M{Y;Nw8Ic7TG`aOuz3MBKfUCQ>>h2ikLxHqL)L|Vscca+Hywg{uSo0;AB<-VT0}j_M^w!pPnurq@LU2d4tv{znZe09r4-OzWUR=(Qp*S z_Ex0Rf`nxf8154`?TEn{oEtc^ThWv&=JGPchj?q#>aA7E@*qZQnAEWD?bjj)ync$& z_icN+{{67t7d5qCDn8U|x*yJ`xR(Wra=W>5W*d|4J;ke-YjHf-UgVhiOH*Qk*}VdT zWL3e+&x5{->q_)My>;ja=}U>Am@xCze^Ev2$gn;oCfa*ruMMZ(iCr3d>>70_7$}~)9BaFkn?Yl zuPfbO3GivdsYN$lOSYUp-8A#A|9i9c@yTU*zJ4~zGOmU@TS{FXvs*x_PF50fAb?Qo zeyww54>r%fCbmXa> z6&O9bTeSL+7=(of*yJXUB&qw%%OfAX6XdZf34gi$(=`pCa#Bluhle`H70Gt8kO#K? z2`^ffRx@U&3R6(72h;q@=*vFY=_OA5a;ruX%B$xB`o|ILoKx-U(|{mXmp{xwU7^fm TAJkKZWki0=v%gUqu2KF0xRldL literal 0 HcmV?d00001 diff --git a/images/courses/pharao.jpg b/images/courses/pharao.jpg new file mode 100644 index 0000000000000000000000000000000000000000..efd8c341949c3e43cc57917d5bd47981b3ce4f07 GIT binary patch literal 1953 zcmb7 z1((R(+)6F?(wZ`{GIz}tofexcwd7^qbKdzrzkAO8@;vvs_sWK4Q@}Qc2i*ezfj|IF zUVv;Aa03)!FgQ#>5e|nV5Q<7j4HcxaG7^JUSJTkd*2C#)W3l>1I|=#*W`5oJ+ z3riat8$ANWk!9X;n9{@Wu28YoCwuIgI@OzxgK=07D@lKtV3yQ2-bs-zjhZN1lm7;|-`9mg+pd zYy?2ctzZ-c1vmpNDu05Tjz6hlaj9}^cf{V^u{lf$yMQblHS}z;a`40k(B}mEXy1I+ zy}R}|X))tWsicnisy0%_I4?aocdWU5DiVI?@y2SQ@jAP!Hb&dg{7j4OFq?zf=R&we z;cskxHsACsnw>Fnx{F(h4TNnjbfiN}c*b(SEB(9N>^_wG*vybcT3 zx*&p%*$bI)-nst!AuOII=@`TDsaHV6ncmHw=(ibR;a{tQ8~$izM)qYV&vsESkf}6p z%p@p)0=`06Ud)a<6PYp7uPKnac(q)AB1x}YIdFMQ#B5o6qPs@tS~(6GLDj0S*bD{* zJ{m0lrXrKi>*@K32AX-nZ54`;wS~< z%-tUt3C$Yi74K4QRYPCt%*QjD?>$IkFcj;DBxVQf@4gx}ud?S1_x}Q&O+AOLee8X_ zIjvLRFL>iB8TgDl+So{c_es$3!(~FJXz9QX@+J1+#+pF-hmEvKZYwT|aDhp8=WW(? zu%N{%n<<0n#<6hnaPf-Qa~(uNRQZ+AaRQeJJcpJt1F0eKNM0l&W8{FmY7z#urQ72TN#x)os3eA&o}BuI`zSn8%Koj;Zx+v_kk;~--b5zhxUj&D}FQ2h#|zJ&-qEBi*j6t)r=+YOAa4f z3gDu;NIpXgp&$MObE-er^T>iLkyw?{ZZ7RKd;eix6jM$%sMF!{_0e#f0ZOR8Dw28=Lsuw1!5*bgGq- z=w|0aca;C6SI=pC?IWf`je}MTZJ!0YeDYq}`#e}PbzSXYJ-_>T;#9;k<5Y^ckN3#E zS@>N#a-v}?sUnpYqT-C3KFz?nT6n|`A^qGZ_w>Xa$tcaH5~=z^qx5U*aoM&BjAQy> zmtKktNWK;mw3AzGQ+=%tAUitwAqmELna!o~9O%n=VLV}SrlxR;)Sp1%`;L(p25;h? zR;sq~d}pqAI+%X0E;-)ZQ%5$>z}6hzt*VVq1vN27{GRC+DPK2n$Qq`C7*1u7FO|9H zyBlG!(W6;Tl2Y2O?+?vi>t*;= z)IP0;PMtW~x*7VA)-Wy_!%z-h*j~Kq6O|n4Uya%&xK1+2_1V|YxPqWd)xMTRp5C>m zJR?N;JWFDyQ$ZGrNmOhb(a+k`9=wiu1z1MA6d)d{af3=(UVOv&$YnQfRe-3BIWU2I zsB`%QXCBnCJ?}JqW{qO>>Y;mFaCgL|($3aPf5dDwj}U#X>`YT)wNykBG%qGLJA&D1 zBema_#Tw+j9iA6A3^(^rsNd-3Ya4i-rgU>C-y<{*xg0QY)^j3qq@K zoRIluG}D+C#=OH27K^Y$rn$Fg%hMJWbc~4+-<43zx`R@d2bH!ycfN#+l27k4wUY{&)#L!4r`Tqxvd>n>Buh{B F=D+lcOV|t%^?6QChUF*1D%e%A%MQ zqb*uT990%|Y@JS1QLmql$1b8V`QYIq@-086y-4Lsv0NMRn^pR+6Fo}%~M)xYPx3$ zr%oFh8yjorkj+hqW(G#a#6KmVq@<*@l(e#pj4~0gh9~~t5w`+p34jjJp`a!JL4!~< zDDD7`0{{d91%dww0tN@6NQonORvrMMP!NuQ!4S}YH$Vs!2Efq@2)v<-x1y$1P;8YJ z(K-W(Q5w{CW%1i)s279(w1U8q^ymlw4IBYLVITx~1PSP)l}BDE6#gF!LI5Zl23JtT z8)B@ywTMA38T__E@jF2J=rRZziUz&`);3&h?C~}jRj!jN|7Ew^`m)JtR_=ubCS9E= zsj;x)YB0bVDc}J#ege$rx0{g2nd?)7){g1dDrc`MTZQ;+@KVeG9TU@+jA^) zmUT+rm}5+G&M!S6XfN5;l+dK7e}tX5%U&r-U{LSHapk7c%fnv2qV41ayLQN2rk;yy zl%@7GqFfcyfmXGzZHL{HF6&PZTzcQsH)w-lx}&=M(>XN74O4UEn%fsr+|@w?y?0fz z5ToZ_Nj=$bE$4+*pKC|hnfZTX8gE^LvCcN^aUi8iyy74|6D+v5jEO&)bE-)M6U4{4 zg?o}$_;y*m0-;0lYpkYgEpLH8nVTK#px>X3*O?)hIwaQ@7g${8%eBaU3z;rdNf*Y3 zLvy}b2&FRCf30?Z+F5#Nv$1G`8hAT48Wny_a#g$2ewFy}nrDF-LGRA1)XNJrTJ!jv zE>@=F_FqTy`D0Fmmu0nw_98^&r4N!m|GMSARW9&IxxqY~dyBHAn=NeTrwQ)4$>F_FXNO>-?C%E2RtPp}{7d34Uz^b(Lw{aBUpazQhkR`&D0 zM^`aP^yb$3nJ^tg^4(!C``jU;3ezFvcbm6aXK#)d_H^7Nse9u~wjpLR?klR%`T~WE z(~p(RZD_SE`Y9=fGp6@7tvuTU71AyZEZFC%ZS9kf2OoK#&7dkStNt++$8LQW6 zek`1NH7YQkhr#S>1i9Z>8FH{=Zx#o$D+~QoWF18b`MMlCPTk1T3)85K-kb_r&0nxi zQCIIW9A_+MzZ^RD7jRup8jd`cFJ*-2#NB+N5H*gO)$*I|)$^Ui*IT}?m;WhfI5hmz zO7Y9Bw_8NvM8H+ki!sI{y1^POYpnX_=@~646ycvaKi_Q$h|t?j=wp>$If*2#nX7i$ zcmKZNc{;~qKEM4nm-O9Us&b;c&@m({(8{h0S(rw#ebvc5eT@Ho4?kHXv<{S3d^+4n zLIx>D%PTKmqwx?6^3HVUVDo+h2JY@&p-X~-kju0dvoYTtYn*Kvr4nVE}ijo?PT&I@ZS5ZZ`%eoNOw zQO_vhNo=xa`AQaB#HvD(Z{lK#(t)@6N}R%msS;A3-O`#GmiI_*>eWE+GQ}fNrg{x* zwns)hAhMTSqQU#CK2b%?+|Yr@o|WvH^8tfOs-qPgEvb-C$G@a*^qaNS8A+1q;q9Hi zC8S;s4ljTe2Lgc=f9Iz-#9i7k{NCkRWYU&`@#332_~P?B4{&@Yjn%XoYTc-(mVSmP z@}1TJrl;3_dtt|F$o^On8x)q2G#H~9VMl<;B|d_Rlq*n8)h*>8s>fu~s2UAn`(53? z>DP~OV)Ee`e0WjG`DprX37cX_u9Y7hb~S1+Nju!E@eRA?&t&Tbof1q@%DlFjJkfPO%{^Ch{k^eJJ|TT)&CvfT*t33+>JZgVkkE@@GtOat@;RgH)#6M` zy6$-8AMI}pr_K$$yESj&75nLe^bhgfo8E`rz(IhShou4;+5CsHI?Yb@x*{e{|A)oSc~{ G9{&T!bQ|UX literal 0 HcmV?d00001 diff --git a/images/courses/planet-earth.jpg b/images/courses/planet-earth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b06b188fb290943d7b2650b325f03a1731de0912 GIT binary patch literal 1501 zcmb7CX*`q(9Dd)KF$ZQc4C{DDnB0>xnb44|7_o9xB4p)gBg>3RvC%})ZLm4-f_i5O^_ZRef_uq>LAXrG_GqDBp6jol4Zt zk*8tD;O5orU{}3AEr3oPR>Qe69$4tgh2Jll-Ar&E`zXx#yu&&vdBRkcXvB~ z^yKW*#5nPx_18L06UEWu+FbVqy#j7QDkUxT%UVrlWvFh)Y8%mLc9h21$B4heXp?77f;_UKgu_7%(nK0x%6x&T}h1MEIM@kLb~Z>4#iSi z?l3O2*=a#jPZE&1NL_1<>urz^FHq%d47B=q{QDM&QbkKUyy@*~=x4EMK04jeo%2QO zcBEgg6D6utyCMN?f?l4tS5{(&!y(1Y^jm5O`T@%_eAdj=)y3|LJ?ivz--a{qJeb`f zNbMYR@pn?+fd zBtg>Ie5~mf_b4_sDk$#Agh_YUM^$qnV5deey*y}5tZWN0N#vwAbmjS#xIVF>u3Ogj zDSKvkagZm{vdp%4{?%hkO7au>FWw>rPI`9zn_<79ye_XAGTNiJG&PEafcPK1<mlJ$sBK|cA);MHGHEeKzD;zIq>5;Uwswrh44J5|IR1IXjEb?8#lD)m=KS}rji^O|N@BsmxC4rZ IB8Bh%1cEwzk^lez literal 0 HcmV?d00001 diff --git a/images/courses/police_hat.jpg b/images/courses/police_hat.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f61197af15568139ab3cdea72cb8c225ae4f4963 GIT binary patch literal 1337 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 literal 0 HcmV?d00001 diff --git a/images/courses/skull.jpg b/images/courses/skull.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9e50666d1b9fc82b0983affc91fce5207f694731 GIT binary patch literal 2043 zcmb7-c{tPy7sr3U8Ov;93<@*GRwIOlHcOUXVaS*nB1J>4gkAj zz!?B0BqS^(2on|-hQnb9l!OQhi9|_@V?-rzGI%+h3>GV|tU-`hR9C`cRZbjM*E*`B zqk|`$GB`=p*U;7>{v!e5a5xHyIv^r)fT(~~ApYOkZ3fUVfDDkKpdtW4gHSZM+X`R- z00M!6An@M^2@41Udn4E$)fEFkC=?125P(8O{=ET0plCoqP)J}BKo}4_l!(b<8{k4&vVHDuAaYmA!g@xiPJzu6~}e%$(Z-s z485{d#s(qrSQ}r@i!tLlNdc?}?bx9SN_o{m?GwyL7&!I7V{_b{ACzlcjj}P44OURk z_G$h>{mj^*ti<=jPjHrM%mA`KLPta4Hq*TIiv>tRy3XSL)p{0!vO4ukZ_Hq8Y$IJ; zI<+F6B+S-5lW=;zFp{y7K6Pys5GZa<1`nNcsrGR9UaX-5;v=?POha1DiA~uOa9R17 z=v)0sov^mU$akYftFp%XSfPb=Jbda?!sDQc`IqaKUIPcHQaJ^L-dH!sBkyY-UcBIZ zkTkkXx*Qt*TDNovFX|W4RiG{mkuH zDtne8eF*t0Ys=OU8M|X)z_DZ42?<<{KIF)LuwyF6w<~>wJiO|zX40W(HXV)gW){MH z;l)z5PHvdyC5b|6XmVNffF`H*x5}0!1=^gAxjdP&9pU<|v&BR9r}e5;%)gRa5>hHE zmc1e`I=-D{g!|Lfh~UEM7^W-Vykjf7D&EYf>fKHdzAay+J0wF0p_g1sk8jnjO&zs~ zi~uE%l^$O~-n>74kbK+-ktskfP`K0j`9Te$vdP5Fo&MdMdRxU$T(lyDhbeyL5HptK zQn&6cj!4kAPbBT!Q#O1imwZ_!K(5e17Dtb`)UF86+?-(t@?H#b+H|&t*qW_+H1u#T zA!R7hwYG%>Zq^#BhS+sGijiQ@__kTS#ZeBjtI{&uM>|*foVnXX35~kON-tU&Ak# zYbJj@kjHr4WOm<9T^|^a+X)Rx-dOK0>ytK6Vo2343L?|`JNO#lpaG1*K^wjZSP?(% zt7yeN^9$Ia@5i+ZDWqN6kUGjt3)ZFQfQT{S52&6k)6>c30dq(%&alRa)vbnoADLS9 zc<%vwImwPvSX!*cE78frH4e92?Z@P7$oOs5OlzaWi(@m=ol_2ePG?Gg0p@;w1=e|J zOMX&z%?Tg(g@HKbR^w)F;);{X-Wu3m2{2eSE241zb;4RsCep1iPs($6MkD1{$ zBN_B3+^=p*<)*=@P2RH~`wQ*vozZY%N3DERC8U1{vu4&}Obc2P5Tu4JN#WGsl*kHIqey=x%hcezb)+0uZUQlsV_wgu$$0J* zsD@qX?IkxD+xDM+KkYIj>(JNJZmwdbGhAN-z!@t!E@%kS!?HCnAbW$pqp z^RJx2ecgulc~z%Y&v`)As7fDrLW-<(f8HWWrrAc?Hr+@2LQ)6_(`ad34TuJ z7`>Sm$X4Ml8qaRL=+f$%pQ%Dk#Jk;kSlQ1B6Gm9~k?Uvqwy6ys4s&)3n%64)$!hZ3 z6St5mdiS}py&;HM?`f~LabpE98yls&ig%xU4=-}450pllIvx~4c4r!EFCU8Nw-3T2 z)?bYL7IvP#wq#>zN$;)X>8H25f3eRNLyd@?CHU+8krnQ6&*wHFI9NGwG3J!gzz1r; zj8t$5r|Ei-bNCS&+d( zDf?x6{^(~oQ07ImEvK>V_+q-nuNM`vl s8F8WCFc!4)7rM>`X47kOWNjYQyG_~_^=v0mX-m}7c*Q~Lr1tK}-x-LA>i_@% literal 0 HcmV?d00001 diff --git a/images/courses/skull_wireframe.jpg b/images/courses/skull_wireframe.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d6b5732a1e770bb8c0a829aa22ff17e44af58552 GIT binary patch literal 1751 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 G0029+x0h7_ literal 0 HcmV?d00001 diff --git a/images/courses/snail.jpg b/images/courses/snail.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7aad5a0ac01276c5e4e4b5175d94fa1e9ac043ec GIT binary patch literal 1187 zcmex=cRarq{j0R}-127d;BW=16jCP7AKLB{__7^E2(n3zGXMF3W27B(h!kff;q10ypN zGb<}QGb==%5vZC)kX1-g*w8UBkxi*^;zAK4W2cP=FNy^CAhRHYJ;S#YMGgk1r=N_>rh7cwy12Wy zX|bNQqoA&EuAE)O(ZA&?w;T+deBPA4OP(uh!7CH<`WM53zmB&1r1k%8uM4S8{uSPA z!r_s4{1x}^FAMsT-u_xW@$rhtzvk8b3CfF*k(0rr36$)#_-cZ)OTPDVF({`|VlP=32%nj)J7sUnp?jX4Uk$$GOlLkLy1r|XIZN)Eo;cq<_qsx?d>^LW&1MzZ96iT9GThS zcK>Ba=uA;KY`I7S` zb;OCCe5usK5+RIj`C5vUtb9Axg=)9%9`^^_`~BnV{eC`P$^qqT0PW%8?gD@y2)L;h zP!0o5fF=U50->(CLSu!NmL?K~L!-2{QET;A>EMislyzhxnPj@2K{Yj}Tad`Ktu*=; z7MsncP+hh;+j}q^*sSjesHLTaT8Sc{(FB$m*^KqSN7)2mGypEZg+LMj!+;P5R6YUl z004tjZ9(9_fvZ942msW8sZtsmfPoMMQ-i_Pp??lQ7zDsEYC1+%9Cd^)$vY%Tj}~~Pzz+fs7{x7CF0bu}yG17rsasI)Cy~-f~ z34yAD5C(7n1}iEmHq9lZ_6PiG(`CM&9;pPn3v7vAA~g=;nuxKu`6)zM*wIf%jJFT& zP%I`Nji#t%y;R8#vPr^v$p16~M_I(UEdq%yNK2{Xl%Qb!L((VMX9 z=*W{fg6N3ua9DH6SqSRF;v1UHZuJyK>3RAbIjk!xxpy6Yg?JsHG84)V!f)(iE}svm z5>cLt-XFYt?ZJlUaZeJf3|Y|$Vaq*fbJg(#ZeAl&T)6OIFy@To{LXxm*hWl-Bf#7= z{DJ)AyLq^+{NuB(Sgv%mK@xoE(78dTHPhF%0Z;q&=DsM|iHcpB7CfWin+FIVq<)Km z7wshU3;UgK@UMHdbULA|GsaY3|Bh3# z7gmzJbbiv=;zu^Og)nqjpL)e>j_*1gbjmX$EDF~qmR#etCqDbDN|tVvF1_eKE|}HR z+0J-_?ULu0wN0=4rTTQ|aMzJdcQ@kQV>CmDy)SW7(n61}?FSat3sx6A`gUr(`Jg$g zaXZ&!ILmV6A$%jU~FP4HzZmKoS#6f?0tC;zTvHtu|gZ;ORZ&@t0G`&(F}*$#}j z+s-!~6`*zf-GgXuv7jXXM>63opPA56o)N6!LPq{+rBTk0GA;lJV#!^CuGcs0Z#R)n z9@`iF-HF1_;(-&BdF%EL$M$^voFnI!?q=pZ{^J$xOWh$cFL3q??!nvXN_4`Ww>=4k zZQrI#UM|*}%O{gLZTT&srKEUKvAnP&;#|?*aSMYu!rTknvSow5c;4ITt7F0sZiVM$ z>K-p2Ct4F4V4SaVYU8n!O=L<BLMH&>xWEn?x%3I|s@eZ2X1)6RPdgj}W?!N9c?9?eCJI|=?j}h~y>_wL z1x7MFfh`KrA4Ah+=s|XV$l8i KY$=7x!M_2vrKGO_ literal 0 HcmV?d00001 diff --git a/images/courses/sphinx.jpg b/images/courses/sphinx.jpg new file mode 100644 index 0000000000000000000000000000000000000000..031bb4480c22ccc9d30727f4c3e9683936d93a1e GIT binary patch literal 1994 zcmb7FdpOhkAOCFM8FQ#znnT3cqG6TmPl;`eHq2#1ZaFTiEX^s2t~QMla$B8TLX>+> z62){1PiUc&m6Jw7>LgL>M<;Z4R?qW0|Nma^=kq-8_viI~zh2M#ueWkoIS$Ys?kslz zgTVk(6+k%wbleW}cLTs?19Jd?hU%%H{2Sl`{VA?KY+sHWmrM8#n3ZO4Vmodw~gk`@?v{!+~Oa?^$2Eo zda=HVz_hisv1lw2haD8a}eQIWO-`vPNwt zU-HT*{m0 z#ZCYjzH2oaODAjq#M}Kt_3elsUOd5D;?qSA^}g!@CAVf@4YL3alZnR)ZEwDCFWgsr3F-tEHods_7>}*+ zT!CvW;q=DBIMh8Xt5;8CLFXn9#nwdLAb&PUF(*^;QyI-wGqMAbzYp5oq2+l zw|7D}F|x(TYd+w1(5+wN9z8DRgd#P;-wfeV?vo&28tt%5CULl&S#mjL@yCgcyNcd& zbYLum!AY$FKj}=Xr@*%HqD;Y4g1UZz5jXIGN0DIn@j7eGjKAY zr2>>^jx;be9gxyi$1@hIPW%{rEVLz{Z+rFQz-|+m4A&nZwBH@{FyRYo^VZZ23J1v? z{qNVO_DY8vpD1qb%*-5R(DQkVaNZzdCsLPtE$k)|SgSAC)*rkkeSPPTUNqVt4Lhk( zV4D1OFXg&uQEkV8!S>U?ocRiM>?c*{9DlM&?bTCCfQYGEYHl1~)zkglHmydc=9&{^ zrnntOO&qxVoSkXzPs(>;Zr&mpEp@8>iXx^KDnYthr!+J>RN7^#{$boGm_UC|`$>CE zEdyVZ{W8O8GIgYUVVk_n{9{)2{Q?9{wn$CD)eps#hzVl7i~y_D{NLx#uMwRs2U9!= z-jV6$F;Y6N^)yLjU1%mwf#j8*9j!1C5yRd0@;+W~Z7xS<+(@F6SQ&RO$8O>`Y)vIU z8p-uXH&h$!DP8cMn@U)NV!m8ApeGl+duQ9TAfR6`ym>|~pZCq)G~wWkaqL*_{8 zex)-#-u9EjI%MB{+B46IHsGZqO%WUTIix*;Z|^uf`H~)4I zozlD$YQk2)=ruSYUurp3=908N^43K~ZWfD$kacF|6s7O9i{4+o-fhStPQF~X%tSx!u>tNOKOq1B literal 0 HcmV?d00001 diff --git a/images/courses/toolbox.jpg b/images/courses/toolbox.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3a3f88178a2fad1a49b9d8dc99875e5c1c6f5bf8 GIT binary patch literal 1636 zcmb7=eLT|%9LImZZT7%w#%R{om^$Ndnun&0OQ)GGx?HA{os_OH8O@!!&Z|-`L>|_{ z<>|6yB)ikq%In_4y~t$HZd^7;7A1WCK7>2G~H}#VM?(!ClaaK zEUoRfIyg9xEu0y2nv1Qy1MQ;(M4?b&9b0EX3pBMi(8x9R!SAu1MtteK%u81z31LaM>XGg>ea0el+*ikg7#8ru1oN#X(s0jTa)Fqo>{xjJc)lra{sPC#!~m_16Te ze6}7PHYiir46k@Kj$s%?yhCVNa%0!X@3>(24jk+ zeuLO=+0rICHwxYgell(O{6o>)o*!4LlT$XWDw9$w@5b(+FSOSi8CRTnYLsvUU)Vu$ zdu112W3r*0&}C+mN(%5f>2z68Y^$Qm^T@}zoSI*%%gW_rDk|^3E^Ewt3wP*8<*@)hzT(sGr3R`EB`GM{^sS zd!}Rqs)Bg=kItZeiFD zo3WX@53y=7d})o_a0A;JGYesqu*st#ZYy7vN=pnHvPq9ba@P^#T>B5qI&N{G7x&7L zv2{UNMt}nU)Rpt-^&b^iamegiY#jJJ0Jg+y5i5S z*|f=+XleDKJlv2eCh>rvJ&o9DnJ~Q+ka%RVsq;XdbBGM7Y^_X@>7kFV?}?(%NDhj} zV+4%}>=%Y*i*qTGib7J*JHpKqH{?E#nl<&6nXuDVu|3Z`b1hEYYAEfyy)TX-953u$ z`etuMWB;7t{S>$=E#ch!b5dy>;acrJ;1#|0=F`~7=I}{dBUb9RkRv^w_<2Wkf0KCG zPeI$c{B6|SDM9r0ZR*ysrth|;$NhBZa}2hzZER#$@1gZxe;3x4+_VLtqCt&DI zb!;Z9cj!_8UpUMuco_2Ce1L^MeRO-tITq~L`PBD$)`3}pk_Tnym1&MvwZx;?v!Cx@ zc&70u`p!JgXjVOWc6Upz$>!mFLtpQ4-^8PV4@s*0qBM1(XZ?=IlFdtC&NW#W?8?f9 zh%J|tIjoK6MTkDd)VGcAQv`4Dl3@*>yPd(_Q!N$s3hgk1jKl%nfuYq7Zj~fC?1kOc zi|pFh>1|NqK`;F=LENFLHvevu?p&{6j`=7r=gwfyVO3~MM90g(np%-{viN0xAb%E% z4Sze5O4fBET~vql4{Jn7%!0dO`F3bMfC~Z;krl_eS%S_O(V`Z3v|iS0tn>zR;xOKErHXoVrUc4uCxinh+vmbuzg2m#^Q0e{3MwCs&4--t(3Hj+f8C0Ax_xf!sF8;Z$ zpsI_TeMbRLix`e+Mdz2~Xu>SBC=ZZruB_{_V+~I5t^FPed{I2UNtdUBu3SVkp F{sV(A)Up5o literal 0 HcmV?d00001 diff --git a/images/courses/triangle.jpg b/images/courses/triangle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e62f7e1ebee14e3f40b4b8a9ec935e7d3cf9b1e0 GIT binary patch literal 1234 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 sKA%sY@*gvjfS_ml2jVO= zvHuKM0L#zsKQgm?8c_tNZK==1VgLgcbqp+DzkFple|IxnaL?(33^uxc+k@xd-Aoa*-f{)UWBW?_uxJQv#JSS0}nqN!_CJh z8NPh~%y9Jh8DKmIZ;$mV_kx8J$e=gxUYhSbv`H0N?V|_J7#O`XW&W$^%D@9P$fl5? zYkDz=$%vjC%QAZz)*b1G3*5QFdgVlbusURP98R%nYE=EKe855Rc<; zuWjUOHsEP_c>hEZ8{57AZ!6fvniZ}u%nG?Ot!!OUcYDn?qj_gP>&y%CQdFAIvU~G; z>wi4UGM$9d*e)bEKepI*{CG_Y!@FC5OP;b$4YF%$%UT|KyYz_bb literal 0 HcmV?d00001 diff --git a/images/details_ir.png b/images/details_ir.png new file mode 100644 index 0000000000000000000000000000000000000000..a307e1d4f6ffa5784b7622d3f8f5291126a00753 GIT binary patch literal 277 zcmeAS@N?(olHy`uVBq!ia0vp^oIvc%!3HG#g7l?;6k~CayA#8@b22Z19JVBHcNd2L zAh=-f^2tCE&H|6fVg?4egCNX!I5Q*|D9B#o>FdgVlbusU&>-Sb&RL+)R8JSj5Rc<; zuQ~EH8}PVXe4o%5@UGrV&w-mdKI;Vst0C@{z;{X5v literal 0 HcmV?d00001 diff --git a/images/details_l.png b/images/details_l.png new file mode 100644 index 0000000000000000000000000000000000000000..794cef97fe0032840bc5e74dc0ebfd4a65729a3f GIT binary patch literal 280 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 QPXGV_07*qoM6N<$f+f2Ot^fc4 literal 0 HcmV?d00001 diff --git a/images/download.png b/images/download.png new file mode 100644 index 0000000000000000000000000000000000000000..7e6636ede6c5613eea10eee08c10d4d9783997ac GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>p^>lFzskoK&=l_3uX4MAP*$?i_ zT`93lZTI;!m0Jt~7Ye7hZeIVU#%C6{!-Mnp_dls-WU2fA({~DE&xyv4H|3Gpbvu4M zUMyeO#wxt4b6c4sPtvkEJofc}o00|ORs1fn8iYsO`e7XNdy}NzvZIer7yLQl_QsIW zh(Y%J&557>UpmhFf4ZF?!(|0Kv(kjqg`N#2u@(Ep)~D@ZJHT^Lo&S(UGNZTyKf73s urhsgM_e7l|8cJ4*6Pag7Xs|FavN3RMxp1PFWs)DzBMhFdelF{r5}E*R>TyQ^ literal 0 HcmV?d00001 diff --git a/images/edit.gif b/images/edit.gif new file mode 100644 index 0000000000000000000000000000000000000000..2fa804d1f48be0ff11a87b72e13ef1cb10064bc2 GIT binary patch literal 231 zcmZ?wbhEHb6k_0KIKsg29|RcwU%GVn?%n@qK;Zuw}2KVuKS?F8yDXddYPVeFx34Ppj0}~GjQ+(=UEP(%LX{IH_9-%00|3!vO5p$i literal 0 HcmV?d00001 diff --git a/images/eye-mini-close.png b/images/eye-mini-close.png new file mode 100644 index 0000000000000000000000000000000000000000..da85d178903a0499039945513eeedf76a3ba9b6c GIT binary patch literal 892 zcmV-?1B3jDP)WdLMvZ6Gl*AZKN6ATls8IUq4HIyE*rGB+SAFflMN19Ob!00007bV*G` z2iXA&3OgF#!`fm1000SaNLh0L03N{r03N{s!)a7g00004XF*Lt006O%3;baP0008h zNklq^9;3q=qVMOO+UDro(|jm18-`l_}jrD>8$w3DAnW+pS=H{+xW(F=ER51hk2=U%~e zU9@YyQ0}94WrW(=KQOhJW9b01K?MPVKQhPU$SCjGcDp-(>f-ySh;Q1z#KZ6igtka7 zU)(KOmCI-<1I*B1*&KDv@H7}Aeu|J_LKZ!-Wql72iXV-yUSGm5CCugWLq1Er#?@@M z+OUwTNvPR=kZ=zgCIeUVNXb=fh*c1AG>~NfxK#}wi;Z2BxKCCBcW%{%h1m~pQJdS$ zNC4l2RmkW`XgbH;8|Sb#k;Kt-5*C$^*t-g(JPEOwWztt?I-FHB+t6>A)6-We*9x0_ zojpi&_o80*d)QzlSl&itQiHi;5d8I58Yrn7T}t4Z<_kwRAmlTj z-Hxsm2nRcmDcN{3r9;&iib@_q#_@OMKWt_5aJ+n&=w*Iahz+~vFMY>S?cPG zDkDypK~m@nN=S7A$|3{pSOL{`@vtL+M6QZ7sX#UiINX6=$iKGK>c3qt$FuEVsU!L> zSfwwgQClE#qL*(?hnbIvd`4j|l|*caM`@@k$xv(oO#?0g9K%rT%xv-Jb>N-&)5Np0 zf|@xcyNpCSm$l^BazhA&xNF)jc|IY_lmCZc+r;#2_&iHo*I_8iSgTw9$%UzR z=Y@&qkFkBv(9g%YFnll-^@(x6$jPn(-4fHuD7RYg8+v^t_3Xt=p?@GP1%sDFO7}Uf z*6~ME?`|#;UtK(bD<}R!d#m5tvs>H$$98q+?)0zte6o9av3Jdm@|wPId;S3)%ZCk^ S)d&^<0000jh)5Sx;d%v_A}}=j}pIwFbJ`2 zI}kpHp;2UY5{tw_xzS~0O4oMpJM@iIYi3#XtyhX&ovC!_0v0Ohb{yow z2Bvd1@)`pf99x(9iE}FTtq0UuneZ4xix$xxE<^AHzJK`uCM5FvkHJ+`j1}w1+Ve=P zRfb&GdqP$#?-_yA(}i(t$|B~L+K?oH+gA=Elp*du{RzwN;?jvh9N*hP>+@SQmp(#` z)TC|<9l$^~gUu#`i2rkZhLe}?fX5fxE`yX32_m2x8T3t^g>KfNDvBcwZf~J$w=h{G zI8{y{YXF{E1z&g$Yh%4ee*aiHLhRa`l-C&5GX)+Dj5r;d;clpfGLAT%RFPIVrS(cq zEYBya%}wSI<4iBd%BO`XzqR^8x7Q~3q=?jvTomd3Jv1W05pLCpwem|bqqOPKzKiE? z-Cx`xXLfGBKlu8(%-r@&?8XI+kh~K68w$~7UiV$#doP3R=R%TQB!CBD!6<41~4f8WMSlDU}w+)X$2X=!0b`6>cZX$jSernjXU-pSGazpS2{rCfFjq~ zjCsAs4n5`U*0;K?nYVcVaV>F^FDWiF5+?*HcAq&qVSA6)#NI67OF|O+rgt1s^kZPK F1^~7zJbwTH literal 0 HcmV?d00001 diff --git a/images/file-manager.png b/images/file-manager.png new file mode 100644 index 0000000000000000000000000000000000000000..d27f5446a8bdeb24cca364fc8346cd2b57362786 GIT binary patch literal 8220 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

    ghd|7 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 literal 0 HcmV?d00001 diff --git a/images/question.gif b/images/question.gif new file mode 100644 index 0000000000000000000000000000000000000000..02fd44948886b81e0da26fbe0133350a90fd15fc GIT binary patch literal 136 zcmZ?wbhEHb6k-r!*vtR|$BrHQPc%^c$->CMz{sEj(g`wyfyLC|1ZUPTeH&#ieS3S=Y}PXo34l41&^6d)m55X+dxd&-`?A4+rbSOmAbKZ$ef`~I)rKfir_ zd#CsMqkm^4ODjbxm(h$o+EGTOW{qWQtyraXH7l=nl~tLHCUUe8%ACEvUhSiz1tm zMjJ7vd&S$tSvz*I$5t-uU1wd~0zoa_IB|&sN`~vL)AL6hP%=y&1ONpVfkq2zFzE$^ z%}Aq-Sg-di@dGEjB}eSBl}k3+5;Xq7qPv@3kOYl?u;?af?2;vMK*=z90EJ^L0*w~b zjKT#7n~_GlH+tDo=Lb%CLXyQW9F+UK*nJLX_<3Psz88I7>98(%++Va8$CDPw_uh}= qgFX`|9p;7gY%YiIydZ-$0RG-VssI20 literal 0 HcmV?d00001 diff --git a/images/rtl_tree/tree_end.gif b/images/rtl_tree/tree_end.gif new file mode 100644 index 0000000000000000000000000000000000000000..b33f857c9514d16a28290a68f026c54464565e0e GIT binary patch literal 107 zcmZ?wbhEHb6krfw*v!Ddz`^jJ6rlK%g^_`QkwFKj6=;G00|SeM!3odRb055_HU2-F Uz0EOgTJoxOIj{ExvoKf#01^p3r~m)} literal 0 HcmV?d00001 diff --git a/images/rtl_tree/tree_expand.gif b/images/rtl_tree/tree_expand.gif new file mode 100644 index 0000000000000000000000000000000000000000..b25febaf477ceec7c56e4034d959e95e51104f5d GIT binary patch literal 117 zcmZ?wbhEHb6krfw*v!Ddz`^jJ6rlK%g^_`QkwFKj6=;G00|Sea!3odRdo7|5RPSfd e@KUs7Q`M?U^ji8sGXMGMx~O&U|A;a&SOWl77C-g? literal 0 HcmV?d00001 diff --git a/images/rtl_tree/tree_horizontal.gif b/images/rtl_tree/tree_horizontal.gif new file mode 100644 index 0000000000000000000000000000000000000000..c654fb6ce3331ca2d586e3983682efadbb1815bc GIT binary patch literal 105 zcmZ?wbhEHb6krfw*v!Ddz`^jJ6rlK%g^_`QkwFKj6=;G00|Seg!3odRdo5m{-F@6a RpfAa@E@RcY9BCH@YXFU`JH-G1 literal 0 HcmV?d00001 diff --git a/images/rtl_tree/tree_split.gif b/images/rtl_tree/tree_split.gif new file mode 100644 index 0000000000000000000000000000000000000000..cbf5395c02e0554f733cc103a58c700e0fd8f0b5 GIT binary patch literal 110 zcmZ?wbhEHb6krfw*v!Ddz`^jJ6rlK%g^_`QkwFKj6=;G00|Se+!3odRb055_HU2-F Xz0EOgTC!K?s}tMqb59OqVXy`O&_zD^ literal 0 HcmV?d00001 diff --git a/images/rtl_tree/tree_vertline.gif b/images/rtl_tree/tree_vertline.gif new file mode 100644 index 0000000000000000000000000000000000000000..542093629d68510de34cde2fc867aa60045beae9 GIT binary patch literal 108 zcmZ?wbhEHb6krfw*v!Ddz`^jJ6rlK%g^_`QkwFKj6=;G00|Ses!3odRQy;vlHU2-F Vy>E)gq-UJVw2p7PCGEms4FFNZJ}3YH literal 0 HcmV?d00001 diff --git a/images/search.gif b/images/search.gif new file mode 100644 index 0000000000000000000000000000000000000000..bbca7436c794a910dff7b1523320dc56d69a51fb GIT binary patch literal 315 zcmZ?wbhEHbv}WLEIP#xCUq69Ce*#0od4}`<6aN2Cm@r|&`SS=~L_P9PHs2{|YIINvG+M89B5~mZJ%z9} zGgu-mq*$E~mU+K9Rxt4_qXdfzXRO6JamjDBs$#wzUTqDH%FQ|n^#XicTs-Dxohsd0 zY-zkKSpuBATzrBZwKFsYc=`BPShD@Argd>NZ{4z8DS@?9gfZ`T6<(|KH!s zpOjd_&mj@<@84f;v%Igbt`;n2W@3_fw7c)|ma53Dvdlj6!aa>V4Q2u*_HoP(e#$2q zN_bmh7=rlCADo|WZ{geC_|){9L{`GD4N?w15(mC)Y>?Qory<9?@org)et=Ae&k+mpc&Dc4tqT??h_H5RAL%q^cyZ^Ys;X*A fYPtZUKmtSkw;TG2$ECG_K4I{5^>bP0l+XkK;jC+E literal 0 HcmV?d00001 diff --git a/images/sign_lang_alternative.png b/images/sign_lang_alternative.png new file mode 100644 index 0000000000000000000000000000000000000000..7810880d542ccd288c77ae71b37b21713550521d GIT binary patch literal 799 zcmV+)1K|9LP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;rAb6VRCwAnl1WIEaTvy*|NHIV%sBcLCgW(7nb{(d745W87!pL?TFyzQjxBTy zf}lEf>C(X>iXf;%hPKc`g)L}qQ<-aNE;-J8+kF4G!`ORxPVe&^e$OMA82}&wF`DyU ze{(UiiU^__rA>Dy&X=so9d8hgpm-!Q&cw zN`ScaZo2J~X}39=LTp0l(Yl6AJf3aXk~x39!_e4%vgpIL_>C7+fTYo!`}jV`-joW9 zK!Aj97Gg2q_h|Lnq5PZ|L?kROd6Rhdh`T2}dO6_Ew%IBZ1OPAt99lL^E5C8mQ0Kd} z?`cE;E5Y-4dvBgPyF+c*fCc~z1fe{6`mLT+x9uBUxoUi%C#?_|0-DfAtj|56I*<`Y zs21aW7d7>R2M%^eshy>Z^K*UulcNf$MV?Rtni^A70SUnZ2n$$JIdkE1haT4^`a8=_ zS1Bn|HD#eEQB+B_wdAXk88Skm)T>B`97<#Mt2gmoF$^nduv{p%p!4FCq{h`$sP+&- z)A2LoeEsa$3D~-pNYyEJOmc+c1@NkK4fPuBs*5{K`G_QeL(gcvxdx$bITc=Zyyv~_ z?@H+ggoIQhYtQ_F`WRJMX7drs?wFA-3=REVso^n&BR7um!H@3TUyA_tk=y=vZ?jUj z$RYs88=dBJLB44^kStssfvsCvC^VQ^3W-P-=9~U(KI5L+S_aU<_!#m**G%i3VDhO| zDRZ^uV>45+-jT{MjpXk9;iJm24USG~aePH=4DfH)d-cQp)XgKq1d{10UQ>&$%k1ie d{hurT4FJ26KHhb+z4ibA002ovPDHLkV1g;Pb9w*( literal 0 HcmV?d00001 diff --git a/images/star.gif b/images/star.gif new file mode 100644 index 0000000000000000000000000000000000000000..e53b7d5f50ce4dcb2ce839e29309377d6504750a GIT binary patch literal 319 zcmZ?wbhEHb6kyGKMbG0F}(f2@Z>qe!zT=<&M+K5$*_Ab!`AH#v*s{N zpUF^L$539uP*}u}ox_lk$&i}Hkd(|28O7l4!Qkk`U}w)@YQ|t>%%G#oAS%kh&(FZa z!@$YOz|72W>C&Y$XU?2FdGf%41AF)GUAlDXoH=u*O`A4(^5ov$-jU2?qN1Ff zoaE$WCnqN}Gc#jjoEfOLTT#K0DFU`Bz5j#R%#;;TtsLe{K( z!5*_1R8*gE9`j}3TmNC@4}%%Z4F?`2REPxfm00O^^mOZV$rv11GeNRAQ_4e=mw|Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RT1Pco~63Ts5^8f$=c}YY;R2b6* z!Cg<&P#6H<_q;u)YrC!;WhsU112M>j{%(JRw_d1;nwYpjM3@W=K}yHArW-w9@9}xq z=Fj`EI2K_J)&Oe(Eji&>=vi$&2e9@)Rlh&aK7QeGPn6f+c)-))0rNR3hsdopV2u_S zc>dzYY`vJ~d6(nhFsSY}59g%gbn*&Cm!$MS0$I=E{rp}%H`!h%Nkf!6>A!xLr2Wl& zvAMdjZ3{p^f&VuAgom>eafc*PfpqosCY4!^>Z@Rb?jQH~ez?Jr3PqSm3_! zeUTAQ^Fcw9L7bixXmNb@*64CNz2bfpXU7LoYQgMy*bOE*BgsJn_NA^Njxgj&9X*#V zd#S&6yb|Oc$p#%-W{4CbXO;kn$RN>nQ*WCc4|?&->aT4YPwt!|AniyQWtnppEMv~J dtzZ~f_z!u)(F(PSu$2G+002ovPDHLkV1lQQ*jE4m literal 0 HcmV?d00001 diff --git a/images/text_alternative.png b/images/text_alternative.png new file mode 100644 index 0000000000000000000000000000000000000000..e256494958d249c9f24fcdd284613106feddd2c0 GIT binary patch literal 861 zcmV-j1ETziP) z(rG4t#(DRi&V8Ch*n@d@-g)Oc=R4o|9*77^MPW{@HLgGV=4osjct@I_Nx?R8lVg*U z^Y2fBlcalCl%JZq^Sb9I6*@3`PTns{X#@z|A~)v zg0UtpEz81xE-9o7nxUqCbF0557_|AHMsNR*0x+Y;9V2w3`jsun!de`pxd|m;$_!0} z%8nh#oYo{T8w}&YlTR0J)xJh^#~`X&yL11hVR(}n-;`CBz=O823+|L8jCTFXi;uS; zV8Pe>x#IKeJVNskc;eiC17JxVG&!0HE+Eb1gkW%{A}w* z6yU~i*zhQ_>BR`W56oinTeP(C>EuNdY-$g*4-N&9k(PjT_eLjKFtDe83inzjN(AXu zF$F2JY+(k1mX39ByRMhUr{}P{{E+JuX;pOoK26Qcz~Y3${2W{*yWNsgcpq?;(r@rt?i$F<7$Xr}>NX zdfivGDaif8kqGE#)-a30J`u}~zE;^uk2*e>uLE5Jq4BJ?fj7kkd3%kxjZXu04K4ms n$&O{RuviNg8ZNErw*UhGI1r260+}Xd00000NkvXXu0mjf20f9c literal 0 HcmV?d00001 diff --git a/images/toc.gif b/images/toc.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe3627ae0e85d279fe3ac19b5f38f93a9542e329 GIT binary patch literal 860 zcmc(ey=s+F5Jh*4MTCg46A4JtnntG35Q`MzPiGZ~*w{?%D+m@BY~*6$fQ5~ng^zHt z2p01IwmyQDSZ&vQc?eH;?#%32d!O$5nc?NGX-nlDUK;l%WcB$kbAi z%2cH~Wm-#7%2JiOWF}kD%2u_yWuv9A(kiPxH>A-cS65glQ+a>>Yy%IFLO zL9HSys-i0l6RjQDQ61f3pwV)qqa5v73p83#g9#T!HY1HTVoZCAvx!rgooVc`l}lw+ zcBOF(1hvlU>`oI0lngt|*&cty0VTuaK>$!-5oola29sVu*o-vVh9yf87(i!QG;SQ9+%C)$Z)Nekq8@5S*zj|r3pbHZ9So5T09 zOw&$%K9>J;4*p)iF;o{=v;^GnD0RsUZK7IjyJ|1CV5fNcw z8EI*08F@HhWM^mR<>8eO5Ri}(6%>_%OAyQWe}F-dgF%8pf|*f}fk}{&S&;Gn5rz^5 z24+S^pex~km5rT+11Q9dBFezT%)%@$#T zZvNR+r|zJCe&Y0VdV<;on>%?Duh(}-z5VibfqBx;&@(?f-l#h&?hGptSX{3f{IB;) z2E%vp=|&8bmmVp8v-s~+Ezb46Q@>wel2<;KeArtiAjW&P)8RL3a!x;&m;Jk~I$L!1 z1$UQnzk5rKb_&GLpO}*SxH?EL+}cDg#ge7*KrQ}C=^ryi>B-F##E{J2M%lAa}VjzujC zT4feAxB7K|-r9*zbqY)4o@r~%vU<3yZAZMwvFF zn59B)sbyxCWaU!kl0gz!O54;!36d49v`GXl^biC-L_y?3lr4r>%;Yi^31x=kjF7pF zO&LQtY;@~!XT9M1W^^ZV9ABuT>mh{ChDTuuy|&7f%|dV4=GAS4eG z*(|GyCAcF+NmLL?>~y=xwMEH^_uMWKNy$lG0J&VNuc>cgPi3x|!$kY@7ZM`ASIhSf zH$;H~VId-J#mWq&Ixrah1AQ18H0wdRi?(g85Ig|t_WgIKluH&d>$4Lu;#8gOvxZ*p zw}vhlvWCSBSmz|$?QE1zs}pEKurxIdvGEJ@IJ=|GWHNdqpdh<&j(^3~rZMEMj`Kp~ zs?^9j+G8-z^7_P-WM;xX0h{dy;$q`LD6;x>dwXEF(E$3`@zeCX*3K+CSWUmNFxZ|S z3DfXoP9W_f`E#E{T5R-g|A1hHI!FzRWf+ee8tjWxQkgGbJ|FD;*hkU%C7PFMDUmZm zIM@6wdy2yqy0&&$M<>x^GJX|BakJ&KWn)H0dPslYU^?}?8|O7!mtW)z9fEUgL{4Yi zn@+zY6gQex$cY)jl9VI}yfdn-tgM~qx%d}){gg1pRZu=H^+NdWw{Bku?J$7n*aDS* z5ZSS_LR2yg_V2H>b1a`&Rdqn?3qb1BHPz|u{?`1u)SZ`G)+t(8Jk{~+=2gGhVaK!9 z6ruOU9r^JXSWYO+DnG0HZA0qo>!HzTW`t1ckcgve*Oj8D^$xm*C%{cQigPQ@Jda7u z^R*+5jg2TTFaHCX22m_K34`tyy3FGcIKFWGt{QFZvcj37C0eZ(rKP2i>+$zD+HBvJ zkB(9o;1tdk?LGHCaaGB*GBY#tA4*G03jzWHrq4eG7yz0BQtT(-2<`v?002ovPDHLk FV1n^@p9=s0 literal 0 HcmV?d00001 diff --git a/images/topic_lock.gif b/images/topic_lock.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe32fea5e9e030e05718549e2d7272ecd54a405a GIT binary patch literal 316 zcmV-C0mJ@BNk%w1VG{rn0K^{vs>#?%iK*P&+-PWMinGY$6jW~g& zHGQG;_xfImpa1{=A^8LW0018VEC2ui022Tb000HK;3tX|!DJ{$bYm+7{lVXuh?`~hin z00+g{!tYxifDjxG1QL2!etm#+0R)9{fQftsln`G40gDfk7L)=ATpbORl$-(r5C$16 z5(EOJlBWnBI3E-Z2&cNcp9>inBPIwCyr>8Z4FoPQ3kJgw2nGfh$vGbY8w?&7+#U^I O)+06>92+t@ApkqvYktrG literal 0 HcmV?d00001 diff --git a/images/transfer.gif b/images/transfer.gif new file mode 100644 index 0000000000000000000000000000000000000000..3131207da03a8143040916c295cd71dd537b0872 GIT binary patch literal 468 zcmZ?wbhEHbjA9UBSj57RHuDSv1H+j!Xa0i$kPQKf|GE8KLxP^;UO&`S0=I=eRW8Zy?S^egpZ5tdL0v$?J Pkpe=BS_bJ;Xpl7k-hIL6 literal 0 HcmV?d00001 diff --git a/images/tree/index.html b/images/tree/index.html new file mode 100644 index 000000000..e69de29bb diff --git a/images/tree/tree_collapse.gif b/images/tree/tree_collapse.gif new file mode 100644 index 0000000000000000000000000000000000000000..1f05065f85883e5d354b784a365b20c0ddf6ab06 GIT binary patch literal 115 zcmZ?wbhEHb6krfw*v!Ddz`^jJ6rlK%g^_`QkwFKj6=;G00|SeK!3odRdo7|5RPSfd c$V(QT6jw1zb?dITUrxPTez#&>ydZ-$0RG-VssI20 literal 0 HcmV?d00001 diff --git a/images/tree/tree_disabled.gif b/images/tree/tree_disabled.gif new file mode 100644 index 0000000000000000000000000000000000000000..7893680c037047dfe8cc6e25ac9e6140bcbbd630 GIT binary patch literal 116 zcmZ?wbhEHb6krfw*v!blz`^jJ6rlK%1*nOUL5BedKxQzoC>osbT)o#K`at!5293OA a(MgviSBO~e%)EJO{qxLb%7 literal 0 HcmV?d00001 diff --git a/images/tree/tree_split.gif b/images/tree/tree_split.gif new file mode 100644 index 0000000000000000000000000000000000000000..e89ce8045e3e48a6ce582b11899ecb70ff8e2a9a GIT binary patch literal 110 zcmZ?wbhEHb6krfw*v!Ddz`^jJ6rlK%g^_`QkwFKj6=;G00|Se+!3odRQy;vlHU2-F Xy)Pto literal 0 HcmV?d00001 diff --git a/images/tree/tree_vertline.gif b/images/tree/tree_vertline.gif new file mode 100644 index 0000000000000000000000000000000000000000..542093629d68510de34cde2fc867aa60045beae9 GIT binary patch literal 108 zcmZ?wbhEHb6krfw*v!Ddz`^jJ6rlK%g^_`QkwFKj6=;G00|Ses!3odRQy;vlHU2-F Vy>E)gq-UJVw2p7PCGEms4FFNZJ}3YH literal 0 HcmV?d00001 diff --git a/images/unlock.gif b/images/unlock.gif new file mode 100644 index 0000000000000000000000000000000000000000..87823b7e6f2acbb7c23ea2e5ad2c4786668e8133 GIT binary patch literal 266 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 JPDHLkV1j#9uOI*b literal 0 HcmV?d00001 diff --git a/images/wand.png b/images/wand.png new file mode 100644 index 0000000000000000000000000000000000000000..44ccbf812879c42cb1f9587d865bcfc337ce6361 GIT binary patch literal 570 zcmV-A0>%A_P)hUn2-Navsqo?5A8 zh}}V^=%7@_%C;=H{tGpIj5CMu*>5J=i;m^t2QTySd)}9aAppozC}++wDz`eOViU-dbRRoz=JsVlZk>N%^azhi%=xTCt9`LQjtqNFW~e|R=r9= z`@I3J^#z@aD5yBuq2DLQO#|4uFW6R5kzPZ+h&6Af&5}POarL&lA~3t5R1i7uh*ffDw@qEs=HBW ze?CI~MkvG6H-MF7r{Yv4kw_q&PNP^XqFgTHXlH@RpLO}3aV{T{Ez=8lo;PHV$Ads1 zfOtF(s5%5V>3qE|&{lueV1Y1j%GF zXt&#NI-LlGLPN>_18TJzN~Mxf*f6pY5Dteo^|P&3>(H{!KTr&_wGRQWb^rhX07*qo IM6N<$g7V1&P5=M^ literal 0 HcmV?d00001 diff --git a/images/x.gif b/images/x.gif new file mode 100644 index 0000000000000000000000000000000000000000..2a0056fe50f7e86ce01a91c496326987bd1d9930 GIT binary patch literal 361 zcmZ?wbhEHb6krfwSZd7R;Nak8Zy#!Fn`LFS!qCvy*Y~)tZkxURC2j2qwzi8cEKcg{ z-_g>#tgCy?z~G3P*>yw18-|9{oSkcYd~TbXu61&{XJK*C*|{t(E-fwXrkB_1h=|9b zq1O`<78VulsIBek>A77~^R%k!Rek-cdGp@SnX`HG=JRXT?BBot$$`Ki;O^-gz@Ye(g^`QF zoJF9dn*wSSB&)MkwQn z1C#iUwFWXaOtBYp&N=>n8xMy>owk?=3lF!Zr?zr6JF7GgucEfTixQ6jGlQDFJR6Uo dx~_{HBdeCPhAprintInfos('INVALID_USER'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} else if (isset($_POST['export'], $_POST['messages'])) { + $inbox_messages = $sent_messages = ''; + + $my_display_name = get_display_name($_SESSION['member_id']); + + // inbox messages + if ($_POST['messages'] == 1 || $_POST['messages'] == 2) { + $sql = "SELECT * FROM ".TABLE_PREFIX."messages WHERE to_member_id=$_SESSION[member_id] ORDER BY date_sent"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $msg = _AT('from') .': ' . get_display_name($row['from_member_id']) . "\r\n"; + $msg .= _AT('to') .': ' . $my_display_name . "\r\n"; + $msg .= _AT('subject').': ' . $row['subject'] . "\r\n"; + $msg .= _AT('date') .': ' . $row['date_sent'] . "\r\n"; + $msg .= _AT('body') .': ' . $row['body'] . "\r\n"; + $msg .= "\r\n=============================================\r\n\r\n"; + + $inbox_messages .= $msg; + } + } + + // sent messages + if ($_POST['messages'] == 1 || $_POST['messages'] == 3) { + $sql = "SELECT * FROM ".TABLE_PREFIX."messages_sent WHERE from_member_id=$_SESSION[member_id] ORDER BY date_sent"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $msg = _AT('from') .': ' . $my_display_name . "\r\n"; + $msg .= _AT('to') .': ' . get_display_name($row['from_member_id']). "\r\n"; + $msg .= _AT('subject').': ' . $row['subject'] . "\r\n"; + $msg .= _AT('date') .': ' . $row['date_sent'] . "\r\n"; + $msg .= _AT('body') .': ' . $row['body'] . "\r\n"; + $msg .= "\r\n=============================================\r\n\r\n"; + + $sent_messages .= $msg; + } + } + + if ($inbox_messages && $sent_messages) { + // add the two to a zip file + require(AT_INCLUDE_PATH.'classes/zipfile.class.php'); // for zipfile + $zipfile = new zipfile(); + $zipfile->add_file($inbox_messages, _AT('inbox').'.txt'); + $zipfile->add_file($sent_messages, _AT('sent_messages').'.txt'); + $zipfile->close(); + $zipfile->send_file(_AT('inbox').'-'.date('Ymd')); + exit; + } else if ($inbox_messages) { + header('Content-Type: text/plain'); + header('Content-Disposition: attachment; filename="'._AT('inbox').'-'.date('Ymd').'.txt"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: '.strlen($inbox_messages)); + + echo $inbox_messages; + exit; + } else if ($sent_messages) { + header('Content-Type: text/plain'); + header('Content-Disposition: attachment; filename="'._AT('sent_messages').'-'.date('Ymd').'.txt"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: '.strlen($sent_messages)); + + echo $sent_messages; + exit; + } // else. nothing to export + +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> + +

    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    + + \ No newline at end of file diff --git a/inbox/index.php b/inbox/index.php new file mode 100644 index 000000000..c2789edeb --- /dev/null +++ b/inbox/index.php @@ -0,0 +1,181 @@ +printInfos('INVALID_USER'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$_GET['view'] = intval($_GET['view']); + +if ($_GET['view']) { + $result = mysql_query("UPDATE ".TABLE_PREFIX."messages SET new=0, date_sent=date_sent WHERE to_member_id=$_SESSION[member_id] AND message_id=$_GET[view]",$db); +} + +if (isset($_GET['delete'])) { + $_GET['delete'] = intval($_GET['delete']); + + if($result = mysql_query("DELETE FROM ".TABLE_PREFIX."messages WHERE to_member_id=$_SESSION[member_id] AND message_id=$_GET[delete]",$db)){ + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + + header('Location: index.php'); + exit; +} else if (isset($_POST['submit_yes'], $_POST['ids'])) { + $ids = $addslashes($_POST['ids']); + + $sql = "DELETE FROM ".TABLE_PREFIX."messages WHERE to_member_id=$_SESSION[member_id] AND message_id IN ($ids)"; + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: index.php'); + exit; +} else if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + + header('Location: index.php'); + exit; +} else if (isset($_POST['delete']) && !isset($_POST['id'])) { + $msg->addError('NO_ITEM_SELECTED'); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (isset($_GET['view']) && $_GET['view']) { + $sql = "SELECT * FROM ".TABLE_PREFIX."messages WHERE message_id=$_GET[view] AND to_member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + + if ($row = mysql_fetch_assoc($result)) { +?> +
      +
    • + + +
      +

      +
      +
      + | +
      +

      +
      + +
      +

      +
      +
      + +
    • +


    + addConfirm('DELETE_MSGS', $hidden_vars); + $msg->printConfirm(); +} + +$sql = "SELECT * FROM ".TABLE_PREFIX."messages WHERE to_member_id=$_SESSION[member_id] ORDER BY date_sent DESC"; +$result = mysql_query($sql,$db); +?> +
    + + + + + + + + + + + + + + + + + + + + + + + + + '; + + echo ''; + + echo ''; + echo ''; + } while ($row = mysql_fetch_assoc($result)); ?> + + + + + + +
      
    title="" onmouseup="this.checked=!this.checked" /> + '; + + $name = get_display_name($row['from_member_id']); + + echo ''; + + if ($_GET['view'] != $row['message_id']) { + echo $name; + } else { + echo ''.$name.''; + } + echo ''; + echo AT_date(_AT('inbox_date_format'), $row['date_sent'], AT_DATE_MYSQL_DATETIME); + echo '
    +
    + + + + \ No newline at end of file diff --git a/inbox/send_message.php b/inbox/send_message.php new file mode 100644 index 000000000..632bc2796 --- /dev/null +++ b/inbox/send_message.php @@ -0,0 +1,258 @@ +printInfos('MSG_SEND_LOGIN'); + + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (($_POST['submit']) || ($_POST['submit_delete'])) { + $missing_fields = array(); + + if (($_POST['to'] == '') || ($_POST['to'] == 0)) { + $missing_fields[] = _AT('to'); + } + if ($_POST['subject'] == '') { + $missing_fields[] = _AT('subject'); + } + if ($_POST['message'] == '') { + $missing_fields[] = _AT('body'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $_POST['subject'] = $addslashes($_POST['subject']); + $_POST['message'] = $addslashes($_POST['message']); + $_POST['to'] = intval($_POST['to']); + + $sql = "INSERT INTO ".TABLE_PREFIX."messages VALUES (NULL, $_SESSION[course_id], $_SESSION[member_id], $_POST[to], NOW(), 1, 0, '$_POST[subject]', '$_POST[message]')"; + $result = mysql_query($sql,$db); + + // sent message box: + $sql = "INSERT INTO ".TABLE_PREFIX."messages_sent VALUES (NULL, $_SESSION[course_id], $_SESSION[member_id], $_POST[to], NOW(), '$_POST[subject]', '$_POST[message]')"; + $result = mysql_query($sql,$db); + + //send email notification if recipient has message notification enabled + $sql_notify = "SELECT first_name, last_name, email, inbox_notify FROM ".TABLE_PREFIX."members WHERE member_id=$_POST[to]"; + $result_notify = mysql_query($sql_notify, $db); + $row_notify = mysql_fetch_assoc($result_notify); + + if ($row_notify['inbox_notify'] == 1) { + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + $body = _AT('notification_new_inbox', get_display_name($_SESSION['member_id']), $_base_href.'bounce.php?course='.$_SESSION['course_id']); + $sender = get_display_name($_SESSION['member_id']); + $mail = new ATutorMailer; + $mail->AddAddress($row_notify['email'], $sender); + $mail->FromName = $_config['site_name']; + $mail->From = $_config['contact_email']; + $mail->Subject = _AT('message_notification'); + $mail->Body = $body; + + if(!$mail->Send()) { + $msg->addError('SENDING_ERROR'); + } + unset($mail); + } + + if ($_POST['submit_delete']) { + $result = mysql_query("DELETE FROM ".TABLE_PREFIX."messages WHERE message_id=$_POST[replied] AND to_member_id=$_SESSION[member_id]",$db); + } else if ($_POST['replied'] != '') { + $result = mysql_query("UPDATE ".TABLE_PREFIX."messages SET replied=1, date_sent=date_sent WHERE message_id=$_POST[replied]",$db); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + if (isset($_SESSION['last_visited_page'])){ + $page = $_SESSION['last_visited_page']; + unset($_SESSION['last_visited_page']); + header('Location: '.$page); + exit; + } + header('Location: index.php'); + exit; + } +} + +$sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$_SESSION[member_id] AND (approved='y' OR approved='a')"; +$result = mysql_query($sql, $db); +$row = mysql_fetch_array($result); + +if ($row['cnt'] == 0) { + require(AT_INCLUDE_PATH.'header.inc.php'); + + $msg->printErrors('SEND_ENROL'); + + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + + +if (($_GET['reply'] == '') && $_GET['id']) { + $onload = 'document.form.subject.focus();'; +} else if ($_GET['reply'] == '') { + $onload = 'document.form.to.focus();'; +} else { + $onload = 'document.form.body.focus();'; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$_GET['reply'] = intval($_GET['reply']); +$_GET['forward'] = intval($_GET['forward']); + +if ($_GET['reply']) { + // get the member_id of the sender + $result = mysql_query("SELECT from_member_id,subject,body FROM ".TABLE_PREFIX."messages WHERE message_id=$_GET[reply] AND to_member_id=$_SESSION[member_id]",$db); + if ($myinfo = mysql_fetch_assoc($result)) { + $reply_to = $myinfo['from_member_id']; + $subject = $myinfo['subject']; + $body = $myinfo['body']; + } +} else if ($_GET['forward']) { + // get the member_id of the sender + $result = mysql_query("SELECT subject, body FROM ".TABLE_PREFIX."messages_sent WHERE message_id=$_GET[forward] AND from_member_id=$_SESSION[member_id]",$db); + if ($myinfo = mysql_fetch_assoc($result)) { + $reply_to = 0; + $subject = $myinfo['subject']; + $body = $myinfo['body']; + } +} +if (isset($_GET['id'])) { + $reply_to = intval($_GET['id']); +} + +/* check to make sure we're in the same course */ +if ($reply_to) { + $sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."course_enrollment E1, ".TABLE_PREFIX."course_enrollment E2 WHERE E1.member_id=$_SESSION[member_id] AND E2.member_id=$reply_to AND E1.course_id=E2.course_id AND (E1.approved='y' OR E1.approved='a') AND (E2.approved='y' OR E2.approved='a')"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $num_of_classmates = $row['cnt']; + + $sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."social_friends SC + WHERE SC.member_id = ".$_SESSION[member_id]." + AND SC.friend_id = ".$reply_to." + OR SC.member_id = ".$reply_to." + AND SC.friend_id = ".$_SESSION[member_id]; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $num_of_contacts = $row['cnt']; + + if ($num_of_classmates+$num_of_contacts == 0) { + $msg->printErrors('SEND_MEMBERS'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } +} + +?> +
    + + +
    +
    + *
    + '; + do { + echo ''; + } while ($row = mysql_fetch_assoc($result)); + echo ''; + } else { + echo ''.get_display_name($reply_to).''; + echo ''; + } ?> +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + '; + } + ?> +
    +
    +
    + + \ No newline at end of file diff --git a/inbox/sent_messages.php b/inbox/sent_messages.php new file mode 100644 index 000000000..c6b81ecfc --- /dev/null +++ b/inbox/sent_messages.php @@ -0,0 +1,182 @@ +printInfos('INVALID_USER'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$_GET['view'] = intval($_GET['view']); + +if ($_GET['delete']) { + $_GET['delete'] = intval($_GET['delete']); + + if($result = mysql_query("DELETE FROM ".TABLE_PREFIX."messages_sent WHERE from_member_id=$_SESSION[member_id] AND message_id=$_GET[delete]",$db)){ + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} else if (isset($_POST['submit_yes'], $_POST['ids'])) { + $ids = $addslashes($_POST['ids']); + + $sql = "DELETE FROM ".TABLE_PREFIX."messages_sent WHERE from_member_id=$_SESSION[member_id] AND message_id IN ($ids)"; + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} else if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} else if (isset($_POST['move'], $_POST['id'])) { + $_POST['id'][] = 0; // to make it non-empty + $_POST['id'] = implode(',', $_POST['id']); + $ids = $addslashes($_POST['id']); + + $sql = "INSERT INTO ".TABLE_PREFIX."messages SELECT 0, course_id, from_member_id, {$_SESSION['member_id']}, date_sent, 0, 0, subject, body FROM ".TABLE_PREFIX."messages_sent WHERE from_member_id=$_SESSION[member_id] AND message_id IN ($ids)"; + mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."messages_sent WHERE from_member_id=$_SESSION[member_id] AND message_id IN ($ids)"; + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} else if ((isset($_POST['delete']) || isset($_POST['move'])) && !isset($_POST['id'])) { + $msg->addError('NO_ITEM_SELECTED'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (isset($_GET['view']) && $_GET['view']) { + $sql = "SELECT * FROM ".TABLE_PREFIX."messages_sent WHERE message_id=$_GET[view] AND from_member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + + if ($row = mysql_fetch_assoc($result)) { +?> +
      +
    • + + +
      +

      +
      +
      + | +
      +

      +
      + +
      +

      +
      +
      +
      +

    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 O0000N2JzpLfsb{ap=PcF$f7XfB>`O zvCSYB17558eWCnHUp+@wrAvB_%*c$0_r+V|r3=++HPPSD&`^TDMT-^j4SW&%x5PuH%erlx9rAl$Ph@tM6> z`taeyPj>w~c){=T<;!)vmlF2_Q9?aAWB zivs}|b`lFI5P|_gDynjrPt{LVPzhr=2j{c32JCv3Ej6AC3?{do=r%Ba!UQU6g8TI8 zQ}X4@m!Kv92vBGVjEfV5g7LPbeFQ=fN_fZSUTrMwvUh%qsoFd+n9P9ewwIS;0dF5v z2^u+#R2>BM9(`ma2Et2*z*HJaf#Fi=uK<%vGGNRMe>a&0gJ}IuP@V9cV6h;WQae#Q zpxSxW!kXywl88yjHtC?fYf~1gO~Ird?aw0Gs8q-tC7|zk+llEv`sj95)fxdoze#4V zwJT$G6O&A*0T{r<-V5=bc0o7pU99TeA*&M&*an0LplHPCM zz6ITfk&^+&3=NW5aV7>P%YJAtF|XzWZTj9{jKAh!ESBtAxpHMY4H;n6sOhn&UIe1| zUXtpjJqUtRm#vI@khov6WXa#mqqf=9Qw_l!Jb3T`7*6%GE`_17$G~Xo)h(tzWYkNH zpkrz&@AN%?{=CdR-9M?s2B6mTr&ezwJfz$#$)#$P&#+9shZi#RWQwk zsWO_ADbq1&WTiG)K$K$XkRPAUUCouwFlLZ>M0Zs$POMUjC9GLqEhZJt&E3b`Y%$)) zuULMT35IGlIy#y>c<`W>(WVBNba!`$=&uapc}GV_cxPl}B;4!smI&z?OCYd(JbIGLQBtZ5GU%X_jI|n>TL)ICxccP`SY{ zoyTz*1`Ur!ef0+0D7Nrefw`cjA3uJqvlkx}(f9J@%jE6bw>1w6V7ONYkn237sW{pd zD^`T-TeofnU_4dA7C@T6F?R#0W57({l(^>|F_ zuogh6aOZqR>|C`3=9@bVp4b(&pg$QEz3}pgr@H- z>LFf$lyTf^;m;E`6F+!0dbHeN=tg09J9q93jH)Y)MRU4Skb1qe-WBF#nJQMT$*O>o z0OZxHSF9f82g4&XJkPy*_d);!@tEujc?&r*H>%X=$x@|=A6lwE<6tf@T#y6ay?YmI z2Y?VKvW(Ve5;3pkL3!8A7O4?}GzySoV`D*ueL9re0nlK1X2FhOQn*aGXsC=Yc*LT6 zUdi}sCf32`!5*Pv0ON}uxpFhrXDv5T+-YsC7C-#Nu@hztOsrJP=g0-0K7AVcmM>pk z3k?b=w*&B{9eZ`5!iXP^)&Q?#WA@a@g)dmmxR6f{wPvPY06?&x0|yQyr%#`brs;@T7cX86LD4hr zV@;Ms7NlmHTM>+UsH`F&?h%9T+_{rnxNu?KXFi^7aID~FdBbkx?L64r)ft_DIu{>GV;Uy`}hC({{8!|bLY#(Zxc^4DX>j*acuv16twnABxX9yxMk(#|sz zNbS;4Gq2M@9%$;RRx*{X4$f3$;p!^LlQYUI$A`^ z@6{{j0Ac_dWzn%zrx_sggawI@)s(WquwJE^ZPpjEtQvBo?=*P;c@Ib9zjp1~%+{@2 z;SnpjB!k3J7+3KTh8SFc_z!`+`bb0(a1nu6K4Z(mKf z%`5$AdDef1hljUXTwZg{i+)=Ov z%QmLffVCPhyKE_=wK+(I)Z6hlXJow2R?OWNjme5#Pa811oDP31<(V~2nX+;J8yp;* zJap(#rY2+_&QuMccBf9AO0HbF^8d6Ok+40bj+7S3HKYPmP z@oZZ9zCi2J@s&n3?NYO%Hvl1OQ>9a8rqhYR$b>LVY7r`?#4W7~&80N!0J3jZ05+}G zHwELQyi3xg8u4D1Phlue!nA%m9uF@ KiIz75gEas+awe(( literal 0 HcmV?d00001 diff --git a/images/file_types/audio.gif b/images/file_types/audio.gif new file mode 100644 index 0000000000000000000000000000000000000000..ea9ee78bcab3fac2e7eca6d8e4d5a2357b354412 GIT binary patch literal 1010 zcmZ?wbhEHb6krfw_|CwfU#x3TqG!=!V!m40bCH2xqkB-7S5S{vP_I`=uXjkFcSw(q z-*V@mg)X%-D(h!fr!9?1T$7l!I3Z_oR?d>_%l+2p-dQ+HQS z-BUMxS7^hD$i`z4O-Ex}j>NSbN$fbB)Ok3y>ri_4!OWh6IeiEIGyHG7o6|IFPwU)$ zvp3J2vt`!Wy=&I(Ti3DRK=-0UJ&O+YFFi7G`LRhWj!j*CV)~ksGuNJ)v*FC#jc4a? zKDTJ=g~i)0F57Ww;D| zYfpY%fBO68v){L$|Gx9$_q~_DpS*hf;MJdpZ~i=f`{(JqKhNI(dGX=ztB-$QfBO6O z^WXPh{(kuO@6-2xUw-`i`t#rSU;lpo{tpBUD+L)C7=+%uxw3Q9hNCyGi`t4XHHa`d z2)*UG?z;Y#<$zZZZ^AeRMbpU~?W z6Mp9aWq?A8ZzUjhfw&)5K?H%~6Q1vxaJ3C+DNyBv--SRnPz0p+?UM=5PYAv1nea|x z!tV-3pl2Cagx&(JW`GDzWGw)O0MI=XIYH`y0BnWFM9xV-23XmgyKnjKzEwzCc7lO% zNz$?_U{wf0=v{`;yEh^+1wbV9Zo#uZ%7>Tg9g|uh^zO!?LuY~39Xj+_By0jumVtp; zBw+v1OAkZ>fVN)|dbb2Riotpmiq zS8Z3|h=Fm9(0i4;IVD2xb$|+h=-GqC3=Evlnl=C}Rs6}q$iSe``vPVJw4OR9V&jlCQD9*cD`UvAKUWxyyBXPd6+~tTr8XayvDheM zaH-f)L_0va)&MH(%wGTi literal 0 HcmV?d00001 diff --git a/images/file_types/binary.gif b/images/file_types/binary.gif new file mode 100644 index 0000000000000000000000000000000000000000..2fca556b1aa24ce5dca5e8ed4954f4890722b0aa GIT binary patch literal 1016 zcmYk54NMzl7{_0_)|G6T$E9%$7j1=*k<@(pkb?7K8qN3?GW2Rje z1}8z<;2oB2EVp?w4pAY4EVq!2j()&dl`R9yfgg}=Y%KzJt=qK1^~1MqqP}@@d7l6C zf8O77mt67QJsNE_0uT@ZKxI*BEe1IWR5q=uA1Pq6wREY@rmexUb!>r=(;g3&zTY-- z3?Dg0jC}aHN(Qte#`25+vcio zbCO1cei!LKjyNnx0wf%y)`nTIkb$+!IWn6icW|VZwHxLbUC5#1SYw1WMA#aVBpc5U ze{$i5ouq9vNz-%-<+OIWZC&oq=uzS;w!N3__||*2@B7aFAIO363xhYmxN@tDxqa#C zM9o`a z{5c^08Wew95a)gu{fp5%%h7p0dS8eJLeYhAZ1IoS(t{ZPFcx|g3;z{=5Q#tfJO1ZN zJhB>J5fkuCmt9s!8ggVg(`EB8L3srLfRK4}6O}0MCuI8-YkLXVd#Ip+I?C@#cPlRX zQ9+*SmY{s)|HQnsm>Q`AKnhy{Ob>u0m2Zb-ZsM@dIvBeRQ&>tN4_!Kki}e5~p;U2k zA!DMz%Br56h+^SP5Ko@knTf;Ziq%hMV>=;mom8qbm}{%ptI&l3dU|J z16P8x-KWaayv1hUzX2qAG?X`6{{b3OLj#O}{}KS1fyMy{Rjo)f;Ku~&lc&&ZETO3ysD;iDnhL;db`Pi%SB?!4>zvTgf|jy~Yh z>E-GQjb@zCES-7$pz`$zf0%vkM`Pieg9cfXXH(fkYknEs`0}Q#XLgyLl&5+9Ms+!b HAYkV|BNp8i literal 0 HcmV?d00001 diff --git a/images/file_types/csv.gif b/images/file_types/csv.gif new file mode 100644 index 0000000000000000000000000000000000000000..dc94f559b208c413e2214ef7cd5757ca8acb9c42 GIT binary patch literal 1050 zcmZ?wbhEHb6krfw_|Cxa{>$G9d%JTtq(6G|$GCv^>5Er0w>^lP5gNTH;>Cx*_h0>< zylDH>B|A+bM2$1~Z$0~M>M8Z`_3w_gPrKGV$y}Q>`B2~I@Bgkp{dMihuY|f7>kPR$ zs}G#J_w)0Qe-GdMnYL{A!p-x)eE%0U%hM=B!z7pQ<;TA-Km57>^1f52sa37|nY%w) zRzK@m|D|HjAfzy6Jw7qscnjn%s@-h27mIG6AF`#&qTowY7hGL4gp znBjiy(KS%PL>)*}Fftp8tOH z`L9Ww$h9ZGUw-&&k}SC4z_p6SucvN#RK4^?>hkDIkA6LQ`)A_DM`v!G{_^ABjiSiCoT#xNf9y$7N57{e%6}QwR^tzpOLq6kx-O$QJ&<$C~`tUNHN)Ks%e)F%PohM4XTVYoVs{C z6<8S9EUma!I2`3tGhyf3VyL8gg2~I`t}dEvxF-zuIJEsQ6YO0alXG!!p)oTDyK!m`ik;Uov|TA>Qh0v7icFLABH z6;C1`u-h@SXlf*B6fE^zV{crs;ArnGy}-H^4M8hiv%R@boiJ2*#>362{i=tv_|K%T MuWzhpXJN1g07G+wasU7T literal 0 HcmV?d00001 diff --git a/images/file_types/doc.gif b/images/file_types/doc.gif new file mode 100644 index 0000000000000000000000000000000000000000..438cfd31d6343b52f8ecd3083092e943009e99d9 GIT binary patch literal 1028 zcmYjQdrXs86u+&lw5EY_SF;9OQbnUPRU0x5C|f$#X^D1=OGqk%tpyR(LN>x{Qwc5Q z*@k>o5#nI^hCIXv4`t$mN%>kxS!6*{9&@mjheeU^Yego|7rUj&;>r2rcYf!0?(f{2 z+#_*^qoS2uzy-?y&?579eUic2r(+(_uwu$K#{R@UdS~mgs|OQmBEP=3JGm=L*?REw zec732@u^P9nFsM14M{mS(+cj!W}4%&pL~_~d;GaSzP(_Q7d*|YsLibG$-;WF3_ZD* ze*3ZN(YdPbg6fCyg_aZQez~SENo)Q_YgVWSm1U;na#Lyrk*4cCt?yM;8q+F9@(d&A ztH;;Cn&xVEaoyvh+GmA7+skmHuAb1>6S{^;Lvw%itpTicw60^Yyz%9woAzt>p0+%) z-hMdL*8R_o$FJLr<9AKZx-3fr!_%V^^TX3F>x^r3);0FrJu$uXr``S6oO|Mh`|p=- z+q`FL!81d8o>LyX!}G%Foqz3pwdf_^cpXb#=c3E`*0;FqTUzn`yXsqZ`BvR)C_qan z001s5SRdT!XcUia{+LHE07)KF8km-dvrb6b49w%apz8)^ z2Bf0=Cy+6LuDtX~`bate4Ain~B>>RZu?UqBXIHGc4(||xsAM2N z_^JkTs{jxnN?vLVc==1`jR1*wY_1y{DM6-4iMhW=7%~1?au%DjKuV8Me9WCsLze{% zNFr+xAPvm#&g&>OF$~kF-;!BOzh?5){`(pQ^9u z!=fus3}iK`g}e5@APNyGpg`VW-Y&$a*n literal 0 HcmV?d00001 diff --git a/images/file_types/dvi.gif b/images/file_types/dvi.gif new file mode 100644 index 0000000000000000000000000000000000000000..6c4e71e63f1ea23d6c50b81f6f4224a908e35d1e GIT binary patch literal 972 zcmZWodq`7Z6hFIOb0Zt}bu(T4F*T^jA|lGD;6|1P2}K146=_5dELKGHfKXdnX?1UA zT3IX~sb%yIE6cg*TE(E`mc5kKbeito+0*oHvvaqIpu^$vJHPWg-#Op8d?`zpB+bo; zf+!dO*v8cDIMKL^Yuv>*?kRe-zxd&S;)a7I4_0T}*W}pJb6eIKo7WqgGmcm@k6Je! zvurMC+FIC@b;3Lh!@1`g4qteD#AGfgw-lCJPF6MZ)wWYLw&JVylDem7uRXhP(@}Bn zW$i=Zy7|p*>$}!ZLxOAQb>~o9m;BZ(zweem^vE5bWoNJNqsQkGeO;2z?e+Ea`+L9m z`@Z_cZ+`E0fB(NK{ zse{>6BBuXCP7IO361*3UDYr2h0OvfqRvQO;O3oI}szJ)QX*4x1C6Zcy~_TtHKQfO+;m-A(9U_;M#E>UZd#I zf-O>c%_#&J5~-|R)V7QI%*%G58Zs{nis&E1A4DV%v(SuAB$L~hV6B>N)8U+NEEWg0 zvsheA8ekS6jA5CURwu*au>z4Qu{H6s*0rab^vW2hOfG(f%8lbq=!@yQI1<(#YFE~a z)|)5%bsUCxC$?wpCf-C8pu3$F02+7h4Qv`WNUH$GK%!Enya`a(!X)*C(NuH3RyTcB zl7Tf@HSv3T>^ZKyhb;S^8M}e{b7j1Vu{0Lio$1$!D)=aY>vQgj=yq|zjl+o zdzHL`n!Jpn!kMkgqqfnr!`jl~`qbn4;q3h5?fm8L{O0fc^!WaImAihIx`my-h@ilW zqQZ}*#F3}QlBmX&tH_tE$(^yysk_v#z}WEi{_^+!_xk?z`Tqa^|NsC00000000000 z00000A^8LW0027xEC2ui01yBW000J(z@0Ee1I=Ww)=H%kI(IbyE{D3=1}a_i8J$+I zSuM9jE|-4<{xid=3p8PB9S>ln@P< zmkbC_Fc680jSUP77)~yUCnpQ5tO*uQ9*T?tj-v?*6izIQWeUE&BL_|^v$YGiBO(D# T9UL4R8W|TA69xqY1VI2hye7PK literal 0 HcmV?d00001 diff --git a/images/file_types/image.gif b/images/file_types/image.gif new file mode 100644 index 0000000000000000000000000000000000000000..01f4c070e3dbba574e70ea5a433c7282d7becbc5 GIT binary patch literal 1025 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 CtNGXf literal 0 HcmV?d00001 diff --git a/images/file_types/oot.gif b/images/file_types/oot.gif new file mode 100644 index 0000000000000000000000000000000000000000..853f444bc403bb5f59ba3063b356f215d31ef18b GIT binary patch literal 1031 zcmYjQ0ZbcZ6#iRV2CO4^)NL7EETNcTSYrlRFwRU%X^R;~7prL4u$5IdnWJcg>7s3| zZD%XHW2+1@Ol82pF$V=_<`}eB)>KnW!47aMSZG%mqgPPofVR~CN}|5JT)y|c@4fHc zB_}KrAcMhRuvo0@>}(#7S6o~y7K_Wu%49N`{P>BQnwtIE-287if-78p zU(V~jc_;ehNAK`XO^ZL9dE@=M!D@7{8XNrR?9GpjH$E};f7a9l&*2+3d2WMLscV|yt1}2w#o@d*6*%L4Aj@9sDAoe-&nr7iG zT}0dvD?cBTHOG!Utglry^?%Vkpd8SeOs!_KS!c57TW~`Qex_{*yJWBLup2Ks8oGXN zyzwefr5-vi;hvtiF%c zYag6P!<7KAAti)m+>0z1zuO=okIwd^w>8idD$%eUV#tuQK|ea{KnMd$Y)8Yj6!Lx= z5`@-p4@qM5D)>_H9xRE}!-!jmu%vnjU?9YW$zaN4kS%R@0BX6k-5sa@dmuywLM$-# zCYT6e3Pdt(ZCv{fh$nD-7~0{uk9nF6y8t}KRG4p#dYKBi){PLY&>9K7U@JXmWs_$> zCEs`(D&I*bvyk=!JPFR8OjB(5&1>BMCZHIQ5NERTLnM>~1uz5NivT1ARNc@tW0gh$ zfD3ZSLGqsfiGFY>QC&SGdOTx)t;c10(NwVQB?KupQZp-Vy5s{ zfl@f7ZbbG7cWlWjPVSSkIRGS&nk0_j3hPiBgSp+3@YT5|b{#W}SVk4bz3;i__NBGa zH>XMSn67D+$lY1wcfTU1G7|PuwW7ig+KU)j;)9CSYO4h8EII7Zb-(?6Lvo6;`{o{{ zhL&((p!WVj{M$3Rvag+r5ulynmJ0G0B<%-On^M@N0dtnxw)2RCBU&t9-n#oq))YHe eU?_85?X9Y+$Slxo-nt7-rQikHE$LJ$c<+fb(!wPdt*w{kdlF^JTwPRY(n#?X3?NFg?QMbCOT?`q7 z02UNzT#rG-rN&yovCT$|Vac%N&lS3AkSQqzH)+~|J%`>Mb8ZFtZ@)#fnNCaEJit0IC3t6!rO;;p3L!Ua#qN)}0+JtM~40@a$OfOG)BrBGN^Di=+=v^o#Kq*OYI z<9>>oERY8j3Qmega6Cd$qCycVC`if`lQbQmX@Avc98HTVTBK=CrxUeWQLE#0Rh-c% z*3=}8#v~*gzjZd9A3ALtX|uCtJNtF-81tQ{z0cG6qxbCP8|NH1t^E_{uionZ>CX4p zeV2#)S4L*9xql0oOo0IxXEuvgtLSh{{jutsU-d7n&fJgQ4M%_HqW2y|1Aj(Ezn>E$ z!NpkUQEZ-%-4|kEG4^07zW7)C;o~^}cU*iDU-~!kIFfj>ocL!Y5m`;FM3bmUNj)i1 z5`6B=&MOE%m(@43a~=VLCjo%T^yb%g5qyxz+(oYMV=|8rLNVeAz9hXZv(FNObZx69 z_&raNo6_W(ny_#zVr@T}{6&=`JG2K3X zGvkzMsP5;@_U*f0E6Xj$B>o-G6l$y;4pmdV`^5v!^24FA8?7(MJ2V%A{oAzE4llcp zRkdQLN(Lt?^3RE6!7f>`Z_CN82VcF=tvnVi`sJfvZ5K=S2J!V0EGs9s3lD8smz6_e H7`*cz-lEi} literal 0 HcmV?d00001 diff --git a/images/file_types/ppt.gif b/images/file_types/ppt.gif new file mode 100644 index 0000000000000000000000000000000000000000..3e706b638b22e69dc7792385395595b6ec233cd6 GIT binary patch literal 1040 zcmYLIe@s(X6uvDKTUdZRyQS+KL#cs;Dog>3Xo8BPMh*VJrOFR2LqR}R7*Qi?C?Kl? z^*sa;q)RQ(shI<|0o#O8TIlKq3%bHAjD??tA$`3finOooZXwxjPVW8AIp4Y8cmKGt zdp;6GihY4ESOnnXw|vfW*%$pe+1ERB@y=YL^X$!z3zd)ZE88zu{dA;2e^jay74{?* zcPAHji)GrBtJ;%aXwpi%PM39MT~`~2)@z-Aaa6KLtSRTHiJYr+@Ha@W> zl-RnNhze_p3{l2xu8$3CjE#J-PtbTEsy!~`+r!)CNy4V%F>Rt9KOEcHk-WQ8EHoYs zR*Qw&lY6zN_WqI?r_YS*%1h|YIoz8Q-+xZ@${oxo`L>j!64sT_`stkB14OZts#iy_#1FG^0Yuc~Kf0ilKrAkei zQbT;DtNQve{>`Agxv#GE$^A!$M)kArv_r5}3Oh<+PdR*C1^ezk9k&`Ltb>!*Kc{R) zlWlm~_ScNt{RQ&S{mvQRV7KKsJ{eBN$;X}2ucsh4JI!2(}9 z<`*3cZyc|d9E(D5vjM=K-^OiO0QkGQZT%Q2K){v42P;LC;M48+KnX@N z5fiuNAdD)mS@R@5jdaU!l7rjM)0|FpCuT;{V5LYfB*-W7L$F+Yw3a1=1gji@Wm@fs z*?Yw7pQ6$OdV7k>u*~{@Ll42oIrb%h0wWbBDnQ$?QO8waGp2F?F+X`vfnv9N3$lb}>n|Tz(VHzX{1<>; zhFN$cS$UX+j|eDCqjdnVOodHIbnTKm4S?-{?;3Rd3BWXieCDKbz1KUzM^rI;@MwkB zFM)!_xVx3cKrb2N9o&-;SxUfGzEAur=bdK{}8*M82KRV8u3L==kaQ2v$}BSgrew2-qKycmA^} z&pTnO((eV$H+bFT?TSx*Q{!=s_O9TnV8=RMH`nXTkTP-?(EZBw6BtCRh3es+Q$gRK zkSdF1-rUf5Ca!u|y$M@$=*5s}zWR?;e?bMk<)OJXRVR>eu7sF2{n*2oFc~zu8^dFt O3@T`D3{N^8MEwgQ9P9A_ literal 0 HcmV?d00001 diff --git a/images/file_types/ps.gif b/images/file_types/ps.gif new file mode 100644 index 0000000000000000000000000000000000000000..ced7c63291dc9bb1009796eb39071dd6b66b560e GIT binary patch literal 990 zcmYjQe@qi+7=Bx>YnhJJFXx;;qDA?`EM`F+u||xIia!?pV=4(t)Igb;nrbRBN@hZk zRKY>y4kR#Blan6~4T*@jh;&j~s8vIv?i3W!YU_b;cdf>WaHaTOn{0dY=F9Uw@AH1o zdtY*!w{9}8&NG4$#sOyk@OyI6;EL*(=Gwrz^MS)>y9$`D0=DZ|^Ydz!L_nYUJ^6mo_-L|vtVgy z=Ps}JRMdMan|jzkd{xc9+LpT)uJtwi`S5Zl&-RX7bq`$kK5KqB*7En*jREo2gW-;+ zqH9pPAC&t-@}t+2d~otfXtMu}@>Ea;hn43e%8R#3U^Mzlj0UCX8#yY3qa*L%jYOtK z-%W)-OhrD%-jByVe2V=u5gVV3O(@eCpoWFWEsXq49BTnIpmh*A7f*JsU2iSZTldFX zpb;6+%poF$Vj{Zvj%{#x7A6sS{-)g4@<1&Ch$!+MZCLitUQ26>X|07wGyAt+MO;lG z2qGX7jY?MM*Y5%%1z6pWw7!|u9U`(>y+O)MbQiZXL^i~`TZokNKQb*r#z$~H3R{9> zBLMD?I2}v(f#1$3g&OtgC?csZlOS209p4l8VUtxoM%-cxn_Pt;E^`h^DMOOUddUx3 zm-P}KSN(Soc|Vawx+s82WKM`K@iu+uo5vA(z~kvdJC7$wp8)IvB++)E_fdehqdQOJ zMzkhji+A_AZj-tODmT_VMrHnd4l|3ofh*x`MZ}Qs8$Heco1i^M!f8Xcqa?fx1(-sh z0U%i@X+_i22}%Pn50OTR7PRKB9%d4l(%u3C)=d@b54PPx~7A)W4@EcQSRDI6_ z*Kns2RvZk~H8$R<+Pc1eH<9% zaF@>ljoE0XFE(7s{yK?yk)EDmFgi1>&eoH>PgiQXEjZYVhrXDZ@vS)u$FdxYG7h#I zE%_zhl-1dk!4fDf_O3LUL)^~IDGOHFcy9TIO-ZHm>7^DsSFHPzEHvl3O(t0L8Lx7< A=l}o! literal 0 HcmV?d00001 diff --git a/images/file_types/psd.gif b/images/file_types/psd.gif new file mode 100644 index 0000000000000000000000000000000000000000..4f6979762290c95035da5a0f7feebcdf4bd14cd9 GIT binary patch literal 951 zcmXAov5VJN5X6V$hiJrbXltX{6anoncwh>Xh;hZjQ`Y7xA%zAv#T87 zP{~>75>VvW$XVw?6jG(pZT$o6#1xM2o4jIq)i67s*>Cj7;(;^sn8#cE{|rcg5I_u) z5JCYpXhI1G+~5f(0tiDSf+(O2l_;WtHguvnlt2R$Mw&sg$BLq)j@dIot_Nm_klAVTx2x1uCYJDpZB4sirE`p>FD_P7P?7 zMru$CTBem+)P}Zcr#8pNN>iqi)9NrsI_Lryb4eGv!qr^UmF{pi_jIQRJj^3K=mjtH zN-uiD+q~18BSoh<)5$4LSRexoL5PK92tyHSp&80>gj;xqGXfD7kr9kSltpC}qY-V< z8O=c^Xu%9}LIf*hfh8!hk}P2e3fSSN7nS zkNEQBXDhewug2p0ub)0Sv-^NAY#zV$=I-aquP**Q`{BjuCHyphXMO8~bEmLz?AVRh zXV<=3es=z^YrFnj+Wqs&w{vg)eROT@==XbHuKaW4x97_nuNPu%>E{05=YITd{lbe^ N$1k1Sv1NvX9{^0o#4G>+ literal 0 HcmV?d00001 diff --git a/images/file_types/qt.gif b/images/file_types/qt.gif new file mode 100644 index 0000000000000000000000000000000000000000..87e187c6927d4d3fef5149f4b81d0aadd4af8fd9 GIT binary patch literal 603 zcmZ?wbhEHb6krfwc*ekRV8a1>g#!f%2Nd|v3-CWskbkdl@W9^TLVUvggoN`21?Te% z_SZKYm@whMh7J2KT)4mgz}{e=1F6$0+}&)>g(|NWo8 z|3@|+d+_Sd`!9b(8%{(t9gS@{64!F1Xu|&MPk&!~^6Tca-?yLtzVqVuy_dgVeE9qN z(_e-B2Z4wb^xdCl@Be)H@vmy??z-u_TIcTTUUX>U@?(=$9Gkx8xPAge%e!oa?xA+E{F%hJ=s&DF)(xv4MCTiP$c z(bq@LIi|ON;^e8*XU>{qw`8f^!bMG9%a-=6>$6<5#&&j`h3t-C_Y{#>6GU?qF}XM^nQzE-;8eJB*t}K;!;>b>+AyA@>L#2MOLw8tQ6BaZ;lES`N`V zd}_acs~9wi@uchsN_A+@eD5)(;30=&BL^o_jE3VxC4Rxa4j1be=USZPtSu~dB_4L; Tl`>A+lOf1-q@BN!fx#L8LC*+o literal 0 HcmV?d00001 diff --git a/images/file_types/rtf.gif b/images/file_types/rtf.gif new file mode 100644 index 0000000000000000000000000000000000000000..67b939081a2847e478c08e9ed19fe7acac0368ba GIT binary patch literal 975 zcmZWo3rJH@7(QED+n|P>)w~O(9X1y-Ax7XwA z`{w)F?_<9E+yg$(Pk;ZQf8dw@$B=(;*gwPtz%Z*vNQ;7o*>cS83gQ1&EoLhTc4Ckh zA|yb<7QR;4LWU=+bS)}fRj5Qj69~P;v2xTQo zHD;y3Y&>Sx{X<5F$Y2SaO9UyGVKG85KVUV4DqR?rbI<1i2%!l2;jb47E(xJ1pd?C7 zs23l4DncaiL{BZzb{d+3lHd*i28OR?x``e;W^>`}1eYoZ1Yl3h@K+ilNpUjVIm|=K zcs*FKMLa3X2N0AMIvBZwiApT9BSCFq85LChUEqN*ODm3qfn(O_@~W(*v9<^!W;-pG zW@u-zbSVxcKo+49isapmtxiQUbf++Toquk(*1G9jeH1?iC>IyBfpS}<5qyd00gME- zCmsAGV11{lKaPaG*d6UiI*8pdAOLqcOAwN|(yO4Ua!4dVXe^51%lJQmq?KrzG+H@D zlqrvhU8B}(jaHc|m2M$NJR2Q3^5~qxsB0z9@^IXq?#_}7L;loN*;J~jwCTJuJZ`~| zsjO+DdP}w1iCr%WV>ft2vi`=5@i9`(K*CAesBq1pDVt}Rm698iO?!M*i!EtSvKIx6OhvnBFbxkVKgCTpa8X7~0hKMYx7`Hc9P8z-FlfTfdGPfyUN!Jw1W L353&p0^t1LN&cHS literal 0 HcmV?d00001 diff --git a/images/file_types/sql.gif b/images/file_types/sql.gif new file mode 100644 index 0000000000000000000000000000000000000000..f51298b3c52d48c58aafb6ed9167e487e99b2ab6 GIT binary patch literal 328 zcmZ?wbhEHb6krfwxXJ(mH#bS`pUKc)%y4XT&i=J&|IaWi@0NIdx#Y|;-Zw{eAD_;d zS;Mt;rrMQ5)t_(IJl(H$bUxet6B(y>7H*uP^y!T8r8UBH8~N_^$EqFEUY37N(?$6qd|URU`utFSm2=})$h?5 z)F7h88_@hoqa$kRvBtKxAEqho1_PHae!r$?_!% waWgQ~Gc+-j2nsRON;1fHce_Zjw$?GoO>viDVGy1>)wRueS=$m{S4ReG00OdNu>b%7 literal 0 HcmV?d00001 diff --git a/images/file_types/sql2.gif b/images/file_types/sql2.gif new file mode 100644 index 0000000000000000000000000000000000000000..adc157b2b2fef80e042cc10752d5272e867d660a GIT binary patch literal 610 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 VtJYSql($|*a@QGW26MFV%)ceL)4l)z literal 0 HcmV?d00001 diff --git a/images/file_types/txt.gif b/images/file_types/txt.gif new file mode 100644 index 0000000000000000000000000000000000000000..3a7f303fd724851d569765a9242b04bf7e911d71 GIT binary patch literal 972 zcmZWoe@s(X6h1921%sx$K7naQ%8!i37(&ecK*zGq!G_to%w%RPaYis^Z0Sr#jA4){ z)XpLhd=Ex+Mw3nkg2F(IQxK)RLMclyLY}}>3f)-W)lB$Xi*Y*|DeZ@@Q(fZq^4Y$e~dXJsy*<0@ZqQaGL8T`^Z zu-`gRQ0Xi@aK(M5 zZTP45-|1h+FI*jI{B5-L7T4jJyyko`_GFElT^pZUn|Lby;gKHBOOF<$$BWXm*FVGi zX9fRL(eLs37nTFwKLbn80>blv??qtwW$@Wb@WtQ3zgB}QYr$11ghs>z00syf5nG5@ z6v6*n4Mc3C#l$eL1W| zOT;XB2O&F>)vVJIF(#bNBSQXv$g~I1Iq|8RAE1| zbe5J(06L^J>-3Rca=3gsLLyJk_tN)IVpCMo(sziFk!yuHdfrXM8az8Kl_)|X+>;3M zl}1QLZN@vtd6-yPj}~r`$Hdkl2+Ph-2^*#aU175u6rF|5Tv+w*f)7MQp88&kPDH!M zpXzMNaBa2|agt?6upP_JQb#*63lK$_PYsSuQ)cYW5%CxKxqSxbz90K_@))ShX}FKd zgInzAxvJ|p64suWlCKxddy@OdaTwxDn=&0CzHKN#_e?he#Cj~9*fe=nr2yCn+vGC& zPe4p7q{pNsr>H77Y|i*B%WSYaV^d4`drZ{O`YoE$x6XsTap?3LWkqxPE)CS{KdQ>s zb~O#xL*jPR?u~EOBxK~X*444JyYV~UvzSILxe8m-H}}par@D*@xu0vz?t(?zmHcI_!R`gpyeT&IJ#UjaofmC*nI literal 0 HcmV?d00001 diff --git a/images/file_types/video.gif b/images/file_types/video.gif new file mode 100644 index 0000000000000000000000000000000000000000..5ddc738340f8febcab03b82862a51069692fe9f7 GIT binary patch literal 1041 zcmYLI4NMbf82-?9wvM>tQnF5QqtbxQi1WyPD9%aoXGq*c60`006X53NR$LtCma!uQ zYgLfWw0cBh&ZHBnnV}hE>yjZAXyJk;R%hxBH)&u%%h2A3%A0~nA%Ds(y>K@dh#SHS?1)|x4jHj@-(MnN$YP16j+U`CIQuo=r- zSQ)P^;k8r%Fp`c55wuWY_Rus(S}~FZw2h!#fbpsrw~pq)XQ%sW?R~Y5zAw*uY0mR? z>s6+;x8YpR{>I^Vj`D9?hTgXheqbFWJ#tl(zq;9f;FRy&iawBj;#4ANyd>|wULO_TJp~#&-7K0Ou!sOzwQ?YyD*zb|p{pncf z&lrSoY$iVbFg_8DPl@p`j6*2RJW4#6OGN)lz{d#*Mr0Yz{*#=Ol8@(;e=j7Z#pFUv zMuCfZhjO20RjEUH2p10mpg_MVY~2CCiY2}orEl%247PM9ZX=3Pg$4lpL7P2R13)$^ z?Z;KPScpg}oxQSLM2S7pYrlH}7gb1UkA03fs-$|OllI6kF52v()*kyRO_tG_cobQK z<)TA*7!8Kzai~*z6iXJR4)qyCmSo&$bj@fqyZX{FNV8U73NESsU(iKx@u9}uh=7aE za6HHF+QhGQ;^K(UcN5w9d=r{)E72|hD>csBLwCkBPUIfM#g5cBfyMs!*n#ZS7)ZIJ z^)6CU>zoKX6&^Ga>g{MKU-p|h5&REe#bzApM{ADYFdqpJ!ei|KsKb^nWU5m9b&#M_5D7v6D(w<`s^hJ5=_Jv!ngkywhY=?Q8-@tmm62Pd=Sh t*j42(HoyFQ)~26}w1IW692)5~jhF9k#y6DIH$A#|{FEl|O@#t%`4|1%>rMaw literal 0 HcmV?d00001 diff --git a/images/file_types/viewlet.gif b/images/file_types/viewlet.gif new file mode 100644 index 0000000000000000000000000000000000000000..5de854bb359ac0260307072202ada9bc24739182 GIT binary patch literal 1052 zcmYk5e^Aq99LJv_=4OH8>yE%sH>VN^2IU#U;G9X5#mu&;$=Wb)b*nr74MlF79W57UQ-v9}i4nBRUyROf@|9GC~^ZLBs&pmg~ z=`%55EG`vM(HcUJ(mWc{NYV^S>z5?ib&C9kNAp)+EjRW8f{rY&=IjHFJeDLsy7|uA z;=*_2WzoM1Kay5_+HmiBN9{#fU3yP}uda|}DBouk`aUja^jCxqRK3wx#Tuv#*WU{t zuVemJ7dlkO8oM7cahEh(|B~Y;isON&P2{`$khIZ6f);mZ-wAD=AbZi(N(DL9*|yL2 zOQb`}m}>}dGzL#Ju<+L7Pn+m#ve%sQAVn?5(iGeEAbn7hY7l2Ew6ksP33hqHviu}; zV$kIcJwBk?3!43)JOteV(0c?_LD0tpjX!7)f%Y({13-HOdS7>`BH?ia=z~BX48zC4 z7zV$yaK%Z7^30RYbL+ivFc81gowTM-T{ns> zYe`MM-7eMVRUrVY&KggmQ7r->KK6)1uXT$ie__Jx4EK&RUP~_ z4C`ayw9L=Xg9{ftPZJFD1T!5pgfPdHU`CDMd;#twc%DfDK*$|)q#&a8&tdWQ&j6r! z`{iQF7Ejaxb2Wy?7E?-y1+m5Os&R+sxPyM7UX9#lFVq{ip#K$yr(pO3jTc1-!>jEs zW|!!wY7b#zQYy7XoKiVS%SJ>Op>4D*PLIJ#%OY%z7+yz2V}C|h#X>8c-QoA;2h8RpAg!E5Gn3m!u%Qm5VBi;K!{?`5fh;dr#lHDG78!n z-1-xU>_lGVQ`VHT1+=4W^pRs?#vQ*Rc8#r@%)1@$;WdU5=XM7q(xYoax)ZD~GpY~1 z^>LyyxAq+SDhD&&au>xEE~<5***yQS{ej=H1+zO2zL{hmFp0Um?_-%eQ`uA|pXMD{ zwy3YoD=G>1&m5phUeFYH2Uqibw~6wE!G5_(v87Wzg7(b(W4Dq|3J&i|DW?XoTSB-I zKCg1-6nO>3OabGBBxgGQ_KxyvSs`6hn$p1c)M`nFe=N_f+|z4z_w?Bc))mvj{g5b9 wZj_MfhuD{jDybB=pl?u0f@q9P_SMmeSEi_fKd2lLXWxue{kTi(Lq#Y42F6M+F8}}l literal 0 HcmV?d00001 diff --git a/images/file_types/vsd.gif b/images/file_types/vsd.gif new file mode 100644 index 0000000000000000000000000000000000000000..aa77d3c1ec93b249b79915f6394715baa3fef639 GIT binary patch literal 1020 zcmZ?wbhEHb6krfw_|Cw4K{BB|YyOM*cUg!tMp9tZ|Bc;+k?hDEmxA z{xP@2r|#*`ywaZr<-85beVbDAD6#T$YR%`ozWqfL_7_duUp9Gf)zsb9Q}@(O-xb<$ zBC_#VMAOmOmLqX3M-n>@Cv_f9?K+g+eK51BJsp$DyKk@5- zhW|~o_O#C3*EZvJ*SzN)v%YoC{?@VJK=-0UJ&O)aTz+i&nv)aOU7Ne{?8L<{CoK3r zY0>w^+b*u$eP!{MAL|d?*nIfbffN6)-28v((XT6ye_ea>>-y8*H=q5!{rvZx7r*bl z{Qcn7pNDV$JbwG<>AOGA-v4>=;qR-De_wz4`}XtS_h0^g`1bG9_kUl0{QLUz-}hht ze*XRs1R_igyetQ<`#uITgx-oUFfdGHJt0+WAoTXZMAkqN_7xLZbA{en0L6vg+CXH( zpBM$Z*g0*BYFt7lnp0&6zFff7H20*S?_e|sjsRshE6&@2gCjl8?WpnPn<-7Y#L~0p;Na)>yXMdCrFV#CHwLs|IjYEgd0D_y~E*{DF+om&H$=(nR*qdGFf^75V^8!1v&|+cS&6U z#J*Q;SKx?&>4ngHmAg45Lhp5e3V`U@gT)LCoX?sz04-Jg$->CMV9THb(g?~E3>-HZ zsyU{7I?^em;@Gkc_}O)?Kp zN)?zUcw)g7jU~Q)(iTzM3Juy8o|nyaad>#)7_&E9(xHZ*k5XAX&7=$xF3fb$ysW3~ zu;I>yv{UVxxqJOtl1;_}hDJsvCWZpV*{s~W?QUzTG}kQsBr3_*siW#W>y4-Y>Cq^wRB~E X1uSGb+9_n3cBJBA6Ki(^1A{dHjc3q* literal 0 HcmV?d00001 diff --git a/images/file_types/xls.gif b/images/file_types/xls.gif new file mode 100644 index 0000000000000000000000000000000000000000..8da8f410460eb7daa3c4e5c0140d7514e41034ab GIT binary patch literal 1028 zcmZ?wbhEHb6krfw_|Cv!>M3R7D{1O0VG<;65-e^KA!-sQY!WA88Yg9($#0y?X97gY zf+i_KX35gl8FE(na>fO`mIZQF<&rk#@^;m7Mj0B`g-TYn>h`sY33V|}4XSP}I_|By zPMxOi9oD`*X8wJa9+T{YCp!d9_lTI`9yQA|VqQ?(jL`Un!O@E%3TEdfE(%Fp5Rtk( zI(=1q%8G={wMkiPQgb(?mn<#F>)T&6VSmxY{biH)R!!YqJ#|mr^j)D1Cn6ht)UHG6-3K#!4(9Y7_|NdaY1W?Bx%=8zwocgFJ!ix0g`4MhEI81; z=upq1Llc)Do4)4cgmu^EZalkq+r^c;uS`DFH~C2Kv?CK|9hUp^ZymUJ;LNSl z=kK1s_UPLEm-jC{`gP^;uWL_!U4Q!f=Cj|opZ~t|;`hCmzn{K&`ry@{hj0Eoe*5R? zyFbs~|9SD@@2ii0Uw``h_VeHOU;cjh_V3g8e_ww5`}*_W_h0{h{{9aHLT_yt7#Jq9 zo>00fA@q)8B5R-syNA%*2qZxjh~PxlT%mUv3=Awlsb?)N3=B+QwgHeEKB41E_r|{$ zfJ`8lMTCJt==F>VzjGKE6o5iXR~3ZbSpXRz?uS(nK_F+s^F0%;wgD{#s+{n<5Xc6K zfb_n7GU53Np?5tK-bqaOUBUSOKhR8}w?L~IAc7NF3xF;Mx@RIMNIejMt?-!0ISI%B za)sW_x%-yy?puYVWhWRI4phdWnf?yS#aa%r3WGlfGV#Dy;}kbwD%5&W2PKb067Dw(q-yZpvq+F z1wiD=vK8ngpxz~Q0T2scwOxTD1|}Av_bPXDN`&6)02Khyvj>YA7&xCbZ2(%T_>+Z^ zfx(?Y2c!{{Cm1*$GW2siNm|M!su?&><>p4`1&5WRzdSiueB4jg%KDp$jgfL&w}_fs z!;1|I56$V3vV0eLvM80sQp5Dz4yNWKa{3wp27vv zSSS&V%y-^{j8^#M>m=)BKk(pL{335m5hW142=v7mzJ0Yw{!P&PXWmO@5FYb8PDjIlhT6E;K%N>s4}Y`t#vnv%3QWgEat(BGEno literal 0 HcmV?d00001 diff --git a/images/file_types/xml.gif b/images/file_types/xml.gif new file mode 100644 index 0000000000000000000000000000000000000000..e40ecfa23394e708585d69d65a960868f78f5946 GIT binary patch literal 970 zcmZWo3rJI86h50@8|FQmsb1^}wn7hf41qE^Rq_=I;7qwvE}HsW}~+bKAG%wx^k_=_jn)PFl7X zwCyZx%PeAo84!eE&iR(S3lB_Yrl7)7SYau?)K1rQmeh8Z*4fTn?K*eu5q16fh5E;p zP0udhV{b4oZdqS-zxT3E@AE#d{e$?@CBE(#-+UC`ei9u6zIU9@$@@MCK9}3~(c>TZ z;{W{B&wul~zxzEu178ON-+u*u3qC>nK zKn8M7>Xmc!1Vg+hI^O*&+#=&BfMUWaP)s<6y|W4~kHH`u%Qg#*2cxAFfN&gprvX#@ zxJ4TIF^ybD_>uP2HxM(5Ci`k90>W!hNh@tyeKX-5v~sOxcrC5mLxg$K4*bG!HI7Oq zf+|!^CVc8YWI~t>wcxoFSaRhq0l@hYCt<49Gi*V<--8fS1^SWCXDKlgD@esrYrbVjok*E4wsGfW@MI|NfLyU~n%lA?JHX;<_*(otg77XH^gqN-~OwvjN-Z{=gt0g_I zkVP`BEAJ#DGZCYszR4ys)LFLwoW{jnZMjQ!yOP(-c zzkz)APk_9NxD$Fa_Y-#_3NT!bN&vMh=O&uQ4k=^+VP}&)xb1WLTsGE6d5xs zB}uO{Th;Md+*3N@;i!qj4$UdkmRCM3o;=sc#bzgT6-`~aE3vELc-QHXqs(RJO8Q2O zpS?Rj45j498n-cffksh&ww-b`Q1&CF(E^W@55Du6awgi;+jNu`1X FzX3rGqF?|3 literal 0 HcmV?d00001 diff --git a/images/file_types/zip.gif b/images/file_types/zip.gif new file mode 100644 index 0000000000000000000000000000000000000000..addcd22826f2c3027dbd283dbc35e078f9f96c76 GIT binary patch literal 1005 zcmZ8g4NO~A6u#{$t1e)#EnO6prON<+e(7EP(tEm<$}8yj;Bw7Xq$!Wp zmdAEgC0#rbr>lMR;_#1fl`mwbOY+(7k{=noIDd z3YvQ`VXd0xmO<+2$f4K;sf2$~<=rTn4=M5gPNMu8{9=G1hHlrCSt+PL20#n|=~Lzv z=+;e{TZBN!m!1ZI2PwrrY&bUS^; zy2uK^_mXOa5JnkK39vcn#=BUMYVZ*cc}Y_Q;+D~mCq{f@w$eq5gSbY!O#qniF6zkWfaqeRcqym5yL~?di5B#~MU^M|_H%=^K&O||hc zafv%+XW0Jb#yrb6FZYz#W(QO+!Ck{$8O9qXf}edkySb?H1~+lXpJk=|4xYX&%;1IrPxJfLbi%8~|M(*V;zowt@#W#tI5no(6Xw7k5GuJ%S` z;tLQI6L~>Lg|+}f=tpR7K$awkWd#)quc>tq>fq=P!0_pv^xP-1l`9!} zk%6oLEW^PD9s~>&RfixdD3)bZJM4_6sIA+3`A+XVMEVfQc0+*~}^apzDe zo9U({=cvw5Ij#El^nk3RglU@F8XO$FAB{%Mv9U3e<^wy|{w1>WR#6whSS$v=-*4pu z_>cGv5DtfDR##V-c%DaneSKShe}DU)06&KroSmITAP}(ffjtB0eV>?{n}g5ivwFPT z+uLi~^)*_9_%nKXdT!9)HLBp$)YQmsfbRkf0FhW@DE%SK^Z)<=07*qoM6N<$fn+a literal 0 HcmV?d00001 diff --git a/images/folder.gif b/images/folder.gif new file mode 100644 index 0000000000000000000000000000000000000000..8c7515eb98401657d5eaafcb6d2e97383eb7fb44 GIT binary patch literal 601 zcmZ?wbhEHb6k!lzc*Xz%|NsBb%$PH0&as6HkF~ZguB)3J9i1H%l;GtRR#MU&8JS>a zW^QU~W@2J$Y-}=f&C}UypUqzTbl!&N^MPpN^Tk_UEZY2h>9&{4w!K`jy!4zFxEE)%yLf*YA6^@xbd%2VZYF{ASycw_6Xt*?I)X zc(dc!+g&H#?K=K;_lb9VPrW;E=KcQD?+%`QfB5`|!{^^0x$xo0`S(XJd^mRT!->lu zk6-$5>dMEn*FWt!^=|)}cL&eCKYRhG=Kax&A5LERc;fPh)7L(oy87|-wU0qlu2-&k zF!#_46B83)uz-VywuItO7Dfh!I0hY%HJ~_QVBgjd*VNq7+ScCD&dSNr%gNo@%);g8 z7w#Rz#xyaGJ!WdCr;8&e`?BTCE%W>$ebfT%EX_@g^`)YCR?UwHaZ$FlFflfe5ZkfK zFErT6LCIWBMpsGkGZ;4WX+|tG(S+?X1iU^|XZr9?zZc?doj*%2-NE zMDXKkU-y;?4;Y<}wTWqJ{rFwtxO1W~c@MjSbJ@#B3sRcd Qc!G+ePHuX7%7MWe09YRtpa1{> literal 0 HcmV?d00001 diff --git a/images/folder_new.gif b/images/folder_new.gif new file mode 100644 index 0000000000000000000000000000000000000000..5f30cfa2ea68ba6483fdccd5a76c31cca74ec4bb GIT binary patch literal 360 zcmZ?wbhEHb6krfwxT?Pe5P&BoJCdA01Z zo^;&3Zku_-4vPs#U0?5Ud%ex}^(OPz#{!Gi=)PWO^yl50|6ssCC7}3|g^__lok0g= z9>`A&Y^?__pL8+d>_4$M)5J(^qU))pKScD5XLl}*d?Vy8*V%SCq{F3Y%YxenMHoLg zF>a94o6T@Bou8@I!!1sUy@ivLkEOv`iMy9wRh5mAk(skgO-Zz9!X#!64!-G1{1YZl u;o#vBm@dyUXE_fS7w;Mtets5CK|TRqUcoiBhZK(-ogQ~0B=oc+gEasNMuxZm literal 0 HcmV?d00001 diff --git a/images/folder_new_sibling.gif b/images/folder_new_sibling.gif new file mode 100644 index 0000000000000000000000000000000000000000..5f30cfa2ea68ba6483fdccd5a76c31cca74ec4bb GIT binary patch literal 360 zcmZ?wbhEHb6krfwxT?Pe5P&BoJCdA01Z zo^;&3Zku_-4vPs#U0?5Ud%ex}^(OPz#{!Gi=)PWO^yl50|6ssCC7}3|g^__lok0g= z9>`A&Y^?__pL8+d>_4$M)5J(^qU))pKScD5XLl}*d?Vy8*V%SCq{F3Y%YxenMHoLg zF>a94o6T@Bou8@I!!1sUy@ivLkEOv`iMy9wRh5mAk(skgO-Zz9!X#!64!-G1{1YZl u;o#vBm@dyUXE_fS7w;Mtets5CK|TRqUcoiBhZK(-ogQ~0B=oc+gEasNMuxZm literal 0 HcmV?d00001 diff --git a/images/folder_new_sub.gif b/images/folder_new_sub.gif new file mode 100644 index 0000000000000000000000000000000000000000..32be15807c905582fcdad0720e49d95d0d66ace3 GIT binary patch literal 387 zcmZ?wbhEHb6krfwSgOTPv_`jTvvJ)v^M)N3Eqkmx57>7faOgkcGU2G}>vcwxj=N7i z>G67#`Lt7B(@%T9-e&uHkK5~Gfv?XSzCD-u`fTOv^WCp6_PoA4J(X} zH=V0>)0$Ov{#P@jXSpa%+VEu7-FaRwCV7NNCF^SDu{hP+NN|2C?|;?qB&S>%A*k7= zp`aiuEiI+ktR<+H-PtWADJeHWP^Gi0S5iWYfgwdedFp~iT72Bz%T-jA73Jk*7W4ijMlo5l|^jhTU!fs=s)Xqw0Z1tuoTd1{<9RJHdN=$v0` zy)K^vtk4^xkOh~*==Dhug-{b=3WXOa^ey4wSiW+Z`h-RKn&-E?>{Db={8t^4ky)&e zn3tKGSdyBe;O^$MU}whwS0ghmWB_ZQ80mV%r)Hl?%LR z+aIyfVzm3xM|rFu9b z>|tSGVPFSh1~xv21Nj~zVjU;$2hLz&VMHil21;{+l?WWTzg|J-AA|GaO&%^Ia}aj%0o8CwNYx*8Vysj# zLox=*#Xuzy68{+XI|+K4a3LvS1-cf58Q6FZ%>NM1z|iKL%qYpip!lykBqOs}Au%sA zH?br&MZw+EO(88aCsm<5u~;EFKd-o?s5BWQQCggtm#z?+lbQ%oc`_930^68^T}> E0B!h}9RL6T literal 0 HcmV?d00001 diff --git a/images/forum/3.gif b/images/forum/3.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe5e522b15588682a59ae074ce128078b719518f GIT binary patch literal 112 zcmZ?wbhEHb6lM@)SjYeZ|NjFK2q^w!VPs%nV$cDJg5(*P9H;biaKDsD^3Dkq5a}!8 zT2pXC%;)At-K1ZwD&f-({XF>O_0_v8ycTkZ{7rC?U)6EurI)B8Yi9MH4lmxVuT~`R IvNBi$0CT@8GXMYp literal 0 HcmV?d00001 diff --git a/images/forum/30.gif b/images/forum/30.gif new file mode 100644 index 0000000000000000000000000000000000000000..10063690f8cc0f694252c676786dd34f4110e95e GIT binary patch literal 379 zcmZ?wbhEHb$MU}whwS0gZqn%-qEE%w#UEkc`Y?g|y6^RE6@yVuj?Q)Wnk16ovB4 zk_-h$s3HY-Pd9}y1p_@xL$0Jsg`mvzj1q;Cdv=W72ppoT?MX3s&dC7VVia%Kx zWf_$E$G2qG*XgSlV_kt@4fMUD=Cs@1Bxd zYTIAQ9lDd*>l8%xL7A2A8k#ZSao3b5cwJO`A?j-8UYB(9p{09dEgVA8s zHSiZ;I-WQ0^zD4kd48Sq-M)3>@|F9W*u-!Ae*^>!0th067`R4)0tX2skwVUD6d^%_ z0*WZ11`RaPLJ!7Z#0WECNHCzlL?D6@ikL7D8B}m05y?nJPRRTWDKt@tVw9pL8qtha z^n{fTV-#Z=SfvG-&ooB#u)n#)^+XO6OftnB%AQ;nH7u~m5^LCClP&hp?hP>FOxn7q zp~Pe$gBi-0bRLGJl9P!{W-4=1&dF3tO%}44rL4(DHnWvI=|nARl;e!B6s}h7561WUu9h2|IC*liO_eCz3Tubsk9KDlWM<*`p zTITK3maN!9t;zI8H>l^`>R+e-9y|b3oQL1~PhjIV=Fstt%J)w;X6whNz3Tmu4h9SCEo{ZY z9mr~&C(r8N{ASMA%zyjpm9yt=ZDJFD@c$7IFbE)s5MtmO2?`t}kVFbOt5Jjm4GJit zgc>x^L<>C_gApUlgdxFz0uzAW zfedCSW72sTl1fe{GMTB&NjWD|DK%NhVwSQd8`;cO_M{WFs8NnH!cw#XwI4VRJGU|W zSu&SU_rWwq^nm+Z(t4wUiYlo_4K>wLkGv%tb?;1hMRw^%Oa&@fp^7Q*&DK55sYE3! zRXHV;9Enk<3RSF9)l{RJ)vBKI<{h5-XWDCbawf-gpo1OinD#9=PAlScqLZEKoEBPK zGBsW3Vwbw68{Opy{wYnVOzH!45e-IxQvK0o=@u`q-Vg)$iTp$_>+Z|hk=Pf z2gn9$;Adc9(wV|L^Y|pjhe--im9O?Pw=bgUe#+r=D zfh7{G2m_cISQt2gn1P9#N1)QI`9hybv1SNDo{52(ft!I1C@7}ty+3ECb%Fsc!uVLl3CuH400K9J-2|5hc0N8YoGynhq literal 0 HcmV?d00001 diff --git a/images/forum/56.gif b/images/forum/56.gif new file mode 100644 index 0000000000000000000000000000000000000000..4a943eea25a0a9dbfc4fc40107ab43d630cd2cca GIT binary patch literal 341 zcmZ?wbhEHbU+MM?PJCwKS?hm^?kej z-U>~8q69I53uFW{11AtN0gYgj5IFN%!47OXSc-*#1*n1@D8*Y*xaW_-sg(^|wKHmw zG$T}SOYCC2*MIWn66P{Sun9&W!&!kQurhD}P2l9%zUv>)K809jWb;{}=5t9focd^R z>gKe?!pPDf>)Ak>E7({6Gw?bt2ekU1taDLnVo7R>LQqI6yBM~Cow)uQi!U|st97$H)aE#My!3EMRfgkfh~|tOyx4pbRTmhAl*?YzMLwD_n{d zBo%?s!~&OLi%~P1-3>8M4&(xm6x603lS(Zln~-Eej4GwjWT0-CBL+7P>>(sKOc83U Yh(@>p$>AUw4kYuCd^@FB1L!4d0OnId*8l(j literal 0 HcmV?d00001 diff --git a/images/forum/frown.gif b/images/forum/frown.gif new file mode 100644 index 0000000000000000000000000000000000000000..b99883b6b0b31df98f6bef2ff8d517496f3aa3ef GIT binary patch literal 878 zcmX9-O{>mv5ItTN&t2$lC|Qk-C=1zmxv67AC`;?1WTPaKGyH=n@LVs7rj z*SPWtY-BagFTd5({AbSF%yaYBjmuZ=Z(|#O@c$7IFbE)s5MtmO2?`t}kVFbOt5Jjm z4GJitgc>x^L<>C_gApUlgdxFz0uzAWfedCSW72sTl1fe{GMTB&NjWD|DK%NhVwSQd8`;cO_M{WFs8NnH!cw#XwI4VR zJGU|WSu&SU_rWwq^nm+Z(t4wUiYlo_4K>wLkGv%tb?;1hMRw^%Oa&@fp^7Q*&DK55 zsYE3!RXHV;9Enk<3RSF9)l{RJ)vBKI<{h5-XWDCbawf-gpo1OinD#9=PAlScqLZEK zoEBPKGBsW3Vwbw68{Opy|b9n9A8P37LJ&7Ao5{lmfaH#l|s z`Q-~g_upZ2_3O(=_iw&EcJ0Nlv%f#@e>`*P%l?n2yL+Ev=fUpDXAf_@KlkL`<0}^r M?!MkSau_@R0I@NALI3~& literal 0 HcmV?d00001 diff --git a/images/forum/happy.gif b/images/forum/happy.gif new file mode 100644 index 0000000000000000000000000000000000000000..fc136ce4d730eb88276f70e2b8bc4fa0cc4108c6 GIT binary patch literal 880 zcmX9-t%{am5Iut76O={JVsR{DFc=j75YCoe6%0n(Y!GZ3#AG;v3WA7u1%tsW@B&6y zaDz9YEZQw%8qb4I`j~g-{LOs3cXqB_-`m0#{^0*3AYc$c5Fy0CH4+p!NFa$6a#o`V z2^thoLB!v>pdv4?hVfDvcX z);$d+CIcDFP{yS5FeH_nOk^@snUiu(rc!FMki{%zO*XQbt?WrBYEh#cXN09_1!_NV z9CmJF_OoOzq3(lejOYRPxuo?*1r=3NjT&mIr5<@pHtOD)@`~)zjhG5lutF76-kYs^ znp25NR;qGJC^-_NP8F(HrK+h$HLF!U<;^=h^Ut)`?&M64=|BfN)G_T_aGX}e=|m?x z)j2J+xMXU&(8VrwO*gvPt?p@GlNVx7GwYPVWeDq-d^bK3U%7lJ?~ckI`{YJ5t!m4{MLU0n|qjJr#6*eUTo&{;n(-yw_oDS z-KWgwy?-{AK7`?SBs09}j-VvjL)nLdA?ZI-Kos=tr7 z%%-!trLD2!?eg#R_SxRx;^X80|Ns5{{rdX)_V)Ji@$u;B=;h_*+}zyQ*x1t2(#Xij zxVX6g|Nj6000000A^8LW0012TEC2ui01yBY000GuU?+}bX`X0W5^U?TW1%!rZP$%b zbcAyMHU&#ilK^c2mQ8XnIGr^LMp7#EHkEC7-hfDn!s85kJ@EC~UZpcerd8wdm)1qcfq RtgWmJ2s4u8dIDP5iC|&3D2NB}04oHw z67d8oIYAn+TIa*BdhBm^{_Vc~yL-Dg4!5z5KluL$2p9wqLMNKrK z8Lj9ED;>rt#x$@>3o@T+jOby1bBXJT9444#iaC@$xh!f}V38%(u)!u{4?#fJ2{hMI?%xmbxivf9H$j=I?>5a zbxsQ{E}5Dxbg@fa(~WL+t9#nl5Xns&%4#XF8)2U1yfvw-}+BrbAUO!v#I=aw3+iKUta#Y^9&ab zp6p)xef$EOn;*YEKHPtP?)KA@E2nRdUtPZbe&@xH*n4^D{-bAaPTzfgefHtEdmq04 Ld~me2gPY+32ek_od=)lW8dBR+x>PQ-ru>kzm0AD!T(13QPnd7@>#>^N>LWClZm2ROE!r&yYeBg(yZTYN8R% zXhlz0=`cnyrh!#jkoinwL=XF$OI%OnFu^2K%%SYbWl_Tdi!8B*4K~?g5AEInBhI9) zdm2hi1~QnTj7jHVNGds*$YiE6C*_<>rPO30i&@H=Y-BTA*^^GxqDDE+2usll)PCSN z?A*reXUSYb-3QYc(F5*tN$ZUYDypO!HPlo~J@S@p)V(w171^a5F%_s_g({}JH(U2K zrxKN{ROOUVawJBbDpavbRa1>>R;zl-n|FBTpJ}h%$(bC}fev=4W7@aiIIW1&iB5K^ zb6RL|$<%bAi(TrPZgjI--P67%FT|c^)+vF@5Y{pIZhRuXfOTKwa>=zM@59kM*>!Z{ zvaV&`K5fa0E!3J!Z*+rt-mU(1@$ab(nBpq@)_($f=}E?>}!}zo22|zK`F2 zEM2>C+2&olF5X>x;@YfTr(eGP@czrUrd9iI-h1%s)$5NRKknGETp8sILKo(H^$->CMpv#~G(hc$x1KYd<(+WIvr20>!6duj7n(6GwaBx-B>W+p5 zTe)Jag{QbA^BQPh73OZvieJON>}yHWmcV(dvK0L1>*R7PXtmU6mvks{Ye=?esWSMe zv$2(E$!qXwX{t`}Ve4z*6H(Drnak_bqS~UVsl0BT%1Z5i)peXa8Z7)9xqMU(t}EZo oz|0`%qr|>(=Wc#>1};`@T_#o;wOhAuNwY>f`9Hd<>&RdY0GjikP5=M^ literal 0 HcmV?d00001 diff --git a/images/forum/tongue.gif b/images/forum/tongue.gif new file mode 100644 index 0000000000000000000000000000000000000000..569d95c39336fc6f987308370dec7212b1d314f0 GIT binary patch literal 881 zcmX9-Pm9lS5Pp=h4nk3K8W$-J4vJl=$HhjQ*D!l0CGGhU4lY_=!L(f5oi%F1!D~?D z6}ZT0JfD0{zvesh{G0i0-@S8VbN3`p;t&2m0s;mB1Q9|ETq8k&g9MUDA!jv;kf1>U zMU+s32AXK02V*c|gqbiT7*Jp$5WxsVOqhoZDmamdWTYY|WPXMenkYmuN>LMyXhth~ z!b*oRiZKnW(t^xq8Y6nx-(2E)B8LeknPLuQPcDlZ7FcA7HEgiS7JF#-1{iTBZQavQ zVlt4y3}s9@4?|MP$wVeIl{qQrWGba53t7xk)?_1_*~*@Dq82sEaYk5*R-pC+$6@C- zWQteMRjQh5RI^&uQ{KG8GyhC`?M}|*m=1KXLmkt;1;=ScoKAGIQ=QX7 zi%X`a3tj9|*L0(s-Rhq9HF+WSG_y_#T!yfY$#>%u@dd2=B9}|9C3zo?-pQ_`6PI-@ z^Y&>=R&1fxWO}0;)bnojuM2;V9)T&Yz;FF0uyG%A{LDt>r>7fp_ScvFAGe?5{JkeP zuKqlHiH)rfUmxvmzc_Jg@7JZ@2ZwJiUf0gXJ-) N_3_Iecg~%{&3`WVemwvH literal 0 HcmV?d00001 diff --git a/images/forum/topic_stick.gif b/images/forum/topic_stick.gif new file mode 100644 index 0000000000000000000000000000000000000000..e6d6bf624377d6e48dc7c6acb1aeb8df40a854a9 GIT binary patch literal 442 zcmZ?wbhEHb6lM@&xN5|3_{L+#xE{@tnWnW1TV^i#{QcMV9XlVsdN*^~+Vc;dM^4=w z*E#>6fPh8g(l32|KWuIPDk!|Wd-v7HZ?8|De7s@9=bD;N4C;q3=IGO0|}yl;!hSv1_mPr9gu2}pBUJ#JIoBw2;^is z(YVSWhs#$xSjm;u!Pif5Wr@eueGi;FpQvcASj(%zwy7rm@xu%TKf|Po^RKTKaWXim zD#yiVSMXI=YjGI~X~%W)RqzPv8%)#CW>x0W?bH`6moZqfM1z?@OJ&}CQA6RS%T%>i zP1`6exm0hrMw7DY#-&QTkLak^vuYpNEum=0FRQ1cqrt}LqoSpAq@sfV(lPD`BTjAg z%Q{l>hTz`^$BrEx+7!+sqjHxq#k4Go7`wOJHpl9nE9 LU<=v=^rkfc81u3e literal 0 HcmV?d00001 diff --git a/images/forum/wink.gif b/images/forum/wink.gif new file mode 100644 index 0000000000000000000000000000000000000000..a86e6db6d5f817d974d66bf1c12dee388c5ad0ec GIT binary patch literal 150 zcmZ?wbhEHb=@u`q-Vg)2on0q!kPl4b$}SC zfuDha$)d$}=J5%P50jLmDzhqr__uI6nRYA@?pkb>_ax%j#@n`C%P+Q~zf+eE9JF`}bLUFI;-`N;YrCix)4hUcGwj)~$Q@?mc?+==t;KU%q_#{rk6O+1$sE zAD=sS?(*f!*RNl{ef#!<2M?Y+dGhk*%eQaee*F0H+qZ9j{`{$0b!gqeGslk~KXc~H zg$oz1T)A@d=FPiz?>>C^@Y%Cx@87@w{Q2|oTTiqr<{vq7nczVP#`uWnpe^X6NAK;^yYz<>TjIZx#>~5@uMi zfLB6NP@qXlT1J+EaUCOrimIAay}E{`miAhvy&H`9jMYtqP0h^Lu3yJsBWYr5XJ_xQ zXD`zRS5;d!H+K)ulcyLACD?4eeSH0{?qg7PwKee%@V0xno`EOGL_IjfE%cr4?u4gD zP)x^&bLH>hd; z*jiSSd)sa=GrN2O$SQ%KGt-Hag56pQIy#T7I1}71uQ)Na^6 z(w603kf+fF1IRJDD9L&f{FcbmmbJ`auZ5k6YZsj_mlvSN?HH7B4bRsqYZ zCa!-z8HWX{v2To#SsqoC6&RRSr^rf;YB1{}5hcD_U5^LS1ruwTJ+d>I_OwpHN<~lt z4r4W~=$+$$md|yLC)ERy4326`sf#QxuStX;gi^)*PMel%+uhrz!3t0vtJlg*4FJLC*P3TWNq%(a>&^x3D0Bbn1a3OdJEWyR$HwR|FZdTQ=Eazz z#DF0SVoY50hLTh*#VOAcOq_3G7|H*e<8pa1RK zw>2kkYFEsE_TtscmoFDDUK|`8{Oi}R?>~QuXHR?f?Ai0@&p&+lke8SD^y$;ruV3%l zwJRql=j+$6FJ8WU^5luXzyE>-3%-B<{^-S13r)9a64s#YFq zYHITH^V_*|=keRm9zTBU>+AdW?b`#_AHR6KZbd@(Z-&<+&xa zRdsar_N<++FK=MTZKPvtVtRV7*?My=&P&G378W-wHLT>Vg>3E_G8^04UDva;mv@+N z;8kz*4}JtC%fG%-CeRrQ`xU?8F5*=a8|tDr#X{mFKv0E-nFO|SWv pYj9e4EL3D|ZD9=zu+%bW^%B(%TNN;K(#nYooZ5wSvqU%;tO28ue{ui- literal 0 HcmV?d00001 diff --git a/images/help4.gif b/images/help4.gif new file mode 100644 index 0000000000000000000000000000000000000000..94d8d7f885a509263620941ea5b7c62e36efd8dc GIT binary patch literal 67 zcmZ?wbhEHb6kyTf8}ZR|4|KF4oWWF Uxhd;k>aC83xfM)Lv_u)K0p61rzyJUM literal 0 HcmV?d00001 diff --git a/images/hidemenu.gif b/images/hidemenu.gif new file mode 100644 index 0000000000000000000000000000000000000000..a4918b5abca47c362280f1103ddaac27ae86543c GIT binary patch literal 270 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 literal 0 HcmV?d00001 diff --git a/images/home-blogs.png b/images/home-blogs.png new file mode 100644 index 0000000000000000000000000000000000000000..ba4fb173a370288041879c6965b80b93fdc987ff GIT binary patch literal 4948 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 literal 0 HcmV?d00001 diff --git a/images/home-blogs_sm.png b/images/home-blogs_sm.png new file mode 100644 index 0000000000000000000000000000000000000000..884afbe424797324e4eb04d3a821780eebaa00d6 GIT binary patch literal 770 zcmV+d1O5DoP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L03N{r03N{s!)a7g00007bV*G`2iXQ5 z5Hcp^I6$`m00M$ZL_t(I%cWCINK{c6J@>x%=Fe%=W>V83by6#uG{}e|16{O;pcVyg z3~bfbeT&xpNYE-FFlf^vk{~iQh<-#OC!tcIOdauOCXM-P-pqUV-mitDX=$4-T<+#O z=R4oI=L7!pu=(Z8xff$|U9p7e-m1XRxNNNTi$k@6CjpKYu@qnOG7&w~k_ufw}K#Pjh|Qx+g*cmLgb^kQWLLL&wpEazx|l3f|fj zY;O$twWsf9a~adZ*wiv?p+E&+M8OX!3AR+QCBYKEf08rC7WM^8FFABRIklL=U?i#x zo#zVvWyZV9zcuJWdU6hlX_4f|<9` zKRSzh{p04@V>RQ9h&TXROBfL`0LMormu~hv`y9S~rDey+L=3$H<5X7SnR6K2I#}(W zzt~b2@i_IXTP{c;B=uc)Ur!H2qSSGHAUC~~yiY{ojX~OW!-$BPd2y$+dnna$W5E2j zn7l?rp?`w@APBFd%!ONzK8!@;=?6qqy^Tu#3!PJVt=zW#E&u=k07*qoM6N<$f*?{+ Aa{vGU literal 0 HcmV?d00001 diff --git a/images/home-chat.png b/images/home-chat.png new file mode 100644 index 0000000000000000000000000000000000000000..76fe33a4f93797f526dc68e127b09d712b18284d GIT binary patch literal 3891 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 BaOnU5 literal 0 HcmV?d00001 diff --git a/images/home-chat_sm.png b/images/home-chat_sm.png new file mode 100644 index 0000000000000000000000000000000000000000..bbb968b5482300d4affe6f8946131f3f95300a07 GIT binary patch literal 601 zcmV-f0;c_mP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXQ5 z5HmZUB^@sS00G%aL_t(I%f*tvYg17a$3OQaO+rWuZCb5JNucUbuo$em=_Cc+GKh;y z{{WSOe}Q9n>mrm=2cbGCNQP)rwM4YKXso2zRI#l>T1*;W^4`72q3^|{MAv@d%RTpS zethq_2M!tvKt%j|5YRx#J1vhk5C_7*ch6^(QUv|Xs_hKVWD2wSm8~&Xo6x^Ho=nD< zC&ou^4faN}O{a(qOwBLnA1r(p$JO#bJkEcSn|E_-j;s58$=D5Ty(|7aJ()g5#fG{o zXaQqD3upt{2nx7-Hpx<9Gg10cxeD9~dh#26-Qj?x6t*K6X-O0?MsUquL4clUh+nma z(^LQq`OonaO-tv$D%!@eh&N4Qk*?KprLK(#L2ED2HR6#bLHvOei*%`Gfk-G&OC1|{ zJHJ?@KL*LTVz}Sp#374VSfMo>=~0Xx4e;dk8kJh3lsMeI)3ygy+eyx33ip=^o9B#3 z0N`S3h>7tL0CuW%9=%-U)3@#7WIAPu?jq^KbW#x5+ODl%pI%tc nzWFBlVh7q8d5ckjLD`*UXcyff$A zy}Mq=-mNPlbq9^lx#wff`^@vaA2SQlT3c&t?f+Ff@P~gyAG8xvO3_mXzVa!@aW?Wf z_q*@Dd(pFZ-gzf|_tjUay3hTIC!V08p`ni(#?{;Jef8_w;Cp{#&$b?eAKA3OH7^OKW5e&K}|_Epz^CEJD#8|dk$pY9re z@JH|b>5nd^>W#@*9la2%W-OHNd9&X?a^!dMxW&(W?z5jA{=r*sKJm`v{qX?0@4ox!@y8#hJvolN~ZK>l<#QSUYr^~1?f{d_)CNiLMdSlgeul?8aNxlIEuaS;cz_NcK72($%k`IEetCDh-JZz+((QCq6o!y& zp?=m(b$+y>+OA)}#@k&(fAy*`MPF$}+4H#|H%hTkLO z=Lz96Zn=IPUZ`5^ zCx;xGtvmFQFKD=K(O|7clU_kVroiqP4J0AWg58EwqiN*VTW{aC`+Kjx`WD8qM@L78 z;L{^-yz$1jF#MmaShr~QvNT(kvvcRpC-1uJE)BBRM;Md_A%yObHbqfHm~DMzu#hnI zgB`w~=Q-tghTr?dfJNKJYE&Ri*6`&`03Sli??B4bH$=Ctzk!A^F)=2sl?!Y4{rBGt zJ2vgwwQJ<|+i&LytgQ^*Re++B*@bfLH#ctF*iGIQP?k&umK#fz!B)&NFm4Q(CCxHs zz|3--W;E&u+P2QoV4+^^w|%xnh|Y8=b?DcSlW(j!bfMj*9PDNQ&3@10V*G9;(L4F8 zk}kK+n>X*+zJ2>pHQ-X2a<4&p44RJ>a6T3#dk9$6*Vm_?VYc}?pL4+JbBOL3t0PDi z)JpIy+)dy!6YUH>TBF-G+(-kStHItfx{l%~WS#LhiY3aU-S_WdvgE3Oa==+ySog}7 zgHRO=Sfb1t1L0X`<`{f6LHsO(W?AcT&;^JAr}QZVqfxl^`)*El&DN1|DwKpoOrIVb z1=Bvcfkz6g&dtuy;6P*BAN>AP`!;SIA1?&m_?iD17)Zq*Y};5iOdC@h#K8jUh)VZ~DbApRi_QojU}1~EjG zz<+H>I2ZKm_kU@>Am__rrs{&~cGmeQX-@p?;;N$p=J4gJ~kXHWlp)j%r{%mjmUT~{;326Gy*ksAx-yFvf}j8QB)7_WFTV7Rp4HgNHQDI(c^0KzpMnN4!&~PSfUFwgLbOguw=|)TkfDfTbA$XaJDP(uNlZ4ML3WT~>zEKPk>yfDL< zP>7{pBh~p%0D9wbVwVDk`-ye z>awB)8H%VvQu?^Xpsb$Bppqd-8vg0WkSD;!nJ9hJ08yNu0a9qcH*eXD6iSka#%Y|S z@aJrx&v8WJ$5WH%K3FkOtW*dKVttNf+Kp)XJVpc^Tem?o>XdSGY?I-IrMhgfX)I-F zSzXMkG?$rMaQ>sY*0lm%RO>$f z{PUm2y7I4473pm_y$WHkLWDIDQ|UmHD{~YYV3`y3xk*i&D?5momqJ0USWsFR@3ku- zE7g^GwZdV)WRRRz_?oTREzPTH+A`%ePz8IP_Bo8|t(eglUwm=rV~;)dS6Gj3udZ?K zI(JeKp5YMIh6lbr#peiW!}qoxNjjZf-;G%_ND*LEXx^v5-o4lh6$)og+D= zz!D#MvKb+Y0%Rsj-&70v(gb5OK~+Jb<;h23YIWQ0a;~GZS_swM>y19gDq=tPTC>+w zC|m~gnP;9kgvIuguf6u#zf{XEX4I~shLvS8pgVx_Dn%IyR{*`t0M-B{=?M^v*08cG zZI&w1M7M^vONq*ALQ0wThO}yO`Vf`UU_P3;IQ@%N0mUH6*|TTge(0fxzV^~fFCEqk z=`cb)TGtG5V9BK^3>Z+98-VRZXqk#K_-H9Z-t50s!C-t_c7NwtuA?hQ(u(;CTrca? z!+zJ-Y-zjA>Dl)uCT93IFD6z5ROc}>=zE6_9s1Xai3w^p=g2`0<=Rw(l)PA004He< zAbEFyrK|&#FcXn06Ipd0Ri$i8(rrNtr%8D>e`eL)lp>+L-7Sc@Vi`DEKwxlWimw@D)(Pgn$rm}b3 z#pflXRvvb;(08x7^!ueiIZ$wFsnr`u{Dh#B>Bv}Mx#XbWwd|fmt`D-5;DWInSs4wl zH8br;R+pV_CDoO$IaczOJuFsZYGbjCiM{zw=+1w1U4XKG##o-!$2QVzkeedgR=Qdd zBG*SQ*GO55W}c`tvSgQYD5sI0gx|fV%1TS=2`N*MtTy8oo+ItEW_(9w2t=)!b7!Y! z3?bLPiM>3~ax-gWe2`+S?3J!Rs~v=rt%8?qj$UI{3>v@-?4;#Jwl-aqxwHZ+DmInz z>uQYZe#L0dHmk~GJC8aSLInQn!YIp=_iKl0{glOA7ny}KRWoRrDHd2U!)>&bEB~yd zo8_4XLnZjihgluH64mdO!AthYxZBGyb>W$W6n2{IpXqf0%KpiFLxt7A`cY6{8Dm`&>JwVGOFo8neQswvy_vdwICaVyFt z9rdITu9z%H-Rx4>`tL8gd`A&sA)<4XR>qo z{=fZe8aJ**oxTBG!*Z!3>5JNc*o{?hnEEx}$+`P-Yx9Z*n41t31J2@Y^h* zJoaIHKv{%i=%c1~vn24-B=Vyq_Jb%0T2UBuvNUPpVk?fL`7j8ZbMx&Jt{&u1xCOfuPR&N4!zMOaO!`65xmdBA_Jj&|OMGmqO1A1K*8e_(K%> zQRKTFKk!;{l*|FZToOmEFbwCRqE-+@bG)Sl6ZnrUF^dbGw*)y6qp~#4xtjmCVG+>E z$s3J+Yh-jC4US?#9OD}~z6l_C5K;^fVc-@pxCq=jO~7bKQCTZo%+b zbiUb+;<%IMf>VND8XuPcxO2%QE1~qXDj4vdPCtW%TqD>hJDtw;k**g+!*9QK1nc2^ z-tDvk&-dHD=Z76s`5>l))H5H)Ned~&kK)wDD*>5pjij9Ydt5)RG5OPrd&*`p?;l+Ys#%;qb+>Qn+>^6Q z)ooJGEp6`Idc!QZLC?J?qrCp`lnjfo=8a{pory*-uOC}=;Zxn3tN;K1=Qe%l1G2az z$S;_|;n|He5GTpo-G!lpRn`N@;VkfoEM{Qf76xHPhFNnYfP(BLp1!W^H`(L_OvE4U zdCmxdPXec%0eI*@tg*24I64>oo8KYt^~cI$usbshedhc3*04s-*9r>mdKI;Vst0RHHX A6#xJL literal 0 HcmV?d00001 diff --git a/images/home-export_content.png b/images/home-export_content.png new file mode 100644 index 0000000000000000000000000000000000000000..0f0799061630cebd3fbcdb6b84defa4f1b51c6eb GIT binary patch literal 3484 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 KMNUMnLSTZ#d79+_ literal 0 HcmV?d00001 diff --git a/images/home-faq.png b/images/home-faq.png new file mode 100644 index 0000000000000000000000000000000000000000..1e82a64b1257ed7f1fd9108889ffbba12ac05ae9 GIT binary patch literal 4355 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 TEy1Y%00000NkvXXu0mjfR#Tl1 literal 0 HcmV?d00001 diff --git a/images/home-file_storage.png b/images/home-file_storage.png new file mode 100644 index 0000000000000000000000000000000000000000..73da12cb2a1a3de6685c86bfe6353a1ca15038c4 GIT binary patch literal 4581 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 literal 0 HcmV?d00001 diff --git a/images/home-glossary_sm.png b/images/home-glossary_sm.png new file mode 100644 index 0000000000000000000000000000000000000000..21aa211b64cbcecf6605a0ba63a5b78a2e83d94b GIT binary patch literal 801 zcmV++1K#|JP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L03N{r03N{s!)a7g00007bV*G`2iXQ5 z5H&N$q%xZT00N;&L_t(I%XN}XPZMDjh0i<8jAAVc(-8_x6e|)5A;HAN#=r)NYvV8Q zAGvkm#-)*jxS%n0!Gcr?9|V0d`g41!>=c?8VAC_}ah5=hQnhf)feOvXwklV42Jyeot#k)#IT*!O(^fKm$e zn!>B+yNF!YVaG3EYxM~4(^ZU&j9_qZ5X;NUmXvY>0E7_KM@L6L=JWYcrBrt;7K2}D zBIpp}ia?hx(9>&RYxM{Kkeo|^B$k$ztWKv>HBIxjAlX;1*Ozj++^s|+(QVr{+4}MnPsL*K_~76GH}6C- z_oN?3HHknf2n{ey4f#zUN)ei-LDMvZVb~TY?;v^O@bK`>#>U32v9Yo4fq?bRnVH#%L?RD`5EcASBz2NH0C=94TUc0Voj3X1_0Q{jo|kJj fn`M$0&-45OP6VZBgOw?100000NkvXXu0mjfhf`>P literal 0 HcmV?d00001 diff --git a/images/home-gradebook_sm.png b/images/home-gradebook_sm.png new file mode 100644 index 0000000000000000000000000000000000000000..0198969edf16af6a6d4a7d2ef25d4d7699bcfdf0 GIT binary patch literal 788 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 vA`S;E7VD&{W-}LVt{;GNX|PV@wQxfRAQ-FeWBO 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 XEEuf}FfKoD00000NkvXXu0mjfL``Y~ literal 0 HcmV?d00001 diff --git a/images/home-polls_sm.png b/images/home-polls_sm.png new file mode 100644 index 0000000000000000000000000000000000000000..fe00fa050a833fade14de445c554b21ff2266632 GIT binary patch literal 918 zcmV;H18Mw;P) z)5~v^RUF3g=bd?{7w4U(okA~6ODQu&AyTU)qaihDP=g^ZV5Kfhx?#trq@YHED?=nm z8X{F=Fli(%K&l}`AXu0%WX=(eO^E>Cfm*WCSBpMff|A0@P&0}b- z@vu|b_butF9--78p(rVogpgvCg!(S1p8cR&zh3^)8!?W*Wjq8aNzH zSpmoJP*=GgH>dKrnER?fW_*%Wj@zr0*Zui{_|PN3iC>TI+Zak*tPO9#w7Q3JJCC3a zV5jvo&RzX%snXq-j2JJCCKFaPcWV{5XYKvx54R5jK;Xmiqfw#6K%KP_V|Ed#`y;sT ztRPX@g88|-V2s`i>)nU8)Kx_5YbwyzXn{x#yz=&N6aWHTQcq{~E~`XS$nyzsS73cG z3QL6WP3FRQEO`2wAm>1$IgG-LK(wkHZA}qNaO(LBV0(k1a%AlSlq*pcu+U!h1V)QL zgVw}BN1A;dn&m(!J1{?U7yV-=mn2ht$Ch6^GqI93 ze+WNbT38m_O&H+JhxUC)WkQh%K3KqM@|PRK9ha^TE=rQe56?E8-@O!mOY_{s^E~~+ zrER?s(gWuj#5~m2gfP8eLrE$<02qDwZRYF#Q&Vl1x2yzvG^f0?x!E#`zrkO#K}6uf z191&uyJ(I-hT*FvIFc>{fH_#|IIB4w*PKo}@s=_t-U8qAV2))0#vr+bkRFJD&g~Ht zCg+jKWo*w;eE?u;o0&f2Z>pWiGPkB0;zk~!#wPeSgJuLNI7oO%#KI^Pr%}w^geyh& z?9h=#00@LWKC|erv2Np98pst8YG{N{93Bxo5>O($wzBNzVh zg|)61U)Dl=s^Xhk-THM1{5S%=@JIV|JKj0xDM_xYzV}>pVm|e*{jX;NfaR@ES)NqA sTB$w0a+2D0+Z&ca0*O=8=#zfKm;e9(07*qoM6N<$f`+80v;Y7A literal 0 HcmV?d00001 diff --git a/images/home-reading_list.png b/images/home-reading_list.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae1ab8c9a2fc9c9e3e99e63fd799682cff043e6 GIT binary patch literal 4825 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 literal 0 HcmV?d00001 diff --git a/images/home-reading_list_sm.png b/images/home-reading_list_sm.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f4dd7928cc5714e002fd2a6e8f2faac0073f00 GIT binary patch literal 593 zcmV-X0l6k>CGhc0FIuigU9U^L+D`6bSr zIEp(+L4eIgaZT(|{B!*DbrTYc1t0J9*MLJm+n zOEVloE20S^g6s|1rvjjuW1W$TV;&TbK2|slm=91)Q{X{Kg;c!Saglq7xo8`(>{A}G zw@`8gScq(adr(4@;>^%e<~7+uNMv&?8L0#%0yj~@DW&fPImKeyFHkRKuNERO3(SOB z?S)niZbFEN`0<|w+LjBKR#DU7F3d&rQGGY&Y)Fv0w002i=Jm_>x9jBX>wg3XCKK`E f>xbvJ_5i;DeEQXvyE?}U00000NkvXXu0mjfKqCEw literal 0 HcmV?d00001 diff --git a/images/home-site_map.png b/images/home-site_map.png new file mode 100644 index 0000000000000000000000000000000000000000..b1f059903d668e6f08a5c7dc8de626600b98cc00 GIT binary patch literal 4744 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 Z{|0DvGQ_~yE>Zvh002ovPDHLkV1hrWs%!uN literal 0 HcmV?d00001 diff --git a/images/home-tests_sm.png b/images/home-tests_sm.png new file mode 100644 index 0000000000000000000000000000000000000000..046811ed7a6ef16be1a54bb860e1f22c6dacdacf GIT binary patch literal 807 zcmV+?1K9kDP)iHtsh1EzPArg^Q zIZrOk#rNsfjaSbMAL;<4h;Z=jvu8dzyz8N&Nb7=z03ZUw?9z%8KQEa6yM5=kUnka& z3?FJk2}L7q>na=T#;<7U*P91xfF`;`6%pVgWgRy0?1ZryL@%z52=-!fGXWGEn4M351L4<+7eDgwo|moqXT+s1&Kmn>-uQQ8mL7XY)w5Zk*(g+<3Y3tmkR!bL zOUKaUtj_pX26sH+=Iorwu}MGd`_%O-_sS}8VpG#fJA)Fcs#ezwtZf?q?Ac70mDv`rVs{$od?VPKeqf<-kUjNtS6ecB*mq<&M97K^6IVsDO zt2$Ru!b+>2S<}_H>$RcInusU_8PMNdf(W{sNlJ3FkrwMJPeBPO#d}Y^a{9TH(#{Y) l0D?dWAV4eUJX#h`!2gmISk&ZKd4B)^002ovPDHLkV1g&sd|Lnj literal 0 HcmV?d00001 diff --git a/images/home-tile_search.png b/images/home-tile_search.png new file mode 100644 index 0000000000000000000000000000000000000000..671839a0c9cbba19672989c52e3e03f1390d7c76 GIT binary patch literal 3426 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 Hj11NQ?`AEC literal 0 HcmV?d00001 diff --git a/images/icon_view.png b/images/icon_view.png new file mode 100644 index 0000000000000000000000000000000000000000..f774b67aa59776e58b5a5b661e778c927493e37a GIT binary patch literal 546 zcmV+-0^R+IP)+DxC-nZF}4NJWop_6NZ=X z7cr#xR59$oxQOA)r%!OFgV-Q3m>dXf*qg`TXdJSg5oE*sO|9E@9$V(AZ=ep>bn(JX zh7}!G!Cr`{(Eg7u=WZ3XJ<+e)i^(TTmSOFV8J=0u^*|RqhhuaXAj^T&g7kpo7#(5- zLG*uYp}v37JFp8JXnVti46qGH51nQJ@j*1Y3y|eNYC(EHd~jS&TvWCVn4tcH kXb>NixUgYtY5@Wa00jYm!@7xu+5i9m07*qoM6N<$f@;I~!~g&Q literal 0 HcmV?d00001 diff --git a/images/index.html b/images/index.html new file mode 100644 index 000000000..945c9b46d --- /dev/null +++ b/images/index.html @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/images/lock.gif b/images/lock.gif new file mode 100644 index 0000000000000000000000000000000000000000..af1eda7f48a15447671019a71727317fbbb41fc6 GIT binary patch literal 266 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 LYHx3)$Y2csRmp~- literal 0 HcmV?d00001 diff --git a/images/move-down.gif b/images/move-down.gif new file mode 100644 index 0000000000000000000000000000000000000000..1e9beb88ff5e3e23b1af43364e58bee3959bb193 GIT binary patch literal 70 zcmZ?wbhEHb6ky#j0_CC3_2hl$P5M+6@x}jo>+}7XMbwMT4X4?Pe^rc=zXy3 V_Vi;JmFKc_jY6Jkw{tL90|4C(FZ=)i literal 0 HcmV?d00001 diff --git a/images/mswitch_plus.gif b/images/mswitch_plus.gif new file mode 100644 index 0000000000000000000000000000000000000000..84ac3532459419c05bf4d53206d14f3864524fde GIT binary patch literal 124 zcmV-?0E7QWNk%w1VG95Y0J9DNva+)O|NmiOVX3L9u&}VPv9YMAsI#-Po}QkZoSc-D zl&7bst*x!Eudo0A{{R30A^8LW000gEEC2ui01E&M000Cj5P=B}ZELvAIWb*GXq?a$ epecd{z?}>+K*bxazWynxO0029>=rm;j literal 0 HcmV?d00001 diff --git a/images/new.gif b/images/new.gif new file mode 100644 index 0000000000000000000000000000000000000000..09eac839424857563b51406ac87ef689add29c4b GIT binary patch literal 591 zcmV-V0vbDGB>gw(7?P;L9+uYqqimS-n z=IrzKSd_BJ$jC2voq(>!XlQ6Aa+z9-qs-0Cgt5qKYHGW?yL_y~khsmG#n!UR*qWM} z&(P3`qp`ZZzxw?B)z#I>$;tNl`*xbF%gf90@$uH!+12Fh&CSid)!}JrX_=Xs;q3C6 zzR{+prc8pJwb0(cz`$HwT;$~Bu&}V?<>lMk+xhwV{QUgp=H~tW{`U6vUY^b0|HZJw7-%XF326M2&u)prfQZusR3=Lkj|BouEB9v9O~B4hljr31&DmB*`wi z6U`GfIyW~43>ZQ!csipSy4|1~+B*dRf*1g1W?*<4X zf;1#}x8nv42nc29QlSDNjU5IIn2=!YhpiGArD+VIOBz7~1#&cqAWX;r4FwDkl*SPh zL5MkWARs_%NQMLjau#@ZqvfY;cQIq~XdnSqDh)h><}eULmrx#1IS>edDN_U!NpmE% zVWF!9tx$P@@Zew(ls6m@gk8&a>bm5e?Gb1(A1z0Jwb> d91tL(1A)f`I!K7%r7si~50EHfQZh;q06V?158eO( literal 0 HcmV?d00001 diff --git a/images/page_add.gif b/images/page_add.gif new file mode 100644 index 0000000000000000000000000000000000000000..f2274dabb0526169c401be59d2c1b5faf2f8ae4e GIT binary patch literal 377 zcmZ?wbhEHb6krfwSgOIGY3OX{8tUvF;~APAlvo{JFtK>jhN3mPRhy0LmK>&G2=;eo}Z@<3z`0dlTU;n8DbQpjDa$=v0}<*8H(yp@$6nLOylVD4&yxb(2Ze}OWmbQ*gUNITfX&Rhr3U!mGNUJhU zU(zk9r1vN@IjhN3mPRhy0LmK>&G2=;eo}Z@<3z`0fAqqo2P0`cDm@_>+Z^ zfx(_ZhXDvceqvx-bRgtafq+#1{v`&*f?-OXD-KTZk~0wLe4Jo3b)TyD!m1fI5?#$6 zELn?nk{&ZDw%mBT!seid&s+!j#zr}fYE4lMC8=@&-aeUDC&A7pdAaE_6PTSiJG*=P zc*SH?XKQk*HB6f^Q(Bd2&dTnEePYro8#LKguAV6)eL$JfiA|$n&4B}Ihc#IhHR4oN eE~_Z3$ub;az0JbH%*@2Z$jH#<{``d_gEau$=+L16 literal 0 HcmV?d00001 diff --git a/images/page_add_sub.gif b/images/page_add_sub.gif new file mode 100644 index 0000000000000000000000000000000000000000..555efeab87aeee699336ca209c3ee9516d50ecdc GIT binary patch literal 403 zcmZ?wbhEHb6krfwSgOh3>>cA7njMr_9bPc8c+!TVHM&)sjq8>isM}`Vu*0Hdk9EhI zE1d`IyAL?@Z@AL4`Aq*2mkCE*Cmna6y7Nl!?w3#zOUcI(gHyMM3VescWr->c6*JbL-z>D#YwK7RZE{phD}zy4DLDEG+9}lSr#psEaN0Dx=EvE tsT8Z2kO0p{W<`w{l~b&Oyj&a`S(q;~F)=c()Z}Dyz3aR|Q{9ol8UW^&*uDS& literal 0 HcmV?d00001 diff --git a/images/page_delete.gif b/images/page_delete.gif new file mode 100644 index 0000000000000000000000000000000000000000..6ab4546b91b659dbb3d329c516997e018275e3a1 GIT binary patch literal 387 zcmZ?wbhEHb6krfwSgOsSY3Te+SopD(O;}FPzp$|4NgL{x9QfbS(YO9e&*n3)*BSNh ze))Qn`Ri@AulKkuI{f_gdBeBoGXI@8@$qWewp)Mp-TV9bcJtAPf4@EG{r+V7x5ul# zJ=^f}<-$j=KK*#L_2;X-f8MS6{qFFa58wWM-uCzNkw0I~fB5?A|M#PRzQ6eQ?c2W} zKmJn&6o0ZXGBDUP=r8~Q$WIJx3l2=x+A1X0fAl{80wI<#AxHJZ6=Gc42i8k)2!yFk zUf~>#x)P{pmSu@_y?K^A+W_SlEmDsJt>8j?!rU}$QTid{RX);?85ERj1j zCU&EiscC7crR`W^Evi&g+eA&zJ@?%AobUUb^PcB(p6C0|n~Ju#6cUgY002P9+REIC zqY6jE58~X!*w`(Ofc$JN%{j_BY_rxI963p_awl@s>g!nahZ!C{MiWeeII8eNgBDtNBb#JLSKPXu-x7gWiY$jSN>F?QRsT+Qvz$X7AJW zg|=M8J%WwQSdC-e{6NODs;Vase-`_fzQn(UV^_xN z;f}lDg3^ri1Nx1fL1mAv8L5$Cz{_w=X(Z(f?Tc(EB~>{sC8WiqZo0**mQ*^nc7{xD z!2<+6pRseu{$1shXy#Q`rcztiLwIGyzzpT2qNuZiiSYBQrU`7^pwFQ#zxr>*{fXGrxW#gDz zZCbthZXh%i<vSpa4`D#tb8+#N4Y52 zMMA98d;`=RkZx7vA~O|gP3Xk*9ubGy=)Xos??Pr{ z$yFl`SBkwxTd1=h>Cqn|4n*kn1Anx6?u}lyls8~qet|1(>tx$%d+qgyB?WSqK!yWf zl{(_l>ji*&UOgZlWk}!B6$vCI-KfmE#$=>@XDeZ<)iKmzpFr9L?tx$MKl9v{WtL72 zpQ6OEJlw=iT%=HZS+C{lHuI1jeBDSZTEn~k*aIir7F#+RPA*(^H8n+easn|$GLhHFO#CmGI_bM_hvL=C#3~z zp^8|=7ox_s-)kfdZ=~%*M2rx2Q#@xcSpn2Uup4Ns+^ryrw{)Jd-BdaqOrfo!*gLuY z>u(dRztr`~1mF~{ozW;BDAo^df_g5m>Ju8qO?b#(O`#)7R6;Ym&2IFtWnFz$#b`|L zg{ziXB8d>`HlkK9KeIevw6-!Bs~FlmN!u=*Q8%qHG~UP&B_~TNQHyG4n|6b-rfw(A zZ7lQOH_xA(_+8C8f?L_kHnC1n{81hjG?11O5seadB~|r_CYvw%jT%KIw!#=YzjMLSab$kyu|6Ry}}o0dOc( zUmXThhwHh(bP+H;1YA!Ys*8X^VL3FlZ@`fgaZGUl7*rh!SBFDg;F<`y4g#iq41kax zH~$9+#|MN2#{2`oI3X@jJ%pAnLR<3~7`|-{=K$JAmFz`MtdMA| zKR!GhA3@Z>M+QM~#}*>Q&Jt}0fos4Z$WFTCx5VSH|KCXqp~-3a&rZa^<#!z5%u%8< zJ}S~53z$X55J|o`1#~1n6zfk~nVn1HIF$eE@OE{w`&J7Zjg9oh1>iL}y*M4el8}cv Rcdl{}z#3(5UT5l)_%|t>WX=Ep literal 0 HcmV?d00001 diff --git a/images/paste_disabled.png b/images/paste_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..5609ddc61ef9ad417cb063bb1984ba3b5edd59a7 GIT binary patch literal 1131 zcmV-x1eE)UP)!NHCj0UEntVq)U+%F4=z$H&J=wOTc5wVKd% zU245vHy=nA7Z=mfXjIUvxxT*shn@if>_;EWL+=1AVzF5CD@i_|pLGLZG0@l7mnD69 zets?|ArNA6a?+xTBuNIv)6)~$wW+D8S4x}M-Q5-U_xJJ}eZt}J>*?uf-}d&l;R*m9 zY0}W`?X8>uD=0ccG!I5-G{f&x{7Z}dTjFzJIA2Ba%s0LGaC zqEcL4T}k8ZWO#VkTD<}Q<1|k$KtEnNCV)~b$Vo>>N1L+7E_Zi#vM-PKV7EC27yt(p zWMgAPPRRdwb!EVvJBZ>!rv5_84Akc>guY&Zl*;A3DozswY86Cqhk{Ql?BuS zXsJ|^?eTbA3=IvTGFkvQ_nQigru9+Vi2Z+|P^dNR)*Kmtf~Fko~&vKZU?|q9k45cE)WP< z8xR*A)m>f~;Qo%4&|Y>q9g#YbNJIuBo*eC5|Ltsa&46l{R|WtunoK6;KlVQ}f_A*~ zSTDyv&o02~xCS8Q*nnyfg=t`5z#7v|TE$7rEKq|gkw{p(BwT^Le_>%ku3SB+pxx^M z*p&g*6+DPHCk{#m(9H?llMC$Iv&xDWy1RBkE461xHxM(Dm39M8x0+{TfPUg~v9rIwpP>{>(zjs;&|oL{hJ?O-&+P)ezrX(*J%8YiVm~J| zPCj+o4O3K*azt26Jd;QOTU%Q?-Eu3auB3d=(`-kq`t|j7nOM;Itk|h=D!05FdXa24 x8^)EK4kmH9sZ#*BN75pG(+#`8htj_S3;>HAwy}2V7jFOn002ovPDHLkV1flm1a$xa literal 0 HcmV?d00001 diff --git a/images/pause.png b/images/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..df812fba228477716d9a72624c6a99c1e422ad70 GIT binary patch literal 807 zcmV+?1K9kDP)z@;o?%Qu_W%F@AY({UO#lFTB>(_`g8%^e{{R4h=l}px z2mk>USO5SzmjD14Z`WEMkN^Mzok>JNRCwA<($8)iRTRhZ@40_G_IMn-P8%A}Ez57E5PU$MhtNCpD z@bLMgSyhbg~L#neB!xccwuW&XGT9_-%v{->`$`=y$UMBJQW znlFBT`FbDt!vH)O4(@NIs*ev4JfscwviAL_m-%B4>e*B0%LKOSsc+eXs=F zEO(5~DDxcWBDS`aA$qRM7ZbiSJ&04i5AUkG!jsA@s9=4O8g5w}njPUCAKDhZ3Sufl-LxPPpae5 z=!ENcIdt(T?`#KJYf4PYVl?(I#^W;pi7_G~b|=d|Qc76>=e<8GihZC2&`7eKWgjXf l8jywe<#|!;#~A(p0|3&LCWqAzuEPKT002ovPDHLkV1fxmR-BiI!1;K@&q6@2tRI65qu?r!C8&eGQ zr~P1CFidQ1OeT|=%$tvIswqiR?1eLL-r?PI&pR{J7;iwXI(i115NjPU2qnq*9HDw) zjk4rkuG|bZIY{%V)_R8^WBqA=)PMT8w@Bd0t7x&n<*s*4B{t^mi9_}5OhTpN0%laK z`Bs}q_<}t%DtPxXG`wj)=0!8Vb!Pgf1X-*@lAp()qjTSP?r@LfH$`{+iGWRG}Kli@VM+?Dl;?< z^9zFhesEakCr*Dhk zpfvsE3r^HD-5jV`ic(f^hY;ia4tJGZQ*|;Kpkf0H&$$@P@q!^tPDqNBHC-zO|3O|z zcAL#Y$ubE^rJ;JG1A-AA5=jX%(-T-0(l>JUmoi%^(0Q#*%!tywEM*OMISXH=5)l8D z25D*>_S90gz?t=N6=TR@IRnqGTlqSHb#8Yja(haYtYS!0V|aHhZ3|p#fp9nsqA2d~ z?#+BCaGnf+3Uew`N;dmJKyGH$x~CGKJ9p1Xiu~y^D2Lt97)Yp#dV1NO6d3)w7RfV}c+c gj^jYnH25RH0G|>>DB@Pn6951J07*qoM6N<$g7PU;x&QzG literal 0 HcmV?d00001 diff --git a/images/preview.png b/images/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..d60ca8a5d0bccba0dcaf38463fbd46f00f2d3be6 GIT binary patch literal 2183 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 JPDHLkV1kbhAx!`P literal 0 HcmV?d00001 diff --git a/images/pub_default.jpg b/images/pub_default.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4ccdd6308978b94f20b903a124022c7f9174d7d7 GIT binary patch literal 20193 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

    ATutor Developer Documentation

    + + + addConfirm('DELETE_MSGS', $hidden_vars); + $msg->printConfirm(); +} + +$msg->printInfos(array('INBOX_SENT_MSGS_TTL', $_config['sent_msgs_ttl'])); + +$sql = "SELECT * FROM ".TABLE_PREFIX."messages_sent WHERE from_member_id=$_SESSION[member_id] ORDER BY date_sent DESC"; +$result = mysql_query($sql,$db); +?> + +
    + + + + + + + + + + + + + + + + + + + + + + + + '; + + if ($_GET['view'] != $row['message_id']) { + echo $name; + } else { + echo ''.$name.''; + } + echo ''; + + echo ''; + + echo ''; + echo ''; + } while ($row = mysql_fetch_assoc($result)); ?> + + + + + + +
     
    + + +
    title="" onmouseup="this.checked=!this.checked" />'; + echo AT_date(_AT('inbox_date_format'), $row['date_sent'], AT_DATE_MYSQL_DATETIME); + echo '
    +
    + + \ No newline at end of file diff --git a/include/classes/CSVExport.class.php b/include/classes/CSVExport.class.php new file mode 100644 index 000000000..9c91ab17a --- /dev/null +++ b/include/classes/CSVExport.class.php @@ -0,0 +1,79 @@ +detectFieldTypes($result); + if (!$field_types) { + return FALSE; + } + $num_fields = count($field_types); + + while ($row = mysql_fetch_row($result)) { + for ($i=0; $i < $num_fields; $i++) { + if ($types[$i] == 'int' || $types[$i] == 'real') { + $content .= $row[$i] . ','; + } else { + $content .= $this->quote($row[$i]) . ','; + } + } + $content = substr($content, 0, -1); + $content .= "\n"; + } + + @mysql_free_result($result); + + return $content; + } + + // public + // given a query result returns an array of field types. + // possible field types are int, string, datetime, or blob... + function detectFieldTypes(&$result) { + $field_types = array(); + $num_fields = @mysql_num_fields($result); + + if (!$num_fields) { + return array(); + } + + for ($i=0; $i< $num_fields; $i++) { + $field_types[] = mysql_field_type($result, $i); + } + return $field_types; + } + + // private + // quote $line so that it's safe to save as a CSV field + function quote($line) { + return '"'.str_replace($this->quote_search, $this->quote_replace, $line).'"'; + } +} + +?> \ No newline at end of file diff --git a/include/classes/CSVImport.class.php b/include/classes/CSVImport.class.php new file mode 100644 index 000000000..10f4e7b3e --- /dev/null +++ b/include/classes/CSVImport.class.php @@ -0,0 +1,157 @@ +quote_search, $this->quote_replace, $input); + + return $input; + } + + // public + function import($tableName, $path, $course_id, $version) { + global $db; + static $table_id_map; + + $fn_name = $tableName.'_convert'; + + // lock the tables + $lock_sql = 'LOCK TABLES ' . TABLE_PREFIX . $tableName. ', ' . TABLE_PREFIX . 'courses WRITE'; + $result = mysql_query($lock_sql, $db); + + // get the field types + $field_types = $this->detectFieldTypes($tableName); + if (!$field_types) { + return FALSE; + } + + // get the name of the primary field + $primary_key_field_name = $this->getPrimaryFieldName($tableName); + // read the rows into an array + $fp = @fopen($path . $tableName . '.csv', 'rb'); + $i = 0; + + // get the name of the primary ID field and the next index + $next_id = 0; + if ($primary_key_field_name) { + // get the next primary ID + $sql = 'SELECT MAX(' . $primary_key_field_name . ') AS next_id FROM ' . TABLE_PREFIX . $tableName; + $result = mysql_query($sql, $db); + $next_id = mysql_fetch_assoc($result); + $next_id = $next_id['next_id']+1; + } + + $rows = array(); + while ($row = @fgetcsv($fp, 70000)) { + if (count($row) && (trim($row[0]) == '')) { + continue; + } + + if (function_exists($fn_name)) { + $row = $fn_name($row, $course_id, $table_id_map, $version); + } + if (!$row) { + continue; + } + if ($row[0] == 0) { + $row[0] = $i; + } + + $table_id_map[$tableName][$row[0]] = $next_id; + if ($primary_key_field_name != NULL) { + $row[0] = $next_id; + } + + $sql = 'REPLACE INTO '.TABLE_PREFIX.$tableName.' VALUES ('; + + foreach($row as $id => $field) { + if (($field_types[$id] != 'int') && ($field_types[$id] != 'real')) { + $field = $this->translateWhitespace($field); + } else if ($field_types[$id] == 'int') { + $field = intval($field); + } + $sql .= "'" . $field."',"; + } + $sql = substr($sql, 0, -1); + $sql .= ')'; + + $result = mysql_query($sql, $db); + $i++; + $next_id++; + } + + // close the file + @fclose($fp); + + // unlock the tables + $lock_sql = 'UNLOCK TABLES'; + $result = mysql_query($lock_sql, $db); + } + +} + +?> \ No newline at end of file diff --git a/include/classes/ContentManager.class.php b/include/classes/ContentManager.class.php new file mode 100644 index 000000000..8f05473a1 --- /dev/null +++ b/include/classes/ContentManager.class.php @@ -0,0 +1,1565 @@ +db = $db; + $this->course_id = intval($course_id); + } + + function initContent( ) { + if (!($this->course_id > 0)) { + return; + } + $sql = "SELECT content_id, content_parent_id, ordering, title, UNIX_TIMESTAMP(release_date) AS u_release_date, content_type + FROM ".TABLE_PREFIX."content + WHERE course_id=$this->course_id + ORDER BY content_parent_id, ordering"; + $result = mysql_query($sql, $this->db); + + /* x could be the ordering or even the content_id */ + /* don't really need the ordering anyway. */ + /* $_menu[content_parent_id][x] = array('content_id', 'ordering', 'title') */ + $_menu = array(); + + /* number of content sections */ + $num_sections = 0; + + $max_depth = array(); + $_menu_info = array(); + + while ($row = mysql_fetch_assoc($result)) { + $num_sections++; + $_menu[$row['content_parent_id']][] = array('content_id'=> $row['content_id'], + 'ordering' => $row['ordering'], + 'title' => htmlspecialchars($row['title']), + 'content_type' => $row['content_type']); + + $_menu_info[$row['content_id']] = array('content_parent_id' => $row['content_parent_id'], + 'title' => htmlspecialchars($row['title']), + 'ordering' => $row['ordering'], + 'u_release_date' => $row['u_release_date'], + 'content_type' => $row['content_type']); + + /* + * add test content asscioations + * find associations per content page, and add it as a sublink. + * @author harris + */ + $test_rs = $this->getContentTestsAssoc($row['content_id']); + while ($test_row = mysql_fetch_assoc($test_rs)){ + $_menu[$row['content_id']][] = array( 'test_id' => $test_row['test_id'], + 'title' => htmlspecialchars($test_row['title']), + 'content_type' => CONTENT_TYPE_CONTENT); + } + /* End of add test content asscioations */ + + if ($row['content_parent_id'] == 0) { + $max_depth[$row['content_id']] = 1; + } else { + $max_depth[$row['content_id']] = $max_depth[$row['content_parent_id']]+1; + } + } + + $this->_menu = $_menu; + + $this->_menu_info = $_menu_info; + + $this->num_sections = $num_sections; + + if (count($max_depth) > 1) { + $this->max_depth = max($max_depth); + } else { + $this->max_depth = 0; + } + + // generate array of all the content ids in the same order that they appear in "content navigation" + $this->_menu_in_order[] = $next_content_id = $this->getNextContentID(0); + while ($next_content_id > 0) + { + $next_content_id = $this->getNextContentID($next_content_id); + + if (in_array($next_content_id, $this->_menu_in_order)) break; + else $this->_menu_in_order[] = $next_content_id; + } + + $this->content_length = count($_menu[0]); + } + + // This function is called by initContent to construct $this->_menu_in_order, an array to + // holds all the content ids in the same order that they appear in "content navigation" + function getNextContentID($content_id, $order=0) { + // return first root content when $content_id is not given + if (!$content_id) { + return $this->_menu[0][0]['content_id']; + } + + $myParent = $this->_menu_info[$content_id]['content_parent_id']; + $myOrder = $this->_menu_info[$content_id]['ordering']; + + // calculate $myOrder, add in the number of tests in front of this content page + if (is_array($this->_menu[$myParent])) { + $num_of_tests = 0; + foreach ($this->_menu[$myParent] as $menuContent) { + if ($menuContent['content_id'] == $content_id) break; + if (isset($menuContent['test_id'])) $num_of_tests++; + } + } + $myOrder += $num_of_tests; + // end of calculating $myOrder + + /* if this content has children, then take the first one. */ + if ( isset($this->_menu[$content_id]) && is_array($this->_menu[$content_id]) && ($order==0) ) { + /* has children */ + // if the child is a test, keep searching for the content id + foreach ($this->_menu[$content_id] as $menuID => $menuContent) + { + if (!empty($menuContent['test_id'])) continue; + else + { + $nextMenu = $this->_menu[$content_id][$menuID]['content_id']; + break; + } + } + + // all children are tests + if (!isset($nextMenu)) + { + if (isset($this->_menu[$myParent][$myOrder]['content_id'])) { + // has sibling + return $this->_menu[$myParent][$myOrder]['content_id']; + } + else { // no sibling + $nextMenu = $this->getNextContentID($myParent, 1); + } + } + return $nextMenu; + } else { + /* no children */ + if (isset($this->_menu[$myParent][$myOrder]) && $this->_menu[$myParent][$myOrder] != '') { + /* Has sibling */ + return $this->_menu[$myParent][$myOrder]['content_id']; + } else { + /* No more siblings */ + if ($myParent != 0) { + return $this->getNextContentID($myParent, 1); + } + } + } + } + + function getContent($parent_id=-1, $length=-1) { + if ($parent_id == -1) { + $my_menu_copy = $this->_menu; + if ($length != -1) { + $my_menu_copy[0] = array_slice($my_menu_copy[0], 0, $length); + } + return $my_menu_copy; + } + return $this->_menu[$parent_id]; + } + + + function &getContentPath($content_id) { + $path = array(); + + $path[] = array('content_id' => $content_id, 'title' => $this->_menu_info[$content_id]['title']); + + $this->getContentPathRecursive($content_id, $path); + + $path = array_reverse($path); + return $path; + } + + + function getContentPathRecursive($content_id, &$path) { + $parent_id = $this->_menu_info[$content_id]['content_parent_id']; + + if ($parent_id > 0) { + $path[] = array('content_id' => $parent_id, 'title' => $this->_menu_info[$parent_id]['title']); + $this->getContentPathRecursive($parent_id, $path); + } + } + + function addContent($course_id, $content_parent_id, $ordering, $title, $text, $keywords, + $related, $formatting, $release_date, $head = '', $use_customized_head = 0, + $test_message = '', $allow_test_export = 1, $content_type = CONTENT_TYPE_CONTENT) { + + if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && ($_SESSION['course_id'] != -1)) { + return false; + } + + // shift the new neighbouring content down + $sql = "UPDATE ".TABLE_PREFIX."content SET ordering=ordering+1 + WHERE ordering>=$ordering + AND content_parent_id=$content_parent_id + AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + + /* main topics all have minor_num = 0 */ + $sql = "INSERT INTO ".TABLE_PREFIX."content + (course_id, + content_parent_id, + ordering, + last_modified, + revision, + formatting, + release_date, + head, + use_customized_head, + keywords, + content_path, + title, + text, + test_message, + allow_test_export, + content_type) + VALUES ($course_id, + $content_parent_id, + $ordering, + NOW(), + 0, + $formatting, + '$release_date', + '$head', + $use_customized_head, + '$keywords', + '', + '$title', + '$text', + '$test_message', + $allow_test_export, + $content_type)"; + + $err = mysql_query($sql, $this->db); + + /* insert the related content */ + $sql = "SELECT LAST_INSERT_ID() AS insert_id"; + $result = mysql_query($sql, $this->db); + $row = mysql_fetch_assoc($result); + $cid = $row['insert_id']; + + $sql = ''; + if (is_array($related)) { + foreach ($related as $x => $related_content_id) { + $related_content_id = intval($related_content_id); + + if ($related_content_id != 0) { + if ($sql != '') { + $sql .= ', '; + } + $sql .= '('.$cid.', '.$related_content_id.')'; + $sql .= ', ('.$related_content_id.', '.$cid.')'; + } + } + + if ($sql != '') { + $sql = 'INSERT INTO '.TABLE_PREFIX.'related_content VALUES '.$sql; + $result = mysql_query($sql, $this->db); + } + } + + return $cid; + } + + function editContent($content_id, $title, $text, $keywords,$related, $formatting, + $release_date, $head, $use_customized_head, $test_message, + $allow_test_export) { + if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) { + return FALSE; + } + + /* update the title, text of the newly moved (or not) content */ + $sql = "UPDATE ".TABLE_PREFIX."content + SET title='$title', head='$head', use_customized_head=$use_customized_head, + text='$text', keywords='$keywords', formatting=$formatting, + revision=revision+1, last_modified=NOW(), release_date='$release_date', + test_message='$test_message', allow_test_export=$allow_test_export + WHERE content_id=$content_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + + /* update the related content */ + $result = mysql_query("DELETE FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id", $this->db); + $sql = ''; + if (is_array($related)) { + foreach ($related as $x => $related_content_id) { + $related_content_id = intval($related_content_id); + + if ($related_content_id != 0) { + if ($sql != '') { + $sql .= ', '; + } + $sql .= '('.$content_id.', '.$related_content_id.')'; + $sql .= ', ('.$related_content_id.', '.$content_id.')'; + } + } + + if ($sql != '') { + /* delete the old related content */ + $result = mysql_query("DELETE FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id", $this->db); + + /* insert the new, and the old related content again */ + $sql = 'INSERT INTO '.TABLE_PREFIX.'related_content VALUES '.$sql; + $result = mysql_query($sql, $this->db); + } + } + } + + function moveContent($content_id, $new_content_parent_id, $new_content_ordering) { + global $msg; + + if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) { + return FALSE; + } + + /* first get the content to make sure it exists */ + $sql = "SELECT ordering, content_parent_id FROM ".TABLE_PREFIX."content WHERE content_id=$content_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + if (!($row = mysql_fetch_assoc($result)) ) { + return FALSE; + } + $old_ordering = $row['ordering']; + $old_content_parent_id = $row['content_parent_id']; + + $sql = "SELECT max(ordering) max_ordering FROM ".TABLE_PREFIX."content WHERE content_parent_id=$old_content_parent_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + $row = mysql_fetch_assoc($result); + $max_ordering = $row['max_ordering']; + + if ($content_id == $new_content_parent_id) { + $msg->addError("NO_SELF_AS_PARENT"); + return; + } + + if ($old_content_parent_id == $new_content_parent_id && $old_ordering == $new_content_ordering) { + $msg->addError("SAME_LOCATION"); + return; + } + + $content_path = $this->getContentPath($new_content_parent_id); + foreach ($content_path as $parent){ + if ($parent['content_id'] == $content_id) { + $msg->addError("NO_CHILD_AS_PARENT"); + return; + } + } + + // if the new_content_ordering is greater than the maximum ordering of the parent content, + // set the $new_content_ordering to the maximum ordering. This happens when move the content + // to the last element under the same parent content. + if ($old_content_parent_id == $new_content_parent_id && $new_content_ordering > $max_ordering) + $new_content_ordering = $max_ordering; + + if (($old_content_parent_id != $new_content_parent_id) || ($old_ordering != $new_content_ordering)) { + // remove the gap left by the moved content + $sql = "UPDATE ".TABLE_PREFIX."content + SET ordering=ordering-1 + WHERE ordering>$old_ordering + AND content_parent_id=$old_content_parent_id + AND content_id<>$content_id + AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + + // shift the new neighbouring content down + $sql = "UPDATE ".TABLE_PREFIX."content + SET ordering=ordering+1 + WHERE ordering>=$new_content_ordering + AND content_parent_id=$new_content_parent_id + AND content_id<>$content_id + AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + + $sql = "UPDATE ".TABLE_PREFIX."content + SET content_parent_id=$new_content_parent_id, ordering=$new_content_ordering + WHERE content_id=$content_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + } + } + + function deleteContent($content_id) { + if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) { + return false; + } + + /* check if exists */ + $sql = "SELECT ordering, content_parent_id FROM ".TABLE_PREFIX."content WHERE content_id=$content_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + if (!($row = @mysql_fetch_assoc($result)) ) { + return false; + } + $ordering = $row['ordering']; + $content_parent_id = $row['content_parent_id']; + + /* check if this content has sub content */ + $children = $this->_menu[$content_id]; + + if (is_array($children) && (count($children)>0) ) { + /* delete its children recursively first*/ + foreach ($children as $x => $info) { + $this->deleteContentRecursive($info['content_id']); + } + } + + /* delete this content page */ + $sql = "DELETE FROM ".TABLE_PREFIX."content WHERE content_id=$content_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + + /* delete this content from member tracking page */ + $sql = "DELETE FROM ".TABLE_PREFIX."member_track WHERE content_id=$content_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + + $sql = "DELETE FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id"; + $result = mysql_query($sql, $this->db); + + /* delete the content tests association */ + $sql = "DELETE FROM ".TABLE_PREFIX."content_tests_assoc WHERE content_id=$content_id"; + $result = mysql_query($sql, $this->db); + + /* delete the content forum association */ + $sql = "DELETE FROM ".TABLE_PREFIX."content_forums_assoc WHERE content_id=$content_id"; + $result = mysql_query($sql, $this->db); + + /* Delete all AccessForAll contents */ + require_once(AT_INCLUDE_PATH.'../mods/_core/imsafa/classes/A4a.class.php'); + $a4a = new A4a($content_id); + $a4a->deleteA4a(); + + /* re-order the rest of the content */ + $sql = "UPDATE ".TABLE_PREFIX."content SET ordering=ordering-1 WHERE ordering>=$ordering AND content_parent_id=$content_parent_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + /* end moving block */ + + /* remove the "resume" to this page, b/c it was deleted */ + $sql = "UPDATE ".TABLE_PREFIX."course_enrollment SET last_cid=0 WHERE course_id=$_SESSION[course_id] AND last_cid=$content_id"; + $result = mysql_query($sql, $this->db); + + return true; + } + + + /* private. call from deleteContent only. */ + function deleteContentRecursive($content_id) { + /* check if this content has sub content */ + $children = $this->_menu[$content_id]; + + if (is_array($children) && (count($children)>0) ) { + /* delete its children recursively first*/ + foreach ($children as $x => $info) { + $this->deleteContent($info['content_id']); + } + } + + /* delete this content page */ + $sql = "DELETE FROM ".TABLE_PREFIX."content WHERE content_id=$content_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $this->db); + + /* delete this content from member tracking page */ + $sql = "DELETE FROM ".TABLE_PREFIX."member_track WHERE content_id=$content_id"; + $result = mysql_query($sql, $this->db); + + $sql = "DELETE FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id"; + $result = mysql_query($sql, $this->db); + + /* delete the content tests association */ + $sql = "DELETE FROM ".TABLE_PREFIX."content_tests_assoc WHERE content_id=$content_id"; + $result = mysql_query($sql, $this->db); + } + + function getContentPage($content_id) { + $sql = "SELECT *, DATE_FORMAT(release_date, '%Y-%m-%d %H:%i:00') AS release_date, release_date+0 AS r_date, NOW()+0 AS n_date FROM ".TABLE_PREFIX."content + WHERE content_id=$content_id"; + $result = mysql_query($sql, $this->db); + + return $result; + } + + /* @See editor/edit_content.php include/html/dropdowns/related_topics.inc.php include/lib/editor_tabs_functions.inc.php */ + function getRelatedContent($content_id, $all=false) { + if ($content_id == 0) { + return; + } + if ($content_id == '') { + return; + } + $related_content = array(); + + if ($all) { + $sql = "SELECT * FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id OR related_content_id=$content_id"; + } else { + $sql = "SELECT * FROM ".TABLE_PREFIX."related_content WHERE content_id=$content_id"; + } + $result = mysql_query($sql, $this->db); + + while ($row = mysql_fetch_assoc($result)) { + if ($row['related_content_id'] != $content_id) { + $related_content[] = $row['related_content_id']; + } else { + $related_content[] = $row['content_id']; + } + } + + return $related_content; + } + + /** + * Return a list of tests associated with the selected content + * @param int the content id that all tests are associated with it. + * @return array list of tests + * @date Sep 10, 2008 + * @author Harris + */ + function & getContentTestsAssoc($content_id){ + $sql = "SELECT ct.test_id, t.title FROM (SELECT * FROM ".TABLE_PREFIX."content_tests_assoc WHERE content_id=$content_id) AS ct LEFT JOIN ".TABLE_PREFIX."tests t ON ct.test_id=t.test_id"; + $result = mysql_query($sql, $this->db); + return $result; + } + + /*TODO***************BOLOGNA***************REMOVE ME**********/ + function & getContentForumsAssoc($content_id){ + $sql = "SELECT cf.forum_id, f.title FROM (SELECT * FROM ".TABLE_PREFIX."content_forums_assoc WHERE content_id=$content_id) AS cf LEFT JOIN ".TABLE_PREFIX."forums f ON cf.forum_id=f.forum_id"; + $result = mysql_query($sql, $this->db); + return $result; + } + + function & cleanOutput($value) { + return stripslashes(htmlspecialchars($value)); + } + + + /* @See include/html/editor_tabs/properties.inc.php */ + /* Access: Public */ + function getNumSections() { + return $this->num_sections; + } + + /* Access: Public */ + function getMaxDepth() { + return $this->max_depth; + } + + /* Access: Public */ + function getContentLength() { + return $this->content_length; + } + + /* @See include/html/dropdowns/local_menu.inc.php */ + function getLocationPositions($parent_id, $content_id) { + $siblings = $this->getContent($parent_id); + for ($i=0;$igetContentPath($content_id); + $parent = 0; + $numbering = ''; + foreach ($path as $page) { + $num = $this->getLocationPositions($parent, $page['content_id']) +1; + $parent = $page['content_id']; + $numbering .= $num.'.'; + } + $numbering = substr($numbering, 0, -1); + + return $numbering; + } + + function getPreviousContent($content_id) { + if (is_array($this->_menu_in_order)) + { + foreach ($this->_menu_in_order as $content_location => $this_content_id) + { + if ($this_content_id == $content_id) break; + } + + for ($i=$content_location-1; $i >= 0; $i--) + { + $content_type = $this->_menu_info[$this->_menu_in_order[$i]]['content_type']; + + if ($content_type == CONTENT_TYPE_CONTENT || $content_type == CONTENT_TYPE_WEBLINK) + return array('content_id' => $this->_menu_in_order[$i], + 'ordering' => $this->_menu_info[$this->_menu_in_order[$i]]['ordering'], + 'title' => $this->_menu_info[$this->_menu_in_order[$i]]['title']); + } + } + return NULL; + } + + function getNextContent($content_id) { + if (is_array($this->_menu_in_order)) + { + foreach ($this->_menu_in_order as $content_location => $this_content_id) + { + if ($this_content_id == $content_id) break; + } + + for ($i=$content_location+1; $i < count($this->_menu_in_order); $i++) + { + $content_type = $this->_menu_info[$this->_menu_in_order[$i]]['content_type']; + + if ($content_type == CONTENT_TYPE_CONTENT || $content_type == CONTENT_TYPE_WEBLINK) + return(array('content_id' => $this->_menu_in_order[$i], + 'ordering' => $this->_menu_info[$this->_menu_in_order[$i]]['ordering'], + 'title' => $this->_menu_info[$this->_menu_in_order[$i]]['title'])); + } + } + return NULL; + } + + /* @See include/header.inc.php */ + function generateSequenceCrumbs($cid) { + global $_base_path; + + $sequence_links = array(); + + $first = $this->getNextContent(0); // get first + if ($_SESSION['prefs']['PREF_NUMBERING'] && $first) { + $first['title'] = $this->getNumbering($first['content_id']).' '.$first['title']; + } + if ($first) { + $first['url'] = $_base_path.url_rewrite('content.php?cid='.$first['content_id']); + $sequence_links['first'] = $first; + } + + if (!$cid && $_SESSION['s_cid']) { + $resume['title'] = $this->_menu_info[$_SESSION['s_cid']]['title']; + + if ($_SESSION['prefs']['PREF_NUMBERING']) { + $resume['title'] = $this->getNumbering($_SESSION['s_cid']).' ' . $resume['title']; + } + + $resume['url'] = $_base_path.url_rewrite('content.php?cid='.$_SESSION['s_cid']); + + $sequence_links['resume'] = $resume; + } else { + if ($cid) { + $previous = $this->getPreviousContent($cid); + } + $next = $this->getNextContent($cid ? $cid : 0); + + if ($_SESSION['prefs']['PREF_NUMBERING']) { + $previous['title'] = $this->getNumbering($previous['content_id']).' '.$previous['title']; + $next['title'] = $this->getNumbering($next['content_id']).' '.$next['title']; + } + + $next['url'] = $_base_path.url_rewrite('content.php?cid='.$next['content_id']); + if (isset($previous['content_id'])) { + $previous['url'] = $_base_path.url_rewrite('content.php?cid='.$previous['content_id']); + } + + if (isset($previous['content_id'])) { + $sequence_links['previous'] = $previous; + } else if ($cid) { + $previous['url'] = $_base_path . url_rewrite('index.php'); + $previous['title'] = _AT('home'); + $sequence_links['previous'] = $previous; + } + if (!empty($next['content_id'])) { + $sequence_links['next'] = $next; + } + } + + return $sequence_links; + } + + /** Generate javascript to hide all root content folders, except the one with current content page + * access: private + * @return print out javascript function initContentMenu() + */ + function initMenu(){ + global $_base_path; + + echo ' +function initContentMenu() { + tree_collapse_icon = "'.$_base_path.'images/tree/tree_collapse.gif"; + tree_expand_icon = "'.$_base_path.'images/tree/tree_expand.gif"; + +'; + + $sql = "SELECT content_id + FROM ".TABLE_PREFIX."content + WHERE course_id=$this->course_id + AND content_type = ".CONTENT_TYPE_FOLDER; + $result = mysql_query($sql, $this->db); + + // collapse all root content folders + while ($row = mysql_fetch_assoc($result)) { + echo ' + if (ATutor.getcookie("c'.$_SESSION['course_id'].'_'.$row['content_id'].'") == "1") + { + jQuery("#folder"+'.$row['content_id'].').show(); + jQuery("#tree_icon"+'.$row['content_id'].').attr("src", tree_collapse_icon); + jQuery("#tree_icon"+'.$row['content_id'].').attr("alt", "'._AT("collapse").'"); + jQuery("#tree_icon"+'.$row['content_id'].').attr("title", "'._AT("collapse").'"); + } + else + { + jQuery("#folder"+'.$row['content_id'].').hide(); + jQuery("#tree_icon"+'.$row['content_id'].').attr("src", tree_expand_icon); + jQuery("#tree_icon"+'.$row['content_id'].').attr("alt", "'._AT("expand").'"); + jQuery("#tree_icon"+'.$row['content_id'].').attr("title", "'._AT("expand").'"); + } +'; + } + + // expand the content folder that has current content + if (isset($_SESSION['s_cid']) && $_SESSION['s_cid'] > 0) { + $current_content_path = $this->getContentPath($_SESSION['s_cid']); + + for ($i=0; $i < count($current_content_path)-1; $i++) + echo ' + jQuery("#folder"+'.$current_content_path[$i]['content_id'].').show(); + jQuery("#tree_icon"+'.$current_content_path[$i]['content_id'].').attr("src", tree_collapse_icon); + jQuery("#tree_icon"+'.$current_content_path[$i]['content_id'].').attr("alt", "'._AT("collapse").'"); + ATutor.setcookie("c'.$_SESSION['course_id'].'_'.$current_content_path[$i]['content_id'].'", "1", 1); +'; + } + echo '}'; // end of javascript function initContentMenu() + } + + /* @See include/html/dropdowns/menu_menu.inc.php */ + function printMainMenu( ) { + if (!($this->course_id > 0)) { + return; + } + + global $_base_path; + + $parent_id = 0; + $depth = 0; + $path = ''; + $children = array(); + $truncate = true; + $ignore_state = true; + + $this->start = true; + + // if change the location of this line, change function switchEditMode(), else condition accordingly + echo '
    '; + + if (authenticate(AT_PRIV_ADMIN,AT_PRIV_RETURN) && !is_mobile_device()) + { + echo "\n".' + '."\n"; + } + $this->printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state); + + // javascript for inline editor + echo ' + +'; + echo '
    '; + } + + /* @See tools/sitemap/index.php */ + function printSiteMapMenu() { + $parent_id = 0; + $depth = 1; + $path = ''; + $children = array(); + $truncate = false; + $ignore_state = true; + + $this->start = true; + $this->printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state, 'sitemap'); + } + + /* @See index.php */ + function printTOCMenu($cid, $top_num) { + $parent_id = $cid; + $depth = 1; + $path = $top_num.'.'; + $children = array(); + $truncate = false; + $ignore_state = false; + + $this->start = true; + $this->printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state); + } + + /* @See index.php include/html/dropdowns/local_menu.inc.php */ + function printSubMenu($cid, $top_num) { + $parent_id = $cid; + $depth = 1; + $path = $top_num.'.'; + $children = array(); + $truncate = true; + $ignore_state = false; + + $this->start = true; + $this->printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state); + } + + /* @See include/html/menu_menu.inc.php */ + /* Access: PRIVATE */ + function printMenu($parent_id, $depth, $path, $children, $truncate, $ignore_state, $from = '') { + global $cid, $_my_uri, $_base_path, $rtl, $substr, $strlen; + static $temp_path; + + if (!isset($temp_path)) { + if ($cid) { + $temp_path = $this->getContentPath($cid); + } else { + $temp_path = $this->getContentPath($_SESSION['s_cid']); + } + } + + $highlighted = array(); + if (is_array($temp_path)) { + foreach ($temp_path as $temp_path_item) { + $_SESSION['menu'][$temp_path_item['content_id']] = 1; + $highlighted[$temp_path_item['content_id']] = true; + } + } + + if ($this->start) { + reset($temp_path); + $this->start = false; + } + + if ( isset($this->_menu[$parent_id]) && is_array($this->_menu[$parent_id]) ) { + $top_level = $this->_menu[$parent_id]; + $counter = 1; + $num_items = count($top_level); + + echo '
    '."\n"; + + foreach ($top_level as $garbage => $content) { + $link = ''; + //tests do not have content id + $content['content_id'] = isset($content['content_id']) ? $content['content_id'] : ''; + + if (!$ignore_state) { + $link .= ''; + } + + $on = false; + + if ( (($_SESSION['s_cid'] != $content['content_id']) || ($_SESSION['s_cid'] != $cid)) && ($content['content_type'] == CONTENT_TYPE_CONTENT || $content['content_type'] == CONTENT_TYPE_WEBLINK)) + { // non-current content nodes with content type "CONTENT_TYPE_CONTENT" + if (isset($highlighted[$content['content_id']])) { + $link .= ''; + $on = true; + } + + //content test extension @harris + //if this is a test link. + if (isset($content['test_id'])){ + $title_n_alt = $content['title']; + $in_link = 'mods/_standard/tests/test_intro.php?tid='.$content['test_id']; + $img_link = ' '.$title_n_alt.''; + } else { + $in_link = 'content.php?cid='.$content['content_id']; + $img_link = ''; + } + + $full_title = $content['title']; + $link .= $img_link . ' '; + + if ($truncate && ($strlen($content['title']) > ($base_title_length-$depth*4)) ) { + $content['title'] = htmlspecialchars(rtrim($substr(htmlspecialchars_decode($content['title']), 0, ($base_title_length-$depth*4)-4))).'...'; + } + + if (isset($content['test_id'])) { + $link .= $content['title']; + } else { + $link .= ''; + if($_SESSION['prefs']['PREF_NUMBERING']){ + $link .= $path.$counter; + } + $link .= ' '.$content['title'].''; + } + + $link .= ''; + if ($on) { + $link .= ''; + } + + // instructors have privilege to delete content + if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !isset($content['test_id']) && !is_mobile_device()) { + $link .= ''._AT('; + } + } + else + { // current content page & nodes with content type "CONTENT_TYPE_FOLDER" + $base_title_length = 33; + if ($_SESSION['prefs']['PREF_NUMBERING']) { + $base_title_length = 26; + } + + if (isset($highlighted[$content['content_id']])) { + $link .= ''; + $on = true; + } + + if ($content['content_type'] == CONTENT_TYPE_CONTENT || $content['content_type'] == CONTENT_TYPE_WEBLINK) + { // current content page + $full_title = $content['title']; + $link .= ''._AT('you_are_here').': ';
+						if($_SESSION['prefs']['PREF_NUMBERING']){
+						  $link .= $path.$counter;
+						}
+						  $link .= $content['title'].''."\n"; + if ($truncate && ($strlen($content['title']) > ($base_title_length-$depth*4)) ) { + $content['title'] = htmlspecialchars(rtrim($substr(htmlspecialchars_decode($content['title']), 0, ($base_title_length-$depth*4)-4))).'...'; + } + $link .= ''; + if($_SESSION['prefs']['PREF_NUMBERING']){ + $link .= $path.$counter; + } + $link .=' '.$content['title'].''; + + // instructors have privilege to delete content + if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !is_mobile_device()) { + $link .= ''._AT('; + } + } + else + { // nodes with content type "CONTENT_TYPE_FOLDER" + $full_title = $content['title']; + if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !is_mobile_device()) { + $link .= ''."\n"; + } + else { + $link .= ''."\n"; + } + + if ($truncate && ($strlen($content['title']) > ($base_title_length-$depth*4)) ) { + $content['title'] = htmlspecialchars(rtrim($substr(htmlspecialchars_decode($content['title']), 0, ($base_title_length-$depth*4)-4))).'...'; + } + if (isset($content['test_id'])) + $link .= $content['title']; + else + $link .= ''; + if($_SESSION['prefs']['PREF_NUMBERING']){ + $link .= $path.$counter; + } + $link .= ' '.$content['title'].''; + + if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !is_mobile_device()) { + $link .= ''."\n"; + } + else { + $link .= ''."\n"; + } + + // instructors have privilege to delete content + if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && !is_mobile_device()) { + $link .= ''._AT('; + } + + } + + if ($on) { + $link .= ''; + } + } + + if ($ignore_state) { + $on = true; + } + + echo ''."\n"; + + if ( isset($this->_menu[$content['content_id']]) && is_array($this->_menu[$content['content_id']]) ) { + /* has children */ + for ($i=0; $i<$depth; $i++) { + if ($children[$i] == 1) { + echo ''."\n"; + } else { + echo ''."\n"; + } + } + + if (($counter == $num_items) && ($depth > 0)) { + echo ''."\n"; + $children[$depth] = 0; + } else if ($counter == $num_items) { + echo ''."\n"; + $children[$depth] = 0; + } else { + echo ''."\n"; + $children[$depth] = 1; + } + + if ($_SESSION['s_cid'] == $content['content_id']) { + if (is_array($this->_menu[$content['content_id']])) { + $_SESSION['menu'][$content['content_id']] = 1; + } + } + + if (isset($_SESSION['menu'][$content['content_id']]) && $_SESSION['menu'][$content['content_id']] == 1) { + if ($on) { + echo ''._AT('collapse').''."\n"; + + } else { + echo ''."\n"; + echo ''._AT('collapse').''."\n"; + echo ''."\n"; + } + } else { + if ($on) { + echo ''._AT('collapse').''."\n"; + + } else { + echo ''."\n"; + echo ''._AT('expand').''; + echo ''."\n"; + } + } + + } else { + /* doesn't have children */ + if ($counter == $num_items) { + for ($i=0; $i<$depth; $i++) { + if ($children[$i] == 1) { + echo ''."\n"; + } else { + echo ''."\n"; + } + } + echo ''."\n"; + } else { + for ($i=0; $i<$depth; $i++) { + if ($children[$i] == 1) { + echo ''."\n"; + } else { + echo ''."\n"; + } + } + echo ''."\n"; + } + echo ''."\n"; + } + + + echo $link; + + echo "\n
    \n\n"; + + if ( $ignore_state || (isset($_SESSION['menu'][$content['content_id']]) && $_SESSION['menu'][$content['content_id']] == 1)) { + + $depth ++; + + $this->printMenu($content['content_id'], + $depth, + $path.$counter.'.', + $children, + $truncate, + $ignore_state, + $from); + + + $depth--; + + } + $counter++; + } // end of foreach + + print "
    \n\n"; + } + } + + /* @See include/html/editor_tabs/properties.inc.php + @See editor/arrange_content.php + + $print_type: "movable" or "related_content" + */ + function printActionMenu($menu, $parent_id, $depth, $path, $children, $print_type = 'movable') { + + global $cid, $_my_uri, $_base_path, $rtl; + + static $end; + + $top_level = $menu[$parent_id]; + + if ( is_array($top_level) ) { + $counter = 1; + $num_items = count($top_level); + foreach ($top_level as $current_num => $content) { + if (isset($content['test_id'])){ + continue; + } + + $link = $buttons = ''; + + echo ''."\n"; + + if ($print_type == 'movable') + { + if ($content['content_id'] == $_POST['moved_cid']) { + $radio_selected = ' checked="checked" '; + } + else { + $radio_selected = ''; + } + + $buttons = ''."\n". + ' '."\n". + ' '."\n"; + + if ($current_num + 1 == count($top_level)) + $buttons .= ' '."\n"; + + $buttons .= ' '."\n". + ''."\n". + ''; + + if ($content['content_type'] == CONTENT_TYPE_FOLDER) + $buttons .= ''; + else + $buttons .= ' '; + + $buttons .= ''."\n". + ''."\n"; + } + + $buttons .= ''."\n"; + if ($print_type == "related_content") + { + if ($content['content_type'] == CONTENT_TYPE_CONTENT || $content['content_type'] == CONTENT_TYPE_WEBLINK) + { + $link .= ''; + } + $link .= ' '."\n"; + + if ( is_array($menu[$content['content_id']]) && !empty($menu[$content['content_id']]) ) { + /* has children */ + + for ($i=0; $i<$depth; $i++) { + if ($children[$i] == 1) { + echo $buttons; + unset($buttons); + if ($end && ($i==0)) { + echo ''; + } else { + echo ''; + } + } else { + echo ''; + } + } + + if (($counter == $num_items) && ($depth > 0)) { + echo ''; + $children[$depth] = 0; + } else { + echo $buttons; + if (($num_items == $counter) && ($parent_id == 0)) { + echo ''; + $end = true; + } else { + echo ''; + } + $children[$depth] = 1; + } + + if ($_SESSION['s_cid'] == $content['content_id']) { + if (is_array($menu[$content['content_id']])) { + $_SESSION['menu'][$content['content_id']] = 1; + } + } + + if ($_SESSION['menu'][$content['content_id']] == 1) { + echo ''._AT('toggle_disabled').''; + + } else { + echo ''._AT('toggle_disabled').''; + } + + } else { + /* doesn't have children */ + if ($counter == $num_items) { + if ($depth) { + echo $buttons; + for ($i=0; $i<$depth; $i++) { + if ($children[$i] == 1) { + if ($end && ($i == 0)) { + echo ''; + } else { + echo ''; + } + } else { + echo ''; + } + } + } else { + echo $buttons; + } + echo ''; + } else { + if ($depth) { + echo $buttons; + $print = false; + for ($i=0; $i<$depth; $i++) { + if ($children[$i] == 1) { + if ($end && !$print) { + $print = true; + echo ''; + } else { + echo ''; + } + } else { + echo ''; + } + } + $print = false; + } else { + echo $buttons; + } + + echo ''; + } + echo ''; + } + + echo ''; + if($_SESSION['prefs']['PREF_NUMBERING']){ + echo $path.$counter; + } + + echo $link; + + echo ''."\n".''."\n"; + + $this->printActionMenu($menu, + $content['content_id'], + ++$depth, + $path.$counter.'.', + $children, + $print_type); + $depth--; + + $counter++; + } + } + } + + /* returns the timestamp of release if this page has not yet been released, or is under a page that has not been released, true otherwise */ + /* finds the max(timestamp) of all parents and returns that, true if less than now */ + /* Access: public */ + function isReleased($cid) { + if ($this->_menu_info[$cid]['content_parent_id'] == 0) { + // this $cid has no parent, so we check its release date directly + if ($this->_menu_info[$cid]['u_release_date'] <= time()) { + // yup! it's released + return true; + } else { + // nope! not released + return $this->_menu_info[$cid]['u_release_date']; + } + } + // this is a sub page, need to check ALL its parents + $parent = $this->isReleased($this->_menu_info[$cid]['content_parent_id']); // recursion + + if ($parent !== TRUE && $parent > $this->_menu_info[$cid]['u_release_date']) { + return $parent; + } else if ($this->_menu_info[$cid]['u_release_date'] <= time()) { + return true; + } else { + return $this->_menu_info[$cid]['u_release_date']; + } + } + + /* returns the first test_id if this page has pre-test(s) to be passed, + * or is under a page that has pre-test(s) to be passed, + * 0 if has no pre-test(s) to be passed + * -1 if one of the pre-test(s) has expired, the content should not be displayed in this case + * Access: public + */ + function getPretest($cid) { + $this_pre_test_id = $this->getOnePretest($cid); + + if ($this->_menu_info[$cid]['content_parent_id'] == 0) { + // this $cid has no parent, so we check its release date directly + return $this_pre_test_id; + } + + // this is a sub page, need to check ALL its parents + $parent_pre_test_id = $this->getOnePretest($this->_menu_info[$cid]['content_parent_id']); + + if ($this_pre_test_id > 0 || $this_pre_test_id == -1) + return $this_pre_test_id; + else if ($parent_pre_test_id > 0 || $parent_pre_test_id == -1) + return $parent_pre_test_id; + else + return 0; + } + + /* returns the first test_id if this content has pre-test(s) to be passed, + * 0 if has no pre-test(s) to be passed + * -1 if one of the pre-test(s) has expired, the content should not be displayed in this case + * Access: public + */ + function getOnePretest($cid) { + global $db, $msg; + include_once(AT_INCLUDE_PATH.'../mods/_standard/tests/lib/test_result_functions.inc.php'); + + $sql = "SELECT *, UNIX_TIMESTAMP(t.start_date) AS start_date, UNIX_TIMESTAMP(t.end_date) AS end_date + FROM ".TABLE_PREFIX."tests t, ".TABLE_PREFIX."content_prerequisites cp + WHERE cp.content_id=".$cid." + AND cp.type = '".CONTENT_PRE_TEST."' + AND cp.item_id=t.test_id"; + $result= mysql_query($sql, $db); + + while ($row = mysql_fetch_assoc($result)) + { + // check to make sure we can access this test + if (!$row['guests'] && ($_SESSION['enroll'] == AT_ENROLL_NO || $_SESSION['enroll'] == AT_ENROLL_ALUMNUS)) { + $msg->addInfo('NOT_ENROLLED'); + } + + if (!$row['guests'] && !authenticate_test($row['test_id'])) { + $msg->addInfo(array('PRETEST_NO_PRIV',$row['title'])); + } + + // if the test is not release, not allow student to view the content + if ($row['start_date'] > time() || $row['end_date'] < time()) { + $msg->addInfo(array('PRETEST_EXPIRED',$row['title'])); + return -1; + } + + $sql = "SELECT tr.result_id, count(*) num_of_questions, sum(ta.score) score, sum(tqa.weight) total_weight + FROM ".TABLE_PREFIX."tests_results tr, ".TABLE_PREFIX."tests_answers ta, ".TABLE_PREFIX."tests_questions_assoc tqa + WHERE tr.test_id = ".$row['test_id']." + AND tr.member_id = ".$_SESSION['member_id']." + AND tr.result_id = ta.result_id + AND tr.test_id = tqa.test_id + AND ta.question_id = tqa.question_id + GROUP BY tr.result_id"; + $result_score = mysql_query($sql, $db); + + $num_of_attempts = 0; + while ($row_score = mysql_fetch_assoc($result_score)) + { + // skip the test when: + // 1. no pass score is defined. this is a survey. + // 2. the student has passed the test + // 3. the test has no question + if (($row['passscore'] == 0 && $row['passpercent'] == 0) || + $row_score['num_of_questions'] == 0 || + ($row['passscore']<>0 && $row_score['score']>=$row['passscore']) || + ($row['passpercent']<>0 && ($row_score['score']/$row_score['total_weight']*100)>=$row['passpercent'])) + continue 2; + + $num_of_attempts++; + } + + if ($row['num_takes'] != AT_TESTS_TAKE_UNLIMITED && $num_of_attempts >= $row['num_takes']) + { + $msg->addInfo(array('PRETEST_FAILED',$row['title'])); + } + else + return $row['test_id']; + } + return 0; + } + + /** + * Return true if this content page allows export, else false. + * @param int content id + * @return true if 'allow_test_export'==1 || is instructor || oauth export into Transformable + */ + function allowTestExport($content_id){ + if (isset($_SESSION['is_admin']) || (isset($_REQUEST['m']) && isset($_REQUEST['c']))) { + return true; + } + $sql = "SELECT allow_test_export FROM ".TABLE_PREFIX."content WHERE content_id=$content_id"; + $result = mysql_query($sql, $this->db); + if ($row = mysql_fetch_assoc($result)){ + if ($row['allow_test_export'] == 1){ + return true; + } + return false; + } + return false; + } + + /** + * This function returns an array of content tools' shortcuts + * @access: public + * @param: $content_row: an array of the current content information + * @return: an array of all the tool shortcuts that apply to the current content or content folder + */ + public static function getToolShortcuts($content_row) + { + global $_base_href, $contentManager; + + $shortcuts = array(); + if (( ($content_row['r_date'] <= $content_row['n_date']) + && ((!$content_row['content_parent_id'] && ($_SESSION['packaging'] == 'top')) + || ($_SESSION['packaging'] == 'all')) + ) || authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) { + $shortcuts[] = array('title' => _AT('export_content'), 'url' => $_base_href . 'mods/_core/imscp/ims_export.php?cid='.$content_row['content_id'], 'icon' => $_base_href . 'images/download.png'); + } + + if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) { + if ($content_row['content_type'] == CONTENT_TYPE_CONTENT) { + $shortcuts[] = array( + 'title' => _AT('edit_this_page'), + 'url' => $_base_href . 'mods/_core/editor/edit_content.php?cid='.$content_row['content_id'], + 'icon' => $_base_href . 'images/medit.gif'); + } + $shortcuts[] = array( + 'title' => _AT('add_sibling_folder'), + 'url' => $_base_href.'mods/_core/editor/edit_content_folder.php?pid='.$contentManager->_menu_info[$content_row['content_id']]['content_parent_id'], + 'icon' => $_base_href . 'images/folder_new_sibling.gif'); + + if ($content_row['content_type'] == CONTENT_TYPE_FOLDER) { + $shortcuts[] = array( + 'title' => _AT('add_sub_folder'), + 'url' => $_base_href . 'mods/_core/editor/edit_content_folder.php?pid='.$content_row['content_id'], + 'icon' => $_base_href . 'images/folder_new_sub.gif'); + } + + $shortcuts[] = array( + 'title' => _AT('add_sibling_page'), + 'url' => $_base_href.'mods/_core/editor/edit_content.php?pid='.$contentManager->_menu_info[$content_row['content_id']]['content_parent_id'], + 'icon' => $_base_href . 'images/page_add_sibling.gif'); + + if ($content_row['content_type'] == CONTENT_TYPE_CONTENT) { + $shortcuts[] = array( + 'title' => _AT('delete_this_page'), + 'url' => $_base_href . 'mods/_core/editor/delete_content.php?cid='.$content_row['content_id'], + 'icon' => $_base_href . 'images/page_delete.gif'); + } + else if ($content_row['content_type'] == CONTENT_TYPE_FOLDER) { + $shortcuts[] = array( + 'title' => _AT('add_sub_page'), + 'url' => $_base_href . 'mods/_core/editor/edit_content.php?pid='.$content_row['content_id'], + 'icon' => $_base_href . 'images/page_add_sub.gif'); + $shortcuts[] = array( + 'title' => _AT('delete_this_folder'), + 'url' => $_base_href . 'mods/_core/editor/delete_content.php?cid='.$content_row['content_id'], + 'icon' => $_base_href . 'images/page_delete.gif'); + } + } + + return $shortcuts; + } +} + +?> \ No newline at end of file diff --git a/include/classes/ContentOutputUtils.class.php b/include/classes/ContentOutputUtils.class.php new file mode 100644 index 000000000..fb99fe38e --- /dev/null +++ b/include/classes/ContentOutputUtils.class.php @@ -0,0 +1,50 @@ +[\W]*

    /"); +define("REPLACE", "\n"); +define("CODESTART", "[code]"); +define("CODEEND", "[/code]"); + +class ContentOutputUtils { + /** + * Recursive function which strips

    tags from between [code] tags and + * replaces them with line feeds + * + * @param string $text + * @return string + */ + public function stripPtags($text) { + $codestart = strpos($text, CODESTART); + if ($codestart == FALSE) { + return $text; + } + else { + $codestart += strlen(CODESTART); + $firstpart = substr($text, 0, $codestart); + + $codeend = strpos($text, CODEEND) + strlen(CODEEND); + $lastpart = substr($text, $codeend); + + $codetext = substr($text, $codestart, $codeend - $codestart); + $newcodetext = preg_replace(PATTERN, REPLACE, $codetext); + $result = $firstpart.$newcodetext.$this->stripPtags($lastpart); + } + return $result; + } +} +?> \ No newline at end of file diff --git a/include/classes/ErrorHandler/ErrorHandler.class.php b/include/classes/ErrorHandler/ErrorHandler.class.php new file mode 100644 index 000000000..1f9bd7e2f --- /dev/null +++ b/include/classes/ErrorHandler/ErrorHandler.class.php @@ -0,0 +1,529 @@ +setFlags(); // false by default + set_error_handler(array(&$this, 'ERROR_HOOK')); + $this->container = array(); + + /** + * check first if the log directory is setup, if not then create a logs dir with a+w && a-r + */ + if (!file_exists(AT_CONTENT_DIR . 'logs/') || !realpath(AT_CONTENT_DIR. 'logs/')) { + $this->makeLogDir(); + } else if (!is_dir(AT_CONTENT_DIR .'logs/')) { + $this->makeLogDir(); + } + } + + /** + * The error handling routine set by set_error_handler(). Mimicking and Exception implementation in OOA + * Must be as quick as possible. Note a few: '\n' -> chr(10) - avoids inline translation in php engine + * 'single quotes', avoid translation again + * Ability define custom errors. See case 'VITAL': + * + * eg call from script, trigger_error('VITAL#There was a problem with the database.','E_USER_ERROR'); + * + * @param string $error_type The type of error being handled. + * @param string $error_msg The error message being handled. + * @param string $error_file The file in which the error occurred. + * @param integer $error_ln The line in which the error occurred. + * @param string $error_context The context in which the error occurred. + * @return Boolean + * @access public + */ + function ERROR_HOOK($error_type, $error_msg, $error_file, $error_ln, $error_context) { + if ($error_type == E_NOTICE || $error_type == E_USER_NOTICE) return; + + $val_phpinfo = ''; + $val_phpinfo_foot = ''; + $val_phpinfo_printed = false; // used to track for the scope of this method whether the server + // has been attached to a log file or e-mail buffer previously + + /** + * Only produce the server configuration once per file + */ + if ($this->todayLogFileExists() === false) { + // lets get some info about the system used by all error codes + ob_start(); + + // grab usefull data from php_info + phpinfo(INFO_GENERAL ^ INFO_CONFIGURATION); + $val_phpinfo .= ob_get_contents(); + ob_end_clean(); + + /* + * Parse $val_phpinfo + */ + + // get a substring of the php info to get rid of the html, head, title, etc. + $val_phpinfo = substr($val_phpinfo, 760, -19); + $val_phpinfo = substr($val_phpinfo, 552); + $val_phpinfo = substr($val_phpinfo, strpos($val_phpinfo, 'System')); + $val_phpinfo .= chr(10); + + $msql_str = ''; + if (defined('MYSQL_NUM')) + $msql_str = "Yes"; + else + $msql_str = "No"; + + $val_phpinfo .= 'MySQL installed? ' . $msql_str . '

    '; + + // replace the 's with tabs and the $nbsp;'s with spaces + $val_phpinfo = str_replace( '', ' ', $val_phpinfo); + $val_phpinfo = str_replace( ' ', ' ', $val_phpinfo); + $val_phpinfo = str_replace( '', ' ', $val_phpinfo); + $val_phpinfo = str_replace( '', ' ', $val_phpinfo); + + $val_phpinfo = str_replace('This program makes use of the Zend Scripting Language Engine:
    Zend Engine v1.3.0, Copyright (c) 1998-2003 Zend Technologies', '', $val_phpinfo); + + $val_phpinfo_foot .= '$_ENV:' . chr(10) . $this->debug($_ENV) . '

    '; + } + + // Everytime + $val_phpuser = '$_SESSION:' . chr(10) . $this->debug($_SESSION); + $val_phpuser .= '$_REQUEST:' . chr(10) . $this->debug($_REQUEST); + $val_phpuser .= '$_COOKIE:' . chr(10) . $this->debug($_COOKIE); + $val_phpuser .= '$_GET:' . chr(10) . $this->debug($_GET); + $val_phpuser .= '$_POST:' . chr(10) . $this->debug($_POST); + + // replace the 's with tabs and the $nbsp;'s with spaces + $val_phpuser = str_replace( '', ' ', $val_phpuser); + $val_phpuser = str_replace( ' ', ' ', $val_phpuser) . '
    '; + + switch($error_type) { + + case E_ERROR: // caught below + case E_USER_ERROR: + + if (substr_count($error_msg, "#") > 0) { + $_error = explode("#", $error_msg); + } else { + $_error = array('', $error_msg); + } + + /** + * eg call, trigger_error('VITAL;There was a problem with the database.',E_USER_ERROR); + * + * List of custom errors go here and the appropriate action is taken + *@ + */ + switch($_error[0]) { + /** + * Custom errors are not guaranteed to be printed for example in footer.inc.php + * Hanlde on a case-by-case basis + */ + case 'VITAL': // @see vital.inc.php + if ($this->LOG_ERR_TO_FILE) { + if ($val_phpinfo_printed === true) { + $val_phpinfo = ''; + } + $this->log_to_files($val_phpinfo, $val_phpinfo_foot, 'ATutor v' . VERSION . '
    '. 'PHP ERROR MESSAGE:' . '

    ' + . $error_msg . ' (error type ' . $error_type . ' in ' + . $error_file . ' on line ' . $error_ln . ') [context: ' + . $error_context . ']

    ' . chr(10) .chr(10) . $val_phpuser ); + + $val_phpinfo_printed = true; + + } + + $this->printError('ATutor has detected an Error - ' . + $_error[1]); + + exit; // done here + break; + + case 'BKP_MEMBER': // @see TableBackup.class.php + if ($this->LOG_ERR_TO_FILE) { + if ($val_phpinfo_printed === true) { + $val_phpinfo = ''; + } + $this->log_to_files($val_phpinfo, $val_phpinfo_foot, 'ATutor v' . VERSION . '
    '. 'PHP ERROR MESSAGE:' . '

    ' + . $error_msg . ' (error type ' . $error_type . ' in ' + . $error_file . ' on line ' . $error_ln . ') [context: ' + . $error_context . ']

    ' . chr(10) .chr(10) . $val_phpuser ); + + $val_phpinfo_printed = true; + + } + + $this->printError('ATutor has detected an Error - ' . + $_error[1]); + + exit; + break; + + default: // standard user error without custom prefix + if ($this->LOG_ERR_TO_FILE) { + if ($val_phpinfo_printed === true) { + $val_phpinfo = ''; + } + + $this->log_to_files($val_phpinfo, $val_phpinfo_foot, 'ATutor v' . VERSION . '
    '. 'PHP ERROR MESSAGE:' . '

    ' + . $error_msg . ' (error type ' . $error_type . ' in ' + . $error_file . ' on line ' . $error_ln . ') [context: ' + . $error_context . ']

    ' . chr(10) .chr(10) . $val_phpuser); + + $val_phpinfo_printed = true; + } + } + + //$this->printError('ATutor has detected an Error - ' . 'Problem spot: ' . $error_msg . ' in ' + // . $this->stripbase($error_file) . ' on line ' . $error_ln); + array_push($this->container, 'Problem spot: ' . $error_msg . ' in ' . $this->stripbase($error_file) . ' on line ' . $error_ln); + + break; + + case E_WARNING: + case E_USER_WARNING: + if ($this->LOG_WARN_TO_FILE) { + if ($val_phpinfo_printed === true) { + $val_phpinfo = ''; + } + + $this->log_to_files($val_phpinfo, $val_phpinfo_foot, 'ATutor v' . VERSION . '
    '. 'PHP ERROR MESSAGE:' . '

    ' + . $error_msg . ' (error type ' . $error_type . ' in ' + . $error_file . ' on line ' . $error_ln . ') [context: ' + . $error_context . ']

    ' . chr(10) . chr(10) . $val_phpuser); + + $val_phpinfo_printed = true; + } + + //$this->printError('ATutor has detected an Error - ' . 'Problem spot: ' . $error_msg . ' in ' + // . $this->stripbase($error_file) . ' on line ' . $error_ln); + array_push($this->container, 'Problem spot: ' . $error_msg . ' in ' . $this->stripbase($error_file) . ' on line ' . $error_ln); + + break; + default: + } + + return true; + } + + /** +ÊÊ * Dump the current error into a file along with an updated profile for that error +ÊÊ * + * @param string the profile to log + * @param string the bug to log +ÊÊ * @access public +ÊÊ */ + function log_to_files($profile, $profile_foot, $buf) { + + if ($profile == '' || $profile_foor = '' || $buf == '') return; + + /** + * Redundancy control for profile/error log creation + */ + $profile_created = true; + $error_created = true; + + $php_head = '' . chr(10); + + // Lets make a unqiue profile key, strip away circumventors of the md5 hashing algo. @see md5 algo src + $temp = strip_tags($profile); + $temp = stripslashes($temp); + $temp = str_replace('/', ' ', $temp); + $temp = str_replace('\$', ' ', $temp); + $temp = str_replace('$', ' ', $temp); + $temp = str_replace('\&' , ' ', $temp); + $temp = str_replace('&' , ' ', $temp); + $temp = str_replace('*' , ' ', $temp); + $temp = str_replace('~' , ' ', $temp); + $temp = str_replace('.' , ' ', $temp); + $temp = str_replace(';' , ' ', $temp); + $temp = str_replace(':' , ' ', $temp); + $temp = str_replace('-' , ' ', $temp); + $temp = str_replace('_' , ' ', $temp); + $temp = str_replace('\'' , ' ', $temp); + $temp = str_replace(',' , ' ', $temp); + $temp = str_replace('@' , ' ', $temp); + $temp = str_replace('#' , ' ', $temp); + + $profile_key = md5($temp); + + $today = getdate(); + + // Uniqueness assumend to be coupled to epoch timestamp + $timestamp_ = $today['mon'] . '-' . $today['mday'] . '-' . $today['year']; + + /** + * Lets make sure we have a log directory made for today + */ + if (!is_dir(AT_CONTENT_DIR . 'logs/' . $timestamp_)) { // create it + $result = @mkdir(AT_CONTENT_DIR . 'logs/' . $timestamp_, 0771); // r+w for owner + + if ($result == 0) { + $this->printError('Fatal. Could not create /content/logs' . '/' . $timestamp_ . '. Please resolve'); + } + } // else already there + + /** + * Go through all the profiles in our directory and lets try and map our md5 key to one of them, + * if its not found then we must be dealing with a new profile, thus create it + */ + $dir_ = AT_CONTENT_DIR . 'logs/' . $timestamp_; + + if (!($dir = opendir($dir_))) { + $msg->printNoLookupFeedback('Could not access /content/logs/' . $timestamp_ . '. Check that the permission for the Server user are r+w to it'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + + exit; + } + + /** + * Run through the todays logs directory and lets get all the profiles + */ + $use_profile = null; + + // loop through folder todays log folder and try and match a profile to our error profile md5 key + while (($file = readdir($dir)) !== false) { + + /* if the name is not a directory */ + if( ($file == '.') || ($file == '..') || is_dir($file) ) { + continue; + } + + if (strpos($file, 'profile') >= 0) { + $check_key = substr($file, strpos($file, '_') + 1); + $check_key = substr($check_key, 0, strpos($check_key, '.log.php')); + + if ($check_key === $profile_key) { // found! + $use_profile = $file; + $profile_created = true; + break; + } + } + } + closedir($dir); // clean it up + + // if $use_profile == null here then we must create a new profile for this error + if ($use_profile == null) { + $use_profile = 'profile_' . $profile_key . '.log.php'; + if ($file_handle = fopen(AT_CONTENT_DIR . 'logs/' . $timestamp_ . '/' . $use_profile, "w")) { + if (!fwrite($file_handle, $php_head . chr(10) . $profile . $profile_foot)) { $profile_created = false; } + } else { $profile_created = false; } + fclose($file_handle); + } // else just use $use_profile as the profile for this error + + // if the creation of the profile_created = false then creation failed and we didnt have an already + // existant one in the dir, profile must exist + if ($profile_created === false) return; + + $timestamp = $timestamp_ . '_' . $today[0]; + + // create a unique error filename including the epoch timestamp + and the profile mapping + $unique_error_log = $timestamp . '_pr' . $profile_key; + + if (is_file(AT_CONTENT_DIR . 'logs/' . $timestamp_ . '/' . $unique_error_log)) { + $unique_error_log .= rand(); // should be enough + } + + $unique_error_log .= '.log.php'; // append suffix + + /* Create error log file */ + if ($file_handle = fopen(AT_CONTENT_DIR . 'logs/' . $timestamp_ . '/' . $unique_error_log, "w")) { + if (!fwrite($file_handle, $php_head . chr(10) . $buf)) { $error_created = false; } + } else { + $error_created = false; + } + fclose($file_handle); + + // check that we created a profile and its error or used an existing profile and created its error + if ($profile_created === true && $error_created === true) { // ok + chmod(AT_CONTENT_DIR . 'logs/' . $timestamp_ . '/' . $unique_error_log, 0771); + chmod(AT_CONTENT_DIR . 'logs/' . $timestamp_ . '/' . $use_profile, 0771); + } else if ($profile_create === true && $error_created === false) { // remove profile + unlink(AT_CONTENT_DIR . 'logs/' . $timestamp_ . '/' . $use_profile); + } + } + + /** +ÊÊ * Restores the error handler to the default error handler +ÊÊ * +ÊÊ * @return void +ÊÊ * @access public +ÊÊ */ + function restoreOrigHandler() { + restore_error_handler(); + } + + /** +Ê * Returns the error handler to ERROR_HOOK() +Ê * +ÊÊ * @return void +ÊÊ * @access public +ÊÊ */ + function returnHandler() { + set_error_handler(array(&$this, 'ERROR_HOOK')); + } + + /** +ÊÊ * Changes the logging preferences +ÊÊ * + * @param Boolean $error_flag Log errors to file? +ÊÊ * @param Boolean $warning_flag Log warnings to file? +ÊÊ * @return void +ÊÊ * @access public +ÊÊ */ + function setFlags($error_flag = true, $warning_flag = true) { + + $this->LOG_ERR_TO_FILE = $error_flag; + $this->LOG_WARN_TO_FILE = $warning_flag; + } + + /** + * Construct a nicely formatted tree view of a variable + * @param var String is the varialbe to construct the output from + * @access private + */ + function debug($var) { + $str_ = '
    ';
    +		
    +		ob_start();
    +		print_r($var);
    +		$str = ob_get_contents();
    +		ob_end_clean();
    +	
    +		$str = str_replace('<', '<', $str);
    +	
    +		$str = str_replace('[', '[', $str);
    +		$str = str_replace(']', ']', $str);
    +		$str = str_replace('=>', '=>', $str);
    +		$str = str_replace('Array', 'Array', $str);
    +		$str .= '
    '; + + $str = $str_ . $str; + return $str; + } + + /** + * Function which strips the path base off a file URL since it is a security risk + * @param String str is the path string + * @return String only the script filename where the error occured + */ + function stripbase($str) { + + $to_root = $_SERVER["PATH_TRANSLATED"]; + + $pos_last = strrpos($to_root, "/"); + $to_root = substr($to_root, $pos_last + 1); + return $to_root; + } + + /** + * Print the error to the browser, dont use any templates or css sheets for flexibility + * @access private + */ + function printError($str) { + if (!AT_DEVEL) return; + + echo '
    '; + echo ''; + echo ''; + echo ''; + echo ''; + echo '
    '; + echo '

    Internal Error Detected

    '; + echo '
      '; + echo '
    • '. $str .'
    • '; + echo'
    '; + echo '
    '; + echo '
    '; + } + + /** + * Create restricted access logs dir + */ + function makeLogDir() { + + $result = @mkdir(AT_CONTENT_DIR . 'logs', 0771); // r+w for owner + if ($result == 0) { + $this->printError('Fatal. Could not create /content/logs. Please resolve'); + } + + } + + /** + * Determine wheter a log file exists for today + * @access private + */ + function todayLogFileExists() { + $today = getdate(); + + $timestamp = $today['mon'] . '-' . $today['mday'] . '-' . $today['year']; + + return (is_file(AT_CONTENT_DIR . 'logs/' . $timestamp . '.log')); + } + + /** + * Run through $container and print all the errors on this page. + * Used to prevent errors from breaking content on the page + * @access public + */ + function showErrors() { + + foreach($this->container as $elem) { + $this->printError('ATutor has detected an Error - ' . + $elem); + unset($elem); + } + } +} +?> \ No newline at end of file diff --git a/include/classes/Message/Message.class.php b/include/classes/Message/Message.class.php new file mode 100644 index 000000000..1c62dbd68 --- /dev/null +++ b/include/classes/Message/Message.class.php @@ -0,0 +1,473 @@ + 'errormessage.tmpl.php', + 'feedback' => 'feedbackmessage.tmpl.php', + 'warning' => 'warningmessage.tmpl.php', + 'info' => 'infomessage.tmpl.php', + 'help' => 'helpmessage.tmpl.php', + 'confirm' => 'confirmmessage.tmpl.php' + ); + + /* + * Static assoc array of message types mapped to Language code prefixes + * @access private + * @see /include/lib/lang_constant.inc.php + * @var array + */ + var $prefix = array( 'error' =>'AT_ERROR_', + 'feedback' => 'AT_FEEDBACK_', + 'warning' => 'AT_WARNING_', + 'info' => 'AT_INFOS_', + 'help' => 'AT_HELP_', + 'confirm' => 'AT_CONFIRM_' + ); + + /** + * Constructor + * @access public + * @param obj $savant Reference to Savant object + * @author Jacek Materna + */ + function Message($savant) { + $this->savant = $savant; + } + + /** + * Print message(s) of type $type. Processes stored messages in session var for type $type + * and translates them into language spec. Then passes processed data to savant template for display + * @access public + * @param string $type error|warning|info|feedback|help|help_pop + * @author Jacek Materna + */ + function printAbstract($type) { + if (!isset($_SESSION['message'][$type])) return; + + $_result = array(); + + foreach($_SESSION['message'][$type] as $e => $item) { + $result = ''; + + if ($type == 'confirm') { + // the confirm msg's have the hidden vars as the last element in the array + $hide_button_no = array_pop($item); + $button_no_text = array_pop($item); + $button_yes_text = array_pop($item); + $hidden_vars = array_pop($item); + + if (count($item) == 1) { + $item = $item[0]; + } + } + + // $item is either just a code or an array of argument with a particular code + if (is_array($item)) { + + + /* this is an array with terms to replace */ + $first = array_shift($item); + $result = _AT($first); // lets translate the code + + if ($result == '') { // if the code is not in the db lets just print out the code for easier trackdown + $result = '[' . $first . ']'; + } + + $terms = $item; + + /* replace the tokens with the terms */ + $result = vsprintf($result, $terms); + + } else { + $result = _AT($item); + if ($result == '') // if the code is not in the db lets just print out the code for easier trackdown + $result = '[' . $item . ']'; + } + + array_push($_result, $result); // append to array + } + + if (count($_result) > 0) { + $this->savant->assign('item', $_result); // pass translated payload to savant var for processing + + if ($type == 'confirm') { + $this->savant->assign('hidden_vars', $hidden_vars); + $this->savant->assign('button_yes_text', $button_yes_text); + $this->savant->assign('button_no_text', $button_no_text); + $this->savant->assign('hide_button_no', $hide_button_no); + + } else if ($type == 'help') { // special case for help message, we need to check a few conditions + $a = (!isset($_GET['e']) && !$_SESSION['prefs']['PREF_HELP'] && !$_GET['h']); + $b = ($_SESSION['prefs']['PREF_CONTENT_ICONS'] == 2); + $c = isset($_GET['e']); + $d = $_SESSION['course_id']; + + $this->savant->assign('a', $a); + $this->savant->assign('b', $b); + $this->savant->assign('c', $c); + $this->savant->assign('d', $d); + } + + $this->savant->display($this->tmpl[$type]); + } + + unset($_SESSION['message'][$type]); + } + + /** + * Add message to be tracked by session obj + * @access public + * @param string $sync ref to type of message + * @param string|array $code code of the message or array(code, args...) + * @author Jacek Materna + */ + function addAbstract($sync, $code) { + $first = ''; // key value for storage + // Convert to strings + if (is_array($code)) { + foreach($code as $e) { + settype($e, "string"); + } + + $code[0] = $this->prefix[$sync] . $code[0]; // add prefix + + $first = $code[0]; + } else { + if (!is_string($code)) + settype($code, "string"); + + $code = $this->prefix[$sync] . $code; + $first = $code; + } + + $payload = $code; + + if (!isset($_SESSION['message'][$sync]) || count($_SESSION['message'][$sync]) == 0) { // fresh + + // PHP 5 + //try { + $_SESSION['message'][$sync] = array($first => $payload); + //} catch (Exception $e) { + // return false; + //} + } else if (isset($_SESSION['message'][$sync][$first])) { // already data there for that code, append + // existing data is either a collection or a single node + if(is_array($_SESSION['message'][$sync][$first])) { // already an array there + if (is_array($payload)) { + // lets ignore the code, its already there as the first element + $elem = array_shift($payload); + foreach($payload as $elem) { + array_push($_SESSION['message'][$sync][$first], $elem); // add ourselves to the chain + } + } else // no array here yet + $_SESSION['message'][$sync][$first][] = $payload; // add ourselves + + } else { // just a string + if (is_array($payload)) { + $temp = $_SESSION['message'][$sync][$first]; // grab it + unset($_SESSION['message'][$sync][$first]); // make sure its gone + + $arr = array($temp); + + // skip first elem, we're asserting here that $first === $payload[0] + $grb = array_shift($payload); + foreach($payload as $elem) { // lets finish building the array + array_push($arr, $elem); + } + + $_SESSION['message'][$sync][$first] = $arr; // put it back + } + } + } else { + + // Already an array there, could be empty or have something in it, append. + // Store key = value for much faster unset as needed + + // PHP 5 + //try { + $new = array($first => $payload); + $final = array_merge((array) $_SESSION['message'][$sync], (array) $new); + + unset($_SESSION['message'][$sync]); + $_SESSION['message'][$sync] = $final; + //} catch (exception $e) { + // return false; + //} + } + } + + /** + * Simply check is a type $type message isset in the session obj + * @access public + * @param string $type what type of message to check for + * @author Jacek Materna + */ + function abstractContains($type) { + return (isset($_SESSION['message'][$type])); + } + + /** + * Deletes the tracked message code $code from the Session obj as well as all + * if its children + * @access public + * @param string $type what type of message to delete + # @param string $code The code to delete + * @author Jacek Materna + */ + function abstractDelete($type, $code) { + if (!is_string($code)) + settype($code, "string"); + + // Lets append the right prefic to this code for searching + $code = $this->prefix[$type] . $code; + + if(isset($_SESSION['message'][$type][$code])) { + unset($_SESSION['message'][$type][$code]); // delete it and its children + } + } + + /** + * Add error message to be tracked by session obj + * @access public + * @param string|array $code code of the message or array(code, args...) + * @author Jacek Materna + */ + function addError($code) { + $this->addAbstract('error', $code); + } + + /** + * Print error messages using Savant template + * @access public + * @author Jacek Materna + */ + function printErrors($optional=null) { + if ($optional != null) // shortcut + $this->addAbstract('error', $optional); + + $this->printAbstract('error'); + } + + + function addConfirm($code, $hidden_vars = '', $button_yes_text='', $button_no_text='', $hide_button_no=false) { + $hidden_vars_string = ''; + if (is_array($hidden_vars)) { + foreach ($hidden_vars as $key => $value) { + $hidden_vars_string .= ''; + } + } + if (!is_array($code)) { + $code = array($code); + } + $code[] = $hidden_vars_string; + $code[] = ($button_yes_text == '') ? _AT("submit_yes") : $button_yes_text; + $code[] = ($button_no_text == '') ? _AT("submit_no") : $button_no_text; + $code[] = $hide_button_no; + + $this->addAbstract('confirm', $code); + } + + function printConfirm($optional=null) { + if ($optional != null) // shortcut + $this->addAbstract('confirm', $optional); + + $this->printAbstract('confirm'); + } + + /** + * Add warning message to be tracked by session obj + * @access public + * @param string|array $code code of the message or array(code, args...) + * @author Jacek Materna + */ + function addWarning($code) { + $this->addAbstract('warning', $code); + } + + /** + * Print warning messages using Savant template + * @access public + * @author Jacek Materna + */ + function printWarnings($optional=null) { + if ($optional != null) // shortcut + $this->addAbstract('warning', $optional); + + $this->printAbstract('warning'); + } + + /** + * Add info message to be tracked by session obj + * @access public + * @param string|array $code code of the message or array(code, args...) + * @author Jacek Materna + */ + function addInfo($code) { + $this->addAbstract('info', $code); + } + + /** + * Print info messages using Savant template + * @access public + * @author Jacek Materna + */ + function printInfos($optional=null) { + if ($optional != null) // shortcut + $this->addAbstract('info', $optional); + + $this->printAbstract('info'); + } + + /** + * Add feedback message to be tracked by session obj + * @access public + * @param string|array $code code of the message or array(code, args...) + * @author Jacek Materna + */ + function addFeedback($code) { + $this->addAbstract('feedback', $code); + } + + /** + * Print feedback messages using Savant template + * @access public + * @author Jacek Materna + */ + function printFeedbacks($optional=null) { + if ($optional != null) // shortcut + $this->addAbstract('feedback', $optional); + + $this->printAbstract('feedback'); + } + + /** + * Add help message to be tracked by session obj + * @access public + * @param string|array $code code of the message or array(code, args...) + * @author Jacek Materna + */ + function addHelp($code) { + $this->addAbstract('help', $code); + } + + /** + * Print help messages using Savant template + * @access public + * @author Jacek Materna + */ + function printHelps($optional=null) { + if ($optional != null) // shortcut + $this->addAbstract('help', $optional); + + $this->printAbstract('help'); + } + + /** + * Dump all the messages in the session to the screen in the following order + * @access public + * @author Jacek Materna + */ + function printAll() { + $this->printAbstract('feedback'); + $this->printAbstract('error'); + $this->printAbstract('warning'); + $this->printAbstract('help'); + $this->printAbstract('info'); + } + + /** + * Print feedback message using Savant template with no Session dialog and + * no database dialog, straight text inside feedback box + * @access public + * @param String String message to display inside feedback box + * @author Jacek Materna + */ + function printNoLookupFeedback($str) { + if (str != null) { + $this->savant->assign('item', array($str)); // pass string to savant var for processing + $this->savant->display($this->tmpl['feedback']); + } + } + + /** + * Method which simply check if a particular message type exists in the session obj + */ + function containsErrors() { + return $this->abstractContains('error'); + } + + function containsFeedbacks() { + return $this->abstractContains('feedback'); + } + + function containsWarnings() { + return $this->abstractContains('warning'); + } + + function containsInfos() { + return $this->abstractContains('info'); + } + + function containsHelps() { + return $this->abstractContains('help'); + } + + /** + * Method that allow deletion of individual Message codes form the Session obj + */ + function deleteError($code) { + $this->abstractDelete('error', $code); + } + + function deleteFeedback($code) { + $this->abstractDelete('feedback', $code); + } + + function deleteWarning($code) { + $this->abstractDelete('warning', $code); + } + + function deleteInfo($code) { + $this->abstractDelete('info', $code); + } + + function deleteHelp($code) { + $this->abstractDelete('help', $code); + } + +} // end of class + + + +?> \ No newline at end of file diff --git a/include/classes/Savant2/Savant2.php b/include/classes/Savant2/Savant2.php new file mode 100644 index 000000000..9dc6ebd4d --- /dev/null +++ b/include/classes/Savant2/Savant2.php @@ -0,0 +1,1793 @@ + 'assign() parameters not correct', + SAVANT2_ERROR_ASSIGNREF => 'assignRef() parameters not correct', + SAVANT2_ERROR_COMPILER => 'compiler not an object or has no compile() method', + SAVANT2_ERROR_NOFILTER => 'filter file not found', + SAVANT2_ERROR_NOPLUGIN => 'plugin file not found', + SAVANT2_ERROR_NOSCRIPT => 'compiled template script file not found', + SAVANT2_ERROR_NOTEMPLATE => 'template source file not found', + SAVANT2_ERROR_COMPILE_FAIL => 'template source failed to compile' + ); +} + + +/** +* +* Provides an object-oriented template system. +* +* Savant2 helps you separate model logic from view logic using PHP as +* the template language. By default, Savant2 does not compile templates. +* However, you may pass an optional compiler object to compile template +* source to include-able PHP code. +* +* Please see the documentation at {@link http://phpsavant.com/}, and be +* sure to donate! :-) +* +* $Id: Savant2.php,v 1.32 2006/03/05 16:58:38 pmjones Exp $ +* +* @author Paul M. Jones +* +* @package Savant2 +* +* @version 2.4.3 stable +* +* @license LGPL http://www.gnu.org/copyleft/lesser.html +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 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 +* Lesser General Public License for more details. +* +*/ + +class Savant2 { + + + /** + * + * PHP5 ONLY: Whether or not to use __autoload(). Default is false. + * + * @access private + * + * @var bool + * + */ + + var $_autoload = false; + + + /** + * + * PHP5 ONLY: What method __call() will alias to. + * + * Generally 'plugin' or 'splugin' (as __call() is intended for those). + * + * @access private + * + * @var string + * + */ + + var $_call = 'plugin'; + + + /** + * + * The custom compiler (pre-processor) object, if any. + * + * @access private + * + * @var object + * + */ + + var $_compiler = null; + + + /** + * + * The class type to use when instantiating error objects. + * + * @access private + * + * @var string + * + */ + + var $_error = null; + + + /** + * + * Array of callbacks used to escape output. + * + * @access private + * + * @var array + * + * @see setEscape() + * + * @see addEscape() + * + * @see escape() + * + * @see _() + * + */ + + var $_escape = array('htmlspecialchars'); + + + /** + * + * Whether or not to extract assigned variables into fetch() scope. + * + * When true, all variables and references assigned to Savant2 are + * extracted into the local scope of the template script at fetch() + * time, and may be addressed as "$varname" instead of + * "$this->varname". The "$this->varname" notation will also work. + * + * When false, you //must// use "$this->varname" in your templates to + * address a variable instead of "$varname". This has three + * benefits: speed (no time spent extracting variables), memory use + * (saves RAM by not making new references to variables), and clarity + * (any $this->varname is obviously an assigned var, and vars created + * within the template are not prefixed with $this). + * + * @access private + * + * @var bool + * + */ + + var $_extract = false; + + + /** + * + * The output of the template script. + * + * @access private + * + * @var string + * + */ + + var $_output = null; + + + /** + * + * The set of search directories for resources (plugins/filters) and + * templates. + * + * @access private + * + * @var array + * + */ + + var $_path = array( + 'resource' => array(), + 'template' => array() + ); + + + /** + * + * Array of resource (plugin/filter) object instances. + * + * @access private + * + * @var array + * + */ + + var $_resource = array( + 'plugin' => array(), + 'filter' => array() + ); + + + /** + * + * Whether or not to automatically self-reference in plugins and filters. + * + * @access private + * + * @var bool + * + */ + + var $_reference = false; + + + /** + * + * Whether or not to restrict template includes only to registered paths. + * + * @access private + * + * @var bool + * + */ + + var $_restrict = false; + + + /** + * + * The path to the compiled template script file. + * + * By default, the template source and template script are the same file. + * + * @access private + * + * @var string + * + */ + + var $_script = null; + + + /** + * + * The name of the default template source file. + * + * @access private + * + * @var string + * + */ + + var $_template = null; + + + // ----------------------------------------------------------------- + // + // Constructor and general property setters + // + // ----------------------------------------------------------------- + + + /** + * + * Constructor. + * + * @access public + * + * @param array $conf An associative array of configuration keys for + * the Savant2 object. Any, or none, of the keys may be set. The + * keys are: + * + * 'template_path' => The default path string or array of directories + * to search for templates. + * + * 'resource_path' => The default path string or array of directories + * to search for plugin and filter resources. + * + * 'error' => The custom error class that Savant2 should use + * when returning errors. + * + * 'extract' => Whether or not to extract variables into the local + * scope when executing a template. + * + * 'template' => The default template source name to use. + * + */ + + function Savant2($conf = array()) + { + // set the default template search dirs + if (isset($conf['template_path'])) { + // user-defined dirs + $this->setPath('template', $conf['template_path']); + } else { + // default directory only + $this->setPath('template', null); + } + + // set the default filter search dirs + if (isset($conf['resource_path'])) { + // user-defined dirs + $this->setPath('resource', $conf['resource_path']); + } else { + // default directory only + $this->setPath('resource', null); + } + + // do we allow __autoload() use? + if (isset($conf['autoload'])) { + $this->setAutoload($conf['autoload']); + } + + // set the error class + if (isset($conf['error'])) { + $this->setError($conf['error']); + } + + // set the extraction flag + if (isset($conf['extract'])) { + $this->setExtract($conf['extract']); + } + + // set the restrict flag + if (isset($conf['restrict'])) { + $this->setRestrict($conf['restrict']); + } + + // set the Savant reference flag + if (isset($conf['reference'])) { + $this->setReference($conf['reference']); + } + + // set the default template + if (isset($conf['template'])) { + $this->setTemplate($conf['template']); + } + + // set the output escaping callbacks + if (isset($config['escape'])) { + call_user_func_array( + array($this, 'setEscape'), + (array) $config['escape'] + ); + } + } + + /** + * + * Sets whether or not __autoload() is used when loading classes. + * + * @access public + * + * @param bool $flag True to use __autoload(), false to not use it. + * + * @return void + * + */ + + function setAutoload($flag) { + $this->_autoload = (bool) $flag; + } + + + /** + * + * Sets a custom compiler/pre-processor for template sources. + * + * By default, Savant2 does not use a compiler; use this to set your + * own custom compiler (pre-processor) for template sources. + * + * @access public + * + * @param object $compiler The compiler object; it must have a + * "compile()" method. If null or false, the current compiler object + * is removed from Savant2. + * + * @return void + * + * @throws object An error object with a SAVANT2_ERROR_COMPILER code. + * + */ + + function setCompiler(&$compiler) + { + if (! $compiler) { + // nullify any current compiler + $this->_compiler = null; + } elseif (is_object($compiler) && method_exists($compiler, 'compile')) { + // refer to a compiler object + $this->_compiler =& $compiler; + } else { + // no usable compiler passed + $this->_compiler = null; + return $this->error(SAVANT2_ERROR_COMPILER); + } + } + + + /** + * + * Sets the method that __call() will alias to. + * + * @access public + * + * @param string $method The Savant2 method for __call() to alias to, + * generally 'plugin' or 'splugin'. + * + * @return void + * + */ + + function setCall($method = 'plugin') + { + $this->_call = $method; + } + + + /** + * + * Sets the custom error class for Savant2 errors. + * + * @access public + * + * @param string $error The name of the custom error class name; if + * null or false, resets the error class to 'Savant2_Error'. + * + * @return void + * + */ + + function setError($error) + { + if (! $error) { + $this->_error = null; + } else { + $this->_error = $error; + } + } + + + /** + * + * Turns path checking on/off. + * + * @access public + * + * @param bool $flag True to turn on path checks, false to turn off. + * + * @return void + * + */ + + function setRestrict($flag = false) + { + if ($flag) { + $this->_restrict = true; + } else { + $this->_restrict = false; + } + } + + + /** + * + * Turns extraction of variables on/off. + * + * @access public + * + * @param bool $flag True to turn on extraction, false to turn off. + * + * @return void + * + */ + + function setExtract($flag = true) + { + if ($flag) { + $this->_extract = true; + } else { + $this->_extract = false; + } + } + + + /** + * + * Sets the automated Savant reference for plugins and filters. + * + * @access public + * + * @param bool $flag Whether to reference Savant2 or not. + * + * @return void + * + */ + + function setReference($flag = false) + { + $this->_reference = $flag; + } + + + /** + * + * Sets the default template name. + * + * @access public + * + * @param string $template The default template name. + * + * @return void + * + */ + + function setTemplate($template) + { + $this->_template = $template; + } + + + /** + * + * Internal version of class_exists() to allow for differing behaviors. + * + * Under PHP4, there is only 1 param to class_exists(); in PHP5, there + * are two. However, if you pass 2 params to the PHP4 version, you get + * a parameter count warning; hence, this method. + * + * Under PHP5, checks $this->_autload to see if __autoload() should be + * called. + * + * @access public + * + * @param string $class A class name. + * + * @return bool Whether or not the class exists. + * + */ + + function _classExists($class) { + if (PHP_VERSION < '5') { + // version 4.x + return class_exists($class); + } else { + // version 5.x + return class_exists($class, $this->_autoload); + } + } + + + // ----------------------------------------------------------------- + // + // Output escaping and management. + // + // ----------------------------------------------------------------- + + + /** + * + * Clears then sets the callbacks to use when calling $this->escape(). + * + * Each parameter passed to this function is treated as a separate + * callback. For example: + * + * + * $savant->setEscape( + * 'stripslashes', + * 'htmlspecialchars', + * array('StaticClass', 'method'), + * array($object, $method) + * ); + * + * + * @access public + * + * @return void + * + */ + + function setEscape() + { + $this->_escape = func_get_args(); + } + + + /** + * + * Adds to the callbacks used when calling $this->escape(). + * + * Each parameter passed to this function is treated as a separate + * callback. For example: + * + * + * $savant->addEscape( + * 'stripslashes', + * 'htmlspecialchars', + * array('StaticClass', 'method'), + * array($object, $method) + * ); + * + * + * @access public + * + * @return void + * + */ + + function addEscape() + { + $args = func_get_args(); + $this->_escape = array_merge($this->_escape, $args); + } + + + /** + * + * Gets the array of output-escaping callbacks. + * + * @access public + * + * @return array The array of output-escaping callbacks. + * + */ + + function getEscape() + { + return $this->_escape; + } + + + /** + * + * Applies escaping to a value. + * + * You can override the predefined escaping callbacks by passing + * added parameters as replacement callbacks. + * + * + * // use predefined callbacks + * $result = $savant->escape($value); + * + * // use replacement callbacks + * $result = $savant->escape( + * $value, + * 'stripslashes', + * 'htmlspecialchars', + * array('StaticClass', 'method'), + * array($object, $method) + * ); + * + * + * @access public + * + * @param mixed $value The value to be escaped. + * + * @return mixed + * + */ + + function escape($value) + { + // were custom callbacks passed? + if (func_num_args() == 1) { + + // no, only a value was passed. + // loop through the predefined callbacks. + foreach ($this->_escape as $func) { + $value = call_user_func($func, $value); + } + + } else { + + // yes, use the custom callbacks instead. + $callbacks = func_get_args(); + + // drop $value + array_shift($callbacks); + + // loop through custom callbacks. + foreach ($callbacks as $func) { + $value = call_user_func($func, $value); + } + + } + + return $value; + } + + + /** + * + * Prints a value after escaping it for output. + * + * You can override the predefined escaping callbacks by passing + * added parameters as replacement callbacks. + * + * + * // use predefined callbacks + * $this->_($value); + * + * // use replacement callbacks + * $this->_( + * $value, + * 'stripslashes', + * 'htmlspecialchars', + * array('StaticClass', 'method'), + * array($object, $method) + * ); + * + * + * @access public + * + * @param mixed $value The value to be escaped and printed. + * + * @return void + * + */ + + function eprint($value) + { + $args = func_get_args(); + echo call_user_func_array( + array($this, 'escape'), + $args + ); + } + + + /** + * + * Alias to eprint() and identical in every way. + * + * @access public + * + * @param mixed $value The value to be escaped and printed. + * + * @return void + * + */ + + function _($value) + { + $args = func_get_args(); + return call_user_func_array( + array($this, 'eprint'), + $args + ); + } + + + + // ----------------------------------------------------------------- + // + // Path management and file finding + // + // ----------------------------------------------------------------- + + + /** + * + * Sets an entire array of search paths. + * + * @access public + * + * @param string $type The type of path to set, typcially 'template' + * or 'resource'. + * + * @param string|array $new The new set of search paths. If null or + * false, resets to the current directory only. + * + * @return void + * + */ + + function setPath($type, $new) + { + // clear out the prior search dirs + $this->_path[$type] = array(); + + // convert from string to path + if (is_string($new) && ! strpos($new, '://')) { + // the search config is a string, and it's not a stream + // identifier (the "://" piece), add it as a path + // string. + $new = explode(PATH_SEPARATOR, $new); + } else { + // force to array + settype($new, 'array'); + } + + // always add the fallback directories as last resort + switch (strtolower($type)) { + case 'template': + $this->addPath($type, '.'); + break; + case 'resource': + $this->addPath($type, dirname(__FILE__) . '/Savant2/'); + break; + } + + // actually add the user-specified directories + foreach ($new as $dir) { + $this->addPath($type, $dir); + } + } + + + /** + * + * Adds a search directory for templates. + * + * @access public + * + * @param string $dir The directory or stream to search. + * + * @return void + * + */ + + function addPath($type, $dir) + { + // no surrounding spaces allowed! + $dir = trim($dir); + + // add trailing separators as needed + if (strpos($dir, '://') && substr($dir, -1) != '/') { + // stream + $dir .= '/'; + } elseif (substr($dir, -1) != DIRECTORY_SEPARATOR) { + // directory + $dir .= DIRECTORY_SEPARATOR; + } + + // add to the top of the search dirs + array_unshift($this->_path[$type], $dir); + } + + + /** + * + * Gets the array of search directories for template sources. + * + * @access public + * + * @return array The array of search directories for template sources. + * + */ + + function getPath($type = null) + { + if (! $type) { + return $this->_path; + } else { + return $this->_path[$type]; + } + } + + + /** + * + * Searches a series of paths for a given file. + * + * @param array $type The type of paths to search (template, plugin, + * or filter). + * + * @param string $file The file name to look for. + * + * @return string|bool The full path and file name for the target file, + * or boolean false if the file is not found in any of the paths. + * + */ + + function findFile($type, $file) + { + // get the set of paths + $set = $this->getPath($type); + + // start looping through them + foreach ($set as $path) { + + // get the path to the file + $fullname = $path . $file; + + // are we doing path checks? + if (! $this->_restrict) { + + // no. this is faster but less secure. + if (file_exists($fullname) && is_readable($fullname)) { + return $fullname; + } + + } else { + + // yes. this is slower, but attempts to restrict + // access only to defined paths. + + // is the path based on a stream? + if (strpos($path, '://') === false) { + // not a stream, so do a realpath() to avoid + // directory traversal attempts on the local file + // system. Suggested by Ian Eure, initially + // rejected, but then adopted when the secure + // compiler was added. + $path = realpath($path); // needed for substr() later + $fullname = realpath($fullname); + } + + // the substr() check added by Ian Eure to make sure + // that the realpath() results in a directory registered + // with Savant so that non-registered directores are not + // accessible via directory traversal attempts. + if (file_exists($fullname) && is_readable($fullname) && + substr($fullname, 0, strlen($path)) == $path) { + return $fullname; + } + } + } + + // could not find the file in the set of paths + return false; + } + + + // ----------------------------------------------------------------- + // + // Variable and reference assignment + // + // ----------------------------------------------------------------- + + + /** + * + * Sets variables for the template. + * + * This method is overloaded; you can assign all the properties of + * an object, an associative array, or a single value by name. + * + * You are not allowed to set variables that begin with an underscore; + * these are either private properties for Savant2 or private variables + * within the template script itself. + * + * + * + * $Savant2 = new Savant2(); + * + * // assign directly + * $Savant2->var1 = 'something'; + * $Savant2->var2 = 'else'; + * + * // assign by name and value + * $Savant2->assign('var1', 'something'); + * $Savant2->assign('var2', 'else'); + * + * // assign by assoc-array + * $ary = array('var1' => 'something', 'var2' => 'else'); + * $Savant2->assign($obj); + * + * // assign by object + * $obj = new stdClass; + * $obj->var1 = 'something'; + * $obj->var2 = 'else'; + * $Savant2->assign($obj); + * + * + * + * Greg Beaver came up with the idea of assigning to public class + * properties. + * + * @access public + * + * @return void + * + * @throws object An error object with a SAVANT2_ERROR_ASSIGN code. + * + */ + + function assign() + { + // this method is overloaded. + $arg = func_get_args(); + + // must have at least one argument. no error, just do nothing. + if (! isset($arg[0])) { + return; + } + + // assign by object + if (is_object($arg[0])) { + // assign public properties + foreach (get_object_vars($arg[0]) as $key => $val) { + if (substr($key, 0, 1) != '_') { + $this->$key = $val; + } + } + return; + } + + // assign by associative array + if (is_array($arg[0])) { + foreach ($arg[0] as $key => $val) { + if (substr($key, 0, 1) != '_') { + $this->$key = $val; + } + } + return; + } + + // assign by string name and mixed value. + // + // we use array_key_exists() instead of isset() becuase isset() + // fails if the value is set to null. + if (is_string($arg[0]) && + substr($arg[0], 0, 1) != '_' && + array_key_exists(1, $arg)) { + $this->$arg[0] = $arg[1]; + } else { + return $this->error(SAVANT2_ERROR_ASSIGN, $arg); + } + } + + + /** + * + * Sets references for the template. + * + * // assign by name and value + * $Savant2->assignRef('ref', $reference); + * + * // assign directly + * $Savant2->ref =& $reference; + * + * Greg Beaver came up with the idea of assigning to public class + * properties. + * + * @access public + * + * @param string $key The name for the reference in the template. + * + * @param mixed &$val The referenced variable. + * + * @return void + * + * @throws object An error object with a SAVANT2_ERROR_ASSIGNREF code. + * + */ + + function assignRef($key, &$val) + { + if (is_string($key) && substr($key, 0, 1) != '_') { + $this->$key =& $val; + } else { + return $this->error( + SAVANT2_ERROR_ASSIGNREF, + array('key' => $key, 'val' => $val) + ); + } + } + + + /** + * + * Unsets assigned variables and references. + * + * @access public + * + * @param mixed $var If null, clears all variables; if a string, clears + * the one variable named by the string; if a sequential array, clears + * the variables names in that array. + * + * @return void + * + */ + + function clear($var = null) + { + if (is_null($var)) { + // clear all variables + $var = array_keys(get_object_vars($this)); + } else { + // clear specific variables + settype($var, 'array'); + } + + // clear out the selected variables + foreach ($var as $name) { + if (substr($name, 0, 1) != '_' && isset($this->$name)) { + unset($this->$name); + } + } + } + + + /** + * + * Gets the current value of one, many, or all assigned variables. + * + * Never returns variables starting with an underscore; these are + * reserved for internal Savant2 use. + * + * @access public + * + * @param mixed $key If null, returns a copy of all variables and + * their values; if an array, returns an only those variables named + * in the array; if a string, returns only that variable. + * + * @return mixed If multiple variables were reqested, returns an + * associative array where the key is the variable name and the + * value is the variable value; if one variable was requested, + * returns the variable value only. + * + */ + + function getVars($key = null) + { + if (is_null($key)) { + $key = array_keys(get_object_vars($this)); + } + + if (is_array($key)) { + // return a series of vars + $tmp = array(); + foreach ($key as $var) { + if (substr($var, 0, 1) != '_' && isset($this->$var)) { + $tmp[$var] = $this->$var; + } + } + return $tmp; + } else { + // return a single var + if (substr($key, 0, 1) != '_' && isset($this->$key)) { + return $this->$key; + } + } + } + + + // ----------------------------------------------------------------- + // + // Template processing + // + // ----------------------------------------------------------------- + + + /** + * + * Loads a template script for execution (does not execute the script). + * + * This will optionally compile the template source into a PHP script + * if a compiler object has been passed into Savant2. + * + * Also good for including templates from the template paths within + * another template, like so: + * + * include $this->loadTemplate('template.tpl.php'); + * + * @access public + * + * @param string $tpl The template source name to look for. + * + * @param bool $setScript Default false; if true, sets the $this->_script + * property to the resulting script path (or null on error). Normally, + * only $this->fetch() will need to set this to true. + * + * @return string The full path to the compiled template script. + * + * @throws object An error object with a SAVANT2_ERROR_NOTEMPLATE code. + * + */ + + function loadTemplate($tpl = null, $setScript = false) + { + // set to default template if none specified. + if (is_null($tpl)) { + $tpl = $this->_template; + } + + // find the template source. + $file = $this->findFile('template', $tpl); + if (! $file) { + return $this->error( + SAVANT2_ERROR_NOTEMPLATE, + array('template' => $tpl) + ); + } + + // are we compiling source into a script? + if (is_object($this->_compiler)) { + // compile the template source and get the path to the + // compiled script (will be returned instead of the + // source path) + $result = $this->_compiler->compile($file); + } else { + // no compiling requested, return the source path + $result = $file; + } + + // is there a script from the compiler? + if (! $result || $this->isError($result)) { + + if ($setScript) { + $this->_script = null; + } + + // return an error, along with any error info + // generated by the compiler. + return $this->error( + SAVANT2_ERROR_NOSCRIPT, + array( + 'template' => $tpl, + 'compiler' => $result + ) + ); + + } else { + + if ($setScript) { + $this->_script = $result; + } + + return $result; + + } + } + + + /** + * + * This is a an alias to loadTemplate() that cannot set the script. + * + * @access public + * + * @param string $tpl The template source name to look for. + * + * @return string The full path to the compiled template script. + * + * @throws object An error object with a SAVANT2_ERROR_NOTEMPLATE code. + * + */ + + function findTemplate($tpl = null) + { + return $this->loadTemplate($tpl, false); + } + + + /** + * + * Executes a template script and returns the results as a string. + * + * @param string $_tpl The name of the template source file ... + * automatically searches the template paths and compiles as needed. + * + * @return string The output of the the template script. + * + * @throws object An error object with a SAVANT2_ERROR_NOSCRIPT code. + * + */ + + function fetch($_tpl = null) + { + // clear prior output + $this->_output = null; + + // load the template script + $_result = $this->loadTemplate($_tpl, true); + + // is there a template script to be processed? + if ($this->isError($_result)) { + return $_result; + } + + // unset so as not to introduce into template scope + unset($_tpl); + unset($_result); + + // never allow a 'this' property + if (isset($this->this)) { + unset($this->this); + } + + // are we extracting variables into local scope? + if ($this->_extract) { + // extract references to this object's public properties. + // this allows variables assigned by-reference to refer all + // the way back to the model logic. variables assigned + // by-copy only refer back to the property. + foreach (array_keys(get_object_vars($this)) as $_prop) { + if (substr($_prop, 0, 1) != '_') { + // set a variable-variable to an object property + // reference + $$_prop =& $this->$_prop; + } + } + + // unset private loop vars + unset($_prop); + } + + // start capturing output into a buffer + ob_start(); + + // include the requested template filename in the local scope + // (this will execute the view logic). + include $this->_script; + + // done with the requested template; get the buffer and + // clear it. + $this->_output = ob_get_contents(); + ob_end_clean(); + + // done! + return $this->applyFilters(); + } + + + /** + * + * Execute and display a template script. + * + * @param string $tpl The name of the template file to parse; + * automatically searches through the template paths. + * + * @return void + * + * @throws object An error object with a SAVANT2_ERROR_NOSCRIPT code. + * + * @see fetch() + * + */ + + function display($tpl = null) + { + $result = $this->fetch($tpl); + if ($this->isError($result)) { + return $result; + } else { + echo $result; + } + } + + + // ----------------------------------------------------------------- + // + // Plugins + // + // ----------------------------------------------------------------- + + + /** + * + * Loads a plugin class and instantiates it within Savant2. + * + * @access public + * + * @param string $name The plugin name (not including Savant2_Plugin_ + * prefix). + * + * @param array $conf An associative array of plugin configuration + * options. + * + * @param bool $savantRef Default false. When true, sets the $Savant + * property of the filter to a reference to this Savant object. + * + * @return void + * + * @throws object An error object with a SAVANT2_ERROR_NOPLUGIN code. + * + */ + + function loadPlugin($name, $conf = array(), $savantRef = null) + { + // if no $savantRef is provided, use the default. + if (is_null($savantRef)) { + $savantRef = $this->_reference; + } + + // some basic information + $class = "Savant2_Plugin_$name"; + $file = "$class.php"; + + // is it loaded? + if (! $this->_classExists($class)) { + + $result = $this->findFile('resource', $file); + if (! $result) { + return $this->error( + SAVANT2_ERROR_NOPLUGIN, + array('plugin' => $name) + ); + } else { + include_once $result; + } + } + + // is it instantiated? + if (! isset($this->_resource['plugin'][$name]) || + ! is_object($this->_resource['plugin'][$name]) || + ! is_a($this->_resource['plugin'][$name], $class)) { + + // instantiate it + $this->_resource['plugin'][$name] = new $class($conf); + + // add a Savant reference if requested + if ($savantRef) { + $this->_resource['plugin'][$name]->Savant = $this; + } + + } + } + + + /** + * + * Unloads one or more plugins from Savant2. + * + * @access public + * + * @param string|array $name The plugin name (not including Savant2_Plugin_ + * prefix). If null, unloads all plugins; if a string, unloads that one + * plugin; if an array, unloads all plugins named as values in the array. + * + * @return void + * + */ + + function unloadPlugin($name = null) + { + if (is_null($name)) { + $this->_resource['plugin'] = array(); + } else { + settype($name, 'array'); + foreach ($name as $key) { + if (isset($this->_resource['plugin'][$key])) { + unset($this->_resource['plugin'][$key]); + } + } + } + } + + + /** + * + * Executes a plugin with arbitrary parameters and returns the + * result. + * + * @access public + * + * @param string $name The plugin name (not including Savant2_Plugin_ + * prefix). + * + * @return mixed The plugin results. + * + * @throws object An error object with a SAVANT2_ERROR_NOPLUGIN code. + * + * @see loadPlugin() + * + */ + + function splugin($name) + { + // attempt to load the plugin + $result = $this->loadPlugin($name); + if ($this->isError($result)) { + return $result; + } + + // call the plugin's "plugin()" method with arguments, + // dropping the first argument (the plugin name) + $args = func_get_args(); + array_shift($args); + return call_user_func_array( + array(&$this->_resource['plugin'][$name], 'plugin'), $args + ); + } + + + /** + * + * Executes a plugin with arbitrary parameters and displays the + * result. + * + * @access public + * + * @param string $name The plugin name (not including Savant2_Plugin_ + * prefix). + * + * @return void + * + * @throws object An error object with a SAVANT2_ERROR_NOPLUGIN code. + * + */ + + function plugin($name) + { + $args = func_get_args(); + + $result = call_user_func_array( + array(&$this, 'splugin'), + $args + ); + + if ($this->isError($result)) { + return $result; + } else { + echo $result; + } + } + + + /** + * + * PHP5 ONLY: Magic method alias to plugin(). + * + * E.g., instead of $this->plugin('form', ...) you would use + * $this->form(...). You can set this to use any other Savant2 method + * by issuing, for example, setCall('splugin') to use splugin() ... which + * is really the only other sensible choice. + * + * @access public + * + * @param string $func The plugin name. + * + * @param array $args Arguments passed to the plugin. + * + * @return void + * + * @throws object An error object with a SAVANT2_ERROR_NOPLUGIN code. + * + */ + + function __call($func, $args) + { + // add the plugin name to the args + array_unshift($args, $func); + + // call the plugin() method + return call_user_func_array( + array(&$this, $this->_call), + $args + ); + } + + + // ----------------------------------------------------------------- + // + // Filters + // + // ----------------------------------------------------------------- + + + /** + * + * Loads a filter class and instantiates it within Savant2. + * + * @access public + * + * @param string $name The filter name (not including Savant2_Filter_ + * prefix). + * + * @param array $conf An associative array of filter configuration + * options. + * + * @param bool $savantRef Default false. When true, sets the $Savant + * property of the filter to a reference to this Savant object. + * + * @return void + * + * @throws object An error object with a SAVANT2_ERROR_NOFILTER code. + * + */ + + function loadFilter($name, $conf = array(), $savantRef = null) + { + // if no $savantRef is provided, use the default. + if (is_null($savantRef)) { + $savantRef = $this->_reference; + } + + // some basic information + $class = "Savant2_Filter_$name"; + $file = "$class.php"; + + // is it loaded? + if (! $this->_classExists($class)) { + + $result = $this->findFile('resource', $file); + if (! $result) { + return $this->error( + SAVANT2_ERROR_NOFILTER, + array('filter' => $name) + ); + } else { + include_once $result; + } + } + + // is it instantiated? + if (! isset($this->_resource['filter'][$name]) || + ! is_object($this->_resource['filter'][$name]) || + ! is_a($this->_resource['filter'][$name], $class)) { + + // instantiate it + $this->_resource['filter'][$name] = new $class($conf); + + // add a Savant reference if requested + if ($savantRef) { + $this->_resource['filter'][$name]->Savant = $this; + } + + } + } + + + /** + * + * Unloads one or more filters from Savant2. + * + * @access public + * + * @param string|array $name The filter name (not including Savant2_Filter_ + * prefix). If null, unloads all filters; if a string, unloads that one + * filter; if an array, unloads all filters named as values in the array. + * + * @return void + * + */ + + function unloadFilter($name = null) + { + if (is_null($name)) { + $this->_resource['filter'] = array(); + } else { + settype($name, 'array'); + foreach ($name as $key) { + if (isset($this->_resource['filter'][$key])) { + unset($this->_resource['filter'][$key]); + } + } + } + } + + + /** + * + * Apply all loaded filters, in order, to text. + * + * @access public + * + * @param string $text The text to which filters should be applied. + * If null, sets the text to $this->_output. + * + * @return string The text after being passed through all loded + * filters. + * + */ + + function applyFilters($text = null) + { + // set to output text if no text specified + if (is_null($text)) { + $text = $this->_output; + } + + // get the list of filter names... + $filter = array_keys($this->_resource['filter']); + + // ... and apply them each in turn. + foreach ($filter as $name) { + $this->_resource['filter'][$name]->filter($text); + } + + // done + return $text; + } + + + // ----------------------------------------------------------------- + // + // Error handling + // + // ----------------------------------------------------------------- + + + /** + * + * Returns an error object. + * + * @access public + * + * @param int $code A SAVANT2_ERROR_* constant. + * + * @param array $info An array of error-specific information. + * + * @return object An error object of the type specified by + * $this->_error. + * + */ + + function &error($code, $info = array()) + { + // the error config array + $conf = array( + 'code' => $code, + 'text' => 'Savant2: ', + 'info' => (array) $info + ); + + // set an error message from the globals + if (isset($GLOBALS['_SAVANT2']['error'][$code])) { + $conf['text'] .= $GLOBALS['_SAVANT2']['error'][$code]; + } else { + $conf['text'] .= '???'; + } + + // set up the error class name + if ($this->_error) { + $class = 'Savant2_Error_' . $this->_error; + } else { + $class = 'Savant2_Error'; + } + + // set up the error class file name + $file = $class . '.php'; + + // is it loaded? + if (! $this->_classExists($class)) { + + // find the error class + $result = $this->findFile('resource', $file); + if (! $result) { + // could not find the custom error class, revert to + // Savant_Error base class. + $class = 'Savant2_Error'; + $result = dirname(__FILE__) . '/Savant2/Error.php'; + } + + // include the error class + include_once $result; + } + + // instantiate and return the error class + $err = new $class($conf); + return $err; + } + + + /** + * + * Tests if an object is of the Savant2_Error class. + * + * @access public + * + * @param object &$obj The object to be tested. + * + * @return boolean True if $obj is an error object of the type + * Savant2_Error, or is a subclass that Savant2_Error. False if not. + * + */ + + function isError(&$obj) + { + if (is_object($obj)) { + if (is_a($obj, 'Savant2_Error') || + is_subclass_of($obj, 'Savant2_Error')) { + return true; + } + } + + return false; + } +} +?> \ No newline at end of file diff --git a/include/classes/Savant2/Savant2/Compiler.php b/include/classes/Savant2/Savant2/Compiler.php new file mode 100644 index 000000000..633317789 --- /dev/null +++ b/include/classes/Savant2/Savant2/Compiler.php @@ -0,0 +1,72 @@ + +* +* @package Savant2 +* +* @license LGPL http://www.gnu.org/copyleft/lesser.html +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 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 +* Lesser General Public License for more details. +* +*/ + +class Savant2_Compiler { + + /** + * + * Reference to the "parent" Savant object. + * + */ + + var $Savant = null; + + + /** + * + * Constructor. + * + * @access public + * + */ + + function Savant2_Compiler($conf = array()) + { + settype($conf, 'array'); + foreach ($conf as $key => $val) { + $this->$key = $val; + } + } + + + /** + * + * Stub method for extended behaviors. + * + * @access public + * + * @return void + * + */ + + function compile($tpl) + { + } +} +?> \ No newline at end of file diff --git a/include/classes/Savant2/Savant2/Error.php b/include/classes/Savant2/Savant2/Error.php new file mode 100644 index 000000000..f18da7188 --- /dev/null +++ b/include/classes/Savant2/Savant2/Error.php @@ -0,0 +1,125 @@ + +* +* @package Savant2 +* +* @license LGPL http://www.gnu.org/copyleft/lesser.html +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 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 +* Lesser General Public License for more details. +* +*/ + +class Savant2_Error { + + + /** + * + * The error code, typically a SAVANT_ERROR_* constant. + * + * @access public + * + * @var int + * + */ + + var $code = null; + + + /** + * + * An array of error-specific information. + * + * @access public + * + * @var array + * + */ + + var $info = array(); + + + /** + * + * The error message text. + * + * @access public + * + * @var string + * + */ + + var $text = null; + + + /** + * + * A debug backtrace for the error, if any. + * + * @access public + * + * @var array + * + */ + + var $backtrace = null; + + + /** + * + * Constructor. + * + * @access public + * + * @param array $conf An associative array where the key is a + * Savant2_Error property and the value is the value for that + * property. + * + */ + + function Savant2_Error($conf = array()) + { + // set public properties + foreach ($conf as $key => $val) { + $this->$key = $val; + } + + // generate a backtrace + if (function_exists('debug_backtrace')) { + $this->backtrace = debug_backtrace(); + } + + // extended behaviors + $this->error(); + } + + + /** + * + * Stub method for extended behaviors. + * + * @access public + * + * @return void + * + */ + + function error() + { + } +} +?> \ No newline at end of file diff --git a/include/classes/Savant2/Savant2/Filter.php b/include/classes/Savant2/Savant2/Filter.php new file mode 100644 index 000000000..6adb6523d --- /dev/null +++ b/include/classes/Savant2/Savant2/Filter.php @@ -0,0 +1,76 @@ + +* +* @package Savant2 +* +* @license LGPL http://www.gnu.org/copyleft/lesser.html +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 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 +* Lesser General Public License for more details. +* +*/ + +class Savant2_Filter { + + /** + * + * Optional reference to the calling Savant object. + * + * @var object + * + */ + + var $Savant = null; + + + /** + * + * Constructor. + * + * @access public + * + */ + + function Savant2_Filter($conf = array()) + { + settype($conf, 'array'); + foreach ($conf as $key => $val) { + $this->$key = $val; + } + } + + + /** + * + * Stub method for extended behaviors. + * + * @access public + * + * @param string &$text The text to filter. + * + * @return void + * + */ + + function filter(&$text) + { + } +} +?> \ No newline at end of file diff --git a/include/classes/Savant2/Savant2/Plugin.php b/include/classes/Savant2/Savant2/Plugin.php new file mode 100644 index 000000000..835e22d95 --- /dev/null +++ b/include/classes/Savant2/Savant2/Plugin.php @@ -0,0 +1,74 @@ + +* +* @package Savant2 +* +* @license LGPL http://www.gnu.org/copyleft/lesser.html +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 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 +* Lesser General Public License for more details. +* +*/ + +class Savant2_Plugin { + + /** + * + * Optional reference to the calling Savant object. + * + * @var object + * + */ + + var $Savant = null; + + + /** + * + * Constructor. + * + * @access public + * + */ + + function Savant2_Plugin($conf = array()) + { + settype($conf, 'array'); + foreach ($conf as $key => $val) { + $this->$key = $val; + } + } + + + /** + * + * Stub method for extended behaviors. + * + * @access public + * + * @return void + * + */ + + function plugin() + { + } +} +?> \ No newline at end of file diff --git a/include/classes/Savant2/Savant2/Savant2_Error_exception.php b/include/classes/Savant2/Savant2/Savant2_Error_exception.php new file mode 100644 index 000000000..48e01adea --- /dev/null +++ b/include/classes/Savant2/Savant2/Savant2_Error_exception.php @@ -0,0 +1,53 @@ + +* +* @package Savant2 +* +* @license LGPL http://www.gnu.org/copyleft/lesser.html +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 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 +* Lesser General Public License for more details. +* +*/ + +class Savant2_Error_exception extends Savant2_Error { + + + /** + * + * Throws an Savant2_Exception in PHP5. + * + * @return void + * + */ + + function error() + { + throw new Savant2_Exception($this->text, $this->code); + } +} +?> \ No newline at end of file diff --git a/include/classes/Savant2/Savant2/Savant2_Error_pear.php b/include/classes/Savant2/Savant2/Savant2_Error_pear.php new file mode 100644 index 000000000..1963122ac --- /dev/null +++ b/include/classes/Savant2/Savant2/Savant2_Error_pear.php @@ -0,0 +1,56 @@ + +* +* @package Savant2 +* +* @license LGPL http://www.gnu.org/copyleft/lesser.html +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 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 +* Lesser General Public License for more details. +* +*/ + +class Savant2_Error_pear extends Savant2_Error { + + + /** + * + * Extended behavior for PEAR_Error. + * + * @access public + * + * @return void + * + */ + + function error() + { + // throw a PEAR_Error + PEAR::throwError($this->text, $this->code, $this->info); + } +} +?> \ No newline at end of file diff --git a/include/classes/Savant2/Savant2/Savant2_Error_stack.php b/include/classes/Savant2/Savant2/Savant2_Error_stack.php new file mode 100644 index 000000000..e532891f5 --- /dev/null +++ b/include/classes/Savant2/Savant2/Savant2_Error_stack.php @@ -0,0 +1,60 @@ + +* +* @package Savant2 +* +* @license LGPL http://www.gnu.org/copyleft/lesser.html +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 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 +* Lesser General Public License for more details. +* +*/ + +class Savant2_Error_stack extends Savant2_Error { + + + /** + * + * Pushes an error onto the PEAR_ErrorStack. + * + * @return void + * + */ + + function error() + { + // push an error onto the stack + PEAR_ErrorStack::staticPush( + 'Savant2', // package name + $this->code, // error code + null, // error level + $this->info, // user info + $this->text // error message + ); + } +} +?> \ No newline at end of file diff --git a/include/classes/Savant2/Savant2/Savant2_Plugin_cycle.php b/include/classes/Savant2/Savant2/Savant2_Plugin_cycle.php new file mode 100644 index 000000000..52440336f --- /dev/null +++ b/include/classes/Savant2/Savant2/Savant2_Plugin_cycle.php @@ -0,0 +1,97 @@ + +* +* @package Savant2 +* +* @license LGPL http://www.gnu.org/copyleft/lesser.html +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as +* published by the Free Software Foundation; either version 2.1 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 +* Lesser General Public License for more details. +* +*/ + +class Savant2_Plugin_cycle extends Savant2_Plugin { + + /** + * + * An associative array of predefined cycle value sets. + * + * You can preset cycle values via Savant::loadPlugin(). + * + * $conf = array( + * 'values' => array( + * 'lightdark' => array('light', 'dark'), + * 'threesome' => array('one', 'two', 'three') + * ) + * ); + * + * $Savant->loadPlugin('cycle', $conf); + * + * ... and in your template you can call: + * + * $this->plugin('cycle', 'lightdark', $iteration); + * + * @access public + * + * @var array + * + */ + + var $values = array(); + + + /** + * + * Cycles through a series of values. + * + * @access public + * + * @param string|array $cycle If a string, the preset cycle value key to use + * from $this->cycles; if an array, use the array as the cycle values. + * + * @param int $iteration The iteration number for the cycle. + * + * @param int $repeat The number of times to repeat each cycle value. + * + * @return mixed The value of the cycle iteration. + * + */ + + function plugin($cycle, $iteration, $repeat = 1) + { + // get the proper value set as an array + if (is_string($cycle) && isset($this->values[$cycle])) { + $values = (array) $this->values[$cycle]; + } else { + $values = (array) $cycle; + } + + // prevent divide-by-zero errors + if ($repeat == 0) { + $repeat = 1; + } + + // return the perper value for iteration and repetition + return $values[($iteration / $repeat) % count($values)]; + } +} +?> \ No newline at end of file diff --git a/include/classes/UrlRewrite/ContentUrl.class.php b/include/classes/UrlRewrite/ContentUrl.class.php new file mode 100644 index 000000000..50c4b0204 --- /dev/null +++ b/include/classes/UrlRewrite/ContentUrl.class.php @@ -0,0 +1,114 @@ +rule = array(0=>'cid'); + } + + /** + * Construct pretty url by the given query string. + */ + function constructPrettyUrl($query){ + $url = ''; //url to be returned + $bookmark = ''; //html bookmark + + if (empty($query)){ + return ''; + } + + //Take out bookmark + if (($pos = strpos($query, '#'))!==FALSE){ + $bookmark = substr($query, $pos); + $query = substr($query, 0, $pos); + } + + //If this is already a pretty url,but without mod_apache rule + //unwrap it and reconstruct + if (is_array($query)){ + $new_query = ''; + foreach($query as $fk=>$fv){ + if (preg_match('/\.php/', $fv)==1){ + continue; //skip the php file + } + + //check if this is part of the rule, if so,add it, o/w ignore + if (array_search($fv, $this->rule)!==FALSE){ + $new_query .= $fv . '=' . $query[$fk+1] . SEP; + } + } + $query = $new_query; //done + } + + $temp = explode(SEP, $query); + foreach ($temp as $index=>$attributes){ + if(empty($attributes)){ + //skip the ones that are empty. + continue; + } + list($key, $value) = preg_split('/\=/', $attributes, 2); + $query_parts[$key] = $value; + } + + $query_string = ''; + //construct pretty url on mapping + foreach ($this->rule as $key=>$value){ + //if this value is empty, the url construction should quit. + if ($query_parts[$value] ==''){ + break; + } + $url .= $query_parts[$value].'/'; + + //if the query parts are not in the defined rules, set it back to query string again + if ($query_parts[$this->rule[$key]]!=''){ + $query_parts[$this->rule[$key]] = ''; + } + } + + //Go through the query_parts again, and for those values that are not empty + // add it to the querystring + foreach($query_parts as $key=>$value){ + //paginator are handle differently + if ($value!='' && $key!='page'){ + $query_string .= $key.'='.$value.SEP; + } + } + //take out the last sep. + $query_string = substr($query_string, 0, -1); + + //handle paginators +// if ($query_parts['page']!=''){ +// $url .= '/'.$query_parts['page'].'.html'; +// } + + //append query string at the back + if ($query_string!=''){ + $url .= '?'.$query_string; + } + + //finally, append bookmark if not emptied + if ($bookmark!=''){ + $url .= $bookmark; + } + + return $url; + } +} +?> \ No newline at end of file diff --git a/include/classes/UrlRewrite/FileStorageUrl.class.php b/include/classes/UrlRewrite/FileStorageUrl.class.php new file mode 100644 index 000000000..eaf5ed1d6 --- /dev/null +++ b/include/classes/UrlRewrite/FileStorageUrl.class.php @@ -0,0 +1,152 @@ +query parts] + + // constructor + // @param the filename that was being called, this can be index.php, comments.php, revisions.php + function FileStorageUrl($filename) { + if ($filename == ''){ + $filename = 'index.php'; + } + $this->rule = array(0=>'action', 1=>'ot', 2=>'oid', 3=>'folder'); //default 3=folder, but it can be id as well for comment + $this->filename = $filename; + } + + // + function setRule($id, $ruleName){ + $this->rule[$id] = $ruleName; + } + + /** + * Construct pretty url by the given query string. + * Note: This method will be a bit different from ForumsUrl, TestsUrl, ContentUrl because it has browse/comment in the rule which + * doesn't exist in the actual query. + * @param string the query string of the url + * @param string filename of the request, this consists of revisions.php, index.php, comments.php + */ + function constructPrettyUrl($query){ + if (empty($query)){ + return ''; + } + + //If this is already a pretty url,but without mod_apache rule + //unwrap it and reconstruct + if (is_array($query)){ + $new_query = ''; + foreach($query as $fk=>$fv){ + if (preg_match('/\.php/', $fv)==1){ + continue; //skip the php file + } + + //check if this is part of the rule, if so,add it, o/w ignore + if (array_search($fv, $this->rule)!==FALSE){ + $new_query .= $fv . '=' . $query[$fk+1] . SEP; + } + } + $query = $new_query; //done + } + + $temp = explode(SEP, $query); + foreach ($temp as $index=>$attributes){ + if(empty($attributes)){ + //skip the ones that are empty. + continue; + } + list($key, $value) = preg_split('/\=/', $attributes, 2); + $query_parts[$key] = $value; + } + + $query_string = ''; + //determine if this uses 'browse' or 'comment' + $prefix = $this->configRule($this->filename); + if ($prefix != '') { + $url .= $prefix.'/' ; //add either index, revision or comment to the url + } + + //construct pretty url on mapping + foreach ($this->rule as $key=>$value){ + + //if this is action, skip it. + if ($value == 'action'){ + continue; + } elseif ($query_parts[$value] ==''){ + //if this value is empty, the url construction should quit. + break; + } + $url .= $query_parts[$value].'/'; + + //if the query parts are not in the defined rules, set it back to query string again + if ($query_parts[$this->rule[$key]]!=''){ + $query_parts[$this->rule[$key]] = ''; + } + } + + //Go through the query_parts again, and for those values that are not empty + // add it to the querystring + foreach($query_parts as $key=>$value){ + //paginator are handle differently + if ($value!='' && $key!='page'){ + $query_string .= $key.'='.$value.SEP; + } + } + //take out the last sep. + $query_string = substr($query_string, 0, -1); + + //handle paginators + if ($query_parts['page']!=''){ + $url .= '/'.$query_parts['page'].'.html'; + } + + //append query string at the back + if ($query_string!=''){ + $url .= '?'.$query_string; + } + + return $url; + } + + + /** + * A helper method for constructPrettyUrl + * @param string filename + */ + function configRule($filename){ + //run through the query once, extract if it uses id or folder. + //if 'id', it is comments.php + //if 'folder', it is index.php + if ($filename=='comments.php'){ + $this->setRule(3, 'id'); + return 'comments'; + } elseif ($filename=='revisions.php'){ + $this->setRule(3, 'id'); + return 'revisions'; + } else { + $this->setRule(3, 'folder'); +// return 'index'; + return ''; + } + + } + + +} +?> \ No newline at end of file diff --git a/include/classes/UrlRewrite/ForumsUrl.class.php b/include/classes/UrlRewrite/ForumsUrl.class.php new file mode 100644 index 000000000..2f0dc911c --- /dev/null +++ b/include/classes/UrlRewrite/ForumsUrl.class.php @@ -0,0 +1,105 @@ +rule = array(0=>'fid', 1=>'pid'); + } + + + /** + * Construct pretty url by the given query string. + */ + function constructPrettyUrl($query){ + if (empty($query)){ + return ''; + } + + //If this is already a pretty url,but without mod_apache rule + //unwrap it and reconstruct + if (is_array($query)){ + $new_query = ''; + foreach($query as $fk=>$fv){ + if (preg_match('/\.php/', $fv)==1){ + continue; //skip the php file + } + + //check if this is part of the rule, if so,add it, o/w ignore + if (array_search($fv, $this->rule)!==FALSE){ + $new_query .= $fv . '=' . $query[$fk+1] . SEP; + } elseif (preg_match('/([0-9]+)\.html/', $fv, $matches)==1){ + $new_query .= 'page=' . $matches[1] . SEP; + } + } + $query = $new_query; //done + } + + $temp = explode(SEP, $query); + foreach ($temp as $index=>$attributes){ + if(empty($attributes)){ + //skip the ones that are empty. + continue; + } + list($key, $value) = preg_split('/\=/', $attributes, 2); + $query_parts[$key] = $value; + } + + $query_string = ''; + + //construct pretty url on mapping + foreach ($this->rule as $key=>$value){ + + //if this value is empty, the url construction should quit. + if ($query_parts[$value] ==''){ + break; + } + $url .= $query_parts[$value].'/'; + + //if the query parts are not in the defined rules, set it back to query string again + if ($query_parts[$this->rule[$key]]!=''){ + $query_parts[$this->rule[$key]] = ''; + } + } + + //Go through the query_parts again, and for those values that are not empty + // add it to the querystring + foreach($query_parts as $key=>$value){ + //paginator are handle differently + if ($value!='' && $key!='page'){ + $query_string .= $key.'='.$value.SEP; + } + } + //take out the last sep. + $query_string = substr($query_string, 0, -1); + + //handle paginators + if ($query_parts['page']!=''){ + $url .= $query_parts['page'].'.html'; + } + + //append query string at the back + if ($query_string!=''){ + $url .= '?'.$query_string; + } + + return $url; + } +} +?> \ No newline at end of file diff --git a/include/classes/UrlRewrite/GlossaryUrl.class.php b/include/classes/UrlRewrite/GlossaryUrl.class.php new file mode 100644 index 000000000..87b14cbc4 --- /dev/null +++ b/include/classes/UrlRewrite/GlossaryUrl.class.php @@ -0,0 +1,115 @@ +rule = array(0=>'p'); + } + + /** + * Construct pretty url by the given query string. + */ + function constructPrettyUrl($query){ + if (empty($query)){ + return ''; + } + + //Take out bookmark + if (($pos = strpos($query, '#'))!==FALSE){ + $bookmark = substr($query, $pos); + $query = substr($query, 0, $pos); + } + + //If this is already a pretty url,but without mod_apache rule + //unwrap it and reconstruct + if (is_array($query)){ + $new_query = ''; + foreach($query as $fk=>$fv){ + if (preg_match('/\.php/', $fv)==1){ + continue; //skip the php file + } + + //check if this is part of the rule, if so,add it, o/w ignore + if (array_search($fv, $this->rule)!==FALSE){ + $new_query .= $fv . '=' . $query[$fk+1] . SEP; + } elseif (preg_match('/([0-9]+)\.html/', $fv, $matches)==1){ + $new_query .= 'page=' . $matches[1] . SEP; + } + } + $query = $new_query; //done + } + + $temp = explode(SEP, $query); + foreach ($temp as $index=>$attributes){ + if(empty($attributes)){ + //skip the ones that are empty. + continue; + } + list($key, $value) = preg_split('/\=/', $attributes, 2); + $query_parts[$key] = $value; + } + + $query_string = ''; + + //construct pretty url on mapping + foreach ($this->rule as $key=>$value){ + + //if this value is empty, the url construction should quit. + if ($query_parts[$value] ==''){ + break; + } + $url .= $query_parts[$value].'/'; + + //if the query parts are not in the defined rules, set it back to query string again + if ($query_parts[$this->rule[$key]]!=''){ + $query_parts[$this->rule[$key]] = ''; + } + } + + //Go through the query_parts again, and for those values that are not empty + // add it to the querystring + foreach($query_parts as $key=>$value){ + //paginator are handle differently + if ($value!='' && $key!='page'){ + $query_string .= $key.'='.$value.SEP; + } + } + //take out the last sep. + $query_string = substr($query_string, 0, -1); + + //handle paginators + if ($query_parts['page']!=''){ + $url .= $query_parts['page'].'.html'; + } + + //append query string at the back + if ($query_string!=''){ + $url .= '?'.$query_string; + } + + //finally, append bookmark if not emptied + if ($bookmark!=''){ + $url .= $bookmark; + } + + return $url; + } +} +?> \ No newline at end of file diff --git a/include/classes/UrlRewrite/TestsUrl.class.php b/include/classes/UrlRewrite/TestsUrl.class.php new file mode 100644 index 000000000..ce8885335 --- /dev/null +++ b/include/classes/UrlRewrite/TestsUrl.class.php @@ -0,0 +1,111 @@ +query parts] +// var $className; //the name of this class + + // constructor + function TestsUrl() { + $this->rule = array(0=>'tid', 1=>'action'); + } + + + /** + * Construct pretty url by the given query string. + */ + function constructPrettyUrl($query){ + $url = ''; //url to be returned + + if (empty($query)){ + return ''; + } + + //If this is already a pretty url,but without mod_apache rule + //unwrap it and reconstruct + if (is_array($query)){ + $new_query = ''; + foreach($query as $fk=>$fv){ + if (preg_match('/\.php/', $fv)==1){ + continue; //skip the php file + } + + //check if this is part of the rule, if so,add it, o/w ignore + if (array_search($fv, $this->rule)!==FALSE){ + $new_query .= $fv . '=' . $query[$fk+1] . SEP; + } elseif (preg_match('/([0-9]+)\.html/', $fv, $matches)==1){ + $new_query .= 'page=' . $matches[1] . SEP; + } + } + $query = $new_query; //done + } + + $temp = explode(SEP, $query); + foreach ($temp as $index=>$attributes){ + if(empty($attributes)){ + //skip the ones that are empty. + continue; + } + list($key, $value) = preg_split('/\=/', $attributes, 2); + $query_parts[$key] = $value; + } + + $query_string = ''; + + //construct pretty url on mapping + foreach ($this->rule as $key=>$value){ + + //if this value is empty, the url construction should quit. + if ($query_parts[$value] ==''){ + break; + } + $url .= $query_parts[$value].'/'; + + //if the query parts are not in the defined rules, set it back to query string again + if ($query_parts[$this->rule[$key]]!=''){ + $query_parts[$this->rule[$key]] = ''; + } + } + + //Go through the query_parts again, and for those values that are not empty + // add it to the querystring + foreach($query_parts as $key=>$value){ + //paginator are handle differently + if ($value!='' && $key!='page'){ + $query_string .= $key.'='.$value.SEP; + } + } + //take out the last sep. + $query_string = substr($query_string, 0, -1); + + //handle paginators + if ($query_parts['page']!=''){ + $url .= $query_parts['page'].'.html'; + } + + //append query string at the back + if ($query_string!=''){ + $url .= '?'.$query_string; + } + + return $url; + } +} +?> \ No newline at end of file diff --git a/include/classes/UrlRewrite/UrlParser.class.php b/include/classes/UrlRewrite/UrlParser.class.php new file mode 100644 index 000000000..15a400017 --- /dev/null +++ b/include/classes/UrlRewrite/UrlParser.class.php @@ -0,0 +1,138 @@ +course_id; [1]->class obj + + // Constructor + function UrlParser($pathinfo=''){ + if ($pathinfo==''){ + $pathinfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; + } + $this->parsePathInfo($pathinfo); + } + + /** + * This function will take the pathinfo and return an array of elements + * retrieved from the path info. + * An ATutor pathinfo will always be in the format of /// + * course_slug is the course_slug defined in course preference (or course id if it's empty) + * type is the folder, particularlly forums, content, tests, blogs, mods, etc. + * parts is the extra info about this url request. + * @param string the pathinfo from the URL + * @access private + */ + function parsePathinfo($pathinfo){ + global $db; + $pathinfo = strtolower($pathinfo); + + //remove AT_PRETTY_URL_HANDLER from the path info. + if (($pos=strpos($pathinfo, AT_PRETTY_URL_HANDLER))!==FALSE){ + $pathinfo = substr($pathinfo, $pos); + } + + /* + * matches[1] = course slug/id + * matches[2] = path + * matches[3] = useless, just a place holder + * matches[4] = filename + * matches[5] = query string in pretty format + * @http://ca3.php.net/preg_match + */ + if (strpos($pathinfo, 'mods')!==FALSE){ + //If this is a mod, its file name will be longer with mods/ infront + preg_match('/^\/[\w\-]+\/?$|(\/[\w]+)(\/mods(\/[\w]+)+)\/([\w\_\.]+\.php)([\/\w\W]*)/', $pathinfo, $matches); + } else { + preg_match('/^\/[\w\-]+\/?$|(\/[\w]+)(([\/\w]*))\/([\w\_\.]+\.php)([\/\w\W]*)/', $pathinfo, $matches); + } + + if (empty($matches)){ + //no matches. + $matches[1] = 0; + } elseif (sizeof($matches)==1){ + //if the url consist of just the course slug, the size would be just 2 + $matches[1] = $matches[0]; + } + + //take out the front slash + $matches[1] = preg_replace('/\//', '', $matches[1]); + $course_id = $matches[1]; + + //check if this is a course slug or course id. + if (preg_match('/^[\d]+$/', $matches[1])==0){ + //it's a course slug, log into the course. + $sql = "SELECT course_id FROM ".TABLE_PREFIX."courses WHERE course_dir_name='$matches[1]'"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + if ($row['course_id']!=''){ + $course_id = $row['course_id']; + } else { + $course_id = 0; + } + } + + //Check if there are any matches for prettied query string, if not, use the actual query. + if (!isset($matches[5]) || $matches[5] == ''){ + $matches[5] = $_SERVER['QUERY_STRING']; + } + + //Create object based on this path. + $matches[2] = isset($matches[2]) ? $matches[2] : ''; + $matches[4] = isset($matches[4]) ? $matches[4] : ''; + $url_obj = new UrlRewrite($matches[2], $matches[4], $matches[5]); + + $this->path_array = array($course_id, $url_obj); + } + + + /** + * return the path array + */ + function getPathArray(){ + return $this->path_array; + } + + + /** + * Returns course_id if config_[course_dir_name] is switched off, + * otherwise, return the course dir name. + * Called by vitals.inc.php + * + * @param int course id + * @return mixed course id if config[course_dir_name] is 0, course_dir_name otherwise + */ + function getCourseDirName($course_id){ + global $db; + $course_id = intval($course_id); + + $sql = "SELECT course_dir_name FROM ".TABLE_PREFIX."courses WHERE course_id=$course_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + if ($row['course_dir_name']!=''){ + $course_id = $row['course_dir_name']; + } + + return $course_id; + } +} +?> \ No newline at end of file diff --git a/include/classes/UrlRewrite/UrlRewrite.class.php b/include/classes/UrlRewrite/UrlRewrite.class.php new file mode 100644 index 000000000..e4ad9062a --- /dev/null +++ b/include/classes/UrlRewrite/UrlRewrite.class.php @@ -0,0 +1,381 @@ +isEmpty = true; + } else { + $this->isEmpty = false; + } + $this->path = $path; + $this->filename = $filename; + $this->query = $query; + } + + /** + * Returns the link that points to this object as a page. + * @access public + */ + function redirect(){ + //redirect to that url. + return '/'.$this->getPage(); + } + + /** + * Parser for the pathinfo, return an array with mapped key values similar to the querystring. + * @access public + * @return array key=>value, where keys and values have the same meaning as the ones in the query strings. + */ + function parsePrettyQuery(){ + global $_config; + $result = array(); + + //return empty array if query is empty + if (empty($this->query)){ + return $result; + } + + //if course_dir_name is disabled from admin. + if ($_config['pretty_url']==0){ + return $this->query; + } + + //If the first char is /, cut it + if (strpos($this->query, '/') == 0){ + $query_parts = explode('/', substr($this->query, 1)); + } else { + $query_parts = explode('/', $this->query); + } + + //dynamically create the array + //assumption: pathinfo ALWAYS in the format of key1/value1/key2/value2/key3/value3/etc... + foreach ($query_parts as $array_index=>$key_value){ + if($array_index%2 == 0 && $query_parts[$array_index]!=''){ + $result[$key_value] = $query_parts[$array_index+1]; + } + } + return $result; + } + + + /** + * Parser for the querystrings url + * @access public + * @param string querystring + * @return array an array of mapped keys and values like the querystrings. + * + * NOTE: Stopped using this function since we've decided to dynamically create the URL. + * See: parsePrettyQuery() + */ + function parseQuery($query){ + //return empty array if query is empty + if (empty($query)){ + return array(); + } + + parse_str($this->query, $result); + return $result; + } + + + /** + * Construct the pretty url based on the given query. + * @access public + * @param string the pathinfo query + * @return string pretty url + */ + function constructPrettyUrl($query){ + global $_config; + $bookmark = ''; + + if (empty($query)){ + return ''; + } + + //Take out bookmark, and store it. + if (($pos = strpos($query, '#'))!==FALSE){ + $bookmark = substr($query, $pos); + $query = substr($query, 0, $pos); + } + + //If this is already a pretty url,but without mod_apache rule + //unwrap it and reconstruct + if (is_array($query)){ + $new_query = ''; + foreach($query as $fk=>$fv){ + if (preg_match('/\.php/', $fv)==1){ + continue; //skip the php file + } + + //check if this is part of the rule, if so,add it, o/w ignore + if (array_search($fv, $this->rule)!==FALSE){ + $new_query .= $fv . '=' . $query[$fk+1] . SEP; + } elseif (preg_match('/([0-9]+)\.html/', $fv, $matches)==1){ + $new_query .= 'page=' . $matches[1] . SEP; + } + } + $query = $new_query; //done + } + + //do not change query if pretty url is disabled + if ($_config['pretty_url'] == 0){ + $pretty_url = $query; + } else { + $pretty_url = ''; //init url + $query_parts = explode(SEP, $query); + foreach ($query_parts as $index=>$attributes){ + if(empty($attributes)){ + //skip the ones that are empty. + continue; + } + list($key, $value) = preg_split('/\=/', $attributes, 2); + $pretty_url .= $key . '/' . $value .'/'; + } + } + + //finally, append bookmark if not emptied + if ($bookmark!=''){ + $pretty_url .= $bookmark; + } + + return $pretty_url; + } + + + /** + * This function is used to convert the input URL to a pretty URL. + * @param int course id + * @param string normal URL, WITHOUT the :// + * @return pretty url + */ + function convertToPrettyUrl($course_id, $url){ + global $_config, $db; + $pretty_url = ''; + + if (strpos($url, '?')!==FALSE){ + list($front, $end) = preg_split('/\?/', $url); + } else { + $front = $url; + $end = ''; + } + $front_array = explode('/', $front); + if ($front_array[1]=='_standard' || $front_array[1]=='_core' ){ + //shift the first 2 elements. + array_shift($front_array); + array_shift($front_array); + $front = implode('/', $front_array); + } + //find out what kind of link this is, pretty url? relative url? or PHP_SELF url? + $dir_deep = substr_count(AT_INCLUDE_PATH, '..'); + $url_parts = explode('/', $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']); + $host_dir = implode('/', array_slice($url_parts, 0, count($url_parts) - $dir_deep-1)); + //The link is a bounce link + if(preg_match('/bounce.php\?course=([\d]+)$/', $url, $matches)==1){ + if (!empty($course_id)) { + $pretty_url = $course_id; //course_id should be assigned by vitals depending on the system pref. + } else { + $pretty_url = $matches[1]; //happens when course dir name is disabled + } + } elseif(in_array(AT_PRETTY_URL_HANDLER, $front_array)===TRUE){ + //The relative link is a pretty URL + $front_result = array(); + //spit out the URL in between AT_PRETTY_URL_HANDLER to *.php + //note, pretty url is defined to be AT_PRETTY_URL_HANDLER/course_slug/type/location/... + //ie. AT_PRETTY_URL_HANDLER/1/forum/view.php/... + while (($needle = array_search(AT_PRETTY_URL_HANDLER, $front_array)) !== FALSE){ + $front_array = array_slice($front_array, $needle + 1); + } + $front_array = array_slice($front_array, $needle + 1); //+2 because we want the entries after the course_slug + + //Handle url differently IF mod_rewrite is enabled, and if there are no query strings at the back, + //then we will have to reuse the current pathinfo to reconstruct the query. + if ($_config['apache_mod_rewrite'] > 0 && $end==''){ + $end = $front_array; //let the class handles it + } + + /* Overwrite pathinfo + * ie. /go.php/1/forum/view.php/fid/1/pid/17/?fid=1&pid=17&page=5 + * In the above case, cut off the original pathinfo, and replace it with the new querystrings + * If querystring is empty, then use the old one, ie. /go.php/1/forum/view.php/fid/1/pid/17/. + */ + foreach($front_array as $fk=>$fv){ + array_push($front_result, $fv); + if (!empty($end) && preg_match('/\.php/', $fv)==1){ + break; + } + } + $front = implode('/', $front_result); + } elseif (strpos($front, $host_dir)!==FALSE){ + //Not a relative link, it contains the full PHP_SELF path. + $front = substr($front, strlen($host_dir)+1); //stripe off the slash after the host_dir as well + } elseif ($course_id == ''){ + //if this is my start page + return $url; + } + //Turn querystring to pretty URL + if ($pretty_url==''){ + //Get the original course id back + $sql = "SELECT course_id FROM ".TABLE_PREFIX."courses WHERE course_dir_name='$course_id'"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $course_orig = $course_id; + + if ($row['course_id']!=''){ + $course_orig = $row['course_id']; + } + + //Add course id in if both course_id or course_dir_name are not there + if (preg_match('/^\/?('.$course_id.'|'.$course_orig.')\//', $front)==0){ + $pretty_url = $course_id.'/'; + } + + //check if there are any rules overwriting the original rules + //TODO: have a better way to do this + // extend modularity into this. + $obj =& $this; //default + //Overwrite the UrlRewrite obj if there are any private rules + if ($_config['apache_mod_rewrite'] > 0){ + //take out '.php' if any exists. (Apply only to non-modules, otherwise it might cause problems) + if (preg_match('/^mods/', $front)!=1){ + if ($end=='' && preg_match('/index\.php$/', $front)==1){ + $pretty_url .= preg_replace('/index.php/', '', $front); + } else { + $pretty_url .= preg_replace('/\.php/', '', $front); + } + } else { + $pretty_url .= $front; + } + + if (preg_match('/forum\/(index|view|list)\.php/', $front)==1) { + $pretty_url = $course_id.'/forum'; + $obj = new ForumsUrl(); + } elseif (preg_match('/(content\.php)(\/cid(\/\d+))?/', $front, $matches)==1){ + $pretty_url = $course_id.'/content'; + //if there are other pretty url queries at the back, append it + //Note: this is to fix the hopping content problem between diff courses + if (isset($matches[3]) && $matches[3] != ''){ + $pretty_url .= $matches[3]; + } + $obj = new ContentUrl(); + } elseif (preg_match('/file_storage\/((index|revisions|comments)\.php)?/', $front, $matches)==1){ + $pretty_url = $course_id.'/file_storage'; + $obj = new FileStorageUrl($matches[1]); + } elseif (preg_match('/tools\/test_intro\.php/', $front)==1){ + $pretty_url = $course_id.'/tests_surveys'; + $obj = new TestsUrl(); + } elseif (preg_match('/glossary\/index\.php/', $front)==1){ + $pretty_url = $course_id.'/glossary'; + $obj = new GlossaryUrl(); + } + } else { + $pretty_url .= $front; + } + + if ($end != ''){ + //if pretty url is turned off, use '?' to separate the querystring. + ($_config['pretty_url'] == 0)? $qs_sep = '?': $qs_sep = '/'; + $pretty_url .= $qs_sep.$obj->constructPrettyUrl($end); + } + } + //if mod_rewrite is switched on, defined in constants.inc.php + if ($_config['apache_mod_rewrite'] > 0){ + return $pretty_url; + } + return AT_PRETTY_URL_HANDLER.'/'.$pretty_url; + } + + + /** + * Return the paths where this script is + */ + function getPath(){ + //for 2.0, most of the paths are moved into mods/_standard + //map the paths. + $hmap = array ( + '/glossary' => 'mods/_core', + '/groups' => 'mods/_core', + '/imscp' => 'mods/_core', + '/blogs' => 'mods/_standard', + '/chat' => 'mods/_standard', + '/directory' => 'mods/_standard', + '/faq' => 'mods/_standard', + '/file_storage' => 'mods/_standard', + '/forums/forum' => 'mods/_standard', + '/google_search'=> 'mods/_standard', + '/gradebook' => 'mods/_standard', + '/links' => 'mods/_standard', + '/photos' => 'mods/_standard', + '/polls' => 'mods/_standard', + '/sitemap' => 'mods/_standard', + '/social' => 'mods/_standard', + '/student_tools'=> 'mods/_standard', + '/tile_search' => 'mods/_standard', + '/tests' => 'mods/_standard', + '/tracker' => 'mods/_standard', + '/reading_list' => 'mods/_standard' + ); + + if ($this->path != ''){ + $path = substr($this->path, 1).'/'; + if (isset($hmap[$this->path])){ + $path = $hmap[$this->path].'/'.$path; + } + return $path; + } + return ''; + } + + /** + * Return the script name + */ + function getFileName(){ + return $this->filename; + } + + /** + * Return the link of the page. + */ + function getPage(){ + return $this->getPath().$this->getFileName(); + } + + /** + * Return true if path, filename, and query are empty. + */ + function isEmpty(){ + return $this->isEmpty; + } +} +?> \ No newline at end of file diff --git a/include/classes/XML/XML_HTMLSax/HTMLSax/XML_HTMLSax_Decorators.php b/include/classes/XML/XML_HTMLSax/HTMLSax/XML_HTMLSax_Decorators.php new file mode 100644 index 000000000..e730bb5ac --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/HTMLSax/XML_HTMLSax_Decorators.php @@ -0,0 +1,306 @@ + Original port from Python | +// | Authors: Harry Fuecks Port to PEAR + more | +// | Authors: Many @ Sitepointforums Advanced PHP Forums | +// +----------------------------------------------------------------------+ +// +// $Id$ +// +/** +* Decorators for dealing with parser options +* @package XML_HTMLSax +* @version $Id$ +* @see XML_HTMLSax::set_option +*/ +/** +* Trims the contents of element data from whitespace at start and end +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_Trim { + /** + * Original handler object + * @var object + * @access private + */ + var $orig_obj; + /** + * Original handler method + * @var string + * @access private + */ + var $orig_method; + /** + * Constructs XML_HTMLSax_Trim + * @param object handler object being decorated + * @param string original handler method + * @access protected + */ + function XML_HTMLSax_Trim(&$orig_obj, $orig_method) { + $this->orig_obj =& $orig_obj; + $this->orig_method = $orig_method; + } + /** + * Trims the data + * @param XML_HTMLSax + * @param string element data + * @access protected + */ + function trimData(&$parser, $data) { + $data = trim($data); + if ($data != '') { + $this->orig_obj->{$this->orig_method}($parser, $data); + } + } +} +/** +* Coverts tag names to upper case +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_CaseFolding { + /** + * Original handler object + * @var object + * @access private + */ + var $orig_obj; + /** + * Original open handler method + * @var string + * @access private + */ + var $orig_open_method; + /** + * Original close handler method + * @var string + * @access private + */ + var $orig_close_method; + /** + * Constructs XML_HTMLSax_CaseFolding + * @param object handler object being decorated + * @param string original open handler method + * @param string original close handler method + * @access protected + */ + function XML_HTMLSax_CaseFolding(&$orig_obj, $orig_open_method, $orig_close_method) { + $this->orig_obj =& $orig_obj; + $this->orig_open_method = $orig_open_method; + $this->orig_close_method = $orig_close_method; + } + /** + * Folds up open tag callbacks + * @param XML_HTMLSax + * @param string tag name + * @param array tag attributes + * @access protected + */ + function foldOpen(&$parser, $tag, $attrs=array(), $empty = FALSE) { + $this->orig_obj->{$this->orig_open_method}($parser, strtoupper($tag), $attrs, $empty); + } + /** + * Folds up close tag callbacks + * @param XML_HTMLSax + * @param string tag name + * @access protected + */ + function foldClose(&$parser, $tag, $empty = FALSE) { + $this->orig_obj->{$this->orig_close_method}($parser, strtoupper($tag), $empty); + } +} +/** +* Breaks up data by linefeed characters, resulting in additional +* calls to the data handler +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_Linefeed { + /** + * Original handler object + * @var object + * @access private + */ + var $orig_obj; + /** + * Original handler method + * @var string + * @access private + */ + var $orig_method; + /** + * Constructs XML_HTMLSax_LineFeed + * @param object handler object being decorated + * @param string original handler method + * @access protected + */ + function XML_HTMLSax_LineFeed(&$orig_obj, $orig_method) { + $this->orig_obj =& $orig_obj; + $this->orig_method = $orig_method; + } + /** + * Breaks the data up by linefeeds + * @param XML_HTMLSax + * @param string element data + * @access protected + */ + function breakData(&$parser, $data) { + $data = explode("\n",$data); + foreach ( $data as $chunk ) { + $this->orig_obj->{$this->orig_method}($parser, $chunk); + } + } +} +/** +* Breaks up data by tab characters, resulting in additional +* calls to the data handler +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_Tab { + /** + * Original handler object + * @var object + * @access private + */ + var $orig_obj; + /** + * Original handler method + * @var string + * @access private + */ + var $orig_method; + /** + * Constructs XML_HTMLSax_Tab + * @param object handler object being decorated + * @param string original handler method + * @access protected + */ + function XML_HTMLSax_Tab(&$orig_obj, $orig_method) { + $this->orig_obj =& $orig_obj; + $this->orig_method = $orig_method; + } + /** + * Breaks the data up by linefeeds + * @param XML_HTMLSax + * @param string element data + * @access protected + */ + function breakData(&$parser, $data) { + $data = explode("\t",$data); + foreach ( $data as $chunk ) { + $this->orig_obj->{$this->orig_method}($this, $chunk); + } + } +} +/** +* Breaks up data by XML entities and parses them with html_entity_decode(), +* resulting in additional calls to the data handler
    +* Requires PHP 4.3.0+ +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_Entities_Parsed { + /** + * Original handler object + * @var object + * @access private + */ + var $orig_obj; + /** + * Original handler method + * @var string + * @access private + */ + var $orig_method; + /** + * Constructs XML_HTMLSax_Entities_Parsed + * @param object handler object being decorated + * @param string original handler method + * @access protected + */ + function XML_HTMLSax_Entities_Parsed(&$orig_obj, $orig_method) { + $this->orig_obj =& $orig_obj; + $this->orig_method = $orig_method; + } + /** + * Breaks the data up by XML entities + * @param XML_HTMLSax + * @param string element data + * @access protected + */ + function breakData(&$parser, $data) { + $data = preg_split('/(&.+?;)/',$data,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + foreach ( $data as $chunk ) { + $chunk = html_entity_decode($chunk,ENT_NOQUOTES); + $this->orig_obj->{$this->orig_method}($this, $chunk); + } + } +} +/** +* Compatibility with older PHP versions +*/ +if (version_compare(phpversion(), '4.3', '<') && !function_exists('html_entity_decode') ) { + function html_entity_decode($str, $style=ENT_NOQUOTES) { + return strtr($str, + array_flip(get_html_translation_table(HTML_ENTITIES,$style))); + } +} +/** +* Breaks up data by XML entities but leaves them unparsed, +* resulting in additional calls to the data handler
    +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_Entities_Unparsed { + /** + * Original handler object + * @var object + * @access private + */ + var $orig_obj; + /** + * Original handler method + * @var string + * @access private + */ + var $orig_method; + /** + * Constructs XML_HTMLSax_Entities_Unparsed + * @param object handler object being decorated + * @param string original handler method + * @access protected + */ + function XML_HTMLSax_Entities_Unparsed(&$orig_obj, $orig_method) { + $this->orig_obj =& $orig_obj; + $this->orig_method = $orig_method; + } + /** + * Breaks the data up by XML entities + * @param XML_HTMLSax + * @param string element data + * @access protected + */ + function breakData(&$parser, $data) { + $data = preg_split('/(&.+?;)/',$data,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + foreach ( $data as $chunk ) { + $this->orig_obj->{$this->orig_method}($this, $chunk); + } + } +} +?> \ No newline at end of file diff --git a/include/classes/XML/XML_HTMLSax/HTMLSax/XML_HTMLSax_States.php b/include/classes/XML/XML_HTMLSax/HTMLSax/XML_HTMLSax_States.php new file mode 100644 index 000000000..35bb4960e --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/HTMLSax/XML_HTMLSax_States.php @@ -0,0 +1,292 @@ + Original port from Python | +// | Authors: Harry Fuecks Port to PEAR + more | +// | Authors: Many @ Sitepointforums Advanced PHP Forums | +// +----------------------------------------------------------------------+ +// +// $Id$ +// +/** +* Main parser components +* @package XML_HTMLSax +* @version $Id$ +*/ +/** +* Define parser states +*/ +define('XML_HTMLSAX_STATE_STOP', 0); +define('XML_HTMLSAX_STATE_START', 1); +define('XML_HTMLSAX_STATE_TAG', 2); +define('XML_HTMLSAX_STATE_OPENING_TAG', 3); +define('XML_HTMLSAX_STATE_CLOSING_TAG', 4); +define('XML_HTMLSAX_STATE_ESCAPE', 6); +define('XML_HTMLSAX_STATE_JASP', 7); +define('XML_HTMLSAX_STATE_PI', 8); +/** +* StartingState searches for the start of any XML tag +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_StartingState { + /** + * @param XML_HTMLSax_StateParser subclass + * @return constant XML_HTMLSAX_STATE_TAG + * @access protected + */ + function parse(&$context) { + $data = $context->scanUntilString('<'); + if ($data != '') { + $context->handler_object_data-> + {$context->handler_method_data}($context->htmlsax, $data); + } + $context->IgnoreCharacter(); + return XML_HTMLSAX_STATE_TAG; + } +} +/** +* Decides which state to move one from after StartingState +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_TagState { + /** + * @param XML_HTMLSax_StateParser subclass + * @return constant the next state to move into + * @access protected + */ + function parse(&$context) { + switch($context->ScanCharacter()) { + case '/': + return XML_HTMLSAX_STATE_CLOSING_TAG; + break; + case '?': + return XML_HTMLSAX_STATE_PI; + break; + case '%': + return XML_HTMLSAX_STATE_JASP; + break; + case '!': + return XML_HTMLSAX_STATE_ESCAPE; + break; + default: + $context->unscanCharacter(); + return XML_HTMLSAX_STATE_OPENING_TAG; + } + } +} +/** +* Dealing with closing XML tags +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_ClosingTagState { + /** + * @param XML_HTMLSax_StateParser subclass + * @return constant XML_HTMLSAX_STATE_START + * @access protected + */ + function parse(&$context) { + $tag = $context->scanUntilCharacters('/>'); + if ($tag != '') { + $char = $context->scanCharacter(); + if ($char == '/') { + $char = $context->scanCharacter(); + if ($char != '>') { + $context->unscanCharacter(); + } + } + $context->handler_object_element-> + {$context->handler_method_closing}($context->htmlsax, $tag, FALSE); + } + return XML_HTMLSAX_STATE_START; + } +} +/** +* Dealing with opening XML tags +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_OpeningTagState { + /** + * Handles attributes + * @param string attribute name + * @param string attribute value + * @return void + * @access protected + * @see XML_HTMLSax_AttributeStartState + */ + function parseAttributes(&$context) { + $Attributes = array(); + + $context->ignoreWhitespace(); + $attributename = $context->scanUntilCharacters("=/> \n\r\t"); + while ($attributename != '') { + $attributevalue = NULL; + $context->ignoreWhitespace(); + $char = $context->scanCharacter(); + if ($char == '=') { + $context->ignoreWhitespace(); + $char = $context->ScanCharacter(); + if ($char == '"') { + $attributevalue= $context->scanUntilString('"'); + $context->IgnoreCharacter(); + } else if ($char == "'") { + $attributevalue = $context->scanUntilString("'"); + $context->IgnoreCharacter(); + } else { + $context->unscanCharacter(); + $attributevalue = + $context->scanUntilCharacters("> \n\r\t"); + } + } else if ($char !== NULL) { + $attributevalue = true; + $context->unscanCharacter(); + } + $Attributes[$attributename] = $attributevalue; + + $context->ignoreWhitespace(); + $attributename = $context->scanUntilCharacters("=/> \n\r\t"); + } + return $Attributes; + } + + /** + * @param XML_HTMLSax_StateParser subclass + * @return constant XML_HTMLSAX_STATE_START + * @access protected + */ + function parse(&$context) { + $tag = $context->scanUntilCharacters("/> \n\r\t"); + if ($tag != '') { + $this->attrs = array(); + $Attributes = $this->parseAttributes($context); + $char = $context->scanCharacter(); + if ($char == '/') { + $char = $context->scanCharacter(); + if ($char != '>') { + $context->unscanCharacter(); + } + $context->handler_object_element-> + {$context->handler_method_opening}($context->htmlsax, $tag, + $Attributes, TRUE); + $context->handler_object_element-> + {$context->handler_method_closing}($context->htmlsax, $tag, + TRUE); + } else { + $context->handler_object_element-> + {$context->handler_method_opening}($context->htmlsax, $tag, + $Attributes, FALSE); + } + } + return XML_HTMLSAX_STATE_START; + } +} + +/** +* Deals with XML escapes handling comments and CDATA correctly +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_EscapeState { + /** + * @param XML_HTMLSax_StateParser subclass + * @return constant XML_HTMLSAX_STATE_START + * @access protected + */ + function parse(&$context) { + if ($context->parser_options['XML_OPTION_FULL_ESCAPES']==0) { + $char = $context->ScanCharacter(); + if ($char == '-') { + $char = $context->ScanCharacter(); + if ($char == '-') { + $text = $context->scanUntilString('-->'); + $context->IgnoreCharacter(); + $context->IgnoreCharacter(); + } else { + $context->unscanCharacter(); + $text = $context->scanUntilString('>'); + } + } else if ( $char == '[') { + $context->scanUntilString('CDATA['); + for ( $i=0;$i<6;$i++ ) { + $context->IgnoreCharacter(); + } + $text = $context->scanUntilString(']]>'); + $context->IgnoreCharacter(); + $context->IgnoreCharacter(); + } else { + $context->unscanCharacter(); + $text = $context->scanUntilString('>'); + } + } else { + $text = $context->scanUntilString('>'); + } + $context->IgnoreCharacter(); + if ($text != '') { + $context->handler_object_escape-> + {$context->handler_method_escape}($context->htmlsax, $text); + } + return XML_HTMLSAX_STATE_START; + } +} +/** +* Deals with JASP/ASP markup +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_JaspState { + /** + * @param XML_HTMLSax_StateParser subclass + * @return constant XML_HTMLSAX_STATE_START + * @access protected + */ + function parse(&$context) { + $text = $context->scanUntilString('%>'); + if ($text != '') { + $context->handler_object_jasp-> + {$context->handler_method_jasp}($context->htmlsax, $text); + } + $context->IgnoreCharacter(); + $context->IgnoreCharacter(); + return XML_HTMLSAX_STATE_START; + } +} +/** +* Deals with XML processing instructions +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_PiState { + /** + * @param XML_HTMLSax_StateParser subclass + * @return constant XML_HTMLSAX_STATE_START + * @access protected + */ + function parse(&$context) { + $target = $context->scanUntilCharacters(" \n\r\t"); + $data = $context->scanUntilString('?>'); + if ($data != '') { + $context->handler_object_pi-> + {$context->handler_method_pi}($context->htmlsax, $target, $data); + } + $context->IgnoreCharacter(); + $context->IgnoreCharacter(); + return XML_HTMLSAX_STATE_START; + } +} +?> \ No newline at end of file diff --git a/include/classes/XML/XML_HTMLSax/OS/Guess.php b/include/classes/XML/XML_HTMLSax/OS/Guess.php new file mode 100644 index 000000000..95c63dc45 --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/OS/Guess.php @@ -0,0 +1,343 @@ + + * @author Gregory Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since PEAR 0.1 + */ + +// {{{ uname examples + +// php_uname() without args returns the same as 'uname -a', or a PHP-custom +// string for Windows. +// PHP versions prior to 4.3 return the uname of the host where PHP was built, +// as of 4.3 it returns the uname of the host running the PHP code. +// +// PC RedHat Linux 7.1: +// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown +// +// PC Debian Potato: +// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown +// +// PC FreeBSD 3.3: +// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.3: +// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.5: +// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb 6 23:59:23 CET 2002 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.5 w/uname from GNU shellutils: +// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb i386 unknown +// +// HP 9000/712 HP-UX 10: +// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license +// +// HP 9000/712 HP-UX 10 w/uname from GNU shellutils: +// HP-UX host B.10.10 A 9000/712 unknown +// +// IBM RS6000/550 AIX 4.3: +// AIX host 3 4 000003531C00 +// +// AIX 4.3 w/uname from GNU shellutils: +// AIX host 3 4 000003531C00 unknown +// +// SGI Onyx IRIX 6.5 w/uname from GNU shellutils: +// IRIX64 host 6.5 01091820 IP19 mips +// +// SGI Onyx IRIX 6.5: +// IRIX64 host 6.5 01091820 IP19 +// +// SparcStation 20 Solaris 8 w/uname from GNU shellutils: +// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc +// +// SparcStation 20 Solaris 8: +// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20 +// +// Mac OS X (Darwin) +// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh +// +// Mac OS X early versions +// + +// }}} + +/* TODO: + * - define endianness, to allow matchSignature("bigend") etc. + */ + +/** + * Retrieves information about the current operating system + * + * This class uses php_uname() to grok information about the current OS + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Gregory Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.4.4 + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class OS_Guess +{ + var $sysname; + var $nodename; + var $cpu; + var $release; + var $extra; + + function OS_Guess($uname = null) + { + list($this->sysname, + $this->release, + $this->cpu, + $this->extra, + $this->nodename) = $this->parseSignature($uname); + } + + function parseSignature($uname = null) + { + static $sysmap = array( + 'HP-UX' => 'hpux', + 'IRIX64' => 'irix', + ); + static $cpumap = array( + 'i586' => 'i386', + 'i686' => 'i386', + 'ppc' => 'powerpc', + ); + if ($uname === null) { + $uname = php_uname(); + } + $parts = split('[[:space:]]+', trim($uname)); + $n = count($parts); + + $release = $machine = $cpu = ''; + $sysname = $parts[0]; + $nodename = $parts[1]; + $cpu = $parts[$n-1]; + $extra = ''; + if ($cpu == 'unknown') { + $cpu = $parts[$n-2]; + } + + switch ($sysname) { + case 'AIX' : + $release = "$parts[3].$parts[2]"; + break; + case 'Windows' : + switch ($parts[1]) { + case '95/98': + $release = '9x'; + break; + default: + $release = $parts[1]; + break; + } + $cpu = 'i386'; + break; + case 'Linux' : + $extra = $this->_detectGlibcVersion(); + // use only the first two digits from the kernel version + $release = preg_replace('^([0-9]+\.[0-9]+).*', '\1', $parts[2]); + break; + case 'Mac' : + $sysname = 'darwin'; + $nodename = $parts[2]; + $release = $parts[3]; + if ($cpu == 'Macintosh') { + if ($parts[$n - 2] == 'Power') { + $cpu = 'powerpc'; + } + } + break; + case 'Darwin' : + if ($cpu == 'Macintosh') { + if ($parts[$n - 2] == 'Power') { + $cpu = 'powerpc'; + } + } + $release = preg_replace('^([0-9]+\.[0-9]+).*', '\1', $parts[2]); + break; + default: + $release = preg_replace('/-.*/', '', $parts[2]); + break; + } + + + if (isset($sysmap[$sysname])) { + $sysname = $sysmap[$sysname]; + } else { + $sysname = strtolower($sysname); + } + if (isset($cpumap[$cpu])) { + $cpu = $cpumap[$cpu]; + } + return array($sysname, $release, $cpu, $extra, $nodename); + } + + function _detectGlibcVersion() + { + static $glibc = false; + if ($glibc !== false) { + return $glibc; // no need to run this multiple times + } + include_once "System.php"; + if (!file_exists('/usr/bin/cpp') || !is_executable('/usr/bin/cpp')) { + // Use glibc's header file to + // get major and minor version number: + if ($features_file = @fopen('/usr/include/features.h', 'rb') ) { + while (!feof($features_file)) { + $line = fgets($features_file, 8192); + if (!$line || (strpos($line, '#define') === false)) { + continue; + } + if (strpos($line, '__GLIBC__')) { + // major version number #define __GLIBC__ version + $line = preg_split('/\s+/', $line); + $glibc_major = trim($line[2]); + if (isset($glibc_minor)) { + break; + } + continue; + } + if (strpos($line, '__GLIBC_MINOR__')) { + // got the minor version number + // #define __GLIBC_MINOR__ version + $line = preg_split('/\s+/', $line); + $glibc_minor = trim($line[2]); + if (isset($glibc_major)) { + break; + } + continue; + } + } + fclose($features_file); + if (!isset($glibc_major) || !isset($glibc_minor)) { + return $glibc = ''; + } + return $glibc = 'glibc' . trim($glibc_major) . "." . trim($glibc_minor) ; + } + return $glibc = ''; + } + $tmpfile = System::mktemp("glibctest"); + $fp = fopen($tmpfile, "w"); + fwrite($fp, "#include \n__GLIBC__ __GLIBC_MINOR__\n"); + fclose($fp); + $cpp = popen("/usr/bin/cpp $tmpfile", "r"); + $major = $minor = 0; + while ($line = fgets($cpp, 1024)) { + if ($line{0} == '#' || trim($line) == '') { + continue; + } + if (list($major, $minor) = explode(' ', trim($line))) { + break; + } + } + pclose($cpp); + unlink($tmpfile); + if (!($major && $minor) && is_link('/lib/libc.so.6')) { + // Let's try reading the libc.so.6 symlink + if (preg_match('/^libc-([.*])\.so$/', basename(readlink('/lib/libc.so.6')), $matches)) { + list($major, $minor) = explode('.', $matches); + } + } + if (!($major && $minor)) { + return $glibc = ''; + } + return $glibc = "glibc{$major}.{$minor}"; + } + + function getSignature() + { + if (empty($this->extra)) { + return "{$this->sysname}-{$this->release}-{$this->cpu}"; + } + return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}"; + } + + function getSysname() + { + return $this->sysname; + } + + function getNodename() + { + return $this->nodename; + } + + function getCpu() + { + return $this->cpu; + } + + function getRelease() + { + return $this->release; + } + + function getExtra() + { + return $this->extra; + } + + function matchSignature($match) + { + if (is_array($match)) { + $fragments = $match; + } else { + $fragments = explode('-', $match); + } + $n = count($fragments); + $matches = 0; + if ($n > 0) { + $matches += $this->_matchFragment($fragments[0], $this->sysname); + } + if ($n > 1) { + $matches += $this->_matchFragment($fragments[1], $this->release); + } + if ($n > 2) { + $matches += $this->_matchFragment($fragments[2], $this->cpu); + } + if ($n > 3) { + $matches += $this->_matchFragment($fragments[3], $this->extra); + } + return ($matches == $n); + } + + function _matchFragment($fragment, $value) + { + if (strcspn($fragment, '*?') < strlen($fragment)) { + $reg = '/^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '$/i'; + return preg_match($reg, $value); + } + return ($fragment == '*' || !strcasecmp($fragment, $value)); + } + +} +/* + * Local Variables: + * indent-tabs-mode: nil + * c-basic-offset: 4 + * End: + */ +?> diff --git a/include/classes/XML/XML_HTMLSax/PEAR.php b/include/classes/XML/XML_HTMLSax/PEAR.php new file mode 100644 index 000000000..32f922be3 --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/PEAR.php @@ -0,0 +1,1095 @@ + + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Greg Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/**#@+ + * ERROR constants + */ +define('PEAR_ERROR_RETURN', 1); +define('PEAR_ERROR_PRINT', 2); +define('PEAR_ERROR_TRIGGER', 4); +define('PEAR_ERROR_DIE', 8); +define('PEAR_ERROR_CALLBACK', 16); +/** + * WARNING: obsolete + * @deprecated + */ +define('PEAR_ERROR_EXCEPTION', 32); +/**#@-*/ +define('PEAR_ZE2', (function_exists('version_compare') && + version_compare(zend_version(), "2-dev", "ge"))); + +if (substr(PHP_OS, 0, 3) == 'WIN') { + define('OS_WINDOWS', true); + define('OS_UNIX', false); + define('PEAR_OS', 'Windows'); +} else { + define('OS_WINDOWS', false); + define('OS_UNIX', true); + define('PEAR_OS', 'Unix'); // blatant assumption +} + +// instant backwards compatibility +if (!defined('PATH_SEPARATOR')) { + if (OS_WINDOWS) { + define('PATH_SEPARATOR', ';'); + } else { + define('PATH_SEPARATOR', ':'); + } +} + +$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; +$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; +$GLOBALS['_PEAR_destructor_object_list'] = array(); +$GLOBALS['_PEAR_shutdown_funcs'] = array(); +$GLOBALS['_PEAR_error_handler_stack'] = array(); + +@ini_set('track_errors', true); + +/** + * Base class for other PEAR classes. Provides rudimentary + * emulation of destructors. + * + * If you want a destructor in your class, inherit PEAR and make a + * destructor method called _yourclassname (same name as the + * constructor, but with a "_" prefix). Also, in your constructor you + * have to call the PEAR constructor: $this->PEAR();. + * The destructor method will be called without parameters. Note that + * at in some SAPI implementations (such as Apache), any output during + * the request shutdown (in which destructors are called) seems to be + * discarded. If you need to get any debug information from your + * destructor, use error_log(), syslog() or something similar. + * + * IMPORTANT! To use the emulated destructors you need to create the + * objects by reference: $obj = new PEAR_child; + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Greg Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.4.4 + * @link http://pear.php.net/package/PEAR + * @see PEAR_Error + * @since Class available since PHP 4.0.2 + * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear + */ +class PEAR +{ + // {{{ properties + + /** + * Whether to enable internal debug messages. + * + * @var bool + * @access private + */ + var $_debug = false; + + /** + * Default error mode for this object. + * + * @var int + * @access private + */ + var $_default_error_mode = null; + + /** + * Default error options used for this object when error mode + * is PEAR_ERROR_TRIGGER. + * + * @var int + * @access private + */ + var $_default_error_options = null; + + /** + * Default error handler (callback) for this object, if error mode is + * PEAR_ERROR_CALLBACK. + * + * @var string + * @access private + */ + var $_default_error_handler = ''; + + /** + * Which class to use for error objects. + * + * @var string + * @access private + */ + var $_error_class = 'PEAR_Error'; + + /** + * An array of expected errors. + * + * @var array + * @access private + */ + var $_expected_errors = array(); + + // }}} + + // {{{ constructor + + /** + * Constructor. Registers this object in + * $_PEAR_destructor_object_list for destructor emulation if a + * destructor object exists. + * + * @param string $error_class (optional) which class to use for + * error objects, defaults to PEAR_Error. + * @access public + * @return void + */ + function PEAR($error_class = null) + { + $classname = strtolower(get_class($this)); + if ($this->_debug) { + print "PEAR constructor called, class=$classname\n"; + } + if ($error_class !== null) { + $this->_error_class = $error_class; + } + while ($classname && strcasecmp($classname, "pear")) { + $destructor = "_$classname"; + if (method_exists($this, $destructor)) { + global $_PEAR_destructor_object_list; + $_PEAR_destructor_object_list[] = &$this; + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { + register_shutdown_function("_PEAR_call_destructors"); + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; + } + break; + } else { + $classname = get_parent_class($classname); + } + } + } + + // }}} + // {{{ destructor + + /** + * Destructor (the emulated type of...). Does nothing right now, + * but is included for forward compatibility, so subclass + * destructors should always call it. + * + * See the note in the class desciption about output from + * destructors. + * + * @access public + * @return void + */ + function _PEAR() { + if ($this->_debug) { + printf("PEAR destructor called, class=%s\n", strtolower(get_class($this))); + } + } + + // }}} + // {{{ getStaticProperty() + + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); + * You MUST use a reference, or they will not persist! + * + * @access public + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + */ + function &getStaticProperty($class, $var) + { + static $properties; + return $properties[$class][$var]; + } + + // }}} + // {{{ registerShutdownFunc() + + /** + * Use this function to register a shutdown method for static + * classes. + * + * @access public + * @param mixed $func The function name (or array of class/method) to call + * @param mixed $args The arguments to pass to the function + * @return void + */ + function registerShutdownFunc($func, $args = array()) + { + $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); + } + + // }}} + // {{{ isError() + + /** + * Tell whether a value is a PEAR error. + * + * @param mixed $data the value to test + * @param int $code if $data is an error object, return true + * only if $code is a string and + * $obj->getMessage() == $code or + * $code is an integer and $obj->getCode() == $code + * @access public + * @return bool true if parameter is an error + */ + function isError($data, $code = null) + { + if (is_a($data, 'PEAR_Error')) { + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() == $code; + } else { + return $data->getCode() == $code; + } + } + return false; + } + + // }}} + // {{{ setErrorHandling() + + /** + * Sets how errors generated by this object should be handled. + * Can be invoked both in objects and statically. If called + * statically, setErrorHandling sets the default behaviour for all + * PEAR objects. If called in an object, setErrorHandling sets + * the default behaviour for that object. + * + * @param int $mode + * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. + * + * @param mixed $options + * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one + * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * + * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected + * to be the callback function or method. A callback + * function is a string with the name of the function, a + * callback method is an array of two elements: the element + * at index 0 is the object, and the element at index 1 is + * the name of the method to call in the object. + * + * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is + * a printf format string used when printing the error + * message. + * + * @access public + * @return void + * @see PEAR_ERROR_RETURN + * @see PEAR_ERROR_PRINT + * @see PEAR_ERROR_TRIGGER + * @see PEAR_ERROR_DIE + * @see PEAR_ERROR_CALLBACK + * @see PEAR_ERROR_EXCEPTION + * + * @since PHP 4.0.5 + */ + + function setErrorHandling($mode = null, $options = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + $setmode = &$this->_default_error_mode; + $setoptions = &$this->_default_error_options; + } else { + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + } + + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + } + + // }}} + // {{{ expectError() + + /** + * This method is used to tell which errors you expect to get. + * Expected errors are always returned with error mode + * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, + * and this method pushes a new element onto it. The list of + * expected errors are in effect until they are popped off the + * stack with the popExpect() method. + * + * Note that this method can not be called statically + * + * @param mixed $code a single error code or an array of error codes to expect + * + * @return int the new depth of the "expected errors" stack + * @access public + */ + function expectError($code = '*') + { + if (is_array($code)) { + array_push($this->_expected_errors, $code); + } else { + array_push($this->_expected_errors, array($code)); + } + return sizeof($this->_expected_errors); + } + + // }}} + // {{{ popExpect() + + /** + * This method pops one element off the expected error codes + * stack. + * + * @return array the list of error codes that were popped + */ + function popExpect() + { + return array_pop($this->_expected_errors); + } + + // }}} + // {{{ _checkDelExpect() + + /** + * This method checks unsets an error code if available + * + * @param mixed error code + * @return bool true if the error code was unset, false otherwise + * @access private + * @since PHP 4.3.0 + */ + function _checkDelExpect($error_code) + { + $deleted = false; + + foreach ($this->_expected_errors AS $key => $error_array) { + if (in_array($error_code, $error_array)) { + unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); + $deleted = true; + } + + // clean up empty arrays + if (0 == count($this->_expected_errors[$key])) { + unset($this->_expected_errors[$key]); + } + } + return $deleted; + } + + // }}} + // {{{ delExpect() + + /** + * This method deletes all occurences of the specified element from + * the expected error codes stack. + * + * @param mixed $error_code error code that should be deleted + * @return mixed list of error codes that were deleted or error + * @access public + * @since PHP 4.3.0 + */ + function delExpect($error_code) + { + $deleted = false; + + if ((is_array($error_code) && (0 != count($error_code)))) { + // $error_code is a non-empty array here; + // we walk through it trying to unset all + // values + foreach($error_code as $key => $error) { + if ($this->_checkDelExpect($error)) { + $deleted = true; + } else { + $deleted = false; + } + } + return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } elseif (!empty($error_code)) { + // $error_code comes alone, trying to unset it + if ($this->_checkDelExpect($error_code)) { + return true; + } else { + return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } + } else { + // $error_code is empty + return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + } + } + + // }}} + // {{{ raiseError() + + /** + * This method is a wrapper that returns an instance of the + * configured error class with this object's default error + * handling applied. If the $mode and $options parameters are not + * specified, the object's defaults are used. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION. + * + * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter + * specifies the PHP-internal error level (one of + * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * If $mode is PEAR_ERROR_CALLBACK, this + * parameter specifies the callback function or + * method. In other error modes this parameter + * is ignored. + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @param string $error_class The returned error object will be + * instantiated from this class, if specified. + * + * @param bool $skipmsg If true, raiseError will only pass error codes, + * the error message parameter will be dropped. + * + * @access public + * @return object a PEAR error object + * @see PEAR::setErrorHandling + * @since PHP 4.0.5 + */ + function &raiseError($message = null, + $code = null, + $mode = null, + $options = null, + $userinfo = null, + $error_class = null, + $skipmsg = false) + { + // The error is yet a PEAR error object + if (is_object($message)) { + $code = $message->getCode(); + $userinfo = $message->getUserInfo(); + $error_class = $message->getType(); + $message->error_message_prefix = ''; + $message = $message->getMessage(); + } + + if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) { + if ($exp[0] == "*" || + (is_int(reset($exp)) && in_array($code, $exp)) || + (is_string(reset($exp)) && in_array($message, $exp))) { + $mode = PEAR_ERROR_RETURN; + } + } + // No mode given, try global ones + if ($mode === null) { + // Class error handler + if (isset($this) && isset($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + // Global error handler + } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { + $mode = $GLOBALS['_PEAR_default_error_mode']; + $options = $GLOBALS['_PEAR_default_error_options']; + } + } + + if ($error_class !== null) { + $ec = $error_class; + } elseif (isset($this) && isset($this->_error_class)) { + $ec = $this->_error_class; + } else { + $ec = 'PEAR_Error'; + } + if ($skipmsg) { + $a = new $ec($code, $mode, $options, $userinfo); + return $a; + } else { + $a = new $ec($message, $code, $mode, $options, $userinfo); + return $a; + } + } + + // }}} + // {{{ throwError() + + /** + * Simpler form of raiseError with fewer options. In most cases + * message, code and userinfo are enough. + * + * @param string $message + * + */ + function &throwError($message = null, + $code = null, + $userinfo = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + $a = &$this->raiseError($message, $code, null, null, $userinfo); + return $a; + } else { + $a = &PEAR::raiseError($message, $code, null, null, $userinfo); + return $a; + } + } + + // }}} + function staticPushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + $stack[] = array($def_mode, $def_options); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $def_mode = $mode; + $def_options = $options; + break; + + case PEAR_ERROR_CALLBACK: + $def_mode = $mode; + // class/object method callback + if (is_callable($options)) { + $def_options = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + $stack[] = array($mode, $options); + return true; + } + + function staticPopErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + return true; + } + + // {{{ pushErrorHandling() + + /** + * Push a new error handler on top of the error handler options stack. With this + * you can easily override the actual error handler for some code and restore + * it later with popErrorHandling. + * + * @param mixed $mode (same as setErrorHandling) + * @param mixed $options (same as setErrorHandling) + * + * @return bool Always true + * + * @see PEAR::setErrorHandling + */ + function pushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + if (isset($this) && is_a($this, 'PEAR')) { + $def_mode = &$this->_default_error_mode; + $def_options = &$this->_default_error_options; + } else { + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + } + $stack[] = array($def_mode, $def_options); + + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + $stack[] = array($mode, $options); + return true; + } + + // }}} + // {{{ popErrorHandling() + + /** + * Pop the last error handler used + * + * @return bool Always true + * + * @see PEAR::pushErrorHandling + */ + function popErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + return true; + } + + // }}} + // {{{ loadExtension() + + /** + * OS independant PHP extension load. Remember to take care + * on the correct extension name for case sensitive OSes. + * + * @param string $ext The extension name + * @return bool Success or not on the dl() call + */ + function loadExtension($ext) + { + if (!extension_loaded($ext)) { + // if either returns true dl() will produce a FATAL error, stop that + if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { + return false; + } + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + } + return true; + } + + // }}} +} + +// {{{ _PEAR_call_destructors() + +function _PEAR_call_destructors() +{ + global $_PEAR_destructor_object_list; + if (is_array($_PEAR_destructor_object_list) && + sizeof($_PEAR_destructor_object_list)) + { + reset($_PEAR_destructor_object_list); + if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) { + $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); + } + while (list($k, $objref) = each($_PEAR_destructor_object_list)) { + $classname = get_class($objref); + while ($classname) { + $destructor = "_$classname"; + if (method_exists($objref, $destructor)) { + $objref->$destructor(); + break; + } else { + $classname = get_parent_class($classname); + } + } + } + // Empty the object list to ensure that destructors are + // not called more than once. + $_PEAR_destructor_object_list = array(); + } + + // Now call the shutdown functions + if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) { + foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { + call_user_func_array($value[0], $value[1]); + } + } +} + +// }}} +/** + * Standard PEAR error class for PHP 4 + * + * This class is supserseded by {@link PEAR_Exception} in PHP 5 + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Gregory Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.4.4 + * @link http://pear.php.net/manual/en/core.pear.pear-error.php + * @see PEAR::raiseError(), PEAR::throwError() + * @since Class available since PHP 4.0.2 + */ +class PEAR_Error +{ + // {{{ properties + + var $error_message_prefix = ''; + var $mode = PEAR_ERROR_RETURN; + var $level = E_USER_NOTICE; + var $code = -1; + var $message = ''; + var $userinfo = ''; + var $backtrace = null; + + // }}} + // {{{ constructor + + /** + * PEAR_Error constructor + * + * @param string $message message + * + * @param int $code (optional) error code + * + * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, + * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION + * + * @param mixed $options (optional) error level, _OR_ in the case of + * PEAR_ERROR_CALLBACK, the callback function or object/method + * tuple. + * + * @param string $userinfo (optional) additional user/debug info + * + * @access public + * + */ + function PEAR_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + if ($mode === null) { + $mode = PEAR_ERROR_RETURN; + } + $this->message = $message; + $this->code = $code; + $this->mode = $mode; + $this->userinfo = $userinfo; + if (function_exists("debug_backtrace")) { + if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) { + $this->backtrace = debug_backtrace(); + } + } + if ($mode & PEAR_ERROR_CALLBACK) { + $this->level = E_USER_NOTICE; + $this->callback = $options; + } else { + if ($options === null) { + $options = E_USER_NOTICE; + } + $this->level = $options; + $this->callback = null; + } + if ($this->mode & PEAR_ERROR_PRINT) { + if (is_null($options) || is_int($options)) { + $format = "%s"; + } else { + $format = $options; + } + printf($format, $this->getMessage()); + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + trigger_error($this->getMessage(), $this->level); + } + if ($this->mode & PEAR_ERROR_DIE) { + $msg = $this->getMessage(); + if (is_null($options) || is_int($options)) { + $format = "%s"; + if (substr($msg, -1) != "\n") { + $msg .= "\n"; + } + } else { + $format = $options; + } + die(sprintf($format, $msg)); + } + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_callable($this->callback)) { + call_user_func($this->callback, $this); + } + } + if ($this->mode & PEAR_ERROR_EXCEPTION) { + trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING); + eval('$e = new Exception($this->message, $this->code);throw($e);'); + } + } + + // }}} + // {{{ getMode() + + /** + * Get the error mode from an error object. + * + * @return int error mode + * @access public + */ + function getMode() { + return $this->mode; + } + + // }}} + // {{{ getCallback() + + /** + * Get the callback function/method from an error object. + * + * @return mixed callback function or object/method array + * @access public + */ + function getCallback() { + return $this->callback; + } + + // }}} + // {{{ getMessage() + + + /** + * Get the error message from an error object. + * + * @return string full error message + * @access public + */ + function getMessage() + { + return ($this->error_message_prefix . $this->message); + } + + + // }}} + // {{{ getCode() + + /** + * Get error code from an error object + * + * @return int error code + * @access public + */ + function getCode() + { + return $this->code; + } + + // }}} + // {{{ getType() + + /** + * Get the name of this error/exception. + * + * @return string error/exception name (type) + * @access public + */ + function getType() + { + return get_class($this); + } + + // }}} + // {{{ getUserInfo() + + /** + * Get additional user-supplied information. + * + * @return string user-supplied information + * @access public + */ + function getUserInfo() + { + return $this->userinfo; + } + + // }}} + // {{{ getDebugInfo() + + /** + * Get additional debug information supplied by the application. + * + * @return string debug information + * @access public + */ + function getDebugInfo() + { + return $this->getUserInfo(); + } + + // }}} + // {{{ getBacktrace() + + /** + * Get the call backtrace from where the error was generated. + * Supported with PHP 4.3.0 or newer. + * + * @param int $frame (optional) what frame to fetch + * @return array Backtrace, or NULL if not available. + * @access public + */ + function getBacktrace($frame = null) + { + if (defined('PEAR_IGNORE_BACKTRACE')) { + return null; + } + if ($frame === null) { + return $this->backtrace; + } + return $this->backtrace[$frame]; + } + + // }}} + // {{{ addUserInfo() + + function addUserInfo($info) + { + if (empty($this->userinfo)) { + $this->userinfo = $info; + } else { + $this->userinfo .= " ** $info"; + } + } + + // }}} + // {{{ toString() + + /** + * Make a string representation of this object. + * + * @return string a string with an object summary + * @access public + */ + function toString() { + $modes = array(); + $levels = array(E_USER_NOTICE => 'notice', + E_USER_WARNING => 'warning', + E_USER_ERROR => 'error'); + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_array($this->callback)) { + $callback = (is_object($this->callback[0]) ? + strtolower(get_class($this->callback[0])) : + $this->callback[0]) . '::' . + $this->callback[1]; + } else { + $callback = $this->callback; + } + return sprintf('[%s: message="%s" code=%d mode=callback '. + 'callback=%s prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + $callback, $this->error_message_prefix, + $this->userinfo); + } + if ($this->mode & PEAR_ERROR_PRINT) { + $modes[] = 'print'; + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + $modes[] = 'trigger'; + } + if ($this->mode & PEAR_ERROR_DIE) { + $modes[] = 'die'; + } + if ($this->mode & PEAR_ERROR_RETURN) { + $modes[] = 'return'; + } + return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. + 'prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + implode("|", $modes), $levels[$this->level], + $this->error_message_prefix, + $this->userinfo); + } + + // }}} +} + +/* + * Local Variables: + * mode: php + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ +?> diff --git a/include/classes/XML/XML_HTMLSax/PEAR/Autoloader.php b/include/classes/XML/XML_HTMLSax/PEAR/Autoloader.php new file mode 100644 index 000000000..fb3db3c8d --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/PEAR/Autoloader.php @@ -0,0 +1,223 @@ + + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader + * @since File available since Release 0.1 + * @deprecated File deprecated in Release 1.4.0a1 + */ + +// /* vim: set expandtab tabstop=4 shiftwidth=4: */ + +if (!extension_loaded("overload")) { + // die hard without ext/overload + die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader"); +} + +/** + * Include for PEAR_Error and PEAR classes + */ +require_once "PEAR.php"; + +/** + * This class is for objects where you want to separate the code for + * some methods into separate classes. This is useful if you have a + * class with not-frequently-used methods that contain lots of code + * that you would like to avoid always parsing. + * + * The PEAR_Autoloader class provides autoloading and aggregation. + * The autoloading lets you set up in which classes the separated + * methods are found. Aggregation is the technique used to import new + * methods, an instance of each class providing separated methods is + * stored and called every time the aggregated method is called. + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.4.4 + * @link http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader + * @since File available since Release 0.1 + * @deprecated File deprecated in Release 1.4.0a1 + */ +class PEAR_Autoloader extends PEAR +{ + // {{{ properties + + /** + * Map of methods and classes where they are defined + * + * @var array + * + * @access private + */ + var $_autoload_map = array(); + + /** + * Map of methods and aggregate objects + * + * @var array + * + * @access private + */ + var $_method_map = array(); + + // }}} + // {{{ addAutoload() + + /** + * Add one or more autoload entries. + * + * @param string $method which method to autoload + * + * @param string $classname (optional) which class to find the method in. + * If the $method parameter is an array, this + * parameter may be omitted (and will be ignored + * if not), and the $method parameter will be + * treated as an associative array with method + * names as keys and class names as values. + * + * @return void + * + * @access public + */ + function addAutoload($method, $classname = null) + { + if (is_array($method)) { + array_walk($method, create_function('$a,&$b', '$b = strtolower($b);')); + $this->_autoload_map = array_merge($this->_autoload_map, $method); + } else { + $this->_autoload_map[strtolower($method)] = $classname; + } + } + + // }}} + // {{{ removeAutoload() + + /** + * Remove an autoload entry. + * + * @param string $method which method to remove the autoload entry for + * + * @return bool TRUE if an entry was removed, FALSE if not + * + * @access public + */ + function removeAutoload($method) + { + $method = strtolower($method); + $ok = isset($this->_autoload_map[$method]); + unset($this->_autoload_map[$method]); + return $ok; + } + + // }}} + // {{{ addAggregateObject() + + /** + * Add an aggregate object to this object. If the specified class + * is not defined, loading it will be attempted following PEAR's + * file naming scheme. All the methods in the class will be + * aggregated, except private ones (name starting with an + * underscore) and constructors. + * + * @param string $classname what class to instantiate for the object. + * + * @return void + * + * @access public + */ + function addAggregateObject($classname) + { + $classname = strtolower($classname); + if (!class_exists($classname)) { + $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname); + include_once $include_file; + } + $obj = new $classname; + $methods = get_class_methods($classname); + foreach ($methods as $method) { + // don't import priviate methods and constructors + if ($method{0} != '_' && $method != $classname) { + $this->_method_map[$method] = $obj; + } + } + } + + // }}} + // {{{ removeAggregateObject() + + /** + * Remove an aggregate object. + * + * @param string $classname the class of the object to remove + * + * @return bool TRUE if an object was removed, FALSE if not + * + * @access public + */ + function removeAggregateObject($classname) + { + $ok = false; + $classname = strtolower($classname); + reset($this->_method_map); + while (list($method, $obj) = each($this->_method_map)) { + if (is_a($obj, $classname)) { + unset($this->_method_map[$method]); + $ok = true; + } + } + return $ok; + } + + // }}} + // {{{ __call() + + /** + * Overloaded object call handler, called each time an + * undefined/aggregated method is invoked. This method repeats + * the call in the right aggregate object and passes on the return + * value. + * + * @param string $method which method that was called + * + * @param string $args An array of the parameters passed in the + * original call + * + * @return mixed The return value from the aggregated method, or a PEAR + * error if the called method was unknown. + */ + function __call($method, $args, &$retval) + { + $method = strtolower($method); + if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) { + $this->addAggregateObject($this->_autoload_map[$method]); + } + if (isset($this->_method_map[$method])) { + $retval = call_user_func_array(array($this->_method_map[$method], $method), $args); + return true; + } + return false; + } + + // }}} +} + +overload("PEAR_Autoloader"); + +?> diff --git a/include/classes/XML/XML_HTMLSax/PEAR/Common.php b/include/classes/XML/XML_HTMLSax/PEAR/Common.php new file mode 100644 index 000000000..254b680c2 --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/PEAR/Common.php @@ -0,0 +1,1140 @@ + + * @author Tomas V. V. Cox + * @author Greg Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1.0 + * @deprecated File deprecated since Release 1.4.0a1 + */ + +/** + * Include error handling + */ +require_once 'PEAR.php'; + +// {{{ constants and globals + +/** + * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode() + */ +define('PEAR_COMMON_ERROR_INVALIDPHP', 1); +define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+'); +define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '$/'); + +// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1 +define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?'); +define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '$/i'); + +// XXX far from perfect :-) +define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG . + ')(-([.0-9a-zA-Z]+))?'); +define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG . + '$/'); + +define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9_\.]+'); +define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '$/'); + +// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED +define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(\/[a-zA-Z0-9-]+)*'); +define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '$/i'); + +define('_PEAR_CHANNELS_PACKAGE_PREG', '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/(' + . _PEAR_COMMON_PACKAGE_NAME_PREG . ')'); +define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '$/i'); + +define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::(' + . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?'); +define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '$/'); + +/** + * List of temporary files and directories registered by + * PEAR_Common::addTempFile(). + * @var array + */ +$GLOBALS['_PEAR_Common_tempfiles'] = array(); + +/** + * Valid maintainer roles + * @var array + */ +$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper'); + +/** + * Valid release states + * @var array + */ +$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel'); + +/** + * Valid dependency types + * @var array + */ +$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi'); + +/** + * Valid dependency relations + * @var array + */ +$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne'); + +/** + * Valid file roles + * @var array + */ +$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script'); + +/** + * Valid replacement types + * @var array + */ +$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info'); + +/** + * Valid "provide" types + * @var array + */ +$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api'); + +/** + * Valid "provide" types + * @var array + */ +$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup'); + +// }}} + +/** + * Class providing common functionality for PEAR administration classes. + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V. V. Cox + * @author Greg Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.4.4 + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 + * @deprecated This class will disappear, and its components will be spread + * into smaller classes, like the AT&T breakup, as of Release 1.4.0a1 + */ +class PEAR_Common extends PEAR +{ + // {{{ properties + + /** stack of elements, gives some sort of XML context */ + var $element_stack = array(); + + /** name of currently parsed XML element */ + var $current_element; + + /** array of attributes of the currently parsed XML element */ + var $current_attributes = array(); + + /** assoc with information about a package */ + var $pkginfo = array(); + + /** + * User Interface object (PEAR_Frontend_* class). If null, + * the log() method uses print. + * @var object + */ + var $ui = null; + + /** + * Configuration object (PEAR_Config). + * @var object + */ + var $config = null; + + var $current_path = null; + + /** + * PEAR_SourceAnalyzer instance + * @var object + */ + var $source_analyzer = null; + /** + * Flag variable used to mark a valid package file + * @var boolean + * @access private + */ + var $_validPackageFile; + + // }}} + + // {{{ constructor + + /** + * PEAR_Common constructor + * + * @access public + */ + function PEAR_Common() + { + parent::PEAR(); + $this->config = &PEAR_Config::singleton(); + $this->debug = $this->config->get('verbose'); + } + + // }}} + // {{{ destructor + + /** + * PEAR_Common destructor + * + * @access private + */ + function _PEAR_Common() + { + // doesn't work due to bug #14744 + //$tempfiles = $this->_tempfiles; + $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles']; + while ($file = array_shift($tempfiles)) { + if (@is_dir($file)) { + if (!class_exists('System')) { + require_once 'System.php'; + } + System::rm(array('-rf', $file)); + } elseif (file_exists($file)) { + unlink($file); + } + } + } + + // }}} + // {{{ addTempFile() + + /** + * Register a temporary file or directory. When the destructor is + * executed, all registered temporary files and directories are + * removed. + * + * @param string $file name of file or directory + * + * @return void + * + * @access public + */ + function addTempFile($file) + { + if (!class_exists('PEAR_Frontend')) { + require_once 'PEAR/Frontend.php'; + } + PEAR_Frontend::addTempFile($file); + } + + // }}} + // {{{ mkDirHier() + + /** + * Wrapper to System::mkDir(), creates a directory as well as + * any necessary parent directories. + * + * @param string $dir directory name + * + * @return bool TRUE on success, or a PEAR error + * + * @access public + */ + function mkDirHier($dir) + { + $this->log(2, "+ create dir $dir"); + if (!class_exists('System')) { + require_once 'System.php'; + } + return System::mkDir(array('-p', $dir)); + } + + // }}} + // {{{ log() + + /** + * Logging method. + * + * @param int $level log level (0 is quiet, higher is noisier) + * @param string $msg message to write to the log + * + * @return void + * + * @access public + * @static + */ + function log($level, $msg, $append_crlf = true) + { + if ($this->debug >= $level) { + if (!class_exists('PEAR_Frontend')) { + require_once 'PEAR/Frontend.php'; + } + $ui = &PEAR_Frontend::singleton(); + if (is_a($ui, 'PEAR_Frontend')) { + $ui->log($msg, $append_crlf); + } else { + print "$msg\n"; + } + } + } + + // }}} + // {{{ mkTempDir() + + /** + * Create and register a temporary directory. + * + * @param string $tmpdir (optional) Directory to use as tmpdir. + * Will use system defaults (for example + * /tmp or c:\windows\temp) if not specified + * + * @return string name of created directory + * + * @access public + */ + function mkTempDir($tmpdir = '') + { + if ($tmpdir) { + $topt = array('-t', $tmpdir); + } else { + $topt = array(); + } + $topt = array_merge($topt, array('-d', 'pear')); + if (!class_exists('System')) { + require_once 'System.php'; + } + if (!$tmpdir = System::mktemp($topt)) { + return false; + } + $this->addTempFile($tmpdir); + return $tmpdir; + } + + // }}} + // {{{ setFrontendObject() + + /** + * Set object that represents the frontend to be used. + * + * @param object Reference of the frontend object + * @return void + * @access public + */ + function setFrontendObject(&$ui) + { + $this->ui = &$ui; + } + + // }}} + + // {{{ infoFromTgzFile() + + /** + * Returns information about a package file. Expects the name of + * a gzipped tar file as input. + * + * @param string $file name of .tgz file + * + * @return array array with package information + * + * @access public + * @deprecated use PEAR_PackageFile->fromTgzFile() instead + * + */ + function infoFromTgzFile($file) + { + $packagefile = new PEAR_PackageFile($this->config); + $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($pf)) { + $errs = $pf->getUserinfo(); + if (is_array($errs)) { + foreach ($errs as $error) { + $e = $this->raiseError($error['message'], $error['code'], null, null, $error); + } + } + return $pf; + } + return $this->_postProcessValidPackagexml($pf); + } + + // }}} + // {{{ infoFromDescriptionFile() + + /** + * Returns information about a package file. Expects the name of + * a package xml file as input. + * + * @param string $descfile name of package xml file + * + * @return array array with package information + * + * @access public + * @deprecated use PEAR_PackageFile->fromPackageFile() instead + * + */ + function infoFromDescriptionFile($descfile) + { + $packagefile = new PEAR_PackageFile($this->config); + $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($pf)) { + $errs = $pf->getUserinfo(); + if (is_array($errs)) { + foreach ($errs as $error) { + $e = $this->raiseError($error['message'], $error['code'], null, null, $error); + } + } + return $pf; + } + return $this->_postProcessValidPackagexml($pf); + } + + // }}} + // {{{ infoFromString() + + /** + * Returns information about a package file. Expects the contents + * of a package xml file as input. + * + * @param string $data contents of package.xml file + * + * @return array array with package information + * + * @access public + * @deprecated use PEAR_PackageFile->fromXmlstring() instead + * + */ + function infoFromString($data) + { + $packagefile = new PEAR_PackageFile($this->config); + $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false); + if (PEAR::isError($pf)) { + $errs = $pf->getUserinfo(); + if (is_array($errs)) { + foreach ($errs as $error) { + $e = $this->raiseError($error['message'], $error['code'], null, null, $error); + } + } + return $pf; + } + return $this->_postProcessValidPackagexml($pf); + } + // }}} + + /** + * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 + * @return array + */ + function _postProcessValidPackagexml(&$pf) + { + if (is_a($pf, 'PEAR_PackageFile_v2')) { + // sort of make this into a package.xml 1.0-style array + // changelog is not converted to old format. + $arr = $pf->toArray(true); + $arr = array_merge($arr, $arr['old']); + unset($arr['old']); + unset($arr['xsdversion']); + unset($arr['contents']); + unset($arr['compatible']); + unset($arr['channel']); + unset($arr['uri']); + unset($arr['dependencies']); + unset($arr['phprelease']); + unset($arr['extsrcrelease']); + unset($arr['extbinrelease']); + unset($arr['bundle']); + unset($arr['lead']); + unset($arr['developer']); + unset($arr['helper']); + unset($arr['contributor']); + $arr['filelist'] = $pf->getFilelist(); + $this->pkginfo = $arr; + return $arr; + } else { + $this->pkginfo = $pf->toArray(); + return $this->pkginfo; + } + } + // {{{ infoFromAny() + + /** + * Returns package information from different sources + * + * This method is able to extract information about a package + * from a .tgz archive or from a XML package definition file. + * + * @access public + * @param string Filename of the source ('package.xml', '.tgz') + * @return string + * @deprecated use PEAR_PackageFile->fromAnyFile() instead + */ + function infoFromAny($info) + { + if (is_string($info) && file_exists($info)) { + $packagefile = new PEAR_PackageFile($this->config); + $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($pf)) { + $errs = $pf->getUserinfo(); + if (is_array($errs)) { + foreach ($errs as $error) { + $e = $this->raiseError($error['message'], $error['code'], null, null, $error); + } + } + return $pf; + } + return $this->_postProcessValidPackagexml($pf); + } + return $info; + } + + // }}} + // {{{ xmlFromInfo() + + /** + * Return an XML document based on the package info (as returned + * by the PEAR_Common::infoFrom* methods). + * + * @param array $pkginfo package info + * + * @return string XML data + * + * @access public + * @deprecated use a PEAR_PackageFile_v* object's generator instead + */ + function xmlFromInfo($pkginfo) + { + $config = &PEAR_Config::singleton(); + $packagefile = new PEAR_PackageFile($config); + $pf = &$packagefile->fromArray($pkginfo); + $gen = &$pf->getDefaultGenerator(); + return $gen->toXml(PEAR_VALIDATE_PACKAGING); + } + + // }}} + // {{{ validatePackageInfo() + + /** + * Validate XML package definition file. + * + * @param string $info Filename of the package archive or of the + * package definition file + * @param array $errors Array that will contain the errors + * @param array $warnings Array that will contain the warnings + * @param string $dir_prefix (optional) directory where source files + * may be found, or empty if they are not available + * @access public + * @return boolean + * @deprecated use the validation of PEAR_PackageFile objects + */ + function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '') + { + $config = &PEAR_Config::singleton(); + $packagefile = new PEAR_PackageFile($config); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + if (strpos($info, 'fromXmlString($info, PEAR_VALIDATE_NORMAL, ''); + } else { + $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL); + } + PEAR::staticPopErrorHandling(); + if (PEAR::isError($pf)) { + $errs = $pf->getUserinfo(); + if (is_array($errs)) { + foreach ($errs as $error) { + if ($error['level'] == 'error') { + $errors[] = $error['message']; + } else { + $warnings[] = $error['message']; + } + } + } + return false; + } + return true; + } + + // }}} + // {{{ buildProvidesArray() + + /** + * Build a "provides" array from data returned by + * analyzeSourceCode(). The format of the built array is like + * this: + * + * array( + * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), + * ... + * ) + * + * + * @param array $srcinfo array with information about a source file + * as returned by the analyzeSourceCode() method. + * + * @return void + * + * @access public + * + */ + function buildProvidesArray($srcinfo) + { + $file = basename($srcinfo['source_file']); + $pn = ''; + if (isset($this->_packageName)) { + $pn = $this->_packageName; + } + $pnl = strlen($pn); + foreach ($srcinfo['declared_classes'] as $class) { + $key = "class;$class"; + if (isset($this->pkginfo['provides'][$key])) { + continue; + } + $this->pkginfo['provides'][$key] = + array('file'=> $file, 'type' => 'class', 'name' => $class); + if (isset($srcinfo['inheritance'][$class])) { + $this->pkginfo['provides'][$key]['extends'] = + $srcinfo['inheritance'][$class]; + } + } + foreach ($srcinfo['declared_methods'] as $class => $methods) { + foreach ($methods as $method) { + $function = "$class::$method"; + $key = "function;$function"; + if ($method{0} == '_' || !strcasecmp($method, $class) || + isset($this->pkginfo['provides'][$key])) { + continue; + } + $this->pkginfo['provides'][$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + } + + foreach ($srcinfo['declared_functions'] as $function) { + $key = "function;$function"; + if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) { + continue; + } + if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { + $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; + } + $this->pkginfo['provides'][$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + } + + // }}} + // {{{ analyzeSourceCode() + + /** + * Analyze the source code of the given PHP file + * + * @param string Filename of the PHP file + * @return mixed + * @access public + */ + function analyzeSourceCode($file) + { + if (!function_exists("token_get_all")) { + return false; + } + if (!defined('T_DOC_COMMENT')) { + define('T_DOC_COMMENT', T_COMMENT); + } + if (!defined('T_INTERFACE')) { + define('T_INTERFACE', -1); + } + if (!defined('T_IMPLEMENTS')) { + define('T_IMPLEMENTS', -1); + } + if (!$fp = @fopen($file, "r")) { + return false; + } + if (function_exists('file_get_contents')) { + fclose($fp); + $contents = file_get_contents($file); + } else { + $contents = fread($fp, filesize($file)); + fclose($fp); + } + $tokens = token_get_all($contents); +/* + for ($i = 0; $i < sizeof($tokens); $i++) { + @list($token, $data) = $tokens[$i]; + if (is_string($token)) { + var_dump($token); + } else { + print token_name($token) . ' '; + var_dump(rtrim($data)); + } + } +*/ + $look_for = 0; + $paren_level = 0; + $bracket_level = 0; + $brace_level = 0; + $lastphpdoc = ''; + $current_class = ''; + $current_interface = ''; + $current_class_level = -1; + $current_function = ''; + $current_function_level = -1; + $declared_classes = array(); + $declared_interfaces = array(); + $declared_functions = array(); + $declared_methods = array(); + $used_classes = array(); + $used_functions = array(); + $extends = array(); + $implements = array(); + $nodeps = array(); + $inquote = false; + $interface = false; + for ($i = 0; $i < sizeof($tokens); $i++) { + if (is_array($tokens[$i])) { + list($token, $data) = $tokens[$i]; + } else { + $token = $tokens[$i]; + $data = ''; + } + if ($inquote) { + if ($token != '"') { + continue; + } else { + $inquote = false; + continue; + } + } + switch ($token) { + case T_WHITESPACE: + continue; + case ';': + if ($interface) { + $current_function = ''; + $current_function_level = -1; + } + break; + case '"': + $inquote = true; + break; + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + case '{': $brace_level++; continue 2; + case '}': + $brace_level--; + if ($current_class_level == $brace_level) { + $current_class = ''; + $current_class_level = -1; + } + if ($current_function_level == $brace_level) { + $current_function = ''; + $current_function_level = -1; + } + continue 2; + case '[': $bracket_level++; continue 2; + case ']': $bracket_level--; continue 2; + case '(': $paren_level++; continue 2; + case ')': $paren_level--; continue 2; + case T_INTERFACE: + $interface = true; + case T_CLASS: + if (($current_class_level != -1) || ($current_function_level != -1)) { + PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", + PEAR_COMMON_ERROR_INVALIDPHP); + return false; + } + case T_FUNCTION: + case T_NEW: + case T_EXTENDS: + case T_IMPLEMENTS: + $look_for = $token; + continue 2; + case T_STRING: + if (version_compare(zend_version(), '2.0', '<')) { + if (in_array(strtolower($data), + array('public', 'private', 'protected', 'abstract', + 'interface', 'implements', 'throw') + )) { + PEAR::raiseError('Error: PHP5 token encountered in ' . $file . + 'packaging should be done in PHP 5'); + return false; + } + } + if ($look_for == T_CLASS) { + $current_class = $data; + $current_class_level = $brace_level; + $declared_classes[] = $current_class; + } elseif ($look_for == T_INTERFACE) { + $current_interface = $data; + $current_class_level = $brace_level; + $declared_interfaces[] = $current_interface; + } elseif ($look_for == T_IMPLEMENTS) { + $implements[$current_class] = $data; + } elseif ($look_for == T_EXTENDS) { + $extends[$current_class] = $data; + } elseif ($look_for == T_FUNCTION) { + if ($current_class) { + $current_function = "$current_class::$data"; + $declared_methods[$current_class][] = $data; + } elseif ($current_interface) { + $current_function = "$current_interface::$data"; + $declared_methods[$current_interface][] = $data; + } else { + $current_function = $data; + $declared_functions[] = $current_function; + } + $current_function_level = $brace_level; + $m = array(); + } elseif ($look_for == T_NEW) { + $used_classes[$data] = true; + } + $look_for = 0; + continue 2; + case T_VARIABLE: + $look_for = 0; + continue 2; + case T_DOC_COMMENT: + case T_COMMENT: + if (preg_match('!^/\*\*\s!', $data)) { + $lastphpdoc = $data; + if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) { + $nodeps = array_merge($nodeps, $m[1]); + } + } + continue 2; + case T_DOUBLE_COLON: + if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) { + PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", + PEAR_COMMON_ERROR_INVALIDPHP); + return false; + } + $class = $tokens[$i - 1][1]; + if (strtolower($class) != 'parent') { + $used_classes[$class] = true; + } + continue 2; + } + } + return array( + "source_file" => $file, + "declared_classes" => $declared_classes, + "declared_interfaces" => $declared_interfaces, + "declared_methods" => $declared_methods, + "declared_functions" => $declared_functions, + "used_classes" => array_diff(array_keys($used_classes), $nodeps), + "inheritance" => $extends, + "implements" => $implements, + ); + } + + // }}} + // {{{ betterStates() + + /** + * Return an array containing all of the states that are more stable than + * or equal to the passed in state + * + * @param string Release state + * @param boolean Determines whether to include $state in the list + * @return false|array False if $state is not a valid release state + */ + function betterStates($state, $include = false) + { + static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); + $i = array_search($state, $states); + if ($i === false) { + return false; + } + if ($include) { + $i--; + } + return array_slice($states, $i + 1); + } + + // }}} + // {{{ detectDependencies() + + function detectDependencies($any, $status_callback = null) + { + if (!function_exists("token_get_all")) { + return false; + } + if (PEAR::isError($info = $this->infoFromAny($any))) { + return $this->raiseError($info); + } + if (!is_array($info)) { + return false; + } + $deps = array(); + $used_c = $decl_c = $decl_f = $decl_m = array(); + foreach ($info['filelist'] as $file => $fa) { + $tmp = $this->analyzeSourceCode($file); + $used_c = @array_merge($used_c, $tmp['used_classes']); + $decl_c = @array_merge($decl_c, $tmp['declared_classes']); + $decl_f = @array_merge($decl_f, $tmp['declared_functions']); + $decl_m = @array_merge($decl_m, $tmp['declared_methods']); + $inheri = @array_merge($inheri, $tmp['inheritance']); + } + $used_c = array_unique($used_c); + $decl_c = array_unique($decl_c); + $undecl_c = array_diff($used_c, $decl_c); + return array('used_classes' => $used_c, + 'declared_classes' => $decl_c, + 'declared_methods' => $decl_m, + 'declared_functions' => $decl_f, + 'undeclared_classes' => $undecl_c, + 'inheritance' => $inheri, + ); + } + + // }}} + // {{{ getUserRoles() + + /** + * Get the valid roles for a PEAR package maintainer + * + * @return array + * @static + */ + function getUserRoles() + { + return $GLOBALS['_PEAR_Common_maintainer_roles']; + } + + // }}} + // {{{ getReleaseStates() + + /** + * Get the valid package release states of packages + * + * @return array + * @static + */ + function getReleaseStates() + { + return $GLOBALS['_PEAR_Common_release_states']; + } + + // }}} + // {{{ getDependencyTypes() + + /** + * Get the implemented dependency types (php, ext, pkg etc.) + * + * @return array + * @static + */ + function getDependencyTypes() + { + return $GLOBALS['_PEAR_Common_dependency_types']; + } + + // }}} + // {{{ getDependencyRelations() + + /** + * Get the implemented dependency relations (has, lt, ge etc.) + * + * @return array + * @static + */ + function getDependencyRelations() + { + return $GLOBALS['_PEAR_Common_dependency_relations']; + } + + // }}} + // {{{ getFileRoles() + + /** + * Get the implemented file roles + * + * @return array + * @static + */ + function getFileRoles() + { + return $GLOBALS['_PEAR_Common_file_roles']; + } + + // }}} + // {{{ getReplacementTypes() + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getReplacementTypes() + { + return $GLOBALS['_PEAR_Common_replacement_types']; + } + + // }}} + // {{{ getProvideTypes() + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getProvideTypes() + { + return $GLOBALS['_PEAR_Common_provide_types']; + } + + // }}} + // {{{ getScriptPhases() + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getScriptPhases() + { + return $GLOBALS['_PEAR_Common_script_phases']; + } + + // }}} + // {{{ validPackageName() + + /** + * Test whether a string contains a valid package name. + * + * @param string $name the package name to test + * + * @return bool + * + * @access public + */ + function validPackageName($name) + { + return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name); + } + + + // }}} + // {{{ validPackageVersion() + + /** + * Test whether a string contains a valid package version. + * + * @param string $ver the package version to test + * + * @return bool + * + * @access public + */ + function validPackageVersion($ver) + { + return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); + } + + + // }}} + + // {{{ downloadHttp() + + /** + * Download a file through HTTP. Considers suggested file name in + * Content-disposition: header and can run a callback function for + * different events. The callback will be called with two + * parameters: the callback type, and parameters. The implemented + * callback types are: + * + * 'setup' called at the very beginning, parameter is a UI object + * that should be used for all output + * 'message' the parameter is a string with an informational message + * 'saveas' may be used to save with a different file name, the + * parameter is the filename that is about to be used. + * If a 'saveas' callback returns a non-empty string, + * that file name will be used as the filename instead. + * Note that $save_dir will not be affected by this, only + * the basename of the file. + * 'start' download is starting, parameter is number of bytes + * that are expected, or -1 if unknown + * 'bytesread' parameter is the number of bytes read so far + * 'done' download is complete, parameter is the total number + * of bytes read + * 'connfailed' if the TCP connection fails, this callback is called + * with array(host,port,errno,errmsg) + * 'writefailed' if writing to disk fails, this callback is called + * with array(destfile,errmsg) + * + * If an HTTP proxy has been configured (http_proxy PEAR_Config + * setting), the proxy will be used. + * + * @param string $url the URL to download + * @param object $ui PEAR_Frontend_* instance + * @param object $config PEAR_Config instance + * @param string $save_dir (optional) directory to save file in + * @param mixed $callback (optional) function/method to call for status + * updates + * + * @return string Returns the full path of the downloaded file or a PEAR + * error on failure. If the error is caused by + * socket-related errors, the error object will + * have the fsockopen error code available through + * getCode(). + * + * @access public + * @deprecated in favor of PEAR_Downloader::downloadHttp() + */ + function downloadHttp($url, &$ui, $save_dir = '.', $callback = null) + { + if (!class_exists('PEAR_Downloader')) { + require_once 'PEAR/Downloader.php'; + } + return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback); + } + + // }}} + + /** + * @param string $path relative or absolute include path + * @return boolean + * @static + */ + function isIncludeable($path) + { + if (file_exists($path) && is_readable($path)) { + return true; + } + $ipath = explode(PATH_SEPARATOR, ini_get('include_path')); + foreach ($ipath as $include) { + $test = realpath($include . DIRECTORY_SEPARATOR . $path); + if (file_exists($test) && is_readable($test)) { + return true; + } + } + return false; + } +} +require_once 'PEAR/Config.php'; +require_once 'PEAR/PackageFile.php'; +if (!function_exists('file_get_contents')) { + function file_get_contents($filename) + { + $fp = fopen($filename, 'rb'); + $ret = ''; + while (!feof($fp)) { + $ret .= fread($fp, 8092);; + } + return $ret; + } +} +?> \ No newline at end of file diff --git a/include/classes/XML/XML_HTMLSax/PEAR/Config.php b/include/classes/XML/XML_HTMLSax/PEAR/Config.php new file mode 100644 index 000000000..679291b66 --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/PEAR/Config.php @@ -0,0 +1,2054 @@ + + * @author Greg Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * Required for error handling + */ +require_once 'PEAR.php'; +require_once 'PEAR/Registry.php'; +require_once 'PEAR/Installer/Role.php'; +require_once 'System.php'; +require_once 'PEAR/Remote.php'; + +/** + * Last created PEAR_Config instance. + * @var object + */ +$GLOBALS['_PEAR_Config_instance'] = null; +if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) { + $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear'; +} else { + $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR; +} + +// Below we define constants with default values for all configuration +// parameters except username/password. All of them can have their +// defaults set through environment variables. The reason we use the +// PHP_ prefix is for some security, PHP protects environment +// variables starting with PHP_*. + +// default channel and preferred mirror is based on whether we are invoked through +// the "pear" or the "pecl" command + +if (!defined('PEAR_RUNTYPE') || PEAR_RUNTYPE == 'pear') { + define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net'); +} else { + define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net'); +} + +if (getenv('PHP_PEAR_SYSCONF_DIR')) { + define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR')); +} elseif (getenv('SystemRoot')) { + define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot')); +} else { + define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR); +} + +// Default for master_server +if (getenv('PHP_PEAR_MASTER_SERVER')) { + define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER')); +} else { + define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net'); +} + +// Default for http_proxy +if (getenv('PHP_PEAR_HTTP_PROXY')) { + define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY')); +} elseif (getenv('http_proxy')) { + define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy')); +} else { + define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', ''); +} + +// Default for php_dir +if (getenv('PHP_PEAR_INSTALL_DIR')) { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR')); +} else { + if (@is_dir($PEAR_INSTALL_DIR)) { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', + $PEAR_INSTALL_DIR); + } else { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR); + } +} + +// Default for ext_dir +if (getenv('PHP_PEAR_EXTENSION_DIR')) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR')); +} else { + if (ini_get('extension_dir')) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir')); + } elseif (defined('PEAR_EXTENSION_DIR') && @is_dir(PEAR_EXTENSION_DIR)) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR); + } elseif (defined('PHP_EXTENSION_DIR')) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR); + } else { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.'); + } +} + +// Default for doc_dir +if (getenv('PHP_PEAR_DOC_DIR')) { + define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_DOC_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs'); +} + +// Default for bin_dir +if (getenv('PHP_PEAR_BIN_DIR')) { + define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR); +} + +// Default for data_dir +if (getenv('PHP_PEAR_DATA_DIR')) { + define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_DATA_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data'); +} + +// Default for test_dir +if (getenv('PHP_PEAR_TEST_DIR')) { + define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_TEST_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests'); +} + +// Default for cache_dir +if (getenv('PHP_PEAR_CACHE_DIR')) { + define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_CACHE_DIR', + System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . + DIRECTORY_SEPARATOR . 'cache'); +} + +// Default for php_bin +if (getenv('PHP_PEAR_PHP_BIN')) { + define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN')); +} else { + define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR. + DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : '')); +} + +// Default for verbose +if (getenv('PHP_PEAR_VERBOSE')) { + define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE')); +} else { + define('PEAR_CONFIG_DEFAULT_VERBOSE', 1); +} + +// Default for preferred_state +if (getenv('PHP_PEAR_PREFERRED_STATE')) { + define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE')); +} else { + define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable'); +} + +// Default for umask +if (getenv('PHP_PEAR_UMASK')) { + define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK')); +} else { + define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask())); +} + +// Default for cache_ttl +if (getenv('PHP_PEAR_CACHE_TTL')) { + define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL')); +} else { + define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600); +} + +// Default for sig_type +if (getenv('PHP_PEAR_SIG_TYPE')) { + define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE')); +} else { + define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg'); +} + +// Default for sig_bin +if (getenv('PHP_PEAR_SIG_BIN')) { + define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN')); +} else { + define('PEAR_CONFIG_DEFAULT_SIG_BIN', + System::which( + 'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg')); +} + +// Default for sig_keydir +if (getenv('PHP_PEAR_SIG_KEYDIR')) { + define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR')); +} else { + define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', + PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys'); +} + +/** + * This is a class for storing configuration data, keeping track of + * which are system-defined, user-defined or defaulted. + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.4.4 + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Config extends PEAR +{ + // {{{ properties + + /** + * Array of config files used. + * + * @var array layer => config file + */ + var $files = array( + 'system' => '', + 'user' => '', + ); + + var $layers = array(); + + /** + * Configuration data, two-dimensional array where the first + * dimension is the config layer ('user', 'system' and 'default'), + * and the second dimension is keyname => value. + * + * The order in the first dimension is important! Earlier + * layers will shadow later ones when a config value is + * requested (if a 'user' value exists, it will be returned first, + * then 'system' and finally 'default'). + * + * @var array layer => array(keyname => value, ...) + */ + var $configuration = array( + 'user' => array(), + 'system' => array(), + 'default' => array(), + ); + + /** + * Configuration values that can be set for a channel + * + * All other configuration values can only have a global value + * @var array + * @access private + */ + var $_channelConfigInfo = array( + 'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', + 'test_dir', 'php_bin', 'username', 'password', 'verbose', + 'preferred_state', 'umask', 'preferred_mirror', + ); + + /** + * Channels that can be accessed + * @see setChannels() + * @var array + * @access private + */ + var $_channels = array('pear.php.net', 'pecl.php.net', '__uri'); + + /** + * This variable is used to control the directory values returned + * @see setInstallRoot(); + * @var string|false + * @access private + */ + var $_installRoot = false; + + /** + * If requested, this will always refer to the registry + * contained in php_dir + * @var PEAR_Registry + */ + var $_registry = array(); + + /** + * @var array + * @access private + */ + var $_regInitialized = array(); + + /** + * @var bool + * @access private + */ + var $_noRegistry = false; + + /** + * amount of errors found while parsing config + * @var integer + * @access private + */ + var $_errorsFound = 0; + var $_lastError = null; + + /** + * Information about the configuration data. Stores the type, + * default value and a documentation string for each configuration + * value. + * + * @var array layer => array(infotype => value, ...) + */ + var $configuration_info = array( + // Channels/Internet Access + 'default_channel' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_CHANNEL, + 'doc' => 'the default channel to use for all non explicit commands', + 'prompt' => 'Default Channel', + 'group' => 'Internet Access', + ), + 'preferred_mirror' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_CHANNEL, + 'doc' => 'the default server or mirror to use for channel actions', + 'prompt' => 'Default Channel Mirror', + 'group' => 'Internet Access', + ), + 'remote_config' => array( + 'type' => 'password', + 'default' => '', + 'doc' => 'ftp url of remote configuration file to use for synchronized install', + 'prompt' => 'Remote Configuration File', + 'group' => 'Internet Access', + ), + 'auto_discover' => array( + 'type' => 'integer', + 'default' => 0, + 'doc' => 'whether to automatically discover new channels', + 'prompt' => 'Auto-discover new Channels', + 'group' => 'Internet Access', + ), + // Internet Access + 'master_server' => array( + 'type' => 'string', + 'default' => 'pear.php.net', + 'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]', + 'prompt' => 'PEAR server [DEPRECATED]', + 'group' => 'Internet Access', + ), + 'http_proxy' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY, + 'doc' => 'HTTP proxy (host:port) to use when downloading packages', + 'prompt' => 'HTTP Proxy Server Address', + 'group' => 'Internet Access', + ), + // File Locations + 'php_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_PHP_DIR, + 'doc' => 'directory where .php files are installed', + 'prompt' => 'PEAR directory', + 'group' => 'File Locations', + ), + 'ext_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_EXT_DIR, + 'doc' => 'directory where loadable extensions are installed', + 'prompt' => 'PHP extension directory', + 'group' => 'File Locations', + ), + 'doc_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_DOC_DIR, + 'doc' => 'directory where documentation is installed', + 'prompt' => 'PEAR documentation directory', + 'group' => 'File Locations', + ), + 'bin_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_BIN_DIR, + 'doc' => 'directory where executables are installed', + 'prompt' => 'PEAR executables directory', + 'group' => 'File Locations', + ), + 'data_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_DATA_DIR, + 'doc' => 'directory where data files are installed', + 'prompt' => 'PEAR data directory', + 'group' => 'File Locations (Advanced)', + ), + 'test_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR, + 'doc' => 'directory where regression tests are installed', + 'prompt' => 'PEAR test directory', + 'group' => 'File Locations (Advanced)', + ), + 'cache_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR, + 'doc' => 'directory which is used for XMLRPC cache', + 'prompt' => 'PEAR Installer cache directory', + 'group' => 'File Locations (Advanced)', + ), + 'php_bin' => array( + 'type' => 'file', + 'default' => PEAR_CONFIG_DEFAULT_PHP_BIN, + 'doc' => 'PHP CLI/CGI binary for executing scripts', + 'prompt' => 'PHP CLI/CGI binary', + 'group' => 'File Locations (Advanced)', + ), + // Maintainers + 'username' => array( + 'type' => 'string', + 'default' => '', + 'doc' => '(maintainers) your PEAR account name', + 'prompt' => 'PEAR username (for maintainers)', + 'group' => 'Maintainers', + ), + 'password' => array( + 'type' => 'password', + 'default' => '', + 'doc' => '(maintainers) your PEAR account password', + 'prompt' => 'PEAR password (for maintainers)', + 'group' => 'Maintainers', + ), + // Advanced + 'verbose' => array( + 'type' => 'integer', + 'default' => PEAR_CONFIG_DEFAULT_VERBOSE, + 'doc' => 'verbosity level +0: really quiet +1: somewhat quiet +2: verbose +3: debug', + 'prompt' => 'Debug Log Level', + 'group' => 'Advanced', + ), + 'preferred_state' => array( + 'type' => 'set', + 'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE, + 'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified', + 'valid_set' => array( + 'stable', 'beta', 'alpha', 'devel', 'snapshot'), + 'prompt' => 'Preferred Package State', + 'group' => 'Advanced', + ), + 'umask' => array( + 'type' => 'mask', + 'default' => PEAR_CONFIG_DEFAULT_UMASK, + 'doc' => 'umask used when creating files (Unix-like systems only)', + 'prompt' => 'Unix file mask', + 'group' => 'Advanced', + ), + 'cache_ttl' => array( + 'type' => 'integer', + 'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL, + 'doc' => 'amount of secs where the local cache is used and not updated', + 'prompt' => 'Cache TimeToLive', + 'group' => 'Advanced', + ), + 'sig_type' => array( + 'type' => 'set', + 'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE, + 'doc' => 'which package signature mechanism to use', + 'valid_set' => array('gpg'), + 'prompt' => 'Package Signature Type', + 'group' => 'Maintainers', + ), + 'sig_bin' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_SIG_BIN, + 'doc' => 'which package signature mechanism to use', + 'prompt' => 'Signature Handling Program', + 'group' => 'Maintainers', + ), + 'sig_keyid' => array( + 'type' => 'string', + 'default' => '', + 'doc' => 'which key to use for signing with', + 'prompt' => 'Signature Key Id', + 'group' => 'Maintainers', + ), + 'sig_keydir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR, + 'doc' => 'directory where signature keys are located', + 'prompt' => 'Signature Key Directory', + 'group' => 'Maintainers', + ), + // __channels is reserved - used for channel-specific configuration + ); + + // }}} + + // {{{ PEAR_Config([file], [defaults_file]) + + /** + * Constructor. + * + * @param string file to read user-defined options from + * @param string file to read system-wide defaults from + * @param bool determines whether a registry object "follows" + * the value of php_dir (is automatically created + * and moved when php_dir is changed) + * @param bool if true, fails if configuration files cannot be loaded + * + * @access public + * + * @see PEAR_Config::singleton + */ + function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false, + $strict = true) + { + $this->PEAR(); + PEAR_Installer_Role::initializeConfig($this); + $sl = DIRECTORY_SEPARATOR; + if (empty($user_file)) { + if (OS_WINDOWS) { + $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini'; + } else { + $user_file = getenv('HOME') . $sl . '.pearrc'; + } + } + if (empty($system_file)) { + if (OS_WINDOWS) { + $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'; + } else { + $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'; + } + } + + $this->layers = array_keys($this->configuration); + $this->files['user'] = $user_file; + $this->files['system'] = $system_file; + if ($user_file && @file_exists($user_file)) { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $this->readConfigFile($user_file, 'user', $strict); + $this->popErrorHandling(); + if ($this->_errorsFound > 0) { + return; + } + } + + if ($system_file && @file_exists($system_file)) { + $this->mergeConfigFile($system_file, false, 'system', $strict); + if ($this->_errorsFound > 0) { + return; + } + + } + + if (!$ftp_file) { + $ftp_file = $this->get('remote_config'); + } + + if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) { + $this->readFTPConfigFile($ftp_file); + } + + foreach ($this->configuration_info as $key => $info) { + $this->configuration['default'][$key] = $info['default']; + } + + $this->_registry['default'] = new PEAR_Registry($this->configuration['default']['php_dir']); + $this->_registry['default']->setConfig($this); + $this->_regInitialized['default'] = false; + //$GLOBALS['_PEAR_Config_instance'] = &$this; + } + + // }}} + // {{{ singleton([file], [defaults_file]) + + /** + * Static singleton method. If you want to keep only one instance + * of this class in use, this method will give you a reference to + * the last created PEAR_Config object if one exists, or create a + * new object. + * + * @param string (optional) file to read user-defined options from + * @param string (optional) file to read system-wide defaults from + * + * @return object an existing or new PEAR_Config instance + * + * @access public + * + * @see PEAR_Config::PEAR_Config + */ + function &singleton($user_file = '', $system_file = '', $strict = true) + { + if (is_object($GLOBALS['_PEAR_Config_instance'])) { + return $GLOBALS['_PEAR_Config_instance']; + } + + $t_conf = new PEAR_Config($user_file, $system_file, false, $strict); + if ($t_conf->_errorsFound > 0) { + return $t_conf->lastError; + } + + $GLOBALS['_PEAR_Config_instance'] = &$t_conf; + return $GLOBALS['_PEAR_Config_instance']; + } + + // }}} + // {{{ validConfiguration() + + /** + * Determine whether any configuration files have been detected, and whether a + * registry object can be retrieved from this configuration. + * @return bool + * @since PEAR 1.4.0a1 + */ + function validConfiguration() + { + if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) { + return true; + } + return false; + } + + // }}} + // {{{ readConfigFile([file], [layer]) + + /** + * Reads configuration data from a file. All existing values in + * the config layer are discarded and replaced with data from the + * file. + * @param string file to read from, if NULL or not specified, the + * last-used file for the same layer (second param) is used + * @param string config layer to insert data into ('user' or 'system') + * @return bool TRUE on success or a PEAR error on failure + */ + function readConfigFile($file = null, $layer = 'user', $strict = true) + { + if (empty($this->files[$layer])) { + return $this->raiseError("unknown config layer `$layer'"); + } + + if ($file === null) { + $file = $this->files[$layer]; + } + + $data = $this->_readConfigDataFrom($file); + + if (PEAR::isError($data)) { + if ($strict) { + $this->_errorsFound++; + $this->lastError = $data; + + return $data; + } else { + return true; + } + } else { + $this->files[$layer] = $file; + } + + $this->_decodeInput($data); + $this->configuration[$layer] = $data; + $this->_setupChannels(); + if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { + $this->_registry[$layer] = new PEAR_Registry($phpdir); + $this->_registry[$layer]->setConfig($this); + $this->_regInitialized[$layer] = false; + } else { + unset($this->_registry[$layer]); + } + return true; + } + + // }}} + + /** + * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini + * @return true|PEAR_Error + */ + function readFTPConfigFile($path) + { + do { // poor man's try + if (!class_exists('Net_FTP')) { + if (!class_exists('PEAR_Common')) { + require_once 'PEAR/Common.php'; + } + if (PEAR_Common::isIncludeable('Net/FTP.php')) { + include_once 'Net/FTP.php'; + } + } + if (class_exists('Net_FTP') && + (class_exists('PEAR_FTP') || PEAR_Common::isIncludeable('PEAR/FTP.php'))) { + require_once 'PEAR/FTP.php'; + $this->_ftp = new PEAR_FTP; + $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN); + $e = $this->_ftp->init($path); + if (PEAR::isError($e)) { + $this->_ftp->popErrorHandling(); + return $e; + } + $tmp = System::mktemp('-d'); + PEAR_Common::addTempFile($tmp); + $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR . + 'pear.ini', false, FTP_BINARY); + if (PEAR::isError($e)) { + $this->_ftp->popErrorHandling(); + return $e; + } + PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini'); + $this->_ftp->disconnect(); + $this->_ftp->popErrorHandling(); + $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini'; + $e = $this->readConfigFile(null, 'ftp'); + if (PEAR::isError($e)) { + return $e; + } + $fail = array(); + foreach ($this->configuration_info as $key => $val) { + if (in_array($this->getGroup($key), + array('File Locations', 'File Locations (Advanced)')) && + $this->getType($key) == 'directory') { + // any directory configs must be set for this to work + if (!isset($this->configuration['ftp'][$key])) { + $fail[] = $key; + } + } + } + if (count($fail)) { + $fail = '"' . implode('", "', $fail) . '"'; + unset($this->files['ftp']); + unset($this->configuration['ftp']); + return PEAR::raiseError('ERROR: Ftp configuration file must set all ' . + 'directory configuration variables. These variables were not set: ' . + $fail); + } else { + return true; + } + } else { + return PEAR::raiseError('Net_FTP must be installed to use remote config'); + } + } while (false); // poor man's catch + unset($this->files['ftp']); + return PEAR::raiseError('no remote host specified'); + } + + // {{{ _setupChannels() + + /** + * Reads the existing configurations and creates the _channels array from it + */ + function _setupChannels() + { + $set = array_flip(array_values($this->_channels)); + foreach ($this->configuration as $layer => $data) { + $i = 1000; + if (isset($data['__channels'])) { + foreach ($data['__channels'] as $channel => $info) { + $set[$channel] = $i++; + } + } + } + $this->_channels = array_values(array_flip($set)); + $this->setChannels($this->_channels); + } + + // }}} + // {{{ deleteChannel(channel) + + function deleteChannel($channel) + { + foreach ($this->configuration as $layer => $data) { + if (isset($data['__channels'])) { + if (isset($data['__channels'][strtolower($channel)])) { + unset($this->configuration[$layer]['__channels'][strtolower($channel)]); + } + } + } + $this->_channels = array_flip($this->_channels); + unset($this->_channels[strtolower($channel)]); + $this->_channels = array_flip($this->_channels); + } + + // }}} + // {{{ mergeConfigFile(file, [override], [layer]) + + /** + * Merges data into a config layer from a file. Does the same + * thing as readConfigFile, except it does not replace all + * existing values in the config layer. + * @param string file to read from + * @param bool whether to overwrite existing data (default TRUE) + * @param string config layer to insert data into ('user' or 'system') + * @param string if true, errors are returned if file opening fails + * @return bool TRUE on success or a PEAR error on failure + */ + function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true) + { + if (empty($this->files[$layer])) { + return $this->raiseError("unknown config layer `$layer'"); + } + if ($file === null) { + $file = $this->files[$layer]; + } + $data = $this->_readConfigDataFrom($file); + if (PEAR::isError($data)) { + if ($strict) { + $this->_errorsFound++; + $this->lastError = $data; + + return $data; + } else { + return true; + } + } + $this->_decodeInput($data); + if ($override) { + $this->configuration[$layer] = + PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data); + } else { + $this->configuration[$layer] = + PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]); + } + $this->_setupChannels(); + if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { + $this->_registry[$layer] = new PEAR_Registry($phpdir); + $this->_registry[$layer]->setConfig($this); + $this->_regInitialized[$layer] = false; + } else { + unset($this->_registry[$layer]); + } + return true; + } + + // }}} + // {{{ arrayMergeRecursive($arr2, $arr1) + /** + * @param array + * @param array + * @return array + * @static + */ + function arrayMergeRecursive($arr2, $arr1) + { + $ret = array(); + foreach ($arr2 as $key => $data) { + if (!isset($arr1[$key])) { + $ret[$key] = $data; + unset($arr1[$key]); + continue; + } + if (is_array($data)) { + if (!is_array($arr1[$key])) { + $ret[$key] = $arr1[$key]; + unset($arr1[$key]); + continue; + } + $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]); + unset($arr1[$key]); + } + } + return array_merge($ret, $arr1); + } + + // }}} + // {{{ writeConfigFile([file], [layer]) + + /** + * Writes data into a config layer from a file. + * + * @param string|null file to read from, or null for default + * @param string config layer to insert data into ('user' or + * 'system') + * @param string|null data to write to config file or null for internal data [DEPRECATED] + * @return bool TRUE on success or a PEAR error on failure + */ + function writeConfigFile($file = null, $layer = 'user', $data = null) + { + $this->_lazyChannelSetup($layer); + if ($layer == 'both' || $layer == 'all') { + foreach ($this->files as $type => $file) { + $err = $this->writeConfigFile($file, $type, $data); + if (PEAR::isError($err)) { + return $err; + } + } + return true; + } + if (empty($this->files[$layer])) { + return $this->raiseError("unknown config file type `$layer'"); + } + if ($file === null) { + $file = $this->files[$layer]; + } + $data = ($data === null) ? $this->configuration[$layer] : $data; + $this->_encodeOutput($data); + $opt = array('-p', dirname($file)); + if (!@System::mkDir($opt)) { + return $this->raiseError("could not create directory: " . dirname($file)); + } + if (@is_file($file) && !@is_writeable($file)) { + return $this->raiseError("no write access to $file!"); + } + $fp = @fopen($file, "w"); + if (!$fp) { + return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed"); + } + $contents = "#PEAR_Config 0.9\n" . serialize($data); + if (!@fwrite($fp, $contents)) { + return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed"); + } + return true; + } + + // }}} + // {{{ _readConfigDataFrom(file) + + /** + * Reads configuration data from a file and returns the parsed data + * in an array. + * + * @param string file to read from + * + * @return array configuration data or a PEAR error on failure + * + * @access private + */ + function _readConfigDataFrom($file) + { + $fp = @fopen($file, "r"); + if (!$fp) { + return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed"); + } + $size = filesize($file); + $rt = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + if (function_exists('file_get_contents')) { + fclose($fp); + $contents = file_get_contents($file); + } else { + $contents = @fread($fp, $size); + fclose($fp); + } + if (empty($contents)) { + return $this->raiseError('Configuration file "' . $file . '" is empty'); + } + + set_magic_quotes_runtime($rt); + + $version = false; + if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) { + $version = $matches[1]; + $contents = substr($contents, strlen($matches[0])); + } else { + // Museum config file + if (substr($contents,0,2) == 'a:') { + $version = '0.1'; + } + } + if ($version && version_compare("$version", '1', '<')) { + + // no '@', it is possible that unserialize + // raises a notice but it seems to block IO to + // STDOUT if a '@' is used and a notice is raise + $data = unserialize($contents); + + if (!is_array($data) && !$data) { + if ($contents == serialize(false)) { + $data = array(); + } else { + $err = $this->raiseError("PEAR_Config: bad data in $file"); + return $err; + } + } + if (!is_array($data)) { + if (strlen(trim($contents)) > 0) { + $error = "PEAR_Config: bad data in $file"; + $err = $this->raiseError($error); + return $err; + } else { + $data = array(); + } + } + // add parsing of newer formats here... + } else { + $err = $this->raiseError("$file: unknown version `$version'"); + return $err; + } + return $data; + } + + // }}} + // {{{ getConfFile(layer) + /** + * Gets the file used for storing the config for a layer + * + * @param string $layer 'user' or 'system' + */ + + function getConfFile($layer) + { + return $this->files[$layer]; + } + + // }}} + + /** + * @param array information on a role as parsed from its xml file + * @return true|PEAR_Error + * @access private + */ + function _addConfigVars($vars) + { + if (count($vars) > 3) { + return $this->raiseError('Roles can only define 3 new config variables or less'); + } + foreach ($vars as $name => $var) { + if (!is_array($var)) { + return $this->raiseError('Configuration information must be an array'); + } + if (!isset($var['type'])) { + return $this->raiseError('Configuration information must contain a type'); + } else { + if (!in_array($var['type'], + array('string', 'mask', 'password', 'directory', 'file', 'set'))) { + return $this->raiseError( + 'Configuration type must be one of directory, file, string, ' . + 'mask, set, or password'); + } + } + if (!isset($var['default'])) { + return $this->raiseError( + 'Configuration information must contain a default value ("default" index)'); + } else { + if (is_array($var['default'])) { + $real_default = ''; + foreach ($var['default'] as $config_var => $val) { + if (strpos($config_var, 'text') === 0) { + $real_default .= $val; + } elseif (strpos($config_var, 'constant') === 0) { + if (defined($val)) { + $real_default .= constant($val); + } else { + return $this->raiseError( + 'Unknown constant "' . $val . '" requested in ' . + 'default value for configuration variable "' . + $name . '"'); + } + } elseif (isset($this->configuration_info[$config_var])) { + $real_default .= + $this->configuration_info[$config_var]['default']; + } else { + return $this->raiseError( + 'Unknown request for "' . $config_var . '" value in ' . + 'default value for configuration variable "' . + $name . '"'); + } + } + $var['default'] = $real_default; + } + if ($var['type'] == 'integer') { + $var['default'] = (integer) $var['default']; + } + } + if (!isset($var['doc'])) { + return $this->raiseError( + 'Configuration information must contain a summary ("doc" index)'); + } + if (!isset($var['prompt'])) { + return $this->raiseError( + 'Configuration information must contain a simple prompt ("prompt" index)'); + } + if (!isset($var['group'])) { + return $this->raiseError( + 'Configuration information must contain a simple group ("group" index)'); + } + if (isset($this->configuration_info[$name])) { + return $this->raiseError('Configuration variable "' . $name . + '" already exists'); + } + $this->configuration_info[$name] = $var; + } + return true; + } + + // {{{ _encodeOutput(&data) + + /** + * Encodes/scrambles configuration data before writing to files. + * Currently, 'password' values will be base64-encoded as to avoid + * that people spot cleartext passwords by accident. + * + * @param array (reference) array to encode values in + * + * @return bool TRUE on success + * + * @access private + */ + function _encodeOutput(&$data) + { + foreach ($data as $key => $value) { + if ($key == '__channels') { + foreach ($data['__channels'] as $channel => $blah) { + $this->_encodeOutput($data['__channels'][$channel]); + } + } + if (!isset($this->configuration_info[$key])) { + continue; + } + $type = $this->configuration_info[$key]['type']; + switch ($type) { + // we base64-encode passwords so they are at least + // not shown in plain by accident + case 'password': { + $data[$key] = base64_encode($data[$key]); + break; + } + case 'mask': { + $data[$key] = octdec($data[$key]); + break; + } + } + } + return true; + } + + // }}} + // {{{ _decodeInput(&data) + + /** + * Decodes/unscrambles configuration data after reading from files. + * + * @param array (reference) array to encode values in + * + * @return bool TRUE on success + * + * @access private + * + * @see PEAR_Config::_encodeOutput + */ + function _decodeInput(&$data) + { + if (!is_array($data)) { + return true; + } + foreach ($data as $key => $value) { + if ($key == '__channels') { + foreach ($data['__channels'] as $channel => $blah) { + $this->_decodeInput($data['__channels'][$channel]); + } + } + if (!isset($this->configuration_info[$key])) { + continue; + } + $type = $this->configuration_info[$key]['type']; + switch ($type) { + case 'password': { + $data[$key] = base64_decode($data[$key]); + break; + } + case 'mask': { + $data[$key] = decoct($data[$key]); + break; + } + } + } + return true; + } + + // }}} + // {{{ getDefaultChannel([layer]) + /** + * Retrieve the default channel. + * + * On startup, channels are not initialized, so if the default channel is not + * pear.php.net, then initialize the config. + * @param string registry layer + * @return string|false + */ + function getDefaultChannel($layer = null) + { + $ret = false; + if ($layer === null) { + foreach ($this->layers as $layer) { + if (isset($this->configuration[$layer]['default_channel'])) { + $ret = $this->configuration[$layer]['default_channel']; + break; + } + } + } elseif (isset($this->configuration[$layer]['default_channel'])) { + $ret = $this->configuration[$layer]['default_channel']; + } + if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') { + $ret = 'pecl.php.net'; + } + if ($ret) { + if ($ret != 'pear.php.net') { + $this->_lazyChannelSetup(); + } + return $ret; + } + return PEAR_CONFIG_DEFAULT_CHANNEL; + } + + // {{{ get(key, [layer]) + /** + * Returns a configuration value, prioritizing layers as per the + * layers property. + * + * @param string config key + * + * @return mixed the config value, or NULL if not found + * + * @access public + */ + function get($key, $layer = null, $channel = false) + { + if (!isset($this->configuration_info[$key])) { + return null; + } + if ($key == '__channels') { + return null; + } + if ($key == 'default_channel') { + return $this->getDefaultChannel($layer); + } + if (!$channel) { + $channel = $this->getDefaultChannel(); + } elseif ($channel != 'pear.php.net') { + $this->_lazyChannelSetup(); + } + $channel = strtolower($channel); + + $test = (in_array($key, $this->_channelConfigInfo)) ? + $this->_getChannelValue($key, $layer, $channel) : + null; + if ($test !== null) { + if ($this->_installRoot) { + if (in_array($this->getGroup($key), + array('File Locations', 'File Locations (Advanced)')) && + $this->getType($key) == 'directory') { + return $this->_prependPath($test, $this->_installRoot); + } + } + return $test; + } + if ($layer === null) { + foreach ($this->layers as $layer) { + if (isset($this->configuration[$layer][$key])) { + $test = $this->configuration[$layer][$key]; + if ($this->_installRoot) { + if (in_array($this->getGroup($key), + array('File Locations', 'File Locations (Advanced)')) && + $this->getType($key) == 'directory') { + return $this->_prependPath($test, $this->_installRoot); + } + } + if ($key == 'preferred_mirror') { + $reg = &$this->getRegistry(); + if (is_object($reg)) { + $chan = &$reg->getChannel($channel); + if (!$chan->getMirror($test) && $chan->getName() != $test) { + return $channel; // mirror does not exist + } + } + } + return $test; + } + } + } elseif (isset($this->configuration[$layer][$key])) { + $test = $this->configuration[$layer][$key]; + if ($this->_installRoot) { + if (in_array($this->getGroup($key), + array('File Locations', 'File Locations (Advanced)')) && + $this->getType($key) == 'directory') { + return $this->_prependPath($test, $this->_installRoot); + } + } + if ($key == 'preferred_mirror') { + $reg = &$this->getRegistry(); + if (is_object($reg)) { + $chan = &$reg->getChannel($channel); + if (!$chan->getMirror($test) && $chan->getName() != $test) { + return $channel; // mirror does not exist + } + } + } + return $test; + } + return null; + } + + // }}} + // {{{ _getChannelValue(key, value, [layer]) + /** + * Returns a channel-specific configuration value, prioritizing layers as per the + * layers property. + * + * @param string config key + * + * @return mixed the config value, or NULL if not found + * + * @access private + */ + function _getChannelValue($key, $layer, $channel) + { + if ($key == '__channels' || $channel == 'pear.php.net') { + return null; + } + $ret = null; + if ($layer === null) { + foreach ($this->layers as $ilayer) { + if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) { + $ret = $this->configuration[$ilayer]['__channels'][$channel][$key]; + break; + } + } + } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) { + $ret = $this->configuration[$layer]['__channels'][$channel][$key]; + } + if ($key == 'preferred_mirror') { + if ($ret !== null) { + $reg = &$this->getRegistry($layer); + if (is_object($reg)) { + $chan = &$reg->getChannel($channel); + if (!$chan->getMirror($ret) && $chan->getName() != $ret) { + return $channel; // mirror does not exist + } + } + return $ret; + } + if ($channel == $this->getDefaultChannel($layer)) { + return $channel; // we must use the channel name as the preferred mirror + // if the user has not chosen an alternate + } else { + return $this->getDefaultChannel($layer); + } + } + return $ret; + } + + + // }}} + // {{{ set(key, value, [layer]) + + /** + * Set a config value in a specific layer (defaults to 'user'). + * Enforces the types defined in the configuration_info array. An + * integer config variable will be cast to int, and a set config + * variable will be validated against its legal values. + * + * @param string config key + * @param string config value + * @param string (optional) config layer + * @param string channel to set this value for, or null for global value + * @return bool TRUE on success, FALSE on failure + */ + function set($key, $value, $layer = 'user', $channel = false) + { + if ($key == '__channels') { + return false; + } + if (!isset($this->configuration[$layer])) { + return false; + } + if ($key == 'default_channel') { + // can only set this value globally + $channel = 'pear.php.net'; + if ($value != 'pear.php.net') { + $this->_lazyChannelSetup($layer); + } + } + if ($key == 'preferred_mirror') { + if ($channel == '__uri') { + return false; // can't set the __uri pseudo-channel's mirror + } + $reg = &$this->getRegistry($layer); + if (is_object($reg)) { + $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net'); + if (!$chan->getMirror($value) && $chan->getName() != $value) { + return false; // mirror does not exist + } + } + } + if (empty($this->configuration_info[$key])) { + return false; + } + extract($this->configuration_info[$key]); + switch ($type) { + case 'integer': + $value = (int)$value; + break; + case 'set': { + // If a valid_set is specified, require the value to + // be in the set. If there is no valid_set, accept + // any value. + if ($valid_set) { + reset($valid_set); + if ((key($valid_set) === 0 && !in_array($value, $valid_set)) || + (key($valid_set) !== 0 && empty($valid_set[$value]))) + { + return false; + } + } + break; + } + } + if (!$channel) { + $channel = $this->get('default_channel', null, 'pear.php.net'); + } + if (!in_array($channel, $this->_channels)) { + $this->_lazyChannelSetup($layer); + $reg = &$this->getRegistry($layer); + if ($reg) { + $channel = $reg->channelName($channel); + } + if (!in_array($channel, $this->_channels)) { + return false; + } + } + if ($channel != 'pear.php.net') { + if (in_array($key, $this->_channelConfigInfo)) { + $this->configuration[$layer]['__channels'][$channel][$key] = $value; + return true; + } else { + return false; + } + } else { + if ($key == 'default_channel') { + if (!isset($reg)) { + $reg = &$this->getRegistry($layer); + if (!$reg) { + $reg = &$this->getRegistry(); + } + } + if ($reg) { + $value = $reg->channelName($value); + } + if (!$value) { + return false; + } + } + } + $this->configuration[$layer][$key] = $value; + if ($key == 'php_dir' && !$this->_noRegistry) { + if (!isset($this->_registry[$layer]) || + $value != $this->_registry[$layer]->install_dir) { + $this->_registry[$layer] = new PEAR_Registry($value); + $this->_regInitialized[$layer] = false; + $this->_registry[$layer]->setConfig($this); + } + } + return true; + } + + // }}} + function _lazyChannelSetup($uselayer = false) + { + if ($this->_noRegistry) { + return; + } + $merge = false; + foreach ($this->_registry as $layer => $p) { + if ($uselayer && $uselayer != $layer) { + continue; + } + if (!$this->_regInitialized[$layer]) { + if ($layer == 'default' && isset($this->_registry['user']) || + isset($this->_registry['system'])) { + // only use the default registry if there are no alternatives + continue; + } + if (!is_object($this->_registry[$layer])) { + if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) { + $this->_registry[$layer] = new PEAR_Registry($phpdir); + $this->_registry[$layer]->setConfig($this); + $this->_regInitialized[$layer] = false; + } else { + unset($this->_registry[$layer]); + return; + } + } + $this->setChannels($this->_registry[$layer]->listChannels(), $merge); + $this->_regInitialized[$layer] = true; + $merge = true; + } + } + } + // {{{ setChannels() + + /** + * Set the list of channels. + * + * This should be set via a call to {@link PEAR_Registry::listChannels()} + * @param array + * @param bool + * @return bool success of operation + */ + function setChannels($channels, $merge = false) + { + if (!is_array($channels)) { + return false; + } + if ($merge) { + $this->_channels = array_merge($this->_channels, $channels); + } else { + $this->_channels = $channels; + } + foreach ($channels as $channel) { + $channel = strtolower($channel); + if ($channel == 'pear.php.net') { + continue; + } + foreach ($this->layers as $layer) { + if (!isset($this->configuration[$layer]['__channels'][$channel]) + || !is_array($this->configuration[$layer]['__channels'][$channel])) { + $this->configuration[$layer]['__channels'][$channel] = array(); + } + } + } + return true; + } + + // }}} + // {{{ getType(key) + + /** + * Get the type of a config value. + * + * @param string config key + * + * @return string type, one of "string", "integer", "file", + * "directory", "set" or "password". + * + * @access public + * + */ + function getType($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['type']; + } + return false; + } + + // }}} + // {{{ getDocs(key) + + /** + * Get the documentation for a config value. + * + * @param string config key + * + * @return string documentation string + * + * @access public + * + */ + function getDocs($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['doc']; + } + return false; + } + // }}} + // {{{ getPrompt(key) + + /** + * Get the short documentation for a config value. + * + * @param string config key + * + * @return string short documentation string + * + * @access public + * + */ + function getPrompt($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['prompt']; + } + return false; + } + // }}} + // {{{ getGroup(key) + + /** + * Get the parameter group for a config key. + * + * @param string config key + * + * @return string parameter group + * + * @access public + * + */ + function getGroup($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['group']; + } + return false; + } + + // }}} + // {{{ getGroups() + + /** + * Get the list of parameter groups. + * + * @return array list of parameter groups + * + * @access public + * + */ + function getGroups() + { + $tmp = array(); + foreach ($this->configuration_info as $key => $info) { + $tmp[$info['group']] = 1; + } + return array_keys($tmp); + } + + // }}} + // {{{ getGroupKeys() + + /** + * Get the list of the parameters in a group. + * + * @param string $group parameter group + * + * @return array list of parameters in $group + * + * @access public + * + */ + function getGroupKeys($group) + { + $keys = array(); + foreach ($this->configuration_info as $key => $info) { + if ($info['group'] == $group) { + $keys[] = $key; + } + } + return $keys; + } + + // }}} + // {{{ getSetValues(key) + + /** + * Get the list of allowed set values for a config value. Returns + * NULL for config values that are not sets. + * + * @param string config key + * + * @return array enumerated array of set values, or NULL if the + * config key is unknown or not a set + * + * @access public + * + */ + function getSetValues($key) + { + if (isset($this->configuration_info[$key]) && + isset($this->configuration_info[$key]['type']) && + $this->configuration_info[$key]['type'] == 'set') + { + $valid_set = $this->configuration_info[$key]['valid_set']; + reset($valid_set); + if (key($valid_set) === 0) { + return $valid_set; + } + return array_keys($valid_set); + } + return null; + } + + // }}} + // {{{ getKeys() + + /** + * Get all the current config keys. + * + * @return array simple array of config keys + * + * @access public + */ + function getKeys() + { + $keys = array(); + foreach ($this->layers as $layer) { + $test = $this->configuration[$layer]; + if (isset($test['__channels'])) { + foreach ($test['__channels'] as $channel => $configs) { + $keys = array_merge($keys, $configs); + } + } + unset($test['__channels']); + $keys = array_merge($keys, $test); + } + return array_keys($keys); + } + + // }}} + // {{{ remove(key, [layer]) + + /** + * Remove the a config key from a specific config layer. + * + * @param string config key + * + * @param string (optional) config layer + * + * @return bool TRUE on success, FALSE on failure + * + * @access public + */ + function remove($key, $layer = 'user') + { + $channel = $this->getDefaultChannel(); + if ($channel !== 'pear.php.net') { + if (isset($this->configuration[$layer]['__channels'][$channel][$key])) { + unset($this->configuration[$layer]['__channels'][$channel][$key]); + return true; + } + } + if (isset($this->configuration[$layer][$key])) { + unset($this->configuration[$layer][$key]); + return true; + } + return false; + } + + // }}} + // {{{ removeLayer(layer) + + /** + * Temporarily remove an entire config layer. USE WITH CARE! + * + * @param string config key + * + * @param string (optional) config layer + * + * @return bool TRUE on success, FALSE on failure + * + * @access public + */ + function removeLayer($layer) + { + if (isset($this->configuration[$layer])) { + $this->configuration[$layer] = array(); + return true; + } + return false; + } + + // }}} + // {{{ store([layer]) + + /** + * Stores configuration data in a layer. + * + * @param string config layer to store + * + * @return bool TRUE on success, or PEAR error on failure + * + * @access public + */ + function store($layer = 'user', $data = null) + { + return $this->writeConfigFile(null, $layer, $data); + } + + // }}} + // {{{ toDefault(key) + + /** + * Unset the user-defined value of a config key, reverting the + * value to the system-defined one. + * + * @param string config key + * + * @return bool TRUE on success, FALSE on failure + * + * @access public + */ + function toDefault($key) + { + trigger_error("PEAR_Config::toDefault() deprecated, use PEAR_Config::remove() instead", E_USER_NOTICE); + return $this->remove($key, 'user'); + } + + // }}} + // {{{ definedBy(key) + + /** + * Tells what config layer that gets to define a key. + * + * @param string config key + * @param boolean return the defining channel + * + * @return string|array the config layer, or an empty string if not found. + * + * if $returnchannel, the return is an array array('layer' => layername, + * 'channel' => channelname), or an empty string if not found + * + * @access public + */ + function definedBy($key, $returnchannel = false) + { + foreach ($this->layers as $layer) { + $channel = $this->getDefaultChannel(); + if ($channel !== 'pear.php.net') { + if (isset($this->configuration[$layer]['__channels'][$channel][$key])) { + if ($returnchannel) { + return array('layer' => $layer, 'channel' => $channel); + } + return $layer; + } + } + if (isset($this->configuration[$layer][$key])) { + if ($returnchannel) { + return array('layer' => $layer, 'channel' => 'pear.php.net'); + } + return $layer; + } + } + return ''; + } + + // }}} + // {{{ isDefaulted(key) + + /** + * Tells whether a config value has a system-defined value. + * + * @param string config key + * + * @return bool + * + * @access public + * + * @deprecated + */ + function isDefaulted($key) + { + trigger_error("PEAR_Config::isDefaulted() deprecated, use PEAR_Config::definedBy() instead", E_USER_NOTICE); + return $this->definedBy($key) == 'system'; + } + + // }}} + // {{{ isDefined(key) + + /** + * Tells whether a given key exists as a config value. + * + * @param string config key + * + * @return bool whether exists in this object + * + * @access public + */ + function isDefined($key) + { + foreach ($this->layers as $layer) { + if (isset($this->configuration[$layer][$key])) { + return true; + } + } + return false; + } + + // }}} + // {{{ isDefinedLayer(key) + + /** + * Tells whether a given config layer exists. + * + * @param string config layer + * + * @return bool whether exists in this object + * + * @access public + */ + function isDefinedLayer($layer) + { + return isset($this->configuration[$layer]); + } + + // }}} + // {{{ getLayers() + + /** + * Returns the layers defined (except the 'default' one) + * + * @return array of the defined layers + */ + function getLayers() + { + $cf = $this->configuration; + unset($cf['default']); + return array_keys($cf); + } + + // }}} + // {{{ apiVersion() + function apiVersion() + { + return '1.1'; + } + // }}} + + /** + * @return PEAR_Registry + */ + function &getRegistry($use = null) + { + if ($use === null) { + $layer = 'user'; + } else { + $layer = $use; + } + if (isset($this->_registry[$layer])) { + return $this->_registry[$layer]; + } elseif ($use === null && isset($this->_registry['system'])) { + return $this->_registry['system']; + } elseif ($use === null && isset($this->_registry['default'])) { + return $this->_registry['default']; + } elseif ($use) { + $a = false; + return $a; + } else { + // only go here if null was passed in + die("CRITICAL ERROR: Registry could not be initialized from any value"); + } + } + /** + * This is to allow customization like the use of installroot + * @param PEAR_Registry + * @return bool + */ + function setRegistry(&$reg, $layer = 'user') + { + if ($this->_noRegistry) { + return false; + } + if (!in_array($layer, array('user', 'system'))) { + return false; + } + $this->_registry[$layer] = &$reg; + if (is_object($reg)) { + $this->_registry[$layer]->setConfig($this); + } + return true; + } + + function noRegistry() + { + $this->_noRegistry = true; + } + + /** + * @return PEAR_Remote + */ + function &getRemote() + { + $remote = new PEAR_Remote($this); + return $remote; + } + + /** + * @return PEAR_REST + */ + function &getREST($version, $options = array()) + { + $version = str_replace('.', '', $version); + if (!class_exists($class = 'PEAR_REST_' . $version)) { + require_once 'PEAR/REST/' . $version . '.php'; + } + $remote = new $class($this, $options); + return $remote; + } + + /** + * The ftp server is set in {@link readFTPConfigFile()}. It exists only if a + * remote configuration file has been specified + * @return PEAR_FTP|false + */ + function &getFTP() + { + if (isset($this->_ftp)) { + return $this->_ftp; + } else { + $a = false; + return $a; + } + } + + // {{{ _prependPath($path, $prepend) + + function _prependPath($path, $prepend) + { + if (strlen($prepend) > 0) { + if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { + if (preg_match('/^[a-z]:/i', $prepend)) { + $prepend = substr($prepend, 2); + } elseif ($prepend{0} != '\\') { + $prepend = "\\$prepend"; + } + $path = substr($path, 0, 2) . $prepend . substr($path, 2); + } else { + $path = $prepend . $path; + } + } + return $path; + } + // }}} + + /** + * @param string|false installation directory to prepend to all _dir variables, or false to + * disable + */ + function setInstallRoot($root) + { + if (substr($root, -1) == DIRECTORY_SEPARATOR) { + $root = substr($root, 0, -1); + } + $old = $this->_installRoot; + $this->_installRoot = $root; + if (($old != $root) && !$this->_noRegistry) { + foreach (array_keys($this->_registry) as $layer) { + if ($layer == 'ftp' || !isset($this->_registry[$layer])) { + continue; + } + $this->_registry[$layer] = + new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net')); + $this->_registry[$layer]->setConfig($this); + $this->_regInitialized[$layer] = false; + } + } + } +} + +?> diff --git a/include/classes/XML/XML_HTMLSax/PEAR/Remote.php b/include/classes/XML/XML_HTMLSax/PEAR/Remote.php new file mode 100644 index 000000000..12d66825d --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/PEAR/Remote.php @@ -0,0 +1,519 @@ + + * @author Greg Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * needed for PEAR_Error + */ +require_once 'PEAR.php'; +require_once 'PEAR/Config.php'; + +/** + * This is a class for doing remote operations against the central + * PEAR database. + * + * @nodep XML_RPC_Value + * @nodep XML_RPC_Message + * @nodep XML_RPC_Client + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Greg Beaver + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.4.4 + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class PEAR_Remote extends PEAR +{ + // {{{ properties + + var $config = null; + var $cache = null; + /** + * @var PEAR_Registry + * @access private + */ + var $_registry; + + // }}} + + // {{{ PEAR_Remote(config_object) + + function PEAR_Remote(&$config) + { + $this->PEAR(); + $this->config = &$config; + $this->_registry = &$this->config->getRegistry(); + } + + // }}} + // {{{ setRegistry() + + function setRegistry(&$reg) + { + $this->_registry = &$reg; + } + // }}} + // {{{ getCache() + + + function getCache($args) + { + $id = md5(serialize($args)); + $cachedir = $this->config->get('cache_dir'); + $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id; + if (!file_exists($filename)) { + return null; + } + + $fp = fopen($filename, 'rb'); + if (!$fp) { + return null; + } + if (function_exists('file_get_contents')) { + fclose($fp); + $content = file_get_contents($filename); + } else { + $content = fread($fp, filesize($filename)); + fclose($fp); + } + $result = array( + 'age' => time() - filemtime($filename), + 'lastChange' => filemtime($filename), + 'content' => unserialize($content), + ); + return $result; + } + + // }}} + + // {{{ saveCache() + + function saveCache($args, $data) + { + $id = md5(serialize($args)); + $cachedir = $this->config->get('cache_dir'); + if (!file_exists($cachedir)) { + System::mkdir(array('-p', $cachedir)); + } + $filename = $cachedir.'/xmlrpc_cache_'.$id; + + $fp = @fopen($filename, "wb"); + if ($fp) { + fwrite($fp, serialize($data)); + fclose($fp); + } + } + + // }}} + + // {{{ clearCache() + + function clearCache($method, $args) + { + array_unshift($args, $method); + array_unshift($args, $this->config->get('default_channel')); // cache by channel + $id = md5(serialize($args)); + $cachedir = $this->config->get('cache_dir'); + $filename = $cachedir.'/xmlrpc_cache_'.$id; + if (file_exists($filename)) { + @unlink($filename); + } + } + + // }}} + // {{{ call(method, [args...]) + + function call($method) + { + $_args = $args = func_get_args(); + + $server_channel = $this->config->get('default_channel'); + $channel = $this->_registry->getChannel($server_channel); + if ($channel) { + $mirror = $this->config->get('preferred_mirror'); + if ($channel->getMirror($mirror)) { + if ($channel->supports('xmlrpc', $method, $mirror)) { + $server_channel = $server_host = $mirror; // use the preferred mirror + $server_port = $channel->getPort($mirror); + } elseif (!$channel->supports('xmlrpc', $method)) { + return $this->raiseError("Channel $server_channel does not " . + "support xml-rpc method $method"); + } + } + if (!isset($server_host)) { + if (!$channel->supports('xmlrpc', $method)) { + return $this->raiseError("Channel $server_channel does not support " . + "xml-rpc method $method"); + } else { + $server_host = $server_channel; + $server_port = $channel->getPort(); + } + } + } else { + return $this->raiseError("Unknown channel '$server_channel'"); + } + + array_unshift($_args, $server_channel); // cache by channel + $this->cache = $this->getCache($_args); + $cachettl = $this->config->get('cache_ttl'); + // If cache is newer than $cachettl seconds, we use the cache! + if ($this->cache !== null && $this->cache['age'] < $cachettl) { + return $this->cache['content']; + } + if (extension_loaded("xmlrpc")) { + $result = call_user_func_array(array(&$this, 'call_epi'), $args); + if (!PEAR::isError($result)) { + $this->saveCache($_args, $result); + } + return $result; + } elseif (!@include_once 'XML/RPC.php') { + return $this->raiseError("For this remote PEAR operation you need to load the xmlrpc extension or install XML_RPC"); + } + + array_shift($args); + $username = $this->config->get('username'); + $password = $this->config->get('password'); + $eargs = array(); + foreach($args as $arg) { + $eargs[] = $this->_encode($arg); + } + $f = new XML_RPC_Message($method, $eargs); + if ($this->cache !== null) { + $maxAge = '?maxAge='.$this->cache['lastChange']; + } else { + $maxAge = ''; + } + $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; + if ($proxy = parse_url($this->config->get('http_proxy'))) { + $proxy_host = @$proxy['host']; + if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { + $proxy_host = 'https://' . $proxy_host; + } + $proxy_port = @$proxy['port']; + $proxy_user = @urldecode(@$proxy['user']); + $proxy_pass = @urldecode(@$proxy['pass']); + } + $shost = $server_host; + if ($channel->getSSL()) { + $shost = "https://$shost"; + } + $c = new XML_RPC_Client('/' . $channel->getPath('xmlrpc') + . $maxAge, $shost, $server_port, $proxy_host, $proxy_port, + $proxy_user, $proxy_pass); + if ($username && $password) { + $c->setCredentials($username, $password); + } + if ($this->config->get('verbose') >= 3) { + $c->setDebug(1); + } + $r = $c->send($f); + if (!$r) { + return $this->raiseError("XML_RPC send failed"); + } + $v = $r->value(); + if ($e = $r->faultCode()) { + if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) { + return $this->cache['content']; + } + return $this->raiseError($r->faultString(), $e); + } + + $result = XML_RPC_decode($v); + $this->saveCache($_args, $result); + return $result; + } + + // }}} + + // {{{ call_epi(method, [args...]) + + function call_epi($method) + { + do { + if (extension_loaded("xmlrpc")) { + break; + } + if (OS_WINDOWS) { + $ext = 'dll'; + } elseif (PHP_OS == 'HP-UX') { + $ext = 'sl'; + } elseif (PHP_OS == 'AIX') { + $ext = 'a'; + } else { + $ext = 'so'; + } + $ext = OS_WINDOWS ? 'dll' : 'so'; + @dl("xmlrpc-epi.$ext"); + if (extension_loaded("xmlrpc")) { + break; + } + @dl("xmlrpc.$ext"); + if (extension_loaded("xmlrpc")) { + break; + } + return $this->raiseError("unable to load xmlrpc extension"); + } while (false); + $server_channel = $this->config->get('default_channel'); + $channel = $this->_registry->getChannel($server_channel); + if ($channel) { + $mirror = $this->config->get('preferred_mirror'); + if ($channel->getMirror($mirror)) { + if ($channel->supports('xmlrpc', $method, $mirror)) { + $server_channel = $server_host = $mirror; // use the preferred mirror + $server_port = $channel->getPort($mirror); + } elseif (!$channel->supports('xmlrpc', $method)) { + return $this->raiseError("Channel $server_channel does not " . + "support xml-rpc method $method"); + } + } + if (!isset($server_host)) { + if (!$channel->supports('xmlrpc', $method)) { + return $this->raiseError("Channel $server_channel does not support " . + "xml-rpc method $method"); + } else { + $server_host = $server_channel; + $server_port = $channel->getPort(); + } + } + } else { + return $this->raiseError("Unknown channel '$server_channel'"); + } + $params = func_get_args(); + array_shift($params); + $method = str_replace("_", ".", $method); + $request = xmlrpc_encode_request($method, $params); + if ($http_proxy = $this->config->get('http_proxy')) { + $proxy = parse_url($http_proxy); + $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; + $proxy_host = @$proxy['host']; + if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { + $proxy_host = 'ssl://' . $proxy_host; + } + $proxy_port = @$proxy['port']; + $proxy_user = @urldecode(@$proxy['user']); + $proxy_pass = @urldecode(@$proxy['pass']); + $fp = @fsockopen($proxy_host, $proxy_port); + $use_proxy = true; + if ($channel->getSSL()) { + $server_host = "https://$server_host"; + } + } else { + $use_proxy = false; + $ssl = $channel->getSSL(); + $fp = @fsockopen(($ssl ? 'ssl://' : '') . $server_host, $server_port); + if (!$fp) { + $server_host = "$ssl$server_host"; // for error-reporting + } + } + if (!$fp && $http_proxy) { + return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed"); + } elseif (!$fp) { + return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed"); + } + $len = strlen($request); + $req_headers = "Host: $server_host:$server_port\r\n" . + "Content-type: text/xml\r\n" . + "Content-length: $len\r\n"; + $username = $this->config->get('username'); + $password = $this->config->get('password'); + if ($username && $password) { + $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n"; + $tmp = base64_encode("$username:$password"); + $req_headers .= "Authorization: Basic $tmp\r\n"; + } + if ($this->cache !== null) { + $maxAge = '?maxAge='.$this->cache['lastChange']; + } else { + $maxAge = ''; + } + + if ($use_proxy && $proxy_host != '' && $proxy_user != '') { + $req_headers .= 'Proxy-Authorization: Basic ' + .base64_encode($proxy_user.':'.$proxy_pass) + ."\r\n"; + } + + if ($this->config->get('verbose') > 3) { + print "XMLRPC REQUEST HEADERS:\n"; + var_dump($req_headers); + print "XMLRPC REQUEST BODY:\n"; + var_dump($request); + } + + if ($use_proxy && $proxy_host != '') { + $post_string = "POST http://".$server_host; + if ($proxy_port > '') { + $post_string .= ':'.$server_port; + } + } else { + $post_string = "POST "; + } + + $path = '/' . $channel->getPath('xmlrpc'); + fwrite($fp, ($post_string . $path . "$maxAge HTTP/1.0\r\n$req_headers\r\n$request")); + $response = ''; + $line1 = fgets($fp, 2048); + if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) { + return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server"); + } + switch ($matches[1]) { + case "200": // OK + break; + case "304": // Not Modified + return $this->cache['content']; + case "401": // Unauthorized + if ($username && $password) { + return $this->raiseError("PEAR_Remote ($server_host:$server_port) " . + ": authorization failed", 401); + } else { + return $this->raiseError("PEAR_Remote ($server_host:$server_port) " . + ": authorization required, please log in first", 401); + } + default: + return $this->raiseError("PEAR_Remote ($server_host:$server_port) : " . + "unexpected HTTP response", (int)$matches[1], null, null, + "$matches[1] $matches[2]"); + } + while (trim(fgets($fp, 2048)) != ''); // skip rest of headers + while ($chunk = fread($fp, 10240)) { + $response .= $chunk; + } + fclose($fp); + if ($this->config->get('verbose') > 3) { + print "XMLRPC RESPONSE:\n"; + var_dump($response); + } + $ret = xmlrpc_decode($response); + if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) { + if ($ret['__PEAR_TYPE__'] == 'error') { + if (isset($ret['__PEAR_CLASS__'])) { + $class = $ret['__PEAR_CLASS__']; + } else { + $class = "PEAR_Error"; + } + if ($ret['code'] === '') $ret['code'] = null; + if ($ret['message'] === '') $ret['message'] = null; + if ($ret['userinfo'] === '') $ret['userinfo'] = null; + if (strtolower($class) == 'db_error') { + $ret = $this->raiseError(PEAR::errorMessage($ret['code']), + $ret['code'], null, null, + $ret['userinfo']); + } else { + $ret = $this->raiseError($ret['message'], $ret['code'], + null, null, $ret['userinfo']); + } + } + } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0]) + && is_array($ret[0]) && + !empty($ret[0]['faultString']) && + !empty($ret[0]['faultCode'])) { + extract($ret[0]); + $faultString = "XML-RPC Server Fault: " . + str_replace("\n", " ", $faultString); + return $this->raiseError($faultString, $faultCode); + } elseif (is_array($ret) && sizeof($ret) == 2 && !empty($ret['faultString']) && + !empty($ret['faultCode'])) { + extract($ret); + $faultString = "XML-RPC Server Fault: " . + str_replace("\n", " ", $faultString); + return $this->raiseError($faultString, $faultCode); + } + return $ret; + } + + // }}} + + // {{{ _encode + + // a slightly extended version of XML_RPC_encode + function _encode($php_val) + { + global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double; + global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct; + + $type = gettype($php_val); + $xmlrpcval = new XML_RPC_Value; + + switch($type) { + case "array": + reset($php_val); + $firstkey = key($php_val); + end($php_val); + $lastkey = key($php_val); + reset($php_val); + if ($firstkey === 0 && is_int($lastkey) && + ($lastkey + 1) == count($php_val)) { + $is_continuous = true; + reset($php_val); + $size = count($php_val); + for ($expect = 0; $expect < $size; $expect++, next($php_val)) { + if (key($php_val) !== $expect) { + $is_continuous = false; + break; + } + } + if ($is_continuous) { + reset($php_val); + $arr = array(); + while (list($k, $v) = each($php_val)) { + $arr[$k] = $this->_encode($v); + } + $xmlrpcval->addArray($arr); + break; + } + } + // fall though if not numerical and continuous + case "object": + $arr = array(); + while (list($k, $v) = each($php_val)) { + $arr[$k] = $this->_encode($v); + } + $xmlrpcval->addStruct($arr); + break; + case "integer": + $xmlrpcval->addScalar($php_val, $XML_RPC_Int); + break; + case "double": + $xmlrpcval->addScalar($php_val, $XML_RPC_Double); + break; + case "string": + case "NULL": + $xmlrpcval->addScalar($php_val, $XML_RPC_String); + break; + case "boolean": + $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean); + break; + case "unknown type": + default: + return null; + } + return $xmlrpcval; + } + + // }}} + +} + +?> diff --git a/include/classes/XML/XML_HTMLSax/System.php b/include/classes/XML/XML_HTMLSax/System.php new file mode 100644 index 000000000..de4fe3838 --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/System.php @@ -0,0 +1,580 @@ + + * @copyright 1997-2008 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR.php'; +require_once 'Console/Getopt.php'; + +$GLOBALS['_System_temp_files'] = array(); + +/** +* System offers cross plattform compatible system functions +* +* Static functions for different operations. Should work under +* Unix and Windows. The names and usage has been taken from its respectively +* GNU commands. The functions will return (bool) false on error and will +* trigger the error with the PHP trigger_error() function (you can silence +* the error by prefixing a '@' sign after the function call). +* +* Documentation on this class you can find in: +* http://pear.php.net/manual/ +* +* Example usage: +* if (!@System::rm('-r file1 dir1')) { +* print "could not delete file1 or dir1"; +* } +* +* In case you need to to pass file names with spaces, +* pass the params as an array: +* +* System::rm(array('-r', $file1, $dir1)); +* +* @category pear +* @package System +* @author Tomas V.V. Cox +* @copyright 1997-2008 The PHP Group +* @license http://www.php.net/license/3_0.txt PHP License 3.0 +* @version Release: 1.4.4 +* @link http://pear.php.net/package/PEAR +* @since Class available since Release 0.1 +*/ +class System +{ + /** + * returns the commandline arguments of a function + * + * @param string $argv the commandline + * @param string $short_options the allowed option short-tags + * @param string $long_options the allowed option long-tags + * @return array the given options and there values + * @access private + */ + function _parseArgs($argv, $short_options, $long_options = null) + { + if (!is_array($argv) && $argv !== null) { + $argv = preg_split('/\s+/', $argv, -1, PREG_SPLIT_NO_EMPTY); + } + return Console_Getopt::getopt2($argv, $short_options); + } + + /** + * Output errors with PHP trigger_error(). You can silence the errors + * with prefixing a "@" sign to the function call: @System::mkdir(..); + * + * @param mixed $error a PEAR error or a string with the error message + * @return bool false + * @access private + */ + function raiseError($error) + { + if (PEAR::isError($error)) { + $error = $error->getMessage(); + } + trigger_error($error, E_USER_WARNING); + return false; + } + + /** + * Creates a nested array representing the structure of a directory + * + * System::_dirToStruct('dir1', 0) => + * Array + * ( + * [dirs] => Array + * ( + * [0] => dir1 + * ) + * + * [files] => Array + * ( + * [0] => dir1/file2 + * [1] => dir1/file3 + * ) + * ) + * @param string $sPath Name of the directory + * @param integer $maxinst max. deep of the lookup + * @param integer $aktinst starting deep of the lookup + * @return array the structure of the dir + * @access private + */ + + function _dirToStruct($sPath, $maxinst, $aktinst = 0) + { + $struct = array('dirs' => array(), 'files' => array()); + if (($dir = @opendir($sPath)) === false) { + System::raiseError("Could not open dir $sPath"); + return $struct; // XXX could not open error + } + $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ? + $list = array(); + while (false !== ($file = readdir($dir))) { + if ($file != '.' && $file != '..') { + $list[] = $file; + } + } + closedir($dir); + sort($list); + if ($aktinst < $maxinst || $maxinst == 0) { + foreach($list as $val) { + $path = $sPath . DIRECTORY_SEPARATOR . $val; + if (is_dir($path) && !is_link($path)) { + $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1); + $struct = array_merge_recursive($tmp, $struct); + } else { + $struct['files'][] = $path; + } + } + } + return $struct; + } + + /** + * Creates a nested array representing the structure of a directory and files + * + * @param array $files Array listing files and dirs + * @return array + * @see System::_dirToStruct() + */ + function _multipleToStruct($files) + { + $struct = array('dirs' => array(), 'files' => array()); + settype($files, 'array'); + foreach ($files as $file) { + if (is_dir($file) && !is_link($file)) { + $tmp = System::_dirToStruct($file, 0); + $struct = array_merge_recursive($tmp, $struct); + } else { + $struct['files'][] = $file; + } + } + return $struct; + } + + /** + * The rm command for removing files. + * Supports multiple files and dirs and also recursive deletes + * + * @param string $args the arguments for rm + * @return mixed PEAR_Error or true for success + * @access public + */ + function rm($args) + { + $opts = System::_parseArgs($args, 'rf'); // "f" do nothing but like it :-) + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + foreach($opts[0] as $opt) { + if ($opt[0] == 'r') { + $do_recursive = true; + } + } + $ret = true; + if (isset($do_recursive)) { + $struct = System::_multipleToStruct($opts[1]); + foreach($struct['files'] as $file) { + if (!@unlink($file)) { + $ret = false; + } + } + foreach($struct['dirs'] as $dir) { + if (!@rmdir($dir)) { + $ret = false; + } + } + } else { + foreach ($opts[1] as $file) { + $delete = (is_dir($file)) ? 'rmdir' : 'unlink'; + if (!@$delete($file)) { + $ret = false; + } + } + } + return $ret; + } + + /** + * Make directories. + * + * The -p option will create parent directories + * @param string $args the name of the director(y|ies) to create + * @return bool True for success + * @access public + */ + function mkDir($args) + { + $opts = System::_parseArgs($args, 'pm:'); + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + $mode = 0777; // default mode + foreach($opts[0] as $opt) { + if ($opt[0] == 'p') { + $create_parents = true; + } elseif($opt[0] == 'm') { + // if the mode is clearly an octal number (starts with 0) + // convert it to decimal + if (strlen($opt[1]) && $opt[1]{0} == '0') { + $opt[1] = octdec($opt[1]); + } else { + // convert to int + $opt[1] += 0; + } + $mode = $opt[1]; + } + } + $ret = true; + if (isset($create_parents)) { + foreach($opts[1] as $dir) { + $dirstack = array(); + while (!@is_dir($dir) && $dir != DIRECTORY_SEPARATOR) { + array_unshift($dirstack, $dir); + $dir = dirname($dir); + } + while ($newdir = array_shift($dirstack)) { + if (!is_writeable(dirname($newdir))) { + $ret = false; + break; + } + if (!mkdir($newdir, $mode)) { + $ret = false; + } + } + } + } else { + foreach($opts[1] as $dir) { + if (!@is_dir($dir) && !mkdir($dir, $mode)) { + $ret = false; + } + } + } + return $ret; + } + + /** + * Concatenate files + * + * Usage: + * 1) $var = System::cat('sample.txt test.txt'); + * 2) System::cat('sample.txt test.txt > final.txt'); + * 3) System::cat('sample.txt test.txt >> final.txt'); + * + * Note: as the class use fopen, urls should work also (test that) + * + * @param string $args the arguments + * @return boolean true on success + * @access public + */ + function &cat($args) + { + $ret = null; + $files = array(); + if (!is_array($args)) { + $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); + } + for($i=0; $i < count($args); $i++) { + if ($args[$i] == '>') { + $mode = 'wb'; + $outputfile = $args[$i+1]; + break; + } elseif ($args[$i] == '>>') { + $mode = 'ab+'; + $outputfile = $args[$i+1]; + break; + } else { + $files[] = $args[$i]; + } + } + if (isset($mode)) { + if (!$outputfd = fopen($outputfile, $mode)) { + $err = System::raiseError("Could not open $outputfile"); + return $err; + } + $ret = true; + } + foreach ($files as $file) { + if (!$fd = fopen($file, 'r')) { + System::raiseError("Could not open $file"); + continue; + } + while ($cont = fread($fd, 2048)) { + if (isset($outputfd)) { + fwrite($outputfd, $cont); + } else { + $ret .= $cont; + } + } + fclose($fd); + } + if (@is_resource($outputfd)) { + fclose($outputfd); + } + return $ret; + } + + /** + * Creates temporary files or directories. This function will remove + * the created files when the scripts finish its execution. + * + * Usage: + * 1) $tempfile = System::mktemp("prefix"); + * 2) $tempdir = System::mktemp("-d prefix"); + * 3) $tempfile = System::mktemp(); + * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); + * + * prefix -> The string that will be prepended to the temp name + * (defaults to "tmp"). + * -d -> A temporary dir will be created instead of a file. + * -t -> The target dir where the temporary (file|dir) will be created. If + * this param is missing by default the env vars TMP on Windows or + * TMPDIR in Unix will be used. If these vars are also missing + * c:\windows\temp or /tmp will be used. + * + * @param string $args The arguments + * @return mixed the full path of the created (file|dir) or false + * @see System::tmpdir() + * @access public + */ + function mktemp($args = null) + { + static $first_time = true; + $opts = System::_parseArgs($args, 't:d'); + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + foreach($opts[0] as $opt) { + if($opt[0] == 'd') { + $tmp_is_dir = true; + } elseif($opt[0] == 't') { + $tmpdir = $opt[1]; + } + } + $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp'; + if (!isset($tmpdir)) { + $tmpdir = System::tmpdir(); + } + if (!System::mkDir(array('-p', $tmpdir))) { + return false; + } + $tmp = tempnam($tmpdir, $prefix); + if (isset($tmp_is_dir)) { + unlink($tmp); // be careful possible race condition here + if (!mkdir($tmp, 0700)) { + return System::raiseError("Unable to create temporary directory $tmpdir"); + } + } + $GLOBALS['_System_temp_files'][] = $tmp; + if ($first_time) { + PEAR::registerShutdownFunc(array('System', '_removeTmpFiles')); + $first_time = false; + } + return $tmp; + } + + /** + * Remove temporary files created my mkTemp. This function is executed + * at script shutdown time + * + * @access private + */ + function _removeTmpFiles() + { + if (count($GLOBALS['_System_temp_files'])) { + $delete = $GLOBALS['_System_temp_files']; + array_unshift($delete, '-r'); + System::rm($delete); + $GLOBALS['_System_temp_files'] = array(); + } + } + + /** + * Get the path of the temporal directory set in the system + * by looking in its environments variables. + * Note: php.ini-recommended removes the "E" from the variables_order setting, + * making unavaible the $_ENV array, that s why we do tests with _ENV + * + * @return string The temporal directory on the system + */ + function tmpdir() + { + if (OS_WINDOWS) { + if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) { + return $var; + } + if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) { + return $var; + } + if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) { + return $var; + } + return getenv('SystemRoot') . '\temp'; + } + if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) { + return $var; + } + return '/tmp'; + } + + /** + * The "which" command (show the full path of a command) + * + * @param string $program The command to search for + * @param mixed $fallback Value to return if $program is not found + * + * @return mixed A string with the full path or false if not found + * @author Stig Bakken + */ + function which($program, $fallback = false) + { + // avaible since 4.3.0RC2 + if (defined('PATH_SEPARATOR')) { + $path_delim = PATH_SEPARATOR; + } else { + $path_delim = OS_WINDOWS ? ';' : ':'; + } + // full path given + if (basename($program) != $program) { + $path_elements[] = dirname($program); + $program = basename($program); + } else { + // Honor safe mode + if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) { + $path = getenv('PATH'); + if (!$path) { + $path = getenv('Path'); // some OSes are just stupid enough to do this + } + } + $path_elements = explode($path_delim, $path); + } + + if (OS_WINDOWS) { + $exe_suffixes = getenv('PATHEXT') + ? explode($path_delim, getenv('PATHEXT')) + : array('.exe','.bat','.cmd','.com'); + // allow passing a command.exe param + if (strpos($program, '.') !== false) { + array_unshift($exe_suffixes, ''); + } + // is_executable() is not available on windows for PHP4 + $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file'; + } else { + $exe_suffixes = array(''); + $pear_is_executable = 'is_executable'; + } + + foreach ($exe_suffixes as $suff) { + foreach ($path_elements as $dir) { + $file = $dir . DIRECTORY_SEPARATOR . $program . $suff; + if ($pear_is_executable($file)) { + return $file; + } + } + } + return $fallback; + } + + /** + * The "find" command + * + * Usage: + * + * System::find($dir); + * System::find("$dir -type d"); + * System::find("$dir -type f"); + * System::find("$dir -name *.php"); + * System::find("$dir -name *.php -name *.htm*"); + * System::find("$dir -maxdepth 1"); + * + * Params implmented: + * $dir -> Start the search at this directory + * -type d -> return only directories + * -type f -> return only files + * -maxdepth -> max depth of recursion + * -name -> search pattern (bash style). Multiple -name param allowed + * + * @param mixed Either array or string with the command line + * @return array Array of found files + * + */ + function find($args) + { + if (!is_array($args)) { + $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); + } + $dir = array_shift($args); + $patterns = array(); + $depth = 0; + $do_files = $do_dirs = true; + for ($i = 0; $i < count($args); $i++) { + switch ($args[$i]) { + case '-type': + if (in_array($args[$i+1], array('d', 'f'))) { + if ($args[$i+1] == 'd') { + $do_files = false; + } else { + $do_dirs = false; + } + } + $i++; + break; + case '-name': + if (OS_WINDOWS) { + if ($args[$i+1]{0} == '\\') { + // prepend drive + $args[$i+1] = addslashes(substr(getcwd(), 0, 2) . $args[$i + 1]); + } + } + $patterns[] = "(" . preg_replace(array('/\./', '/\*/'), + array('\.', '.*', ), + $args[$i+1]) + . ")"; + $i++; + break; + case '-maxdepth': + $depth = $args[$i+1]; + break; + } + } + $path = System::_dirToStruct($dir, $depth); + if ($do_files && $do_dirs) { + $files = array_merge($path['files'], $path['dirs']); + } elseif ($do_dirs) { + $files = $path['dirs']; + } else { + $files = $path['files']; + } + if (count($patterns)) { + $patterns = implode('|', $patterns); + $ret = array(); + for ($i = 0; $i < count($files); $i++) { + if (preg_match("#^$patterns\$#", $files[$i])) { + $ret[] = $files[$i]; + } + } + return $ret; + } + return $files; + } +} +?> diff --git a/include/classes/XML/XML_HTMLSax/XML_HTMLSax.php b/include/classes/XML/XML_HTMLSax/XML_HTMLSax.php new file mode 100644 index 000000000..64ab450cb --- /dev/null +++ b/include/classes/XML/XML_HTMLSax/XML_HTMLSax.php @@ -0,0 +1,671 @@ + Original port from Python | +// | Authors: Harry Fuecks Port to PEAR + more | +// | Authors: Many @ Sitepointforums Advanced PHP Forums | +// +----------------------------------------------------------------------+ +// +// $Id$ +// +/** +* Main parser components +* @package XML_HTMLSax +* @version $Id$ +*/ +/** +* Required classes +*/ +require_once(AT_INCLUDE_PATH.'classes/XML/XML_HTMLSax/PEAR.php'); +if (!defined('XML_HTMLSAX')) { + define('XML_HTMLSAX', AT_INCLUDE_PATH . 'classes/XML/XML_HTMLSax/'); /* 'XML/' had to be removed to work. joel/ATRC */ +} +require_once(XML_HTMLSAX . 'HTMLSax/XML_HTMLSax_States.php'); +require_once(XML_HTMLSAX . 'HTMLSax/XML_HTMLSax_Decorators.php'); +/** +* Base State Parser +* @package XML_HTMLSax +* @access protected +* @abstract +*/ +class XML_HTMLSax_StateParser { + /** + * Instance of user front end class to be passed to callbacks + * @var XML_HTMLSax + * @access private + */ + var $htmlsax; + /** + * User defined object for handling elements + * @var object + * @access private + */ + var $handler_object_element; + /** + * User defined open tag handler method + * @var string + * @access private + */ + var $handler_method_opening; + /** + * User defined close tag handler method + * @var string + * @access private + */ + var $handler_method_closing; + /** + * User defined object for handling data in elements + * @var object + * @access private + */ + var $handler_object_data; + /** + * User defined data handler method + * @var string + * @access private + */ + var $handler_method_data; + /** + * User defined object for handling processing instructions + * @var object + * @access private + */ + var $handler_object_pi; + /** + * User defined processing instruction handler method + * @var string + * @access private + */ + var $handler_method_pi; + /** + * User defined object for handling JSP/ASP tags + * @var object + * @access private + */ + var $handler_object_jasp; + /** + * User defined JSP/ASP handler method + * @var string + * @access private + */ + var $handler_method_jasp; + /** + * User defined object for handling XML escapes + * @var object + * @access private + */ + var $handler_object_escape; + /** + * User defined XML escape handler method + * @var string + * @access private + */ + var $handler_method_escape; + /** + * User defined handler object or NullHandler + * @var object + * @access private + */ + var $handler_default; + /** + * Parser options determining parsing behavior + * @var array + * @access private + */ + var $parser_options = array(); + /** + * XML document being parsed + * @var string + * @access private + */ + var $rawtext; + /** + * Position in XML document relative to start (0) + * @var int + * @access private + */ + var $position; + /** + * Length of the XML document in characters + * @var int + * @access private + */ + var $length; + /** + * Array of state objects + * @var array + * @access private + */ + var $State = array(); + + /** + * Constructs XML_HTMLSax_StateParser setting up states + * @var XML_HTMLSax instance of user front end class + * @access protected + */ + function XML_HTMLSax_StateParser (& $htmlsax) { + $this->htmlsax = & $htmlsax; + $this->State[XML_HTMLSAX_STATE_START] = new XML_HTMLSax_StartingState(); + + $this->State[XML_HTMLSAX_STATE_CLOSING_TAG] = new XML_HTMLSax_ClosingTagState(); + $this->State[XML_HTMLSAX_STATE_TAG] = new XML_HTMLSax_TagState(); + $this->State[XML_HTMLSAX_STATE_OPENING_TAG] = new XML_HTMLSax_OpeningTagState(); + + $this->State[XML_HTMLSAX_STATE_PI] = new XML_HTMLSax_PiState(); + $this->State[XML_HTMLSAX_STATE_JASP] = new XML_HTMLSax_JaspState(); + $this->State[XML_HTMLSAX_STATE_ESCAPE] = new XML_HTMLSax_EscapeState(); + } + + /** + * Moves the position back one character + * @access protected + * @return void + */ + function unscanCharacter() { + $this->position -= 1; + } + + /** + * Moves the position forward one character + * @access protected + * @return void + */ + function ignoreCharacter() { + $this->position += 1; + } + + /** + * Returns the next character from the XML document or void if at end + * @access protected + * @return mixed + */ + function scanCharacter() { + if ($this->position < $this->length) { + return $this->rawtext{$this->position++}; + } + } + + /** + * Returns a string from the current position to the next occurance + * of the supplied string + * @param string string to search until + * @access protected + * @return string + */ + function scanUntilString($string) { + $start = $this->position; + $this->position = @strpos($this->rawtext, $string, $start); + if ($this->position === FALSE) { + $this->position = $this->length; + } + return substr($this->rawtext, $start, $this->position - $start); + } + + /** + * Returns a string from the current position until the first instance of + * one of the characters in the supplied string argument + * @param string string to search until + * @access protected + * @return string + * @abstract + */ + function scanUntilCharacters($string) {} + + /** + * Moves the position forward past any whitespace characters + * @access protected + * @return void + * @abstract + */ + function ignoreWhitespace() {} + + /** + * Begins the parsing operation, setting up any decorators, depending on + * parse options invoking _parse() to execute parsing + * @param string XML document to parse + * @access protected + * @return void + */ + function parse($data) { + if ($this->parser_options['XML_OPTION_TRIM_DATA_NODES']==1) { + $decorator = new XML_HTMLSax_Trim( + $this->handler_object_data, + $this->handler_method_data); + $this->handler_object_data =& $decorator; + $this->handler_method_data = 'trimData'; + } + if ($this->parser_options['XML_OPTION_CASE_FOLDING']==1) { + $open_decor = new XML_HTMLSax_CaseFolding( + $this->handler_object_element, + $this->handler_method_opening, + $this->handler_method_closing); + $this->handler_object_element =& $open_decor; + $this->handler_method_opening ='foldOpen'; + $this->handler_method_closing ='foldClose'; + } + if ($this->parser_options['XML_OPTION_LINEFEED_BREAK']==1) { + $decorator = new XML_HTMLSax_Linefeed( + $this->handler_object_data, + $this->handler_method_data); + $this->handler_object_data =& $decorator; + $this->handler_method_data = 'breakData'; + } + if ($this->parser_options['XML_OPTION_TAB_BREAK']==1) { + $decorator = new XML_HTMLSax_Tab( + $this->handler_object_data, + $this->handler_method_data); + $this->handler_object_data =& $decorator; + $this->handler_method_data = 'breakData'; + } + if ($this->parser_options['XML_OPTION_ENTITIES_UNPARSED']==1) { + $decorator = new XML_HTMLSax_Entities_Unparsed( + $this->handler_object_data, + $this->handler_method_data); + $this->handler_object_data =& $decorator; + $this->handler_method_data = 'breakData'; + } + if ($this->parser_options['XML_OPTION_ENTITIES_PARSED']==1) { + $decorator = new XML_HTMLSax_Entities_Parsed( + $this->handler_object_data, + $this->handler_method_data); + $this->handler_object_data =& $decorator; + $this->handler_method_data = 'breakData'; + } + $this->rawtext = $data; + $this->length = strlen($data); + $this->position = 0; + $this->_parse(); + } + + /** + * Performs the parsing itself, delegating calls to a specific parser + * state + * @param constant state object to parse with + * @access protected + * @return void + */ + function _parse($state = XML_HTMLSAX_STATE_START) { + do { + $state = $this->State[$state]->parse($this); + } while ($state != XML_HTMLSAX_STATE_STOP && + $this->position < $this->length); + } +} + +/** +* Parser for PHP Versions below 4.3.0. Uses a slower parsing mechanism than +* the equivalent PHP 4.3.0+ subclass of StateParser +* @package XML_HTMLSax +* @access protected +* @see XML_HTMLSax_StateParser_Gtet430 +*/ +class XML_HTMLSax_StateParser_Lt430 extends XML_HTMLSax_StateParser { + /** + * Constructs XML_HTMLSax_StateParser_Lt430 defining available + * parser options + * @var XML_HTMLSax instance of user front end class + * @access protected + */ + function XML_HTMLSax_StateParser_Lt430(& $htmlsax) { + parent::XML_HTMLSax_StateParser($htmlsax); + $this->parser_options['XML_OPTION_TRIM_DATA_NODES'] = 0; + $this->parser_options['XML_OPTION_CASE_FOLDING'] = 0; + $this->parser_options['XML_OPTION_LINEFEED_BREAK'] = 0; + $this->parser_options['XML_OPTION_TAB_BREAK'] = 0; + $this->parser_options['XML_OPTION_ENTITIES_PARSED'] = 0; + $this->parser_options['XML_OPTION_ENTITIES_UNPARSED'] = 0; + $this->parser_options['XML_OPTION_FULL_ESCAPES'] = 0; + } + + /** + * Returns a string from the current position until the first instance of + * one of the characters in the supplied string argument + * @param string string to search until + * @access protected + * @return string + */ + function scanUntilCharacters($string) { + $startpos = $this->position; + while ($this->position < $this->length && strpos($string, $this->rawtext{$this->position}) === FALSE) { + $this->position++; + } + return substr($this->rawtext, $startpos, $this->position - $startpos); + } + + /** + * Moves the position forward past any whitespace characters + * @access protected + * @return void + */ + function ignoreWhitespace() { + while ($this->position < $this->length && + strpos(" \n\r\t", $this->rawtext{$this->position}) !== FALSE) { + $this->position++; + } + } + + /** + * Begins the parsing operation, setting up the unparsed XML entities + * decorator if necessary then delegating further work to parent + * @param string XML document to parse + * @access protected + * @return void + */ + function parse($data) { + parent::parse($data); + } +} + +/** +* Parser for PHP Versions equal to or greater than 4.3.0. Uses a faster +* parsing mechanism than the equivalent PHP < 4.3.0 subclass of StateParser +* @package XML_HTMLSax +* @access protected +* @see XML_HTMLSax_StateParser_Lt430 +*/ +class XML_HTMLSax_StateParser_Gtet430 extends XML_HTMLSax_StateParser { + /** + * Constructs XML_HTMLSax_StateParser_Gtet430 defining available + * parser options + * @var XML_HTMLSax instance of user front end class + * @access protected + */ + function XML_HTMLSax_StateParser_Gtet430(& $htmlsax) { + parent::XML_HTMLSax_StateParser($htmlsax); + $this->parser_options['XML_OPTION_TRIM_DATA_NODES'] = 0; + $this->parser_options['XML_OPTION_CASE_FOLDING'] = 0; + $this->parser_options['XML_OPTION_LINEFEED_BREAK'] = 0; + $this->parser_options['XML_OPTION_TAB_BREAK'] = 0; + $this->parser_options['XML_OPTION_ENTITIES_PARSED'] = 0; + $this->parser_options['XML_OPTION_ENTITIES_UNPARSED'] = 0; + $this->parser_options['XML_OPTION_FULL_ESCAPES'] = 0; + } + /** + * Returns a string from the current position until the first instance of + * one of the characters in the supplied string argument. + * @param string string to search until + * @access protected + * @return string + */ + function scanUntilCharacters($string) { + $startpos = $this->position; + $length = strcspn($this->rawtext, $string, $startpos); + $this->position += $length; + return substr($this->rawtext, $startpos, $length); + } + + /** + * Moves the position forward past any whitespace characters + * @access protected + * @return void + */ + function ignoreWhitespace() { + $this->position += strspn($this->rawtext, " \n\r\t", $this->position); + } + + /** + * Begins the parsing operation, setting up the parsed and unparsed + * XML entity decorators if necessary then delegating further work + * to parent + * @param string XML document to parse + * @access protected + * @return void + */ + function parse($data) { + parent::parse($data); + } +} + +/** +* Default NullHandler for methods which were not set by user +* @package XML_HTMLSax +* @access protected +*/ +class XML_HTMLSax_NullHandler { + /** + * Generic handler method which does nothing + * @access protected + * @return void + */ + function DoNothing() { + } +} + +/** +* User interface class. All user calls should only be made to this class +* @package XML_HTMLSax +* @access public +*/ +class XML_HTMLSax extends Pear { + /** + * Instance of concrete subclass of XML_HTMLSax_StateParser + * @var XML_HTMLSax_StateParser + * @access private + */ + var $state_parser; + + /** + * Constructs XML_HTMLSax selecting concrete StateParser subclass + * depending on PHP version being used as well as setting the default + * NullHandler for all callbacks
    + * Example: + *
    +    * $myHandler = new MyHandler();
    +    * $parser = new XML_HTMLSax();
    +    * $parser->set_object($myHandler);
    +    * $parser->set_option('XML_OPTION_CASE_FOLDING');
    +    * $parser->set_element_handler('myOpenHandler','myCloseHandler');
    +    * $parser->set_data_handler('myDataHandler');
    +    * $parser->parser($xml);
    +    * 
    + * @access public + */ + function XML_HTMLSax() { + if (version_compare(phpversion(), '4.3', 'ge')) { + $this->state_parser = new XML_HTMLSax_StateParser_Gtet430($this); + } else { + $this->state_parser = new XML_HTMLSax_StateParser_Lt430($this); + } + $nullhandler = new XML_HTMLSax_NullHandler(); + $this->set_object($nullhandler); + $this->set_element_handler('DoNothing', 'DoNothing'); + $this->set_data_handler('DoNothing'); + $this->set_pi_handler('DoNothing'); + $this->set_jasp_handler('DoNothing'); + $this->set_escape_handler('DoNothing'); + } + + /** + * Sets the user defined handler object. Returns a PEAR Error + * if supplied argument is not an object. + * @param object handler object containing SAX callback methods + * @access public + * @return mixed + */ + function set_object(&$object) { + if ( is_object($object) ) { + $this->state_parser->handler_default =& $object; + return true; + } else { + return $this->raiseError('XML_HTMLSax::set_object requires '. + 'an object instance'); + } + } + + /** + * Sets a parser option. Returns a PEAR Error if option is invalid
    + * Available options: + *
      + *
    • XML_OPTION_TRIM_DATA_NODES: trim whitespace off the beginning + * and end of data passed to the data handler
    • + *
    • XML_OPTION_LINEFEED_BREAK: linefeeds result in additional data + * handler calls
    • + *
    • XML_OPTION_TAB_BREAK: tabs result in additional data handler + * calls
    • + *
    • XML_OPTION_ENTIES_UNPARSED: XML entities are returned as + * seperate data handler calls in unparsed form
    • + *
    • XML_OPTION_ENTIES_PARSED: (PHP 4.3.0+ only) XML entities are + * returned as seperate data handler calls and are parsed with + * PHP's html_entity_decode() function
    • + *
    + * @param string name of parser option + * @param int (optional) 1 to switch on, 0 for off + * @access public + * @return boolean + */ + function set_option($name, $value=1) { + if ( array_key_exists($name,$this->state_parser->parser_options) ) { + $this->state_parser->parser_options[$name] = $value; + return true; + } else { + return $this->raiseError('XML_HTMLSax::set_option('.$name.') illegal'); + } + } + + /** + * Sets the data handler method which deals with the contents of XML + * elements.
    + * The handler method must accept two arguments, the first being an + * instance of XML_HTMLSax and the second being the contents of an + * XML element e.g. + *
    +    * function myDataHander(& $parser,$data){}
    +    * 
    + * @param string name of method + * @access public + * @return void + * @see set_object + */ + function set_data_handler($data_method) { + $this->state_parser->handler_object_data =& $this->state_parser->handler_default; + $this->state_parser->handler_method_data = $data_method; + } + + /** + * Sets the open and close tag handlers + *
    The open handler method must accept three arguments; the parser, + * the tag name and an array of attributes e.g. + *
    +    * function myOpenHander(& $parser,$tagname,$attrs=array()){}
    +    * 
    + * The close handler method must accept two arguments; the parser and + * the tag name e.g. + *
    +    * function myCloseHander(& $parser,$tagname){}
    +    * 
    + * @param string name of open method + * @param string name of close method + * @access public + * @return void + * @see set_object + */ + function set_element_handler($opening_method, $closing_method) { + $this->state_parser->handler_object_element =& $this->state_parser->handler_default; + $this->state_parser->handler_method_opening = $opening_method; + $this->state_parser->handler_method_closing = $closing_method; + } + + /** + * Sets the processing instruction handler method e.g. for PHP open + * and close tags
    + * The handler method must accept three arguments; the parser, the + * PI target and data inside the PI + *
    +    * function myPIHander(& $parser,$target, $data){}
    +    * 
    + * @param string name of method + * @access public + * @return void + * @see set_object + */ + function set_pi_handler($pi_method) { + $this->state_parser->handler_object_pi =& $this->state_parser->handler_default; + $this->state_parser->handler_method_pi = $pi_method; + } + + /** + * Sets the XML escape handler method e.g. for comments and doctype + * declarations
    + * The handler method must accept two arguments; the parser and the + * contents of the escaped section + *
    +    * function myEscapeHander(& $parser, $data){}
    +    * 
    + * @param string name of method + * @access public + * @return void + * @see set_object + */ + function set_escape_handler($escape_method) { + $this->state_parser->handler_object_escape =& $this->state_parser->handler_default; + $this->state_parser->handler_method_escape = $escape_method; + } + + /** + * Sets the JSP/ASP markup handler
    + * The handler method must accept two arguments; the parser and + * body of the JASP tag + *
    +    * function myJaspHander(& $parser, $data){}
    +    * 
    + * @param string name of method + * @access public + * @return void + * @see set_object + */ + function set_jasp_handler ($jasp_method) { + $this->state_parser->handler_object_jasp =& $this->state_parser->handler_default; + $this->state_parser->handler_method_jasp = $jasp_method; + } + + /** + * Returns the current string position of the "cursor" inside the XML + * document + *
    Intended for use from within a user defined handler called + * via the $parser reference e.g. + *
    +    * function myDataHandler(& $parser,$data) {
    +    *     echo( 'Current position: '.$parser->get_current_position() );
    +    * }
    +    * 
    + * @access public + * @return int + * @see get_length + */ + function get_current_position() { + return $this->state_parser->position; + } + + /** + * Returns the string length of the XML document being parsed + * @access public + * @return int + */ + function get_length() { + return $this->state_parser->length; + } + + /** + * Start parsing some XML + * @param string XML document + * @access public + * @return void + */ + function parse($data) { + $this->state_parser->parse($data); + } +} +?> \ No newline at end of file diff --git a/include/classes/cssparser.php b/include/classes/cssparser.php new file mode 100644 index 000000000..f2a19b761 --- /dev/null +++ b/include/classes/cssparser.php @@ -0,0 +1,238 @@ +html = ($html != false); + $this->Clear(); + } + + function finalize() { + unset($this->css); + } + + function Clear() { + unset($this->css); + $this->css = array(); + if($this->html) { + /* + $this->Add("ADDRESS", ""); + $this->Add("APPLET", ""); + $this->Add("AREA", ""); + $this->Add("A", "text-decoration : underline; color : Blue;"); + $this->Add("A:visited", "color : Purple;"); + $this->Add("BASE", ""); + $this->Add("BASEFONT", ""); + $this->Add("BIG", ""); + $this->Add("BLOCKQUOTE", ""); + $this->Add("BODY", ""); + $this->Add("BR", ""); + $this->Add("B", "font-weight: bold;"); + $this->Add("CAPTION", ""); + $this->Add("CENTER", ""); + $this->Add("CITE", ""); + $this->Add("CODE", ""); + $this->Add("DD", ""); + $this->Add("DFN", ""); + $this->Add("DIR", ""); + $this->Add("DIV", ""); + $this->Add("DL", ""); + $this->Add("DT", ""); + $this->Add("EM", ""); + $this->Add("FONT", ""); + $this->Add("FORM", ""); + $this->Add("H1", ""); + $this->Add("H2", ""); + $this->Add("H3", ""); + $this->Add("H4", ""); + $this->Add("H5", ""); + $this->Add("H6", ""); + $this->Add("HEAD", ""); + $this->Add("HR", ""); + $this->Add("HTML", ""); + $this->Add("IMG", ""); + $this->Add("INPUT", ""); + $this->Add("ISINDEX", ""); + $this->Add("I", "font-style: italic;"); + $this->Add("KBD", ""); + $this->Add("LINK", ""); + $this->Add("LI", ""); + $this->Add("MAP", ""); + $this->Add("MENU", ""); + $this->Add("META", ""); + $this->Add("OL", ""); + $this->Add("OPTION", ""); + $this->Add("PARAM", ""); + $this->Add("PRE", ""); + $this->Add("P", ""); + $this->Add("SAMP", ""); + $this->Add("SCRIPT", ""); + $this->Add("SELECT", ""); + $this->Add("SMALL", ""); + $this->Add("STRIKE", ""); + $this->Add("STRONG", ""); + $this->Add("STYLE", ""); + $this->Add("SUB", ""); + $this->Add("SUP", ""); + $this->Add("TABLE", ""); + $this->Add("TD", ""); + $this->Add("TEXTAREA", ""); + $this->Add("TH", ""); + $this->Add("TITLE", ""); + $this->Add("TR", ""); + $this->Add("TT", ""); + $this->Add("UL", ""); + $this->Add("U", "text-decoration : underline;"); + */ + $this->Add("VAR", ""); + + } + } + + function SetHTML($html) { + $this->html = ($html != false); + } + + function Add($key, $codestr) { + $key = strtolower($key); + $codestr = strtolower($codestr); + if(!isset($this->css[$key])) { + $this->css[$key] = array(); + } + $codes = explode(";",$codestr); + if(count($codes) > 0) { + foreach($codes as $code) { + $code = trim($code); + list($codekey, $codevalue) = explode(":",$code,2); + if(strlen($codekey) > 0) { + $this->css[$key][trim($codekey)] = trim($codevalue); + } + } + } + } + + function Get($key, $property) { + $key = strtolower($key); + $property = strtolower($property); + + list($tag, $subtag) = explode(":",$key); + list($tag, $class) = explode(".",$tag); + list($tag, $id) = explode("#",$tag); + $result = ""; + foreach($this->css as $_tag => $value) { + list($_tag, $_subtag) = explode(":",$_tag); + list($_tag, $_class) = explode(".",$_tag); + list($_tag, $_id) = explode("#",$_tag); + + $tagmatch = (strcmp($tag, $_tag) == 0) | (strlen($_tag) == 0); + $subtagmatch = (strcmp($subtag, $_subtag) == 0) | (strlen($_subtag) == 0); + $classmatch = (strcmp($class, $_class) == 0) | (strlen($_class) == 0); + $idmatch = (strcmp($id, $_id) == 0); + + if($tagmatch & $subtagmatch & $classmatch & $idmatch) { + $temp = $_tag; + if((strlen($temp) > 0) & (strlen($_class) > 0)) { + $temp .= ".".$_class; + } elseif(strlen($temp) == 0) { + $temp = ".".$_class; + } + if((strlen($temp) > 0) & (strlen($_subtag) > 0)) { + $temp .= ":".$_subtag; + } elseif(strlen($temp) == 0) { + $temp = ":".$_subtag; + } + if(isset($this->css[$temp][$property])) { + $result = $this->css[$temp][$property]; + } + } + } + return $result; + } + + function GetSection($key) { + $key = strtolower($key); + + list($tag, $subtag) = explode(":",$key); + list($tag, $class) = explode(".",$tag); + list($tag, $id) = explode("#",$tag); + $result = array(); + foreach($this->css as $_tag => $value) { + list($_tag, $_subtag) = explode(":",$_tag); + list($_tag, $_class) = explode(".",$_tag); + list($_tag, $_id) = explode("#",$_tag); + + $tagmatch = (strcmp($tag, $_tag) == 0) | (strlen($_tag) == 0); + $subtagmatch = (strcmp($subtag, $_subtag) == 0) | (strlen($_subtag) == 0); + $classmatch = (strcmp($class, $_class) == 0) | (strlen($_class) == 0); + $idmatch = (strcmp($id, $_id) == 0); + + if($tagmatch & $subtagmatch & $classmatch & $idmatch) { + $temp = $_tag; + if((strlen($temp) > 0) & (strlen($_class) > 0)) { + $temp .= ".".$_class; + } elseif(strlen($temp) == 0) { + $temp = ".".$_class; + } + if((strlen($temp) > 0) & (strlen($_subtag) > 0)) { + $temp .= ":".$_subtag; + } elseif(strlen($temp) == 0) { + $temp = ":".$_subtag; + } + foreach($this->css[$temp] as $property => $value) { + $result[$property] = $value; + } + } + } + return $result; + } + + function ParseStr($str) { + $this->Clear(); + // Remove comments + $str = preg_replace("/\/\*(.*)?\*\//Usi", "", $str); + // Parse this damn csscode + $parts = explode("}",$str); + if(count($parts) > 0) { + foreach($parts as $part) { + list($keystr,$codestr) = explode("{",$part); + $keys = explode(",",trim($keystr)); + if(count($keys) > 0) { + foreach($keys as $key) { + if(strlen($key) > 0) { + $key = str_replace("\n", "", $key); + $key = str_replace("\\", "", $key); + $this->Add($key, trim($codestr)); + } + } + } + } + } + // + return (count($this->css) > 0); + } + + function Parse($filename) { + $this->Clear(); + if(file_exists($filename)) { + return $this->ParseStr(file_get_contents($filename)); + } else { + return false; + } + } + + function GetCSS() { + $result = ""; + foreach($this->css as $key => $values) { + $result .= $key." {\n"; + foreach($values as $key => $value) { + $result .= " $key: $value;\n"; + } + $result .= "}\n\n"; + } + return $result; + } +} +?> diff --git a/include/classes/feedcreator.class.php b/include/classes/feedcreator.class.php new file mode 100644 index 000000000..69e285470 --- /dev/null +++ b/include/classes/feedcreator.class.php @@ -0,0 +1,1545 @@ +useCached(); // use cached version if age<1 hour +$rss->title = "PHP news"; +$rss->description = "daily news from the PHP scripting world"; + +//optional +$rss->descriptionTruncSize = 500; +$rss->descriptionHtmlSyndicated = true; + +$rss->link = "http://www.dailyphp.net/news"; +$rss->syndicationURL = "http://www.dailyphp.net/".$_SERVER["PHP_SELF"]; + +$image = new FeedImage(); +$image->title = "dailyphp.net logo"; +$image->url = "http://www.dailyphp.net/images/logo.gif"; +$image->link = "http://www.dailyphp.net"; +$image->description = "Feed provided by dailyphp.net. Click to visit."; + +//optional +$image->descriptionTruncSize = 500; +$image->descriptionHtmlSyndicated = true; + +$rss->image = $image; + +// get your news items from somewhere, e.g. your database: +mysql_select_db($dbHost, $dbUser, $dbPass); +$res = mysql_query("SELECT * FROM news ORDER BY newsdate DESC"); +while ($data = mysql_fetch_object($res)) { + $item = new FeedItem(); + $item->title = $data->title; + $item->link = $data->url; + $item->description = $data->short; + + //optional + item->descriptionTruncSize = 500; + item->descriptionHtmlSyndicated = true; + + $item->date = $data->newsdate; + $item->source = "http://www.dailyphp.net"; + $item->author = "John Doe"; + + $rss->addItem($item); +} + +// valid format strings are: RSS0.91, RSS1.0, RSS2.0, PIE0.1 (deprecated), +// MBOX, OPML, ATOM, ATOM0.3, HTML, JS +echo $rss->saveFeed("RSS1.0", "news/feed.xml"); + + +*************************************************************************** +* A little setup * +**************************************************************************/ + +// your local timezone, set to "" to disable or for GMT +define("TIME_ZONE",""); + + +/** + * Version string. + **/ +define("FEEDCREATOR_VERSION", "FeedCreator 1.7.2"); + +//Greg Gay +//Added ATutor charset encoding, see line 502 +//define("MY_ENCODING" , $myLang->getCharacterSet()); + +/** + * A FeedItem is a part of a FeedCreator feed. + * + * @author Kai Blankenhorn + * @since 1.3 + */ +class FeedItem extends HtmlDescribable { + /** + * Mandatory attributes of an item. + */ + var $title, $description, $link; + + /** + * Optional attributes of an item. + */ + var $author, $authorEmail, $image, $category, $comments, $guid, $source, $creator; + + /** + * Publishing date of an item. May be in one of the following formats: + * + * RFC 822: + * "Mon, 20 Jan 03 18:05:41 +0400" + * "20 Jan 03 18:05:41 +0000" + * + * ISO 8601: + * "2003-01-20T18:05:41+04:00" + * + * Unix: + * 1043082341 + */ + var $date; + + /** + * Any additional elements to include as an assiciated array. All $key => $value pairs + * will be included unencoded in the feed item in the form + * <$key>$value + * Again: No encoding will be used! This means you can invalidate or enhance the feed + * if $value contains markup. This may be abused to embed tags not implemented by + * the FeedCreator class used. + */ + var $additionalElements = Array(); + + // on hold + // var $source; +} + + + +/** + * An FeedImage may be added to a FeedCreator feed. + * @author Kai Blankenhorn + * @since 1.3 + */ +class FeedImage extends HtmlDescribable { + /** + * Mandatory attributes of an image. + */ + var $title, $url, $link; + + /** + * Optional attributes of an image. + */ + var $width, $height, $description; +} + + + +/** + * An HtmlDescribable is an item within a feed that can have a description that may + * include HTML markup. + */ +class HtmlDescribable { + /** + * Indicates whether the description field should be rendered in HTML. + */ + var $descriptionHtmlSyndicated; + + /** + * Indicates whether and to how many characters a description should be truncated. + */ + var $descriptionTruncSize; + + /** + * Returns a formatted description field, depending on descriptionHtmlSyndicated and + * $descriptionTruncSize properties + * @return string the formatted description + */ + function getDescription() { + $descriptionField = new FeedHtmlField($this->description); + $descriptionField->syndicateHtml = $this->descriptionHtmlSyndicated; + $descriptionField->truncSize = $this->descriptionTruncSize; + return $descriptionField->output(); + } + +} + + +/** + * An FeedHtmlField describes and generates + * a feed, item or image html field (probably a description). Output is + * generated based on $truncSize, $syndicateHtml properties. + * @author Pascal Van Hecke + * @version 1.6 + */ +class FeedHtmlField { + /** + * Mandatory attributes of a FeedHtmlField. + */ + var $rawFieldContent; + + /** + * Optional attributes of a FeedHtmlField. + * + */ + var $truncSize, $syndicateHtml; + + /** + * Creates a new instance of FeedHtmlField. + * @param $string: if given, sets the rawFieldContent property + */ + function FeedHtmlField($parFieldContent) { + if ($parFieldContent) { + $this->rawFieldContent = $parFieldContent; + } + } + + + /** + * Creates the right output, depending on $truncSize, $syndicateHtml properties. + * @return string the formatted field + */ + function output() { + // when field available and syndicated in html we assume + // - valid html in $rawFieldContent and we enclose in CDATA tags + // - no truncation (truncating risks producing invalid html) + if (!$this->rawFieldContent) { + $result = ""; + } elseif ($this->syndicateHtml) { + $result = "rawFieldContent."]]>"; + } else { + if ($this->truncSize and is_int($this->truncSize)) { + $result = FeedCreator::iTrunc(htmlspecialchars($this->rawFieldContent),$this->truncSize); + } else { + $result = htmlspecialchars($this->rawFieldContent); + } + } + return $result; + } + +} + + + +/** + * UniversalFeedCreator lets you choose during runtime which + * format to build. + * For general usage of a feed class, see the FeedCreator class + * below or the example above. + * + * @since 1.3 + * @author Kai Blankenhorn + */ +class UniversalFeedCreator extends FeedCreator { + var $_feed; + + function _setFormat($format) { + switch (strtoupper($format)) { + + case "2.0": + // fall through + case "RSS2.0": + $this->_feed = new RSSCreator20(); + break; + + case "1.0": + // fall through + case "RSS1.0": + $this->_feed = new RSSCreator10(); + break; + + case "0.91": + // fall through + case "RSS0.91": + $this->_feed = new RSSCreator091(); + break; + + case "PIE0.1": + $this->_feed = new PIECreator01(); + break; + + case "MBOX": + $this->_feed = new MBOXCreator(); + break; + + case "OPML": + $this->_feed = new OPMLCreator(); + break; + + case "ATOM": + // fall through: always the latest ATOM version + + case "ATOM0.3": + $this->_feed = new AtomCreator03(); + break; + + case "HTML": + $this->_feed = new HTMLCreator(); + break; + + case "JS": + // fall through + case "JAVASCRIPT": + $this->_feed = new JSCreator(); + break; + + default: + $this->_feed = new RSSCreator091(); + break; + } + + $vars = get_object_vars($this); + foreach ($vars as $key => $value) { + // prevent overwriting of properties "contentType", "encoding"; do not copy "_feed" itself + if (!in_array($key, array("_feed", "contentType", "encoding"))) { + $this->_feed->{$key} = $this->{$key}; + } + } + } + + /** + * Creates a syndication feed based on the items previously added. + * + * @see FeedCreator::addItem() + * @param string format format the feed should comply to. Valid values are: + * "PIE0.1", "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3", "HTML", "JS" + * @return string the contents of the feed. + */ + function createFeed($format = "RSS0.91") { + $this->_setFormat($format); + return $this->_feed->createFeed(); + } + + + + /** + * Saves this feed as a file on the local disk. After the file is saved, an HTTP redirect + * header may be sent to redirect the use to the newly created file. + * @since 1.4 + * + * @param string format format the feed should comply to. Valid values are: + * "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM", "ATOM0.3", "HTML", "JS" + * @param string filename optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). + * @param boolean displayContents optional send the content of the file or not. If true, the file will be sent in the body of the response. + */ + function saveFeed($format="RSS0.91", $filename="", $displayContents=true) { + $this->_setFormat($format); + $this->_feed->saveFeed($filename, $displayContents); + } + + + /** + * Turns on caching and checks if there is a recent version of this feed in the cache. + * If there is, an HTTP redirect header is sent. + * To effectively use caching, you should create the FeedCreator object and call this method + * before anything else, especially before you do the time consuming task to build the feed + * (web fetching, for example). + * + * @param string format format the feed should comply to. Valid values are: + * "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3". + * @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). + * @param timeout int optional the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour) + */ + function useCached($format="RSS0.91", $filename="", $timeout=3600) { + $this->_setFormat($format); + $this->_feed->useCached($filename, $timeout); + } + +} + + +/** + * FeedCreator is the abstract base implementation for concrete + * implementations that implement a specific format of syndication. + * + * @abstract + * @author Kai Blankenhorn + * @since 1.4 + */ +class FeedCreator extends HtmlDescribable { + + /** + * Mandatory attributes of a feed. + */ + var $title, $description, $link; + + //var $my_charset; + /** + * Optional attributes of a feed. + */ + var $syndicationURL, $image, $language, $copyright, $pubDate, $lastBuildDate, $editor, $editorEmail, $webmaster, $category, $docs, $ttl, $rating, $skipHours, $skipDays; + + /** + * The url of the external xsl stylesheet used to format the naked rss feed. + * Ignored in the output when empty. + */ + var $xslStyleSheet = ""; + + + /** + * @access private + */ + var $items = Array(); + + + /** + * This feed's MIME content type. + * @since 1.4 + * @access private + */ + var $contentType = "application/xml"; + + + /** + * This feed's character encoding. + * @since 1.6.1 + **/ + //Modified to accommodate ATutor charsets + //Greg Gay + + //var $encoding = MY_ENCODING; + var $encoding = "utf-8"; + //var $encoding = "ISO-8859-1"; + + + /** + * Any additional elements to include as an assiciated array. All $key => $value pairs + * will be included unencoded in the feed in the form + * <$key>$value + * Again: No encoding will be used! This means you can invalidate or enhance the feed + * if $value contains markup. This may be abused to embed tags not implemented by + * the FeedCreator class used. + */ + var $additionalElements = Array(); + + + /** + * Adds an FeedItem to the feed. + * + * @param object FeedItem $item The FeedItem to add to the feed. + * @access public + */ + function addItem($item) { + $this->items[] = $item; + } + + + /** + * Truncates a string to a certain length at the most sensible point. + * First, if there's a '.' character near the end of the string, the string is truncated after this character. + * If there is no '.', the string is truncated after the last ' ' character. + * If the string is truncated, " ..." is appended. + * If the string is already shorter than $length, it is returned unchanged. + * + * @static + * @param string string A string to be truncated. + * @param int length the maximum length the string should be truncated to + * @return string the truncated string + */ + function iTrunc($string, $length) { + if (strlen($string)<=$length) { + return $string; + } + + $pos = strrpos($string,"."); + if ($pos>=$length-4) { + $string = substr($string,0,$length-4); + $pos = strrpos($string,"."); + } + if ($pos>=$length*0.4) { + return substr($string,0,$pos+1)." ..."; + } + + $pos = strrpos($string," "); + if ($pos>=$length-4) { + $string = substr($string,0,$length-4); + $pos = strrpos($string," "); + } + if ($pos>=$length*0.4) { + return substr($string,0,$pos)." ..."; + } + + return substr($string,0,$length-4)." ..."; + + } + + + /** + * Creates a comment indicating the generator of this feed. + * The format of this comment seems to be recognized by + * Syndic8.com. + */ + function _createGeneratorComment() { + return "\n"; + } + + + /** + * Creates a string containing all additional elements specified in + * $additionalElements. + * @param elements array an associative array containing key => value pairs + * @param indentString string a string that will be inserted before every generated line + * @return string the XML tags corresponding to $additionalElements + */ + function _createAdditionalElements($elements, $indentString="") { + $ae = ""; + if (is_array($elements)) { + foreach($elements AS $key => $value) { + $ae.= $indentString."<$key>$value\n"; + } + } + return $ae; + } + + function _createStylesheetReferences() { + $xml = ""; + if ($this->cssStyleSheet) $xml .= "cssStyleSheet."\" type=\"text/css\"?>\n"; + if ($this->xslStyleSheet) $xml .= "xslStyleSheet."\" type=\"text/xsl\"?>\n"; + return $xml; + } + + + /** + * Builds the feed's text. + * @abstract + * @return string the feed's complete text + */ + function createFeed() { + } + + /** + * Generate a filename for the feed cache file. The result will be $_SERVER["PHP_SELF"] with the extension changed to .xml. + * For example: + * + * echo $_SERVER["PHP_SELF"]."\n"; + * echo FeedCreator::_generateFilename(); + * + * would produce: + * + * /rss/latestnews.php + * latestnews.xml + * + * @return string the feed cache filename + * @since 1.4 + * @access private + */ + function _generateFilename() { + $fileInfo = pathinfo($_SERVER["PHP_SELF"]); + return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".xml"; + } + + + /** + * @since 1.4 + * @access private + */ + function _redirect($filename) { + // attention, heavily-commented-out-area + + // maybe use this in addition to file time checking + //Header("Expires: ".date("r",time()+$this->_timeout)); + + /* no caching at all, doesn't seem to work as good: + Header("Cache-Control: no-cache"); + Header("Pragma: no-cache"); + */ + + // HTTP redirect, some feed readers' simple HTTP implementations don't follow it + //Header("Location: ".$filename); + + Header("Content-Type: ".$this->contentType."; charset=".$this->encoding."; filename=".basename($filename)); + Header("Content-Disposition: inline; filename=".basename($filename)); + readfile($filename, "r"); + die(); + } + + /** + * Turns on caching and checks if there is a recent version of this feed in the cache. + * If there is, an HTTP redirect header is sent. + * To effectively use caching, you should create the FeedCreator object and call this method + * before anything else, especially before you do the time consuming task to build the feed + * (web fetching, for example). + * @since 1.4 + * @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). + * @param timeout int optional the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour) + */ + function useCached($filename="", $timeout=3600) { + $this->_timeout = $timeout; + if ($filename=="") { + $filename = $this->_generateFilename(); + } + if (file_exists($filename) AND (time()-filemtime($filename) < $timeout)) { + $this->_redirect($filename); + } + } + + + /** + * Saves this feed as a file on the local disk. After the file is saved, a redirect + * header may be sent to redirect the user to the newly created file. + * @since 1.4 + * + * @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()). + * @param redirect boolean optional send an HTTP redirect header or not. If true, the user will be automatically redirected to the created file. + */ + function saveFeed($filename="", $displayContents=true) { + if ($filename=="") { + $filename = $this->_generateFilename(); + } + $feedFile = fopen($filename, "w+"); + if ($feedFile) { + fputs($feedFile,$this->createFeed()); + fclose($feedFile); + if ($displayContents) { + $this->_redirect($filename); + } + } else { + echo "
    Error creating feed file, please check write permissions.
    "; + } + } + +} + + +/** + * FeedDate is an internal class that stores a date for a feed or feed item. + * Usually, you won't need to use this. + */ +class FeedDate { + var $unix; + + /** + * Creates a new instance of FeedDate representing a given date. + * Accepts RFC 822, ISO 8601 date formats as well as unix time stamps. + * @param mixed $dateString optional the date this FeedDate will represent. If not specified, the current date and time is used. + */ + function FeedDate($dateString="") { + if ($dateString=="") $dateString = date("r"); + + if (is_integer($dateString)) { + $this->unix = $dateString; + return; + } + if (preg_match("~(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?(\\d{1,2})\\s+([a-zA-Z]{3})\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+(.*)~",$dateString,$matches)) { + $months = Array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12); + $this->unix = mktime($matches[4],$matches[5],$matches[6],$months[$matches[2]],$matches[1],$matches[3]); + if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') { + $tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60; + } else { + if (strlen($matches[7])==1) { + $oneHour = 3600; + $ord = ord($matches[7]); + if ($ord < ord("M")) { + $tzOffset = (ord("A") - $ord - 1) * $oneHour; + } elseif ($ord >= ord("M") AND $matches[7]!="Z") { + $tzOffset = ($ord - ord("M")) * $oneHour; + } elseif ($matches[7]=="Z") { + $tzOffset = 0; + } + } + switch ($matches[7]) { + case "UT": + case "GMT": $tzOffset = 0; + } + } + $this->unix += $tzOffset; + return; + } + if (preg_match("~(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(.*)~",$dateString,$matches)) { + $this->unix = mktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]); + if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') { + $tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60; + } else { + if ($matches[7]=="Z") { + $tzOffset = 0; + } + } + $this->unix += $tzOffset; + return; + } + $this->unix = 0; + } + + /** + * Gets the date stored in this FeedDate as an RFC 822 date. + * + * @return a date in RFC 822 format + */ + function rfc822() { + //return gmdate("r",$this->unix); + $date = gmdate("D, d M Y H:i:s", $this->unix); + if (TIME_ZONE!="") $date .= " ".str_replace(":","",TIME_ZONE); + return $date; + } + + /** + * Gets the date stored in this FeedDate as an ISO 8601 date. + * + * @return a date in ISO 8601 format + */ + function iso8601() { + $date = gmdate("Y-m-d\TH:i:sO",$this->unix); + $date = substr($date,0,22) . ':' . substr($date,-2); + if (TIME_ZONE!="") $date = str_replace("+00:00",TIME_ZONE,$date); + return $date; + } + + /** + * Gets the date stored in this FeedDate as unix time stamp. + * + * @return a date as a unix time stamp + */ + function unix() { + return $this->unix; + } +} + + +/** + * RSSCreator10 is a FeedCreator that implements RDF Site Summary (RSS) 1.0. + * + * @see http://www.purl.org/rss/1.0/ + * @since 1.3 + * @author Kai Blankenhorn + */ +class RSSCreator10 extends FeedCreator { + + /** + * Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0. + * The feed will contain all items previously added in the same order. + * @return string the feed's complete text + */ + function createFeed() { + $feed = "encoding."\"?>\n"; + $feed.= $this->_createGeneratorComment(); + if ($this->cssStyleSheet=="") { + $cssStyleSheet = "http://www.w3.org/2000/08/w3c-synd/style.css"; + } + $feed.= $this->_createStylesheetReferences(); + $feed.= "\n"; + $feed.= " syndicationURL."\">\n"; + $feed.= " ".htmlspecialchars($this->title)."\n"; + $feed.= " ".htmlspecialchars($this->description)."\n"; + $feed.= " ".$this->link."\n"; + if ($this->image!=null) { + $feed.= " image->url."\" />\n"; + } + $now = new FeedDate(); + $feed.= " ".htmlspecialchars($now->iso8601())."\n"; + $feed.= " \n"; + $feed.= " \n"; + for ($i=0;$iitems);$i++) { + $feed.= " items[$i]->link)."\"/>\n"; + } + $feed.= " \n"; + $feed.= " \n"; + $feed.= " \n"; + if ($this->image!=null) { + $feed.= " image->url."\">\n"; + $feed.= " ".$this->image->title."\n"; + $feed.= " ".$this->image->link."\n"; + $feed.= " ".$this->image->url."\n"; + $feed.= " \n"; + } + $feed.= $this->_createAdditionalElements($this->additionalElements, " "); + + for ($i=0;$iitems);$i++) { + $feed.= " items[$i]->link)."\">\n"; + //$feed.= " Posting\n"; + $feed.= " text/html\n"; + if ($this->items[$i]->date!=null) { + $itemDate = new FeedDate($this->items[$i]->date); + $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n"; + } + if ($this->items[$i]->source!="") { + $feed.= " ".htmlspecialchars($this->items[$i]->source)."\n"; + } + if ($this->items[$i]->author!="") { + $feed.= " ".htmlspecialchars($this->items[$i]->author)."\n"; + } + $feed.= " ".htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," ")))."\n"; + $feed.= " ".htmlspecialchars($this->items[$i]->link)."\n"; + $feed.= " ".htmlspecialchars($this->items[$i]->description)."\n"; + $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); + $feed.= " \n"; + } + $feed.= "\n"; + return $feed; + } +} + + + +/** + * RSSCreator091 is a FeedCreator that implements RSS 0.91 Spec, revision 3. + * + * @see http://my.netscape.com/publish/formats/rss-spec-0.91.html + * @since 1.3 + * @author Kai Blankenhorn + */ +class RSSCreator091 extends FeedCreator { + + /** + * Stores this RSS feed's version number. + * @access private + */ + var $RSSVersion; + + function RSSCreator091() { + $this->_setRSSVersion("0.91"); + $this->contentType = "application/rss+xml"; + } + + /** + * Sets this RSS feed's version number. + * @access private + */ + function _setRSSVersion($version) { + $this->RSSVersion = $version; + } + + /** + * Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0. + * The feed will contain all items previously added in the same order. + * @return string the feed's complete text + */ + function createFeed() { + $feed = "encoding."\"?>\n"; + $feed.= $this->_createGeneratorComment(); + $feed.= $this->_createStylesheetReferences(); + $feed.= "RSSVersion."\">\n"; + $feed.= " \n"; + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."\n"; + $this->descriptionTruncSize = 500; + $feed.= " ".$this->getDescription()."\n"; + $feed.= " ".$this->link."\n"; + $now = new FeedDate(); + $feed.= " ".htmlspecialchars($now->rfc822())."\n"; + $feed.= " ".FEEDCREATOR_VERSION."\n"; + + if ($this->image!=null) { + $feed.= " \n"; + $feed.= " ".$this->image->url."\n"; + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->image->title),100)."\n"; + $feed.= " ".$this->image->link."\n"; + if ($this->image->width!="") { + $feed.= " ".$this->image->width."\n"; + } + if ($this->image->height!="") { + $feed.= " ".$this->image->height."\n"; + } + if ($this->image->description!="") { + $feed.= " ".$this->image->getDescription()."\n"; + } + $feed.= " \n"; + } + if ($this->language!="") { + $feed.= " ".$this->language."\n"; + } + if ($this->copyright!="") { + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->copyright),100)."\n"; + } + if ($this->editor!="") { + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->editor),100)."\n"; + } + if ($this->webmaster!="") { + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->webmaster),100)."\n"; + } + if ($this->pubDate!="") { + $pubDate = new FeedDate($this->pubDate); + $feed.= " ".htmlspecialchars($pubDate->rfc822())."\n"; + } + if ($this->category!="") { + $feed.= " ".htmlspecialchars($this->category)."\n"; + } + if ($this->docs!="") { + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->docs),500)."\n"; + } + if ($this->ttl!="") { + $feed.= " ".htmlspecialchars($this->ttl)."\n"; + } + if ($this->rating!="") { + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->rating),500)."\n"; + } + if ($this->skipHours!="") { + $feed.= " ".htmlspecialchars($this->skipHours)."\n"; + } + if ($this->skipDays!="") { + $feed.= " ".htmlspecialchars($this->skipDays)."\n"; + } + $feed.= $this->_createAdditionalElements($this->additionalElements, " "); + + for ($i=0;$iitems);$i++) { + $feed.= " \n"; + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."\n"; + $feed.= " ".htmlspecialchars($this->items[$i]->link)."\n"; + $feed.= " ".$this->items[$i]->getDescription()."\n"; + + if ($this->items[$i]->author!="") { + $feed.= " ".htmlspecialchars($this->items[$i]->author)."\n"; + } + /* + // on hold + if ($this->items[$i]->source!="") { + $feed.= " ".htmlspecialchars($this->items[$i]->source)."\n"; + } + */ + if ($this->items[$i]->category!="") { + $feed.= " ".htmlspecialchars($this->items[$i]->category)."\n"; + } + if ($this->items[$i]->comments!="") { + $feed.= " ".htmlspecialchars($this->items[$i]->comments)."\n"; + } + if ($this->items[$i]->date!="") { + $itemDate = new FeedDate($this->items[$i]->date); + $feed.= " ".htmlspecialchars($itemDate->rfc822())."\n"; + } + if ($this->items[$i]->guid!="") { + $feed.= " ".htmlspecialchars($this->items[$i]->guid)."\n"; + } + $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); + $feed.= " \n"; + } + $feed.= " \n"; + $feed.= "\n"; + return $feed; + } +} + + + +/** + * RSSCreator20 is a FeedCreator that implements RDF Site Summary (RSS) 2.0. + * + * @see http://backend.userland.com/rss + * @since 1.3 + * @author Kai Blankenhorn + */ +class RSSCreator20 extends RSSCreator091 { + + function RSSCreator20() { + parent::_setRSSVersion("2.0"); + } + +} + + +/** + * PIECreator01 is a FeedCreator that implements the emerging PIE specification, + * as in http://intertwingly.net/wiki/pie/Syntax. + * + * @deprecated + * @since 1.3 + * @author Scott Reynen and Kai Blankenhorn + */ +class PIECreator01 extends FeedCreator { + + function PIECreator01() { + $this->encoding = "utf-8"; + } + + function createFeed() { + $feed = "encoding."\"?>\n"; + $feed.= $this->_createStylesheetReferences(); + $feed.= "\n"; + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."\n"; + $this->truncSize = 500; + $feed.= " ".$this->getDescription()."\n"; + $feed.= " ".$this->link."\n"; + for ($i=0;$iitems);$i++) { + $feed.= " \n"; + $feed.= " ".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."\n"; + $feed.= " ".htmlspecialchars($this->items[$i]->link)."\n"; + $itemDate = new FeedDate($this->items[$i]->date); + $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n"; + $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n"; + $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n"; + $feed.= " ".htmlspecialchars($this->items[$i]->guid)."\n"; + if ($this->items[$i]->author!="") { + $feed.= " \n"; + $feed.= " ".htmlspecialchars($this->items[$i]->author)."\n"; + if ($this->items[$i]->authorEmail!="") { + $feed.= " ".$this->items[$i]->authorEmail."\n"; + } + $feed.=" \n"; + } + $feed.= " \n"; + $feed.= "
    ".$this->items[$i]->getDescription()."
    \n"; + $feed.= "
    \n"; + $feed.= "
    \n"; + } + $feed.= "
    \n"; + return $feed; + } +} + + +/** + * AtomCreator03 is a FeedCreator that implements the atom specification, + * as in http://www.intertwingly.net/wiki/pie/FrontPage. + * Please note that just by using AtomCreator03 you won't automatically + * produce valid atom files. For example, you have to specify either an editor + * for the feed or an author for every single feed item. + * + * Some elements have not been implemented yet. These are (incomplete list): + * author URL, item author's email and URL, item contents, alternate links, + * other link content types than text/html. Some of them may be created with + * AtomCreator03::additionalElements. + * + * @see FeedCreator#additionalElements + * @since 1.6 + * @author Kai Blankenhorn , Scott Reynen + */ +class AtomCreator03 extends FeedCreator { + + function AtomCreator03() { + $this->contentType = "application/atom+xml"; + $this->encoding = "utf-8"; + } + + function createFeed() { + $feed = "encoding."\"?>\n"; + $feed.= $this->_createGeneratorComment(); + $feed.= $this->_createStylesheetReferences(); + $feed.= "language!="") { + $feed.= " xml:lang=\"".$this->language."\""; + } + $feed.= ">\n"; + $feed.= " ".htmlspecialchars($this->title)."\n"; + $feed.= " ".htmlspecialchars($this->description)."\n"; + $feed.= " link)."\"/>\n"; + $feed.= " ".htmlspecialchars($this->link)."\n"; + $now = new FeedDate(); + $feed.= " ".htmlspecialchars($now->iso8601())."\n"; + if ($this->editor!="") { + $feed.= " \n"; + $feed.= " ".$this->editor."\n"; + if ($this->editorEmail!="") { + $feed.= " ".$this->editorEmail."\n"; + } + $feed.= " \n"; + } + $feed.= " ".FEEDCREATOR_VERSION."\n"; + $feed.= $this->_createAdditionalElements($this->additionalElements, " "); + for ($i=0;$iitems);$i++) { + $feed.= " \n"; + $feed.= " ".htmlspecialchars(strip_tags($this->items[$i]->title))."\n"; + $feed.= " items[$i]->link)."\"/>\n"; + if ($this->items[$i]->date=="") { + $this->items[$i]->date = time(); + } + $itemDate = new FeedDate($this->items[$i]->date); + $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n"; + $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n"; + $feed.= " ".htmlspecialchars($itemDate->iso8601())."\n"; + $feed.= " ".htmlspecialchars($this->items[$i]->link)."\n"; + $feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " "); + if ($this->items[$i]->author!="") { + $feed.= " \n"; + $feed.= " ".htmlspecialchars($this->items[$i]->author)."\n"; + $feed.= " \n"; + } + if ($this->items[$i]->description!="") { + $feed.= " ".htmlspecialchars($this->items[$i]->description)."\n"; + } + $feed.= " \n"; + } + $feed.= "\n"; + return $feed; + } +} + + +/** + * MBOXCreator is a FeedCreator that implements the mbox format + * as described in http://www.qmail.org/man/man5/mbox.html + * + * @since 1.3 + * @author Kai Blankenhorn + */ +class MBOXCreator extends FeedCreator { + + function MBOXCreator() { + $this->contentType = "text/plain"; + $this->encoding = "ISO-8859-15"; + } + + function qp_enc($input = "", $line_max = 76) { + $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); + $lines = preg_split("/(?:\r\n|\r|\n)/", $input); + $eol = "\r\n"; + $escape = "="; + $output = ""; + while( list(, $line) = each($lines) ) { + //$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary + $linlen = strlen($line); + $newline = ""; + for($i = 0; $i < $linlen; $i++) { + $c = substr($line, $i, 1); + $dec = ord($c); + if ( ($dec == 32) && ($i == ($linlen - 1)) ) { // convert space at eol only + $c = "=20"; + } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required + $h2 = floor($dec/16); $h1 = floor($dec%16); + $c = $escape.$hex["$h2"].$hex["$h1"]; + } + if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted + $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay + $newline = ""; + } + $newline .= $c; + } // end of for + $output .= $newline.$eol; + } + return trim($output); + } + + + /** + * Builds the MBOX contents. + * @return string the feed's complete text + */ + function createFeed() { + for ($i=0;$iitems);$i++) { + if ($this->items[$i]->author!="") { + $from = $this->items[$i]->author; + } else { + $from = $this->title; + } + $itemDate = new FeedDate($this->items[$i]->date); + $feed.= "From ".strtr(MBOXCreator::qp_enc($from)," ","_")." ".date("D M d H:i:s Y",$itemDate->unix())."\n"; + $feed.= "Content-Type: text/plain;\n"; + $feed.= " charset=\"".$this->encoding."\"\n"; + $feed.= "Content-Transfer-Encoding: quoted-printable\n"; + $feed.= "Content-Type: text/plain\n"; + $feed.= "From: \"".MBOXCreator::qp_enc($from)."\"\n"; + $feed.= "Date: ".$itemDate->rfc822()."\n"; + $feed.= "Subject: ".MBOXCreator::qp_enc(FeedCreator::iTrunc($this->items[$i]->title,100))."\n"; + $feed.= "\n"; + $body = chunk_split(MBOXCreator::qp_enc($this->items[$i]->description)); + $feed.= preg_replace("~\nFrom ([^\n]*)(\n?)~","\n>From $1$2\n",$body); + $feed.= "\n"; + $feed.= "\n"; + } + return $feed; + } + + /** + * Generate a filename for the feed cache file. Overridden from FeedCreator to prevent XML data types. + * @return string the feed cache filename + * @since 1.4 + * @access private + */ + function _generateFilename() { + $fileInfo = pathinfo($_SERVER["PHP_SELF"]); + return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".mbox"; + } +} + + +/** + * OPMLCreator is a FeedCreator that implements OPML 1.0. + * + * @see http://opml.scripting.com/spec + * @author Dirk Clemens, Kai Blankenhorn + * @since 1.5 + */ +class OPMLCreator extends FeedCreator { + + function OPMLCreator() { + $this->encoding = "utf-8"; + } + + function createFeed() { + $feed = "encoding."\"?>\n"; + $feed.= $this->_createGeneratorComment(); + $feed.= $this->_createStylesheetReferences(); + $feed.= "\n"; + $feed.= " \n"; + $feed.= " ".htmlspecialchars($this->title)."\n"; + if ($this->pubDate!="") { + $date = new FeedDate($this->pubDate); + $feed.= " ".$date->rfc822()."\n"; + } + if ($this->lastBuildDate!="") { + $date = new FeedDate($this->lastBuildDate); + $feed.= " ".$date->rfc822()."\n"; + } + if ($this->editor!="") { + $feed.= " ".$this->editor."\n"; + } + if ($this->editorEmail!="") { + $feed.= " ".$this->editorEmail."\n"; + } + $feed.= " \n"; + $feed.= " \n"; + for ($i=0;$iitems);$i++) { + $feed.= " items[$i]->title,"\n\r"," "))); + $feed.= " title=\"".$title."\""; + $feed.= " text=\"".$title."\""; + //$feed.= " description=\"".htmlspecialchars($this->items[$i]->description)."\""; + $feed.= " url=\"".htmlspecialchars($this->items[$i]->link)."\""; + $feed.= "/>\n"; + } + $feed.= " \n"; + $feed.= "\n"; + return $feed; + } +} + + + +/** + * HTMLCreator is a FeedCreator that writes an HTML feed file to a specific + * location, overriding the createFeed method of the parent FeedCreator. + * The HTML produced can be included over http by scripting languages, or serve + * as the source for an IFrame. + * All output by this class is embedded in
    tags to enable formatting + * using CSS. + * + * @author Pascal Van Hecke + * @since 1.7 + */ +class HTMLCreator extends FeedCreator { + + var $contentType = "text/html"; + + /** + * Contains HTML to be output at the start of the feed's html representation. + */ + var $header; + + /** + * Contains HTML to be output at the end of the feed's html representation. + */ + var $footer ; + + /** + * Contains HTML to be output between entries. A separator is only used in + * case of multiple entries. + */ + var $separator; + + /** + * Used to prefix the stylenames to make sure they are unique + * and do not clash with stylenames on the users' page. + */ + var $stylePrefix; + + /** + * Determines whether the links open in a new window or not. + */ + var $openInNewWindow = true; + + var $imageAlign ="right"; + + /** + * In case of very simple output you may want to get rid of the style tags, + * hence this variable. There's no equivalent on item level, but of course you can + * add strings to it while iterating over the items ($this->stylelessOutput .= ...) + * and when it is non-empty, ONLY the styleless output is printed, the rest is ignored + * in the function createFeed(). + */ + var $stylelessOutput =""; + + /** + * Writes the HTML. + * @return string the scripts's complete text + */ + function createFeed() { + // if there is styleless output, use the content of this variable and ignore the rest + if ($this->stylelessOutput!="") { + return $this->stylelessOutput; + } + + //if no stylePrefix is set, generate it yourself depending on the script name + if ($this->stylePrefix=="") { + $this->stylePrefix = str_replace(".", "_", $this->_generateFilename())."_"; + } + + //set an openInNewWindow_token_to be inserted or not + if ($this->openInNewWindow) { + $targetInsert = " target='_blank'"; + } + + // use this array to put the lines in and implode later with "document.write" javascript + $feedArray = array(); + if ($this->image!=null) { + $imageStr = "". + "".
+							FeedCreator::iTrunc(htmlspecialchars($this->image->title),100).
+							"image->width) { + $imageStr .=" width='".$this->image->width. "' "; + } + if ($this->image->height) { + $imageStr .=" height='".$this->image->height."' "; + } + $imageStr .="/>"; + $feedArray[] = $imageStr; + } + + if ($this->title) { + $feedArray[] = ""; + } + if ($this->getDescription()) { + $feedArray[] = "
    ". + str_replace("]]>", "", str_replace("getDescription())). + "
    "; + } + + if ($this->header) { + $feedArray[] = "
    ".$this->header."
    "; + } + + for ($i=0;$iitems);$i++) { + if ($this->separator and $i > 0) { + $feedArray[] = "
    ".$this->separator."
    "; + } + + if ($this->items[$i]->title) { + if ($this->items[$i]->link) { + $feedArray[] = + ""; + } else { + $feedArray[] = + "
    ". + FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100). + "
    "; + } + } + if ($this->items[$i]->getDescription()) { + $feedArray[] = + "
    ". + str_replace("]]>", "", str_replace("items[$i]->getDescription())). + "
    "; + } + } + if ($this->footer) { + $feedArray[] = "
    ".$this->footer."
    "; + } + + $feed= "".join($feedArray, "\r\n"); + return $feed; + } + + /** + * Overrrides parent to produce .html extensions + * + * @return string the feed cache filename + * @since 1.4 + * @access private + */ + function _generateFilename() { + $fileInfo = pathinfo($_SERVER["PHP_SELF"]); + return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".html"; + } +} + + +/** + * JSCreator is a class that writes a js file to a specific + * location, overriding the createFeed method of the parent HTMLCreator. + * + * @author Pascal Van Hecke + */ +class JSCreator extends HTMLCreator { + var $contentType = "text/javascript"; + + /** + * writes the javascript + * @return string the scripts's complete text + */ + function createFeed() + { + $feed = parent::createFeed(); + $feedArray = explode("\n",$feed); + + $jsFeed = ""; + foreach ($feedArray as $value) { + $jsFeed .= "document.write('".trim(addslashes($value))."');\n"; + } + return $jsFeed; + } + + /** + * Overrrides parent to produce .js extensions + * + * @return string the feed cache filename + * @since 1.4 + * @access private + */ + function _generateFilename() { + $fileInfo = pathinfo($_SERVER["PHP_SELF"]); + return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".js"; + } + +} + + + +/*** TEST SCRIPT ********************************************************* + +//include("feedcreator.class.php"); + +$rss = new UniversalFeedCreator(); +$rss->useCached(); +$rss->title = "PHP news"; +$rss->description = "daily news from the PHP scripting world"; + +//optional +//$rss->descriptionTruncSize = 500; +//$rss->descriptionHtmlSyndicated = true; +//$rss->xslStyleSheet = "http://feedster.com/rss20.xsl"; + +$rss->link = "http://www.dailyphp.net/news"; +$rss->feedURL = "http://www.dailyphp.net/".$PHP_SELF; + +$image = new FeedImage(); +$image->title = "dailyphp.net logo"; +$image->url = "http://www.dailyphp.net/images/logo.gif"; +$image->link = "http://www.dailyphp.net"; +$image->description = "Feed provided by dailyphp.net. Click to visit."; + +//optional +$image->descriptionTruncSize = 500; +$image->descriptionHtmlSyndicated = true; + +$rss->image = $image; + +// get your news items from somewhere, e.g. your database: +//mysql_select_db($dbHost, $dbUser, $dbPass); +//$res = mysql_query("SELECT * FROM news ORDER BY newsdate DESC"); +//while ($data = mysql_fetch_object($res)) { + $item = new FeedItem(); + $item->title = "This is an the test title of an item"; + $item->link = "http://localhost/item/"; + $item->description = "description in
    HTML"; + + //optional + //item->descriptionTruncSize = 500; + $item->descriptionHtmlSyndicated = true; + + $item->date = time(); + $item->source = "http://www.dailyphp.net"; + $item->author = "John Doe"; + + $rss->addItem($item); +//} + +// valid format strings are: RSS0.91, RSS1.0, RSS2.0, PIE0.1, MBOX, OPML, ATOM0.3, HTML, JS +echo $rss->saveFeed("RSS0.91", "feed.xml"); + + + +***************************************************************************/ +?> \ No newline at end of file diff --git a/include/classes/nusoap.php b/include/classes/nusoap.php new file mode 100644 index 000000000..7db799cc8 --- /dev/null +++ b/include/classes/nusoap.php @@ -0,0 +1,5467 @@ + +* @version $Id$ +* @access public +*/ +class nusoap_base { + + var $title = 'NuSOAP'; + var $version = '0.6.7'; + var $revision = '$Revision: 1.1 $'; + var $error_str = false; + var $debug_str = ''; + // toggles automatic encoding of special characters as entities + // (should always be true, I think) + var $charencoding = true; + + /** + * set schema version + * + * @var XMLSchemaVersion + * @access public + */ + var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema'; + + /** + * set charset encoding for outgoing messages + * + * @var soap_defencoding + * @access public + */ + //var $soap_defencoding = 'UTF-8'; + var $soap_defencoding = 'ISO-8859-1'; + + /** + * load namespace uris into an array of uri => prefix + * + * @var namespaces + * @access public + */ + var $namespaces = array( + 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/', + 'xsd' => 'http://www.w3.org/2001/XMLSchema', + 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/', + 'si' => 'http://soapinterop.org/xsd'); + var $usedNamespaces = array(); + + /** + * load types into typemap array + * is this legacy yet? + * no, this is used by the xmlschema class to verify type => namespace mappings. + * @var typemap + * @access public + */ + var $typemap = array( + 'http://www.w3.org/2001/XMLSchema' => array( + 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double', + 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'', + 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string', + // derived datatypes + 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'', + 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer', + 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer', + 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''), + 'http://www.w3.org/1999/XMLSchema' => array( + 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double', + 'float'=>'double','dateTime'=>'string', + 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'), + 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'), + 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'), + 'http://xml.apache.org/xml-soap' => array('Map') + ); + + /** + * entities to convert + * + * @var xmlEntities + * @access public + */ + var $xmlEntities = array('quot' => '"','amp' => '&', + 'lt' => '<','gt' => '>','apos' => "'"); + + /** + * adds debug data to the class level debug string + * + * @param string $string debug data + * @access private + */ + function debug($string){ + $this->debug_str .= get_class($this).": $string\n"; + } + + /** + * expands entities, e.g. changes '<' to '<'. + * + * @param string $val The string in which to expand entities. + * @access private + */ + function expandEntities($val) { + if ($this->charencoding) { + $val = str_replace('&', '&', $val); + $val = str_replace("'", ''', $val); + $val = str_replace('"', '"', $val); + $val = str_replace('<', '<', $val); + $val = str_replace('>', '>', $val); + } + return $val; + } + + /** + * returns error string if present + * + * @return boolean $string error string + * @access public + */ + function getError(){ + if($this->error_str != ''){ + return $this->error_str; + } + return false; + } + + /** + * sets error string + * + * @return boolean $string error string + * @access private + */ + function setError($str){ + $this->error_str = $str; + } + + /** + * detect if array is a simple array or a struct (associative array) + * + * @param $val The PHP array + * @return string (arraySimple|arrayStruct) + * @access private + */ + function isArraySimpleOrStruct($val) { + $keyList = array_keys($val); + foreach ($keyList as $keyListValue) { + if (!is_int($keyListValue)) { + return 'arrayStruct'; + } + } + return 'arraySimple'; + } + + /** + * serializes PHP values in accordance w/ section 5. Type information is + * not serialized if $use == 'literal'. + * + * @return string + * @access public + */ + function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){ + if(is_object($val) && get_class($val) == 'soapval'){ + return $val->serialize($use); + } + $this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use"); + // if no name, use item + $name = (!$name|| is_numeric($name)) ? 'soapVal' : $name; + // if name has ns, add ns prefix to name + $xmlns = ''; + if($name_ns){ + $prefix = 'nu'.rand(1000,9999); + $name = $prefix.':'.$name; + $xmlns .= " xmlns:$prefix=\"$name_ns\""; + } + // if type is prefixed, create type prefix + if($type_ns != '' && $type_ns == $this->namespaces['xsd']){ + // need to fix this. shouldn't default to xsd if no ns specified + // w/o checking against typemap + $type_prefix = 'xsd'; + } elseif($type_ns){ + $type_prefix = 'ns'.rand(1000,9999); + $xmlns .= " xmlns:$type_prefix=\"$type_ns\""; + } + // serialize attributes if present + $atts = ''; + if($attributes){ + foreach($attributes as $k => $v){ + $atts .= " $k=\"$v\""; + } + } + // serialize if an xsd built-in primitive type + if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){ + if(is_bool($val) && !$val){ + $val = 0; + } else if (is_string($val)) { + $val = $this->expandEntities($val); + } + if ($use == 'literal') { + return "<$name$xmlns>$val"; + } else { + return "<$name$xmlns xsi:type=\"xsd:$type\">$val"; + } + } + // detect type and serialize + $xml = ''; + switch(true) { + case ($type == '' && is_null($val)): + if ($use == 'literal') { + // TODO: depends on nillable + $xml .= "<$name$xmlns/>"; + } else { + $xml .= "<$name$xmlns xsi:nil=\"true\"/>"; + } + break; + case (is_bool($val) || $type == 'boolean'): + if(!$val){ + $val = 0; + } + if ($use == 'literal') { + $xml .= "<$name$xmlns $atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val"; + } + break; + case (is_int($val) || is_long($val) || $type == 'int'): + if ($use == 'literal') { + $xml .= "<$name$xmlns $atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val"; + } + break; + case (is_float($val)|| is_double($val) || $type == 'float'): + if ($use == 'literal') { + $xml .= "<$name$xmlns $atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val"; + } + break; + case (is_string($val) || $type == 'string'): + $val = $this->expandEntities($val); + if ($use == 'literal') { + $xml .= "<$name$xmlns $atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val"; + } + break; + case is_object($val): + $name = get_class($val); + foreach(get_object_vars($val) as $k => $v){ + $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use); + } + $xml .= '<'.$name.'>'.$pXml.''; + break; + break; + case (is_array($val) || $type): + // detect if struct or array + $valueType = $this->isArraySimpleOrStruct($val); + if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){ + $i = 0; + if(is_array($val) && count($val)> 0){ + foreach($val as $v){ + if(is_object($v) && get_class($v) == 'soapval'){ + $tt_ns = $v->type_ns; + $tt = $v->type; + } else { + $tt = gettype($v); + } + $array_types[$tt] = 1; + $xml .= $this->serialize_val($v,'item',false,false,false,false,$use); + ++$i; + } + if(count($array_types) > 1){ + $array_typename = 'xsd:ur-type'; + } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) { + $array_typename = 'xsd:'.$tt; + } elseif($tt == 'array' || $tt == 'Array'){ + $array_typename = 'SOAP-ENC:Array'; + } else { + // if type is prefixed, create type prefix + if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){ + $array_typename = 'xsd:' . $tt; + } elseif ($tt_ns) { + $tt_prefix = 'ns' . rand(1000, 9999); + $array_typename = "$tt_prefix:$tt"; + $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\""; + } else { + $array_typename = $tt; + } + } + $array_type = $i; + if ($use == 'literal') { + $type_str = ''; + } else if (isset($type) && isset($type_prefix)) { + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\""; + } + // empty array + } else { + if ($use == 'literal') { + $type_str = ''; + } else if (isset($type) && isset($type_prefix)) { + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = " xsi:type=\"SOAP-ENC:Array\""; + } + } + $xml = "<$name$xmlns$type_str$atts>".$xml.""; + } else { + // got a struct + if(isset($type) && isset($type_prefix)){ + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = ''; + } + if ($use == 'literal') { + $xml .= "<$name$xmlns $atts>"; + } else { + $xml .= "<$name$xmlns$type_str$atts>"; + } + foreach($val as $k => $v){ + // Apache Map + if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') { + $xml .= ''; + $xml .= $this->serialize_val($k,'key',false,false,false,false,$use); + $xml .= $this->serialize_val($v,'value',false,false,false,false,$use); + $xml .= ''; + } else { + $xml .= $this->serialize_val($v,$k,false,false,false,false,$use); + } + } + $xml .= ""; + } + break; + default: + $xml .= 'not detected, got '.gettype($val).' for '.$val; + break; + } + return $xml; + } + + /** + * serialize message + * + * @param string body + * @param string headers optional + * @param array namespaces optional + * @param string style optional (rpc|document) + * @param string use optional (encoded|literal) + * @return string message + * @access public + */ + function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded'){ + // TODO: add an option to automatically run utf8_encode on $body and $headers + // if $this->soap_defencoding is UTF-8. Not doing this automatically allows + // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1 + + // serialize namespaces + $ns_string = ''; + foreach(array_merge($this->namespaces,$namespaces) as $k => $v){ + $ns_string .= " xmlns:$k=\"$v\""; + } + if($style == 'rpc' && $use == 'encoded') { + $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string; + } + + // serialize headers + if($headers){ + $headers = "".$headers.""; + } + // serialize envelope + return + 'soap_defencoding .'"?'.">". + '". + $headers. + "". + $body. + "". + ""; + } + + function formatDump($str){ + $str = htmlspecialchars($str); + return nl2br($str); + } + + /** + * contracts a qualified name + * + * @param string $string qname + * @return string contracted qname + * @access private + */ + function contractQname($qname){ + // get element namespace + //$this->xdebug("Contract $qname"); + if (strrpos($qname, ':')) { + // get unqualified name + $name = substr($qname, strrpos($qname, ':') + 1); + // get ns + $ns = substr($qname, 0, strrpos($qname, ':')); + $p = $this->getPrefixFromNamespace($ns); + if ($p) { + return $p . ':' . $name; + } + return $qname; + } else { + return $qname; + } + } + + /** + * expands a qualified name + * + * @param string $string qname + * @return string expanded qname + * @access private + */ + function expandQname($qname){ + // get element prefix + if(strpos($qname,':') && !ereg('^http://',$qname)){ + // get unqualified name + $name = substr(strstr($qname,':'),1); + // get ns prefix + $prefix = substr($qname,0,strpos($qname,':')); + if(isset($this->namespaces[$prefix])){ + return $this->namespaces[$prefix].':'.$name; + } else { + return $qname; + } + } else { + return $qname; + } + } + + /** + * returns the local part of a prefixed string + * returns the original string, if not prefixed + * + * @param string + * @return string + * @access public + */ + function getLocalPart($str){ + if($sstr = strrchr($str,':')){ + // get unqualified name + return substr( $sstr, 1 ); + } else { + return $str; + } + } + + /** + * returns the prefix part of a prefixed string + * returns false, if not prefixed + * + * @param string + * @return mixed + * @access public + */ + function getPrefix($str){ + if($pos = strrpos($str,':')){ + // get prefix + return substr($str,0,$pos); + } + return false; + } + + /** + * pass it a prefix, it returns a namespace + * returns false if no namespace registered with the given prefix + * + * @param string + * @return mixed + * @access public + */ + function getNamespaceFromPrefix($prefix){ + if (isset($this->namespaces[$prefix])) { + return $this->namespaces[$prefix]; + } + //$this->setError("No namespace registered for prefix '$prefix'"); + return false; + } + + /** + * returns the prefix for a given namespace (or prefix) + * or false if no prefixes registered for the given namespace + * + * @param string + * @return mixed + * @access public + */ + function getPrefixFromNamespace($ns) { + foreach ($this->namespaces as $p => $n) { + if ($ns == $n || $ns == $p) { + $this->usedNamespaces[$p] = $n; + return $p; + } + } + return false; + } + + function varDump($data) { + ob_start(); + var_dump($data); + $ret_val = ob_get_contents(); + ob_end_clean(); + return $ret_val; + } +} + +// XML Schema Datatype Helper Functions + +//xsd:dateTime helpers + +/** +* convert unix timestamp to ISO 8601 compliant date string +* +* @param string $timestamp Unix time stamp +* @access public +*/ +function timestamp_to_iso8601($timestamp,$utc=true){ + $datestr = date('Y-m-d\TH:i:sO',$timestamp); + if($utc){ + $eregStr = + '([0-9]{4})-'. // centuries & years CCYY- + '([0-9]{2})-'. // months MM- + '([0-9]{2})'. // days DD + 'T'. // separator T + '([0-9]{2}):'. // hours hh: + '([0-9]{2}):'. // minutes mm: + '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss... + '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's + + if(ereg($eregStr,$datestr,$regs)){ + return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]); + } + return false; + } else { + return $datestr; + } +} + +/** +* convert ISO 8601 compliant date string to unix timestamp +* +* @param string $datestr ISO 8601 compliant date string +* @access public +*/ +function iso8601_to_timestamp($datestr){ + $eregStr = + '([0-9]{4})-'. // centuries & years CCYY- + '([0-9]{2})-'. // months MM- + '([0-9]{2})'. // days DD + 'T'. // separator T + '([0-9]{2}):'. // hours hh: + '([0-9]{2}):'. // minutes mm: + '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss... + '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's + if(ereg($eregStr,$datestr,$regs)){ + // not utc + if($regs[8] != 'Z'){ + $op = substr($regs[8],0,1); + $h = substr($regs[8],1,2); + $m = substr($regs[8],strlen($regs[8])-2,2); + if($op == '-'){ + $regs[4] = $regs[4] + $h; + $regs[5] = $regs[5] + $m; + } elseif($op == '+'){ + $regs[4] = $regs[4] - $h; + $regs[5] = $regs[5] - $m; + } + } + return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z"); + } else { + return false; + } +} + +function usleepWindows($usec) +{ + $start = gettimeofday(); + + do + { + $stop = gettimeofday(); + $timePassed = 1000000 * ($stop['sec'] - $start['sec']) + + $stop['usec'] - $start['usec']; + } + while ($timePassed < $usec); +} + +?> +* @version $Id$ +* @access public +*/ +class soap_fault extends nusoap_base { + + var $faultcode; + var $faultactor; + var $faultstring; + var $faultdetail; + + /** + * constructor + * + * @param string $faultcode (client | server) + * @param string $faultactor only used when msg routed between multiple actors + * @param string $faultstring human readable error message + * @param string $faultdetail + */ + function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){ + $this->faultcode = $faultcode; + $this->faultactor = $faultactor; + $this->faultstring = $faultstring; + $this->faultdetail = $faultdetail; + } + + /** + * serialize a fault + * + * @access public + */ + function serialize(){ + $ns_string = ''; + foreach($this->namespaces as $k => $v){ + $ns_string .= "\n xmlns:$k=\"$v\""; + } + $return_msg = + 'soap_defencoding.'"?>'. + '\n". + ''. + ''. + ''.$this->expandEntities($this->faultcode).''. + ''.$this->expandEntities($this->faultactor).''. + ''.$this->expandEntities($this->faultstring).''. + ''.$this->serialize_val($this->faultdetail).''. + ''. + ''. + ''; + return $return_msg; + } +} + + + +?> +* @version $Id$ +* @access public +*/ +class XMLSchema extends nusoap_base { + + // files + var $schema = ''; + var $xml = ''; + // namespaces + var $enclosingNamespaces; + // schema info + var $schemaInfo = array(); + var $schemaTargetNamespace = ''; + // types, elements, attributes defined by the schema + var $attributes = array(); + var $complexTypes = array(); + var $currentComplexType = false; + var $elements = array(); + var $currentElement = false; + var $simpleTypes = array(); + var $currentSimpleType = false; + // imports + var $imports = array(); + // parser vars + var $parser; + var $position = 0; + var $depth = 0; + var $depth_array = array(); + var $message = array(); + var $defaultNamespace = array(); + + /** + * constructor + * + * @param string $schema schema document URI + * @param string $xml xml document URI + * @param string $namespaces namespaces defined in enclosing XML + * @access public + */ + function XMLSchema($schema='',$xml='',$namespaces=array()){ + + $this->debug('xmlschema class instantiated, inside constructor'); + // files + $this->schema = $schema; + $this->xml = $xml; + + // namespaces + $this->enclosingNamespaces = $namespaces; + $this->namespaces = array_merge($this->namespaces, $namespaces); + + // parse schema file + if($schema != ''){ + $this->debug('initial schema file: '.$schema); + $this->parseFile($schema, 'schema'); + } + + // parse xml file + if($xml != ''){ + $this->debug('initial xml file: '.$xml); + $this->parseFile($xml, 'xml'); + } + + } + + /** + * parse an XML file + * + * @param string $xml, path/URL to XML file + * @param string $type, (schema | xml) + * @return boolean + * @access public + */ + function parseFile($xml,$type){ + // parse xml file + if($xml != ""){ + $xmlStr = @join("",@file($xml)); + if($xmlStr == ""){ + $msg = 'Error reading XML from '.$xml; + $this->setError($msg); + $this->debug($msg); + return false; + } else { + $this->debug("parsing $xml"); + $this->parseString($xmlStr,$type); + $this->debug("done parsing $xml"); + return true; + } + } + return false; + } + + /** + * parse an XML string + * + * @param string $xml path or URL + * @param string $type, (schema|xml) + * @access private + */ + function parseString($xml,$type){ + // parse xml string + if($xml != ""){ + + // Create an XML parser. + $this->parser = xml_parser_create(); + // Set the options for parsing the XML data. + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + + // Set the object for the parser. + xml_set_object($this->parser, $this); + + // Set the element handlers for the parser. + if($type == "schema"){ + xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement'); + xml_set_character_data_handler($this->parser,'schemaCharacterData'); + } elseif($type == "xml"){ + xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement'); + xml_set_character_data_handler($this->parser,'xmlCharacterData'); + } + + // Parse the XML file. + if(!xml_parse($this->parser,$xml,true)){ + // Display an error message. + $errstr = sprintf('XML error parsing XML schema on line %d: %s', + xml_get_current_line_number($this->parser), + xml_error_string(xml_get_error_code($this->parser)) + ); + $this->debug($errstr); + $this->debug("XML payload:\n" . $xml); + $this->setError($errstr); + } + + xml_parser_free($this->parser); + } else{ + $this->debug('no xml passed to parseString()!!'); + $this->setError('no xml passed to parseString()!!'); + } + } + + /** + * start-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @param string $attrs associative array of attributes + * @access private + */ + function schemaStartElement($parser, $name, $attrs) { + + // position in the total number of elements, starting from 0 + $pos = $this->position++; + $depth = $this->depth++; + // set self as current value for this depth + $this->depth_array[$depth] = $pos; + $this->message[$pos] = array('cdata' => ''); + if ($depth > 0) { + $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]]; + } else { + $this->defaultNamespace[$pos] = false; + } + + // get element prefix + if($prefix = $this->getPrefix($name)){ + // get unqualified name + $name = $this->getLocalPart($name); + } else { + $prefix = ''; + } + + // loop thru attributes, expanding, and registering namespace declarations + if(count($attrs) > 0){ + foreach($attrs as $k => $v){ + // if ns declarations, add to class level array of valid namespaces + if(ereg("^xmlns",$k)){ + //$this->xdebug("$k: $v"); + //$this->xdebug('ns_prefix: '.$this->getPrefix($k)); + if($ns_prefix = substr(strrchr($k,':'),1)){ + //$this->xdebug("Add namespace[$ns_prefix] = $v"); + $this->namespaces[$ns_prefix] = $v; + } else { + $this->defaultNamespace[$pos] = $v; + if (! $this->getPrefixFromNamespace($v)) { + $this->namespaces['ns'.(count($this->namespaces)+1)] = $v; + } + } + if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema'){ + $this->XMLSchemaVersion = $v; + $this->namespaces['xsi'] = $v.'-instance'; + } + } + } + foreach($attrs as $k => $v){ + // expand each attribute + $k = strpos($k,':') ? $this->expandQname($k) : $k; + $v = strpos($v,':') ? $this->expandQname($v) : $v; + $eAttrs[$k] = $v; + } + $attrs = $eAttrs; + } else { + $attrs = array(); + } + // find status, register data + switch($name){ + case 'all': + case 'choice': + case 'sequence': + //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement"); + $this->complexTypes[$this->currentComplexType]['compositor'] = $name; + if($name == 'all' || $name == 'sequence'){ + $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; + } + break; + case 'attribute': + //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']); + $this->xdebug("parsing attribute " . $this->varDump($attrs)); + if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { + $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + if (!strpos($v, ':')) { + // no namespace in arrayType attribute value... + if ($this->defaultNamespace[$pos]) { + // ...so use the default + $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + } + } + } + if(isset($attrs['name'])){ + $this->attributes[$attrs['name']] = $attrs; + $aname = $attrs['name']; + } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){ + if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { + $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + } else { + $aname = ''; + } + } elseif(isset($attrs['ref'])){ + $aname = $attrs['ref']; + $this->attributes[$attrs['ref']] = $attrs; + } + + if(isset($this->currentComplexType)){ + $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs; + } elseif(isset($this->currentElement)){ + $this->elements[$this->currentElement]['attrs'][$aname] = $attrs; + } + // arrayType attribute + if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){ + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + $prefix = $this->getPrefix($aname); + if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){ + $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + } else { + $v = ''; + } + if(strpos($v,'[,]')){ + $this->complexTypes[$this->currentComplexType]['multidimensional'] = true; + } + $v = substr($v,0,strpos($v,'[')); // clip the [] + if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){ + $v = $this->XMLSchemaVersion.':'.$v; + } + $this->complexTypes[$this->currentComplexType]['arrayType'] = $v; + } + break; + case 'complexType': + if(isset($attrs['name'])){ + $this->xdebug('processing named complexType '.$attrs['name']); + $this->currentElement = false; + $this->currentComplexType = $attrs['name']; + $this->complexTypes[$this->currentComplexType] = $attrs; + $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; + if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){ + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + } else { + $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; + } + }else{ + $this->xdebug('processing unnamed complexType for element '.$this->currentElement); + $this->currentComplexType = $this->currentElement . '_ContainedType'; + $this->currentElement = false; + $this->complexTypes[$this->currentComplexType] = $attrs; + $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; + if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){ + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + } else { + $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; + } + } + break; + case 'element': + // elements defined as part of a complex type should + // not really be added to $this->elements, but for some + // reason, they are + if(isset($attrs['type'])){ + $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']); + $this->currentElement = $attrs['name']; + $this->elements[ $attrs['name'] ] = $attrs; + $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; + if (!isset($this->elements[ $attrs['name'] ]['form'])) { + $this->elements[ $attrs['name'] ]['form'] = $this->schemaInfo['elementFormDefault']; + } + $ename = $attrs['name']; + } elseif(isset($attrs['ref'])){ + $ename = $attrs['ref']; + } else { + $this->xdebug("processing untyped element ".$attrs['name']); + $this->currentElement = $attrs['name']; + $this->elements[ $attrs['name'] ] = $attrs; + $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; + $this->elements[ $attrs['name'] ]['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType'; + if (!isset($this->elements[ $attrs['name'] ]['form'])) { + $this->elements[ $attrs['name'] ]['form'] = $this->schemaInfo['elementFormDefault']; + } + } + if(isset($ename) && $this->currentComplexType){ + $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs; + } + break; + // we ignore enumeration values + //case 'enumeration': + //break; + case 'import': + if (isset($attrs['schemaLocation'])) { + //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']); + $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false); + } else { + //$this->xdebug('import namespace ' . $attrs['namespace']); + $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true); + if (! $this->getPrefixFromNamespace($attrs['namespace'])) { + $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace']; + } + } + break; + case 'restriction': + //$this->xdebug("in restriction for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement"); + if($this->currentElement){ + $this->elements[$this->currentElement]['type'] = $attrs['base']; + } elseif($this->currentSimpleType){ + $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base']; + } elseif($this->currentComplexType){ + $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base']; + if(strstr($attrs['base'],':') == ':Array'){ + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + } + } + break; + case 'schema': + $this->schemaInfo = $attrs; + $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix); + if (isset($attrs['targetNamespace'])) { + $this->schemaTargetNamespace = $attrs['targetNamespace']; + } + if (!isset($attrs['elementFormDefault'])) { + $this->schemaInfo['elementFormDefault'] = 'unqualified'; + } + break; + case 'simpleType': + if(isset($attrs['name'])){ + $this->xdebug("processing simpleType for name " . $attrs['name']); + $this->currentSimpleType = $attrs['name']; + $this->simpleTypes[ $attrs['name'] ] = $attrs; + $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType'; + $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar'; + } else { + //echo 'not parsing: '.$name; + //var_dump($attrs); + } + break; + default: + //$this->xdebug("do not have anything to do for element $name"); + } + } + + /** + * end-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @access private + */ + function schemaEndElement($parser, $name) { + // bring depth down a notch + $this->depth--; + // position of current element is equal to the last value left in depth_array for my depth + if(isset($this->depth_array[$this->depth])){ + $pos = $this->depth_array[$this->depth]; + } + // move on... + if($name == 'complexType'){ + $this->currentComplexType = false; + $this->currentElement = false; + } + if($name == 'element'){ + $this->currentElement = false; + } + if($name == 'simpleType'){ + $this->currentSimpleType = false; + } + } + + /** + * element content handler + * + * @param string $parser XML parser object + * @param string $data element content + * @access private + */ + function schemaCharacterData($parser, $data){ + $pos = $this->depth_array[$this->depth - 1]; + $this->message[$pos]['cdata'] .= $data; + } + + /** + * serialize the schema + * + * @access public + */ + function serializeSchema(){ + + $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion); + $xml = ''; + // imports + if (sizeof($this->imports) > 0) { + foreach($this->imports as $ns => $list) { + foreach ($list as $ii) { + if ($ii['location'] != '') { + $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n"; + } else { + $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n"; + } + } + } + } + // complex types + foreach($this->complexTypes as $typeName => $attrs){ + $contentStr = ''; + // serialize child elements + if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){ + foreach($attrs['elements'] as $element => $eParts){ + if(isset($eParts['ref'])){ + $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n"; + } else { + $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"/>\n"; + } + } + } + // attributes + if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){ + foreach($attrs['attrs'] as $attr => $aParts){ + $contentStr .= " <$schemaPrefix:attribute ref=\"".$this->contractQName($aParts['ref']).'"'; + if(isset($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType'])){ + $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl']; + $contentStr .= ' wsdl:arrayType="'.$this->contractQName($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType']).'"'; + } + $contentStr .= "/>\n"; + } + } + // if restriction + if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){ + $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." \n"; + } + // compositor obviates complex/simple content + if(isset($attrs['compositor']) && ($attrs['compositor'] != '')){ + $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." \n"; + } + // complex or simple content + elseif((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){ + $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." \n"; + } + // finalize complex type + if($contentStr != ''){ + $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." \n"; + } else { + $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n"; + } + $xml .= $contentStr; + } + // simple types + if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){ + foreach($this->simpleTypes as $typeName => $attr){ + $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n contractQName($eParts['type'])."\"/>\n "; + } + } + // elements + if(isset($this->elements) && count($this->elements) > 0){ + foreach($this->elements as $element => $eParts){ + $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n"; + } + } + // attributes + if(isset($this->attributes) && count($this->attributes) > 0){ + foreach($this->attributes as $attr => $aParts){ + $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>"; + } + } + // finish 'er up + $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n"; + foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) { + $el .= " xmlns:$nsp=\"$ns\"\n"; + } + $xml = $el . ">\n".$xml."\n"; + return $xml; + } + + /** + * adds debug data to the clas level debug string + * + * @param string $string debug data + * @access private + */ + function xdebug($string){ + $this->debug('<' . $this->schemaTargetNamespace . '> '.$string); + } + + /** + * get the PHP type of a user defined type in the schema + * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays + * returns false if no type exists, or not w/ the given namespace + * else returns a string that is either a native php type, or 'struct' + * + * @param string $type, name of defined type + * @param string $ns, namespace of type + * @return mixed + * @access public + */ + function getPHPType($type,$ns){ + if(isset($this->typemap[$ns][$type])){ + //print "found type '$type' and ns $ns in typemap
    "; + return $this->typemap[$ns][$type]; + } elseif(isset($this->complexTypes[$type])){ + //print "getting type '$type' and ns $ns from complexTypes array
    "; + return $this->complexTypes[$type]['phpType']; + } + return false; + } + + /** + * returns an array of information about a given type + * returns false if no type exists by the given name + * + * typeDef = array( + * 'elements' => array(), // refs to elements array + * 'restrictionBase' => '', + * 'phpType' => '', + * 'order' => '(sequence|all)', + * 'attrs' => array() // refs to attributes array + * ) + * + * @param string + * @return mixed + * @access public + */ + function getTypeDef($type){ + //$this->debug("in getTypeDef for type $type"); + if(isset($this->complexTypes[$type])){ + $this->xdebug("in getTypeDef, found complexType $type"); + return $this->complexTypes[$type]; + } elseif(isset($this->simpleTypes[$type])){ + $this->xdebug("in getTypeDef, found simpleType $type"); + if (!isset($this->simpleTypes[$type]['phpType'])) { + // get info for type to tack onto the simple type + // TODO: can this ever really apply (i.e. what is a simpleType really?) + $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1); + $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':')); + $etype = $this->getTypeDef($uqType); + if ($etype) { + if (isset($etype['phpType'])) { + $this->simpleTypes[$type]['phpType'] = $etype['phpType']; + } + if (isset($etype['elements'])) { + $this->simpleTypes[$type]['elements'] = $etype['elements']; + } + } + } + return $this->simpleTypes[$type]; + } elseif(isset($this->elements[$type])){ + $this->xdebug("in getTypeDef, found element $type"); + if (!isset($this->elements[$type]['phpType'])) { + // get info for type to tack onto the element + $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1); + $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':')); + $etype = $this->getTypeDef($uqType); + if ($etype) { + if (isset($etype['phpType'])) { + $this->elements[$type]['phpType'] = $etype['phpType']; + } + if (isset($etype['elements'])) { + $this->elements[$type]['elements'] = $etype['elements']; + } + } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') { + $this->elements[$type]['phpType'] = 'scalar'; + } + } + return $this->elements[$type]; + } elseif(isset($this->attributes[$type])){ + $this->xdebug("in getTypeDef, found attribute $type"); + return $this->attributes[$type]; + } + $this->xdebug("in getTypeDef, did not find $type"); + return false; + } + + /** + * returns a sample serialization of a given type, or false if no type by the given name + * + * @param string $type, name of type + * @return mixed + * @access public + */ + function serializeTypeDef($type){ + //print "in sTD() for type $type
    "; + if($typeDef = $this->getTypeDef($type)){ + $str .= '<'.$type; + if(is_array($typeDef['attrs'])){ + foreach($attrs as $attName => $data){ + $str .= " $attName=\"{type = ".$data['type']."}\""; + } + } + $str .= " xmlns=\"".$this->schema['targetNamespace']."\""; + if(count($typeDef['elements']) > 0){ + $str .= ">"; + foreach($typeDef['elements'] as $element => $eData){ + $str .= $this->serializeTypeDef($element); + } + $str .= ""; + } elseif($typeDef['typeClass'] == 'element') { + $str .= ">"; + } else { + $str .= "/>"; + } + return $str; + } + return false; + } + + /** + * returns HTML form elements that allow a user + * to enter values for creating an instance of the given type. + * + * @param string $name, name for type instance + * @param string $type, name of type + * @return string + * @access public + */ + function typeToForm($name,$type){ + // get typedef + if($typeDef = $this->getTypeDef($type)){ + // if struct + if($typeDef['phpType'] == 'struct'){ + $buffer .= ''; + foreach($typeDef['elements'] as $child => $childDef){ + $buffer .= " + + "; + } + $buffer .= '
    $childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):
    '; + // if array + } elseif($typeDef['phpType'] == 'array'){ + $buffer .= ''; + for($i=0;$i < 3; $i++){ + $buffer .= " + + "; + } + $buffer .= '
    array item (type: $typeDef[arrayType]):
    '; + // if scalar + } else { + $buffer .= ""; + } + } else { + $buffer .= ""; + } + return $buffer; + } + + /** + * adds a complex type to the schema + * + * example: array + * + * addType( + * 'ArrayOfstring', + * 'complexType', + * 'array', + * '', + * 'SOAP-ENC:Array', + * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'), + * 'xsd:string' + * ); + * + * example: PHP associative array ( SOAP Struct ) + * + * addType( + * 'SOAPStruct', + * 'complexType', + * 'struct', + * 'all', + * array('myVar'=> array('name'=>'myVar','type'=>'string') + * ); + * + * @param name + * @param typeClass (complexType|simpleType|attribute) + * @param phpType: currently supported are array and struct (php assoc array) + * @param compositor (all|sequence|choice) + * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param elements = array ( name = array(name=>'',type=>'') ) + * @param attrs = array( + * array( + * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType", + * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]" + * ) + * ) + * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string) + * + */ + function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){ + $this->complexTypes[$name] = array( + 'name' => $name, + 'typeClass' => $typeClass, + 'phpType' => $phpType, + 'compositor'=> $compositor, + 'restrictionBase' => $restrictionBase, + 'elements' => $elements, + 'attrs' => $attrs, + 'arrayType' => $arrayType + ); + + $this->xdebug("addComplexType $name: " . $this->varDump($this->complexTypes[$name])); + } + + /** + * adds a simple type to the schema + * + * @param name + * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param typeClass (simpleType) + * @param phpType: (scalar) + * @see xmlschema + * + */ + function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar') { + $this->simpleTypes[$name] = array( + 'name' => $name, + 'typeClass' => $typeClass, + 'phpType' => $phpType, + 'type' => $restrictionBase + ); + + $this->xdebug("addSimpleType $name: " . $this->varDump($this->simpleTypes[$name])); + } +} + + + +?> +* @version $Id$ +* @access public +*/ +class soapval extends nusoap_base { + /** + * constructor + * + * @param string $name optional name + * @param string $type optional type name + * @param mixed $value optional value + * @param string $namespace optional namespace of value + * @param string $type_namespace optional namespace of type + * @param array $attributes associative array of attributes to add to element serialization + * @access public + */ + function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) { + $this->name = $name; + $this->value = $value; + $this->type = $type; + $this->element_ns = $element_ns; + $this->type_ns = $type_ns; + $this->attributes = $attributes; + } + + /** + * return serialized value + * + * @return string XML data + * @access private + */ + function serialize($use='encoded') { + return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use); + } + + /** + * decodes a soapval object into a PHP native type + * + * @param object $soapval optional SOAPx4 soapval object, else uses self + * @return mixed + * @access public + */ + function decode(){ + return $this->value; + } +} + + + +?> +* @version $Id$ +* @access public +*/ +class soap_transport_http extends nusoap_base { + + var $url = ''; + var $uri = ''; + var $scheme = ''; + var $host = ''; + var $port = ''; + var $path = ''; + var $request_method = 'POST'; + var $protocol_version = '1.0'; + var $encoding = ''; + var $outgoing_headers = array(); + var $incoming_headers = array(); + var $outgoing_payload = ''; + var $incoming_payload = ''; + var $useSOAPAction = true; + var $persistentConnection = false; + var $ch = false; // cURL handle + var $username; + var $password; + + /** + * constructor + */ + function soap_transport_http($url){ + $this->url = $url; + + $u = parse_url($url); + foreach($u as $k => $v){ + $this->debug("$k = $v"); + $this->$k = $v; + } + + // add any GET params to path + if(isset($u['query']) && $u['query'] != ''){ + $this->path .= '?' . $u['query']; + } + + // set default port + if(!isset($u['port'])){ + if($u['scheme'] == 'https'){ + $this->port = 443; + } else { + $this->port = 80; + } + } + + $this->uri = $this->path; + + // build headers + ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev); + $this->outgoing_headers['User-Agent'] = $this->title.'/'.$this->version.' ('.$rev[1].')'; + if (!isset($u['port'])) { + $this->outgoing_headers['Host'] = $this->host; + } else { + $this->outgoing_headers['Host'] = $this->host.':'.$this->port; + } + + if (isset($u['user']) && $u['user'] != '') { + $this->setCredentials($u['user'], isset($u['pass']) ? $u['pass'] : ''); + } + } + + function connect($connection_timeout=0,$response_timeout=30){ + // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like + // "regular" socket. + // TODO: disabled for now because OpenSSL must be *compiled* in (not just + // loaded), and until PHP5 stream_get_wrappers is not available. +// if ($this->scheme == 'https') { +// if (version_compare(phpversion(), '4.3.0') >= 0) { +// if (extension_loaded('openssl')) { +// $this->scheme = 'ssl'; +// $this->debug('Using SSL over OpenSSL'); +// } +// } +// } + if ($this->scheme == 'http' || $this->scheme == 'ssl') { + // use persistent connection + if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){ + if (!feof($this->fp)) { + $this->debug('Re-use persistent connection'); + return true; + } + fclose($this->fp); + $this->debug('Closed persistent connection at EOF'); + } + + // munge host if using OpenSSL + if ($this->scheme == 'ssl') { + $host = 'ssl://' . $this->host; + } else { + $host = $this->host; + } + $this->debug('calling fsockopen with host ' . $host); + + // open socket + if($connection_timeout > 0){ + $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout); + } else { + $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str); + } + + // test pointer + if(!$this->fp) { + $this->debug('Couldn\'t open socket connection to server '.$this->url.', Error ('.$this->errno.'): '.$this->error_str); + $this->setError('Couldn\'t open socket connection to server: '.$this->url.', Error ('.$this->errno.'): '.$this->error_str); + return false; + } + + // set response timeout + socket_set_timeout( $this->fp, $response_timeout); + + $this->debug('socket connected'); + return true; + } else if ($this->scheme == 'https') { + if (!extension_loaded('curl')) { + $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS'); + return false; + } + $this->debug('connect using https'); + // init CURL + $this->ch = curl_init(); + // set url + $hostURL = ($this->port != '') ? "https://$this->host:$this->port" : "https://$this->host"; + // add path + $hostURL .= $this->path; + curl_setopt($this->ch, CURLOPT_URL, $hostURL); + // ask for headers in the response output + curl_setopt($this->ch, CURLOPT_HEADER, 1); + // ask for the response output as the return value + curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1); + // encode + // We manage this ourselves through headers and encoding +// if(function_exists('gzuncompress')){ +// curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate'); +// } + // persistent connection + if ($this->persistentConnection) { + // The way we send data, we cannot use persistent connections, since + // there will be some "junk" at the end of our request. + //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true); + $this->persistentConnection = false; + $this->outgoing_headers['Connection'] = 'close'; + } + // set timeout (NOTE: cURL does not have separate connection and response timeouts) + if ($connection_timeout != 0) { + curl_setopt($this->ch, CURLOPT_TIMEOUT, $connection_timeout); + } + + // recent versions of cURL turn on peer/host checking by default, + // while PHP binaries are not compiled with a default location for the + // CA cert bundle, so disable peer/host checking. +//curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt'); + curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0); + + /* + TODO: support client certificates (thanks Tobias Boes) + curl_setopt($this->ch, CURLOPT_CAINFO, '$pathToPemFiles/rootca.pem'); + curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1); + curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1); + curl_setopt($this->ch, CURLOPT_SSLCERT, '$pathToPemFiles/mycert.pem'); + curl_setopt($this->ch, CURLOPT_SSLKEY, '$pathToPemFiles/mykey.pem'); + */ + $this->debug('cURL connection set up'); + return true; + } else { + $this->setError('Unknown scheme ' . $this->scheme); + $this->debug('Unknown scheme ' . $this->scheme); + return false; + } + } + + /** + * send the SOAP message via HTTP + * + * @param string $data message data + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @return string data + * @access public + */ + function send($data, $timeout=0, $response_timeout=30) { + + $this->debug('entered send() with data of length: '.strlen($data)); + + $this->tryagain = true; + $tries = 0; + while ($this->tryagain) { + $this->tryagain = false; + if ($tries++ < 2) { + // make connnection + if (!$this->connect($timeout, $response_timeout)){ + return false; + } + + // send request + if (!$this->sendRequest($data)){ + return false; + } + + // get response + $respdata = $this->getResponse(); + } else { + $this->setError('Too many tries to get an OK response'); + } + } + $this->debug('end of send()'); + return $respdata; + } + + + /** + * send the SOAP message via HTTPS 1.0 using CURL + * + * @param string $msg message data + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @return string data + * @access public + */ + function sendHTTPS($data, $timeout=0, $response_timeout=30) { + return $this->send($data, $timeout, $response_timeout); + } + + /** + * if authenticating, set user credentials here + * + * @param string $username + * @param string $password + * @param string $authtype + * @param array $digestRequest + * @access public + */ + function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array()) { + global $_SERVER; + + // cf. RFC 2617 + if ($authtype == 'basic') { + $this->outgoing_headers['Authorization'] = 'Basic '.base64_encode($username.':'.$password); + } elseif ($authtype == 'digest') { + if (isset($digestRequest['nonce'])) { + $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1; + + // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html) + + // A1 = unq(username-value) ":" unq(realm-value) ":" passwd + $A1 = $username. ':' . $digestRequest['realm'] . ':' . $password; + + // H(A1) = MD5(A1) + $HA1 = md5($A1); + + // A2 = Method ":" digest-uri-value + $A2 = 'POST:' . $this->uri; + + // H(A2) + $HA2 = md5($A2); + + // KD(secret, data) = H(concat(secret, ":", data)) + // if qop == auth: + // request-digest = <"> < KD ( H(A1), unq(nonce-value) + // ":" nc-value + // ":" unq(cnonce-value) + // ":" unq(qop-value) + // ":" H(A2) + // ) <"> + // if qop is missing, + // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <"> + + $unhashedDigest = ''; + $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : ''; + $cnonce = $nonce; + if ($digestRequest['qop'] != '') { + $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2; + } else { + $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2; + } + + $hashedDigest = md5($unhashedDigest); + + $this->outgoing_headers['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->uri . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"'; + } + } + $this->username = $username; + $this->password = $password; + $this->authtype = $authtype; + $this->digestRequest = $digestRequest; + } + + /** + * set the soapaction value + * + * @param string $soapaction + * @access public + */ + function setSOAPAction($soapaction) { + $this->outgoing_headers['SOAPAction'] = '"' . $soapaction . '"'; + } + + /** + * use http encoding + * + * @param string $enc encoding style. supported values: gzip, deflate, or both + * @access public + */ + function setEncoding($enc='gzip, deflate'){ + $this->protocol_version = '1.1'; + $this->outgoing_headers['Accept-Encoding'] = $enc; + $this->outgoing_headers['Connection'] = 'close'; + $this->persistentConnection = false; + set_magic_quotes_runtime(0); + // deprecated + $this->encoding = $enc; + } + + /** + * set proxy info here + * + * @param string $proxyhost + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @access public + */ + function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') { + $this->uri = $this->url; + $this->host = $proxyhost; + $this->port = $proxyport; + if ($proxyusername != '' && $proxypassword != '') { + $this->outgoing_headers['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword); + } + } + + /** + * decode a string that is encoded w/ "chunked' transfer encoding + * as defined in RFC2068 19.4.6 + * + * @param string $buffer + * @param string $lb + * @returns string + * @access public + */ + function decodeChunked($buffer, $lb){ + // length := 0 + $length = 0; + $new = ''; + + // read chunk-size, chunk-extension (if any) and CRLF + // get the position of the linebreak + $chunkend = strpos($buffer, $lb); + if ($chunkend == FALSE) { + $this->debug('no linebreak found in decodeChunked'); + return $new; + } + $temp = substr($buffer,0,$chunkend); + $chunk_size = hexdec( trim($temp) ); + $chunkstart = $chunkend + strlen($lb); + // while (chunk-size > 0) { + while ($chunk_size > 0) { + $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size"); + $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size); + + // Just in case we got a broken connection + if ($chunkend == FALSE) { + $chunk = substr($buffer,$chunkstart); + // append chunk-data to entity-body + $new .= $chunk; + $length += strlen($chunk); + break; + } + + // read chunk-data and CRLF + $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart); + // append chunk-data to entity-body + $new .= $chunk; + // length := length + chunk-size + $length += strlen($chunk); + // read chunk-size and CRLF + $chunkstart = $chunkend + strlen($lb); + + $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb); + if ($chunkend == FALSE) { + break; //Just in case we got a broken connection + } + $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart); + $chunk_size = hexdec( trim($temp) ); + $chunkstart = $chunkend; + } + return $new; + } + + /* + * Writes payload, including HTTP headers, to $this->outgoing_payload. + */ + function buildPayload($data) { + // add content-length header + $this->outgoing_headers['Content-Length'] = strlen($data); + + // start building outgoing payload: + $this->outgoing_payload = "$this->request_method $this->uri HTTP/$this->protocol_version\r\n"; + + // loop thru headers, serializing + foreach($this->outgoing_headers as $k => $v){ + $this->outgoing_payload .= $k.': '.$v."\r\n"; + } + + // header/body separator + $this->outgoing_payload .= "\r\n"; + + // add data + $this->outgoing_payload .= $data; + } + + function sendRequest($data){ + // build payload + $this->buildPayload($data); + + if ($this->scheme == 'http' || $this->scheme == 'ssl') { + // send payload + if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) { + $this->setError('couldn\'t write message data to socket'); + $this->debug('couldn\'t write message data to socket'); + return false; + } + $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload)); + return true; + } else if ($this->scheme == 'https') { + // set payload + // TODO: cURL does say this should only be the verb, and in fact it + // turns out that the URI and HTTP version are appended to this, which + // some servers refuse to work with + //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload); + foreach($this->outgoing_headers as $k => $v){ + $curl_headers[] = "$k: $v"; + } + curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_headers); + if ($this->request_method == "POST") { + curl_setopt($this->ch, CURLOPT_POST, 1); + curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data); + } else { + } + $this->debug('set cURL payload'); + return true; + } + } + + function getResponse(){ + $this->incoming_payload = ''; + + if ($this->scheme == 'http' || $this->scheme == 'ssl') { + // loop until headers have been retrieved + $data = ''; + while (!isset($lb)){ + + // We might EOF during header read. + if(feof($this->fp)) { + $this->incoming_payload = $data; + $this->debug('found no headers before EOF after length ' . strlen($data)); + $this->debug("received before EOF:\n" . $data); + $this->setError('server failed to send headers'); + return false; + } + + $data .= fgets($this->fp, 256); + $pos = strpos($data,"\r\n\r\n"); + if($pos > 1){ + $lb = "\r\n"; + } else { + $pos = strpos($data,"\n\n"); + if($pos > 1){ + $lb = "\n"; + } + } + // remove 100 header + if(isset($lb) && ereg('^HTTP/1.1 100',$data)){ + unset($lb); + $data = ''; + }// + } + // store header data + $this->incoming_payload .= $data; + $this->debug('found end of headers after length ' . strlen($data)); + // process headers + $header_data = trim(substr($data,0,$pos)); + $header_array = explode($lb,$header_data); + foreach($header_array as $header_line){ + $arr = explode(':',$header_line, 2); + if(count($arr) > 1){ + $header_name = strtolower(trim($arr[0])); + $this->incoming_headers[$header_name] = trim($arr[1]); + } else if (isset($header_name)) { + $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; + } + } + + // loop until msg has been received + // TODO: handle chunking in this loop to allow persistent connections with chunking + $content_length = isset($this->incoming_headers['content-length']) ? $this->incoming_headers['content-length'] : 2147483647; + $data = ''; + $strlen = 0; + while (($strlen < $content_length) && (!feof($this->fp))) { + $readlen = min(8192, $content_length - $strlen); + $tmp = fread($this->fp, $readlen); + $strlen += strlen($tmp); + $data .= $tmp; + } + $this->debug('read body of length ' . strlen($data)); + $this->incoming_payload .= $data; + $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server'); + + // close filepointer + if( + //(isset($this->incoming_headers['connection']) && $this->incoming_headers['connection'] == 'close') || + (! $this->persistentConnection) || feof($this->fp)){ + fclose($this->fp); + $this->fp = false; + $this->debug('closed socket'); + } + + // connection was closed unexpectedly + if($this->incoming_payload == ''){ + $this->setError('no response from server'); + return false; + } + + // decode transfer-encoding + if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){ + if(!$data = $this->decodeChunked($data, $lb)){ + $this->setError('Decoding of chunked data failed'); + return false; + } + //print "
    \nde-chunked:\n---------------\n$data\n\n---------------\n
    "; + // set decoded payload + $this->incoming_payload = $header_data.$lb.$lb.$data; + } + + } else if ($this->scheme == 'https') { + // send and receive + $this->debug('send and receive with cURL'); + $this->incoming_payload = curl_exec($this->ch); + $data = $this->incoming_payload; + + $cErr = curl_error($this->ch); + if ($cErr != '') { + $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'
    '; + foreach(curl_getinfo($this->ch) as $k => $v){ + $err .= "$k: $v
    "; + } + $this->debug($err); + $this->setError($err); + curl_close($this->ch); + return false; + } else { + //echo '
    ';
    +			//var_dump(curl_getinfo($this->ch));
    +			//echo '
    '; + } + // close curl + $this->debug('No cURL error, closing cURL'); + curl_close($this->ch); + + // remove 100 header + if (ereg('^HTTP/1.1 100',$data)) { + if ($pos = strpos($data,"\r\n\r\n")) { + $data = ltrim(substr($data,$pos)); + } elseif($pos = strpos($data,"\n\n") ) { + $data = ltrim(substr($data,$pos)); + } + } + + // separate content from HTTP headers + if ($pos = strpos($data,"\r\n\r\n")) { + $lb = "\r\n"; + } elseif( $pos = strpos($data,"\n\n")) { + $lb = "\n"; + } else { + $this->debug('no proper separation of headers and document'); + $this->setError('no proper separation of headers and document'); + return false; + } + $header_data = trim(substr($data,0,$pos)); + $header_array = explode($lb,$header_data); + $data = ltrim(substr($data,$pos)); + $this->debug('found proper separation of headers and document'); + $this->debug('cleaned data, stringlen: '.strlen($data)); + // clean headers + foreach ($header_array as $header_line) { + $arr = explode(':',$header_line,2); + if (count($arr) > 1) { + $this->incoming_headers[strtolower(trim($arr[0]))] = trim($arr[1]); + } + } + } + + // see if we need to resend the request with http digest authentication + if (isset($this->incoming_headers['www-authenticate']) && strstr($header_array[0], '401 Unauthorized')) { + if (substr("Digest ", $this->incoming_headers['www-authenticate'])) { + // remove "Digest " from our elements + $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']); + + // parse elements into array + $digestElements = explode(', ', $digestString); + while (list($key, $val) = each($digestElements)) { + $tempElement = explode('=', $val); + $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]); + } + + // should have (at least) qop, realm, nonce + if (isset($digestRequest['nonce'])) { + $this->debug('found nonce in WWW-Authenticate: ' . $this->incoming_headers['www-authenticate']); + $this->setCredentials($this->username, $this->password, 'digest', $digestRequest); + $this->tryagain = true; + return false; + } + } + $this->debug('HTTP authentication failed'); + $this->setError('HTTP authentication failed'); + return false; + } + + // decode content-encoding + if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){ + if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){ + // if decoding works, use it. else assume data wasn't gzencoded + if(function_exists('gzuncompress')){ + //$timer->setMarker('starting decoding of gzip/deflated content'); + if($this->incoming_headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)){ + $data = $degzdata; + } elseif($this->incoming_headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){ // do our best + $data = $degzdata; + } else { + $this->setError('Errors occurred when trying to decode the data'); + } + //$timer->setMarker('finished decoding of gzip/deflated content'); + //print "\nde-inflated:\n---------------\n$data\n-------------\n"; + // set decoded payload + $this->incoming_payload = $header_data.$lb.$lb.$data; + } else { + $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.'); + } + } + } + + if(strlen($data) == 0){ + $this->debug('no data after headers!'); + $this->setError('no data present after HTTP headers'); + return false; + } + + return $data; + } + + function setContentType($type, $charset = false) { + $this->outgoing_headers['Content-Type'] = $type . ($charset ? '; charset=' . $charset : ''); + } + + function usePersistentConnection(){ + if (isset($this->outgoing_headers['Accept-Encoding'])) { + return false; + } + $this->protocol_version = '1.1'; + $this->persistentConnection = true; + $this->outgoing_headers['Connection'] = 'Keep-Alive'; + return true; + } +} + +?> +* @version $Id$ +* @access public +*/ +class nusoap_server extends nusoap_base { + var $headers = array(); // HTTP headers of request + var $request = ''; // HTTP request + var $requestHeaders = ''; // SOAP headers from request (incomplete namespace resolution) (text) + var $document = ''; // SOAP body request portion (incomplete namespace resolution) (text) + var $requestSOAP = ''; // SOAP payload for request (text) + var $methodURI = ''; // requested method namespace URI + var $methodname = ''; // name of method requested + var $methodparams = array(); // method parameters from request + var $xml_encoding = ''; // character set encoding of incoming (request) messages + var $SOAPAction = ''; // SOAP Action from request + + var $outgoing_headers = array();// HTTP headers of response + var $response = ''; // HTTP response + var $responseHeaders = ''; // SOAP headers for response (text) + var $responseSOAP = ''; // SOAP payload for response (text) + var $methodreturn = false; // method return to place in response + var $fault = false; // SOAP fault for response + var $result = 'successful'; // text indication of result (for debugging) + + var $operations = array(); // assoc array of operations => opData + var $wsdl = false; // wsdl instance + var $externalWSDLURL = false; // URL for WSDL + var $debug_flag = false; // whether to append debug to response as XML comment + + /** + * constructor + * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to. + * + * @param mixed $wsdl file path or URL (string), or wsdl instance (object) + * @access public + */ + function soap_server($wsdl=false){ + + // turn on debugging? + global $debug; + global $_REQUEST; + global $_SERVER; + global $HTTP_SERVER_VARS; + + if (isset($debug)) { + $this->debug_flag = $debug; + } else if (isset($_REQUEST['debug'])) { + $this->debug_flag = $_REQUEST['debug']; + } else if (isset($_SERVER['QUERY_STRING'])) { + $qs = explode('&', $_SERVER['QUERY_STRING']); + foreach ($qs as $v) { + if (substr($v, 0, 6) == 'debug=') { + $this->debug_flag = substr($v, 6); + } + } + } else if (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { + $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']); + foreach ($qs as $v) { + if (substr($v, 0, 6) == 'debug=') { + $this->debug_flag = substr($v, 6); + } + } + } + + // wsdl + if($wsdl){ + if (is_object($wsdl) && is_a($wsdl, 'wsdl')) { + $this->wsdl = $wsdl; + $this->externalWSDLURL = $this->wsdl->wsdl; + $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL); + } else { + $this->debug('Create wsdl from ' . $wsdl); + $this->wsdl = new wsdl($wsdl); + $this->externalWSDLURL = $wsdl; + } + $this->debug("wsdl...\n" . $this->wsdl->debug_str); + $this->wsdl->debug_str = ''; + if($err = $this->wsdl->getError()){ + die('WSDL ERROR: '.$err); + } + } + } + + /** + * processes request and returns response + * + * @param string $data usually is the value of $HTTP_RAW_POST_DATA + * @access public + */ + function service($data){ + global $QUERY_STRING; + if(isset($_SERVER['QUERY_STRING'])){ + $qs = $_SERVER['QUERY_STRING']; + } elseif(isset($GLOBALS['QUERY_STRING'])){ + $qs = $GLOBALS['QUERY_STRING']; + } elseif(isset($QUERY_STRING) && $QUERY_STRING != ''){ + $qs = $QUERY_STRING; + } + + if(isset($qs) && ereg('wsdl', $qs) ){ + // This is a request for WSDL + if($this->externalWSDLURL){ + if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL + header('Location: '.$this->externalWSDLURL); + } else { // assume file + header("Content-Type: text/xml\r\n"); + $fp = fopen($this->externalWSDLURL, 'r'); + fpassthru($fp); + } + } else { + header("Content-Type: text/xml; charset=ISO-8859-1\r\n"); + print $this->wsdl->serialize(); + } + } elseif($data == '' && $this->wsdl){ + // print web interface + print $this->webDescription(); + } else { + // handle the request + $this->parse_request($data); + if (! $this->fault) { + $this->invoke_method(); + } + if (! $this->fault) { + $this->serialize_return(); + } + $this->send_response(); + } + } + + /** + * parses HTTP request headers. + * + * The following fields are set by this function (when successful) + * + * headers + * request + * xml_encoding + * SOAPAction + * + * @access private + */ + function parse_http_headers() { + global $HTTP_SERVER_VARS; + global $_SERVER; + + $this->request = ''; + if(function_exists('getallheaders')){ + $this->headers = getallheaders(); + foreach($this->headers as $k=>$v){ + $this->request .= "$k: $v\r\n"; + $this->debug("$k: $v"); + } + // get SOAPAction header + if(isset($this->headers['SOAPAction'])){ + $this->SOAPAction = str_replace('"','',$this->headers['SOAPAction']); + } + // get the character encoding of the incoming request + if(strpos($this->headers['Content-Type'],'=')){ + $enc = str_replace('"','',substr(strstr($this->headers["Content-Type"],'='),1)); + if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){ + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common + $this->xml_encoding = 'UTF-8'; + } + } elseif(isset($_SERVER) && is_array($_SERVER)){ + foreach ($_SERVER as $k => $v) { + if (substr($k, 0, 5) == 'HTTP_') { + $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($k, 5))))); + } else { + $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $k)))); + } + if ($k == 'Soapaction') { + // get SOAPAction header + $k = 'SOAPAction'; + $v = str_replace('"', '', $v); + $v = str_replace('\\', '', $v); + $this->SOAPAction = $v; + } else if ($k == 'Content-Type') { + // get the character encoding of the incoming request + if (strpos($v, '=')) { + $enc = substr(strstr($v, '='), 1); + $enc = str_replace('"', '', $enc); + $enc = str_replace('\\', '', $enc); + if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) { + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common + $this->xml_encoding = 'UTF-8'; + } + } + $this->headers[$k] = $v; + $this->request .= "$k: $v\r\n"; + $this->debug("$k: $v"); + } + } elseif (is_array($HTTP_SERVER_VARS)) { + foreach ($HTTP_SERVER_VARS as $k => $v) { + if (substr($k, 0, 5) == 'HTTP_') { + $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($k, 5))))); + if ($k == 'Soapaction') { + // get SOAPAction header + $k = 'SOAPAction'; + $v = str_replace('"', '', $v); + $v = str_replace('\\', '', $v); + $this->SOAPAction = $v; + } else if ($k == 'Content-Type') { + // get the character encoding of the incoming request + if (strpos($v, '=')) { + $enc = substr(strstr($v, '='), 1); + $enc = str_replace('"', '', $enc); + $enc = str_replace('\\', '', $enc); + if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) { + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common + $this->xml_encoding = 'UTF-8'; + } + } + $this->headers[$k] = $v; + $this->request .= "$k: $v\r\n"; + $this->debug("$k: $v"); + } + } + } + } + + /** + * parses a request + * + * The following fields are set by this function (when successful) + * + * headers + * request + * xml_encoding + * SOAPAction + * request + * requestSOAP + * methodURI + * methodname + * methodparams + * requestHeaders + * document + * + * This sets the fault field on error + * + * @param string $data XML string + * @access private + */ + function parse_request($data='') { + $this->debug('entering parse_request() on '.date('H:i Y-m-d')); + $this->parse_http_headers(); + $this->debug('got character encoding: '.$this->xml_encoding); + // uncompress if necessary + if (isset($this->headers['Content-Encoding']) && $this->headers['Content-Encoding'] != '') { + $this->debug('got content encoding: ' . $this->headers['Content-Encoding']); + if ($this->headers['Content-Encoding'] == 'deflate' || $this->headers['Content-Encoding'] == 'gzip') { + // if decoding works, use it. else assume data wasn't gzencoded + if (function_exists('gzuncompress')) { + if ($this->headers['Content-Encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) { + $data = $degzdata; + } elseif ($this->headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) { + $data = $degzdata; + } else { + $this->fault('Server', 'Errors occurred when trying to decode the data'); + return; + } + } else { + $this->fault('Server', 'This Server does not support compressed data'); + return; + } + } + } + $this->request .= "\r\n".$data; + $this->requestSOAP = $data; + // parse response, get soap parser obj + $parser = new soap_parser($data,$this->xml_encoding); + // parser debug + $this->debug("parser debug: \n".$parser->debug_str); + // if fault occurred during message parsing + if($err = $parser->getError()){ + $this->result = 'fault: error in msg parsing: '.$err; + $this->fault('Server',"error in msg parsing:\n".$err); + // else successfully parsed request into soapval object + } else { + // get/set methodname + $this->methodURI = $parser->root_struct_namespace; + $this->methodname = $parser->root_struct_name; + $this->debug('method name: '.$this->methodname); + $this->debug('calling parser->get_response()'); + $this->methodparams = $parser->get_response(); + // get SOAP headers + $this->requestHeaders = $parser->getHeaders(); + // add document for doclit support + $this->document = $parser->document; + } + $this->debug('leaving parse_request() on '.date('H:i Y-m-d')); + } + + /** + * invokes a PHP function for the requested SOAP method + * + * The following fields are set by this function (when successful) + * + * methodreturn + * + * Note that the PHP function that is called may also set the following + * fields to affect the response sent to the client + * + * responseHeaders + * outgoing_headers + * + * This sets the fault field on error + * + * @access private + */ + function invoke_method() { + $this->debug('entering invoke_method'); + // does method exist? + if(!function_exists($this->methodname)){ + // "method not found" fault here + $this->debug("method '$this->methodname' not found!"); + $this->result = 'fault: method not found'; + $this->fault('Server',"method '$this->methodname' not defined in service"); + return; + } + if($this->wsdl){ + if(!$this->opData = $this->wsdl->getOperationData($this->methodname)){ + //if( + $this->fault('Server',"Operation '$this->methodname' is not defined in the WSDL for this service"); + return; + } + $this->debug('opData is ' . $this->varDump($this->opData)); + } + $this->debug("method '$this->methodname' exists"); + // evaluate message, getting back parameters + // verify that request parameters match the method's signature + if(! $this->verify_method($this->methodname,$this->methodparams)){ + // debug + $this->debug('ERROR: request not verified against method signature'); + $this->result = 'fault: request failed validation against method signature'; + // return fault + $this->fault('Server',"Operation '$this->methodname' not defined in service."); + return; + } + + // if there are parameters to pass + $this->debug('params var dump '.$this->varDump($this->methodparams)); + if($this->methodparams){ + $this->debug("calling '$this->methodname' with params"); + if (! function_exists('call_user_func_array')) { + $this->debug('calling method using eval()'); + $funcCall = $this->methodname.'('; + foreach($this->methodparams as $param) { + $funcCall .= "\"$param\","; + } + $funcCall = substr($funcCall, 0, -1).')'; + $this->debug('function call:
    '.$funcCall); + @eval("\$this->methodreturn = $funcCall;"); + } else { + $this->debug('calling method using call_user_func_array()'); + $this->methodreturn = call_user_func_array("$this->methodname",$this->methodparams); + } + $this->debug('response var dump'.$this->varDump($this->methodreturn)); + } else { + // call method w/ no parameters + $this->debug("calling $this->methodname w/ no params"); + $m = $this->methodname; + $this->methodreturn = @$m(); + } + $this->debug("leaving invoke_method: called method $this->methodname, received $this->methodreturn of type".gettype($this->methodreturn)); + } + + /** + * serializes the return value from a PHP function into a full SOAP Envelope + * + * The following fields are set by this function (when successful) + * + * responseSOAP + * + * This sets the fault field on error + * + * @access private + */ + function serialize_return() { + $this->debug("Entering serialize_return"); + // if we got nothing back. this might be ok (echoVoid) + if(isset($this->methodreturn) && ($this->methodreturn != '' || is_bool($this->methodreturn))) { + // if fault + if(get_class($this->methodreturn) == 'soap_fault'){ + $this->debug('got a fault object from method'); + $this->fault = $this->methodreturn; + return; + // if return val is soapval object + } elseif(get_class($this->methodreturn) == 'soapval'){ + $this->debug('got a soapval object from method'); + $return_val = $this->methodreturn->serialize(); + // returned other + } else { + $this->debug('got a(n) '.gettype($this->methodreturn).' from method'); + $this->debug('serializing return value'); + if($this->wsdl){ + // weak attempt at supporting multiple output params + if(sizeof($this->opData['output']['parts']) > 1){ + $opParams = $this->methodreturn; + } else { + $opParams = array($this->methodreturn); + } + $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams); + if($errstr = $this->wsdl->getError()){ + $this->debug('got wsdl error: '.$errstr); + $this->fault('Server', 'got wsdl error: '.$errstr); + return; + } + } else { + $return_val = $this->serialize_val($this->methodreturn, 'return'); + } + } + $this->debug('return val: '.$this->varDump($return_val)); + } else { + $return_val = ''; + $this->debug('got no response from method'); + } + $this->debug('serializing response'); + if ($this->wsdl) { + if ($this->opData['style'] == 'rpc') { + $payload = 'methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'methodname."Response>"; + } else { + $payload = $return_val; + } + } else { + $payload = 'methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'methodname."Response>"; + } + $this->result = 'successful'; + if($this->wsdl){ + //if($this->debug_flag){ + $this->debug("WSDL debug data:\n".$this->wsdl->debug_str); + // } + // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces. + $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style']); + } else { + $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders); + } + $this->debug("Leaving serialize_return"); + } + + /** + * sends an HTTP response + * + * The following fields are set by this function (when successful) + * + * outgoing_headers + * response + * + * @access private + */ + function send_response() { + $this->debug('Enter send_response'); + if ($this->fault) { + $payload = $this->fault->serialize(); + $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error"; + $this->outgoing_headers[] = "Status: 500 Internal Server Error"; + } else { + $payload = $this->responseSOAP; + // Some combinations of PHP+Web server allow the Status + // to come through as a header. Since OK is the default + // just do nothing. + // $this->outgoing_headers[] = "HTTP/1.0 200 OK"; + // $this->outgoing_headers[] = "Status: 200 OK"; + } + // add debug data if in debug mode + if(isset($this->debug_flag) && $this->debug_flag){ + while (strpos($this->debug_str, '--')) { + $this->debug_str = str_replace('--', '- -', $this->debug_str); + } + $payload .= ""; + } + $this->outgoing_headers[] = "Server: $this->title Server v$this->version"; + ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev); + $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")"; + // Let the Web server decide about this + //$this->outgoing_headers[] = "Connection: Close\r\n"; + $this->outgoing_headers[] = "Content-Type: text/xml; charset=$this->soap_defencoding"; + //begin code to compress payload - by John + if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['Accept-Encoding'])) { + if (strstr($this->headers['Accept-Encoding'], 'deflate')) { + if (function_exists('gzcompress')) { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + $this->outgoing_headers[] = "Content-Encoding: deflate"; + $payload = gzcompress($payload); + } else { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + } + } else if (strstr($this->headers['Accept-Encoding'], 'gzip')) { + if (function_exists('gzencode')) { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + $this->outgoing_headers[] = "Content-Encoding: gzip"; + $payload = gzencode($payload); + } else { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + } + } + } + //end code + $this->outgoing_headers[] = "Content-Length: ".strlen($payload); + reset($this->outgoing_headers); + foreach($this->outgoing_headers as $hdr){ + header($hdr, false); + } + $this->response = join("\r\n",$this->outgoing_headers)."\r\n".$payload; + print $payload; + } + + /** + * takes the value that was created by parsing the request + * and compares to the method's signature, if available. + * + * @param mixed + * @return boolean + * @access private + */ + function verify_method($operation,$request){ + if(isset($this->wsdl) && is_object($this->wsdl)){ + if($this->wsdl->getOperationData($operation)){ + return true; + } + } elseif(isset($this->operations[$operation])){ + return true; + } + return false; + } + + /** + * add a method to the dispatch map + * + * @param string $methodname + * @param string $in array of input values + * @param string $out array of output values + * @access public + */ + function add_to_map($methodname,$in,$out){ + $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out); + } + + /** + * register a service with the server + * + * @param string $methodname + * @param string $in assoc array of input values: key = param name, value = param type + * @param string $out assoc array of output values: key = param name, value = param type + * @param string $namespace + * @param string $soapaction + * @param string $style optional (rpc|document) + * @param string $use optional (encoded|literal) + * @param string $documentation optional Description to include in WSDL + * @access public + */ + function register($name,$in=false,$out=false,$namespace=false,$soapaction=false,$style=false,$use=false,$documentation=''){ + if($this->externalWSDLURL){ + die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.'); + } + if(false == $in) { + } + if(false == $out) { + } + if(false == $namespace) { + } + if(false == $soapaction) { + $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME']; + $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME']; + $soapaction = "http://$SERVER_NAME$SCRIPT_NAME/$name"; + } + if(false == $style) { + $style = "rpc"; + } + if(false == $use) { + $use = "encoded"; + } + + $this->operations[$name] = array( + 'name' => $name, + 'in' => $in, + 'out' => $out, + 'namespace' => $namespace, + 'soapaction' => $soapaction, + 'style' => $style); + if($this->wsdl){ + $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation); + } + return true; + } + + /** + * create a fault. this also acts as a flag to the server that a fault has occured. + * + * @param string faultcode + * @param string faultstring + * @param string faultactor + * @param string faultdetail + * @access public + */ + function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){ + $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail); + } + + /** + * prints html description of services + * + * @access private + */ + function webDescription(){ + $b = ' + NuSOAP: '.$this->wsdl->serviceName.' + + + + +
    +

    +
    '.$this->wsdl->serviceName.'
    + +
    '; + return $b; + } + + /** + * sets up wsdl object + * this acts as a flag to enable internal WSDL generation + * + * @param string $serviceName, name of the service + * @param string $namespace optional tns namespace + * @param string $endpoint optional URL of service endpoint + * @param string $style optional (rpc|document) WSDL style (also specified by operation) + * @param string $transport optional SOAP transport + * @param string $schemaTargetNamespace optional targetNamespace for service schema + */ + function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false) + { + $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME']; + $SERVER_PORT = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : $GLOBALS['SERVER_PORT']; + if ($SERVER_PORT == 80) { + $SERVER_PORT = ''; + } else { + $SERVER_PORT = ':' . $SERVER_PORT; + } + $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME']; + if(false == $namespace) { + $namespace = "http://$SERVER_NAME/soap/$serviceName"; + } + + if(false == $endpoint) { + if (isset($_SERVER['HTTPS'])) { + $HTTPS = $_SERVER['HTTPS']; + } elseif (isset($GLOBALS['HTTPS'])) { + $HTTPS = $GLOBALS['HTTPS']; + } else { + $HTTPS = '0'; + } + if ($HTTPS == '1' || $HTTPS == 'on') { + $SCHEME = 'https'; + } else { + $SCHEME = 'http'; + } + $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME"; + } + + if(false == $schemaTargetNamespace) { + $schemaTargetNamespace = $namespace; + } + + $this->wsdl = new wsdl; + $this->wsdl->serviceName = $serviceName; + $this->wsdl->endpoint = $endpoint; + $this->wsdl->namespaces['tns'] = $namespace; + $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/'; + $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/'; + if ($schemaTargetNamespace != $namespace) { + $this->wsdl->namespaces['types'] = $schemaTargetNamespace; + } + $this->wsdl->schemas[$schemaTargetNamespace][0] = new xmlschema('', '', $this->wsdl->namespaces); + $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace; + $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true); + $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true); + $this->wsdl->bindings[$serviceName.'Binding'] = array( + 'name'=>$serviceName.'Binding', + 'style'=>$style, + 'transport'=>$transport, + 'portType'=>$serviceName.'PortType'); + $this->wsdl->ports[$serviceName.'Port'] = array( + 'binding'=>$serviceName.'Binding', + 'location'=>$endpoint, + 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/'); + } +} + + + +?> +* @version $Id$ +* @access public +*/ +class wsdl extends nusoap_base { + // URL or filename of the root of this WSDL + var $wsdl; + // define internal arrays of bindings, ports, operations, messages, etc. + var $schemas = array(); + var $currentSchema; + var $message = array(); + var $complexTypes = array(); + var $messages = array(); + var $currentMessage; + var $currentOperation; + var $portTypes = array(); + var $currentPortType; + var $bindings = array(); + var $currentBinding; + var $ports = array(); + var $currentPort; + var $opData = array(); + var $status = ''; + var $documentation = false; + var $endpoint = ''; + // array of wsdl docs to import + var $import = array(); + // parser vars + var $parser; + var $position = 0; + var $depth = 0; + var $depth_array = array(); + // for getting wsdl + var $proxyhost = ''; + var $proxyport = ''; + var $proxyusername = ''; + var $proxypassword = ''; + var $timeout = 0; + var $response_timeout = 30; + + /** + * constructor + * + * @param string $wsdl WSDL document URL + * @param string $proxyhost + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @param integer $timeout set the connection timeout + * @param integer $response_timeout set the response timeout + * @access public + */ + function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30){ + $this->wsdl = $wsdl; + $this->proxyhost = $proxyhost; + $this->proxyport = $proxyport; + $this->proxyusername = $proxyusername; + $this->proxypassword = $proxypassword; + $this->timeout = $timeout; + $this->response_timeout = $response_timeout; + + // parse wsdl file + if ($wsdl != "") { + $this->debug('initial wsdl URL: ' . $wsdl); + $this->parseWSDL($wsdl); + } + // imports + // TODO: handle imports more properly, grabbing them in-line and nesting them + $imported_urls = array(); + $imported = 1; + while ($imported > 0) { + $imported = 0; + // Schema imports + foreach ($this->schemas as $ns => $list) { + foreach ($list as $xs) { + $wsdlparts = parse_url($this->wsdl); // this is bogusly simple! + foreach ($xs->imports as $ns2 => $list2) { + for ($ii = 0; $ii < count($list2); $ii++) { + if (! $list2[$ii]['loaded']) { + $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true; + $url = $list2[$ii]['location']; + if ($url != '') { + $urlparts = parse_url($url); + if (!isset($urlparts['host'])) { + $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . + substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path']; + } + if (! in_array($url, $imported_urls)) { + $this->parseWSDL($url); + $imported++; + $imported_urls[] = $url; + } + } else { + $this->debug("Unexpected scenario: empty URL for unloaded import"); + } + } + } + } + } + } + // WSDL imports + $wsdlparts = parse_url($this->wsdl); // this is bogusly simple! + foreach ($this->import as $ns => $list) { + for ($ii = 0; $ii < count($list); $ii++) { + if (! $list[$ii]['loaded']) { + $this->import[$ns][$ii]['loaded'] = true; + $url = $list[$ii]['location']; + if ($url != '') { + $urlparts = parse_url($url); + if (!isset($urlparts['host'])) { + $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . + substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path']; + } + if (! in_array($url, $imported_urls)) { + $this->parseWSDL($url); + $imported++; + $imported_urls[] = $url; + } + } else { + $this->debug("Unexpected scenario: empty URL for unloaded import"); + } + } + } + } + } + // add new data to operation data + foreach($this->bindings as $binding => $bindingData) { + if (isset($bindingData['operations']) && is_array($bindingData['operations'])) { + foreach($bindingData['operations'] as $operation => $data) { + $this->debug('post-parse data gathering for ' . $operation); + $this->bindings[$binding]['operations'][$operation]['input'] = + isset($this->bindings[$binding]['operations'][$operation]['input']) ? + array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) : + $this->portTypes[ $bindingData['portType'] ][$operation]['input']; + $this->bindings[$binding]['operations'][$operation]['output'] = + isset($this->bindings[$binding]['operations'][$operation]['output']) ? + array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) : + $this->portTypes[ $bindingData['portType'] ][$operation]['output']; + if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){ + $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ]; + } + if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){ + $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ]; + } + if (isset($bindingData['style'])) { + $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style']; + } + $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : ''; + $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : ''; + $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : ''; + } + } + } + } + + /** + * parses the wsdl document + * + * @param string $wsdl path or URL + * @access private + */ + function parseWSDL($wsdl = '') + { + if ($wsdl == '') { + $this->debug('no wsdl passed to parseWSDL()!!'); + $this->setError('no wsdl passed to parseWSDL()!!'); + return false; + } + + // parse $wsdl for url format + $wsdl_props = parse_url($wsdl); + + if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) { + $this->debug('getting WSDL http(s) URL ' . $wsdl); + // get wsdl + $tr = new soap_transport_http($wsdl); + $tr->request_method = 'GET'; + $tr->useSOAPAction = false; + if($this->proxyhost && $this->proxyport){ + $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword); + } + if (isset($wsdl_props['user'])) { + $tr->setCredentials($wsdl_props['user'],$wsdl_props['pass']); + } + $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout); + //$this->debug("WSDL request\n" . $tr->outgoing_payload); + //$this->debug("WSDL response\n" . $tr->incoming_payload); + $this->debug("transport debug data...\n" . $tr->debug_str); + // catch errors + if($err = $tr->getError() ){ + $errstr = 'HTTP ERROR: '.$err; + $this->debug($errstr); + $this->setError($errstr); + unset($tr); + return false; + } + unset($tr); + } else { + // $wsdl is not http(s), so treat it as a file URL or plain file path + if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) { + $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path']; + } else { + $path = $wsdl; + } + $this->debug('getting WSDL file ' . $path); + if ($fp = @fopen($path, 'r')) { + $wsdl_string = ''; + while ($data = fread($fp, 32768)) { + $wsdl_string .= $data; + } + fclose($fp); + } else { + $errstr = "Bad path to WSDL file $path"; + $this->debug($errstr); + $this->setError($errstr); + return false; + } + } + // end new code added + // Create an XML parser. + $this->parser = xml_parser_create(); + // Set the options for parsing the XML data. + // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + // Set the object for the parser. + xml_set_object($this->parser, $this); + // Set the element handlers for the parser. + xml_set_element_handler($this->parser, 'start_element', 'end_element'); + xml_set_character_data_handler($this->parser, 'character_data'); + // Parse the XML file. + if (!xml_parse($this->parser, $wsdl_string, true)) { + // Display an error message. + $errstr = sprintf( + 'XML error parsing WSDL from %s on line %d: %s', + $wsdl, + xml_get_current_line_number($this->parser), + xml_error_string(xml_get_error_code($this->parser)) + ); + $this->debug($errstr); + $this->debug("XML payload:\n" . $wsdl_string); + $this->setError($errstr); + return false; + } + // free the parser + xml_parser_free($this->parser); + // catch wsdl parse errors + if($this->getError()){ + return false; + } + return true; + } + + /** + * start-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @param string $attrs associative array of attributes + * @access private + */ + function start_element($parser, $name, $attrs) + { + if ($this->status == 'schema') { + $this->currentSchema->schemaStartElement($parser, $name, $attrs); + $this->debug_str .= $this->currentSchema->debug_str; + $this->currentSchema->debug_str = ''; + } elseif (ereg('schema$', $name)) { + // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")"); + $this->status = 'schema'; + $this->currentSchema = new xmlschema('', '', $this->namespaces); + $this->currentSchema->schemaStartElement($parser, $name, $attrs); + $this->debug_str .= $this->currentSchema->debug_str; + $this->currentSchema->debug_str = ''; + } else { + // position in the total number of elements, starting from 0 + $pos = $this->position++; + $depth = $this->depth++; + // set self as current value for this depth + $this->depth_array[$depth] = $pos; + $this->message[$pos] = array('cdata' => ''); + // get element prefix + if (ereg(':', $name)) { + // get ns prefix + $prefix = substr($name, 0, strpos($name, ':')); + // get ns + $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : ''; + // get unqualified name + $name = substr(strstr($name, ':'), 1); + } + + if (count($attrs) > 0) { + foreach($attrs as $k => $v) { + // if ns declarations, add to class level array of valid namespaces + if (ereg("^xmlns", $k)) { + if ($ns_prefix = substr(strrchr($k, ':'), 1)) { + $this->namespaces[$ns_prefix] = $v; + } else { + $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v; + } + if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') { + $this->XMLSchemaVersion = $v; + $this->namespaces['xsi'] = $v . '-instance'; + } + } // + // expand each attribute + $k = strpos($k, ':') ? $this->expandQname($k) : $k; + if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') { + $v = strpos($v, ':') ? $this->expandQname($v) : $v; + } + $eAttrs[$k] = $v; + } + $attrs = $eAttrs; + } else { + $attrs = array(); + } + // find status, register data + switch ($this->status) { + case 'message': + if ($name == 'part') { + if (isset($attrs['type'])) { + $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs)); + $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type']; + } + if (isset($attrs['element'])) { + $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element']; + } + } + break; + case 'portType': + switch ($name) { + case 'operation': + $this->currentPortOperation = $attrs['name']; + $this->debug("portType $this->currentPortType operation: $this->currentPortOperation"); + if (isset($attrs['parameterOrder'])) { + $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder']; + } + break; + case 'documentation': + $this->documentation = true; + break; + // merge input/output data + default: + $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : ''; + $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m; + break; + } + break; + case 'binding': + switch ($name) { + case 'binding': + // get ns prefix + if (isset($attrs['style'])) { + $this->bindings[$this->currentBinding]['prefix'] = $prefix; + } + $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs); + break; + case 'header': + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs; + break; + case 'operation': + if (isset($attrs['soapAction'])) { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction']; + } + if (isset($attrs['style'])) { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style']; + } + if (isset($attrs['name'])) { + $this->currentOperation = $attrs['name']; + $this->debug("current binding operation: $this->currentOperation"); + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name']; + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding; + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : ''; + } + break; + case 'input': + $this->opStatus = 'input'; + break; + case 'output': + $this->opStatus = 'output'; + break; + case 'body': + if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs); + } else { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs; + } + break; + } + break; + case 'service': + switch ($name) { + case 'port': + $this->currentPort = $attrs['name']; + $this->debug('current port: ' . $this->currentPort); + $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']); + + break; + case 'address': + $this->ports[$this->currentPort]['location'] = $attrs['location']; + $this->ports[$this->currentPort]['bindingType'] = $namespace; + $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace; + $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location']; + break; + } + break; + } + // set status + switch ($name) { + case 'import': + if (isset($attrs['location'])) { + $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false); + $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')'); + } else { + $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true); + if (! $this->getPrefixFromNamespace($attrs['namespace'])) { + $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace']; + } + $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')'); + } + break; + //wait for schema + //case 'types': + // $this->status = 'schema'; + // break; + case 'message': + $this->status = 'message'; + $this->messages[$attrs['name']] = array(); + $this->currentMessage = $attrs['name']; + break; + case 'portType': + $this->status = 'portType'; + $this->portTypes[$attrs['name']] = array(); + $this->currentPortType = $attrs['name']; + break; + case "binding": + if (isset($attrs['name'])) { + // get binding name + if (strpos($attrs['name'], ':')) { + $this->currentBinding = $this->getLocalPart($attrs['name']); + } else { + $this->currentBinding = $attrs['name']; + } + $this->status = 'binding'; + $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']); + $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']); + } + break; + case 'service': + $this->serviceName = $attrs['name']; + $this->status = 'service'; + $this->debug('current service: ' . $this->serviceName); + break; + case 'definitions': + foreach ($attrs as $name => $value) { + $this->wsdl_info[$name] = $value; + } + break; + } + } + } + + /** + * end-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @access private + */ + function end_element($parser, $name){ + // unset schema status + if (/*ereg('types$', $name) ||*/ ereg('schema$', $name)) { + $this->status = ""; + $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema; + } + if ($this->status == 'schema') { + $this->currentSchema->schemaEndElement($parser, $name); + } else { + // bring depth down a notch + $this->depth--; + } + // end documentation + if ($this->documentation) { + //TODO: track the node to which documentation should be assigned; it can be a part, message, etc. + //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation; + $this->documentation = false; + } + } + + /** + * element content handler + * + * @param string $parser XML parser object + * @param string $data element content + * @access private + */ + function character_data($parser, $data) + { + $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0; + if (isset($this->message[$pos]['cdata'])) { + $this->message[$pos]['cdata'] .= $data; + } + if ($this->documentation) { + $this->documentation .= $data; + } + } + + function getBindingData($binding) + { + if (is_array($this->bindings[$binding])) { + return $this->bindings[$binding]; + } + } + + /** + * returns an assoc array of operation names => operation data + * + * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported) + * @return array + * @access public + */ + function getOperations($bindingType = 'soap') + { + $ops = array(); + if ($bindingType == 'soap') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; + } + // loop thru ports + foreach($this->ports as $port => $portData) { + // binding type of port matches parameter + if ($portData['bindingType'] == $bindingType) { + //$this->debug("getOperations for port $port"); + //$this->debug("port data: " . $this->varDump($portData)); + //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ])); + // merge bindings + if (isset($this->bindings[ $portData['binding'] ]['operations'])) { + $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']); + } + } + } + return $ops; + } + + /** + * returns an associative array of data necessary for calling an operation + * + * @param string $operation , name of operation + * @param string $bindingType , type of binding eg: soap + * @return array + * @access public + */ + function getOperationData($operation, $bindingType = 'soap') + { + if ($bindingType == 'soap') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; + } + // loop thru ports + foreach($this->ports as $port => $portData) { + // binding type of port matches parameter + if ($portData['bindingType'] == $bindingType) { + // get binding + //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) { + foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) { + if ($operation == $bOperation) { + $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation]; + return $opData; + } + } + } + } + } + + /** + * returns an array of information about a given type + * returns false if no type exists by the given name + * + * typeDef = array( + * 'elements' => array(), // refs to elements array + * 'restrictionBase' => '', + * 'phpType' => '', + * 'order' => '(sequence|all)', + * 'attrs' => array() // refs to attributes array + * ) + * + * @param $type string + * @param $ns string + * @return mixed + * @access public + * @see xmlschema + */ + function getTypeDef($type, $ns) { + if ((! $ns) && isset($this->namespaces['tns'])) { + $ns = $this->namespaces['tns']; + } + if (isset($this->schemas[$ns])) { + foreach ($this->schemas[$ns] as $xs) { + $t = $xs->getTypeDef($type); + $this->debug_str .= $xs->debug_str; + $xs->debug_str = ''; + if ($t) { + return $t; + } + } + } + return false; + } + + /** + * serialize the parsed wsdl + * + * @return string , serialization of WSDL + * @access public + */ + function serialize() + { + $xml = 'namespaces as $k => $v) { + $xml .= " xmlns:$k=\"$v\""; + } + // 10.9.02 - add poulter fix for wsdl and tns declarations + if (isset($this->namespaces['wsdl'])) { + $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\""; + } + if (isset($this->namespaces['tns'])) { + $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\""; + } + $xml .= '>'; + // imports + if (sizeof($this->import) > 0) { + foreach($this->import as $ns => $list) { + foreach ($list as $ii) { + if ($ii['location'] != '') { + $xml .= ''; + } else { + $xml .= ''; + } + } + } + } + // types + if (count($this->schemas)>=1) { + $xml .= ''; + foreach ($this->schemas as $ns => $list) { + foreach ($list as $xs) { + $xml .= $xs->serializeSchema(); + } + } + $xml .= ''; + } + // messages + if (count($this->messages) >= 1) { + foreach($this->messages as $msgName => $msgParts) { + $xml .= ''; + if(is_array($msgParts)){ + foreach($msgParts as $partName => $partType) { + // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'
    '; + if (strpos($partType, ':')) { + $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType)); + } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) { + // print 'checking typemap: '.$this->XMLSchemaVersion.'
    '; + $typePrefix = 'xsd'; + } else { + foreach($this->typemap as $ns => $types) { + if (isset($types[$partType])) { + $typePrefix = $this->getPrefixFromNamespace($ns); + } + } + if (!isset($typePrefix)) { + die("$partType has no namespace!"); + } + } + $xml .= ''; + } + } + $xml .= '
    '; + } + } + // bindings & porttypes + if (count($this->bindings) >= 1) { + $binding_xml = ''; + $portType_xml = ''; + foreach($this->bindings as $bindingName => $attrs) { + $binding_xml .= ''; + $binding_xml .= ''; + $portType_xml .= ''; + foreach($attrs['operations'] as $opName => $opParts) { + $binding_xml .= ''; + $binding_xml .= ''; + if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') { + $enc_style = '" encodingStyle="' . $opParts['input']['encodingStyle'] . '"'; + } else { + $enc_style = ''; + } + $binding_xml .= ''; + } + $portType_xml .= ''; + $portType_xml .= ''; + $portType_xml .= ''; + } + $portType_xml .= ''; + $binding_xml .= ''; + } + $xml .= $portType_xml . $binding_xml; + } + // services + $xml .= ''; + if (count($this->ports) >= 1) { + foreach($this->ports as $pName => $attrs) { + $xml .= ''; + $xml .= ''; + $xml .= ''; + } + } + $xml .= ''; + return $xml . ''; + } + + /** + * serialize a PHP value according to a WSDL message definition + * + * TODO + * - multi-ref serialization + * - validate PHP values against type definitions, return errors if invalid + * + * @param string $ type name + * @param mixed $ param value + * @return mixed new param or false if initial value didn't validate + */ + function serializeRPCParameters($operation, $direction, $parameters) + { + $this->debug('in serializeRPCParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion); + + if ($direction != 'input' && $direction != 'output') { + $this->debug('The value of the \$direction argument needs to be either "input" or "output"'); + $this->setError('The value of the \$direction argument needs to be either "input" or "output"'); + return false; + } + if (!$opData = $this->getOperationData($operation)) { + $this->debug('Unable to retrieve WSDL data for operation: ' . $operation); + $this->setError('Unable to retrieve WSDL data for operation: ' . $operation); + return false; + } + $this->debug($this->varDump($opData)); + + // Get encoding style for output and set to current + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) { + $encodingStyle = $opData['output']['encodingStyle']; + $enc_style = $encodingStyle; + } + + // set input params + $xml = ''; + if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) { + + $use = $opData[$direction]['use']; + $this->debug("use=$use"); + $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)'); + if (is_array($parameters)) { + $parametersArrayType = $this->isArraySimpleOrStruct($parameters); + $this->debug('have ' . $parametersArrayType . ' parameters'); + foreach($opData[$direction]['parts'] as $name => $type) { + $this->debug('serializing part "'.$name.'" of type "'.$type.'"'); + // Track encoding style + if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) { + $encodingStyle = $opData[$direction]['encodingStyle']; + $enc_style = $encodingStyle; + } else { + $enc_style = false; + } + // NOTE: add error handling here + // if serializeType returns false, then catch global error and fault + if ($parametersArrayType == 'arraySimple') { + $p = array_shift($parameters); + $this->debug('calling serializeType w/indexed param'); + $xml .= $this->serializeType($name, $type, $p, $use, $enc_style); + } elseif (isset($parameters[$name])) { + $this->debug('calling serializeType w/named param'); + $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style); + } else { + // TODO: only send nillable + $this->debug('calling serializeType w/null param'); + $xml .= $this->serializeType($name, $type, null, $use, $enc_style); + } + } + } else { + $this->debug('no parameters passed.'); + } + } + return $xml; + } + + /** + * serialize a PHP value according to a WSDL message definition + * + * TODO + * - multi-ref serialization + * - validate PHP values against type definitions, return errors if invalid + * + * @param string $ type name + * @param mixed $ param value + * @return mixed new param or false if initial value didn't validate + */ + function serializeParameters($operation, $direction, $parameters) + { + $this->debug('in serializeParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion); + + if ($direction != 'input' && $direction != 'output') { + $this->debug('The value of the \$direction argument needs to be either "input" or "output"'); + $this->setError('The value of the \$direction argument needs to be either "input" or "output"'); + return false; + } + if (!$opData = $this->getOperationData($operation)) { + $this->debug('Unable to retrieve WSDL data for operation: ' . $operation); + $this->setError('Unable to retrieve WSDL data for operation: ' . $operation); + return false; + } + $this->debug($this->varDump($opData)); + + // Get encoding style for output and set to current + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) { + $encodingStyle = $opData['output']['encodingStyle']; + $enc_style = $encodingStyle; + } + + // set input params + $xml = ''; + if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) { + + $use = $opData[$direction]['use']; + $this->debug("use=$use"); + $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)'); + if (is_array($parameters)) { + $parametersArrayType = $this->isArraySimpleOrStruct($parameters); + $this->debug('have ' . $parametersArrayType . ' parameters'); + foreach($opData[$direction]['parts'] as $name => $type) { + $this->debug('serializing part "'.$name.'" of type "'.$type.'"'); + // Track encoding style + if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) { + $encodingStyle = $opData[$direction]['encodingStyle']; + $enc_style = $encodingStyle; + } else { + $enc_style = false; + } + // NOTE: add error handling here + // if serializeType returns false, then catch global error and fault + if ($parametersArrayType == 'arraySimple') { + $p = array_shift($parameters); + $this->debug('calling serializeType w/indexed param'); + $xml .= $this->serializeType($name, $type, $p, $use, $enc_style); + } elseif (isset($parameters[$name])) { + $this->debug('calling serializeType w/named param'); + $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style); + } else { + // TODO: only send nillable + $this->debug('calling serializeType w/null param'); + $xml .= $this->serializeType($name, $type, null, $use, $enc_style); + } + } + } else { + $this->debug('no parameters passed.'); + } + } + return $xml; + } + + /** + * serializes a PHP value according a given type definition + * + * @param string $name , name of type (part) + * @param string $type , type of type, heh (type or element) + * @param mixed $value , a native PHP value (parameter value) + * @param string $use , use for part (encoded|literal) + * @param string $encodingStyle , use to add encoding changes to serialisation + * @return string serialization + * @access public + */ + function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false) + { + $this->debug("in serializeType: $name, $type, $value, $use, $encodingStyle"); + if($use == 'encoded' && $encodingStyle) { + $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"'; + } + + // if a soap_val has been supplied, let its type override the WSDL + if (is_object($value) && get_class($value) == 'soapval') { + // TODO: get attributes from soapval? + if ($value->type_ns) { + $type = $value->type_ns . ':' . $value->type; + } else { + $type = $value->type; + } + $value = $value->value; + $forceType = true; + $this->debug("in serializeType: soapval overrides type to $type, value to $value"); + } else { + $forceType = false; + } + + $xml = ''; + if (strpos($type, ':')) { + $uqType = substr($type, strrpos($type, ':') + 1); + $ns = substr($type, 0, strrpos($type, ':')); + $this->debug("got a prefixed type: $uqType, $ns"); + if ($this->getNamespaceFromPrefix($ns)) { + $ns = $this->getNamespaceFromPrefix($ns); + $this->debug("expanded prefixed type: $uqType, $ns"); + } + + if($ns == $this->XMLSchemaVersion){ + + if (is_null($value)) { + if ($use == 'literal') { + // TODO: depends on nillable + return "<$name/>"; + } else { + return "<$name xsi:nil=\"true\"/>"; + } + } + if ($uqType == 'boolean' && !$value) { + $value = 'false'; + } elseif ($uqType == 'boolean') { + $value = 'true'; + } + if ($uqType == 'string' && gettype($value) == 'string') { + $value = $this->expandEntities($value); + } + // it's a scalar + // TODO: what about null/nil values? + // check type isn't a custom type extending xmlschema namespace + if (!$this->getTypeDef($uqType, $ns)) { + if ($use == 'literal') { + if ($forceType) { + return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value"; + } else { + return "<$name>$value"; + } + } else { + return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\"$encodingStyle>$value"; + } + } + } else if ($ns == 'http://xml.apache.org/xml-soap') { + if ($uqType == 'Map') { + $contents = ''; + foreach($value as $k => $v) { + $this->debug("serializing map element: key $k, value $v"); + $contents .= ''; + $contents .= $this->serialize_val($k,'key',false,false,false,false,$use); + $contents .= $this->serialize_val($v,'value',false,false,false,false,$use); + $contents .= ''; + } + if ($use == 'literal') { + if ($forceType) { + return "<$name xsi:type=\"" . $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap') . ":$uqType\">$contents"; + } else { + return "<$name>$contents"; + } + } else { + return "<$name xsi:type=\"" . $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap') . ":$uqType\"$encodingStyle>$contents"; + } + } + } + } else { + $this->debug("No namespace for type $type"); + $ns = ''; + $uqType = $type; + } + if(!$typeDef = $this->getTypeDef($uqType, $ns)){ + $this->setError("$type ($uqType) is not a supported type."); + $this->debug("$type ($uqType) is not a supported type."); + return false; + } else { + foreach($typeDef as $k => $v) { + $this->debug("typedef, $k: $v"); + } + } + $phpType = $typeDef['phpType']; + $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') ); + // if php type == struct, map value to the element names + if ($phpType == 'struct') { + if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') { + $elementName = $uqType; + if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) { + $elementNS = " xmlns=\"$ns\""; + } + } else { + $elementName = $name; + $elementNS = ''; + } + if (is_null($value)) { + if ($use == 'literal') { + // TODO: depends on nillable + return "<$elementName$elementNS/>"; + } else { + return "<$elementName$elementNS xsi:nil=\"true\"/>"; + } + } + if ($use == 'literal') { + if ($forceType) { + $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">"; + } else { + $xml = "<$elementName$elementNS>"; + } + } else { + $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>"; + } + + if (isset($typeDef['elements']) && is_array($typeDef['elements'])) { + + // toggle whether all elements are present - ideally should validate against schema + if(count($typeDef['elements']) != count($value)){ + $optionals = true; + } + foreach($typeDef['elements'] as $eName => $attrs) { + // if user took advantage of a minOccurs=0, then only serialize named parameters + if(isset($optionals) && !isset($value[$eName])){ + // do nothing + } else { + // TODO: if maxOccurs > 1, then allow serialization of an array + // get value + if (isset($value[$eName])) { + $v = $value[$eName]; + } else { + $v = null; + } + if (isset($attrs['maxOccurs']) && $attrs['maxOccurs'] == 'unbounded' && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') { + $vv = $v; + foreach ($vv as $k => $v) { + if (isset($attrs['type'])) { + // serialize schema-defined type + $xml .= $this->serializeType($eName, $attrs['type'], $v, $use, $encodingStyle); + } else { + // serialize generic type + $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use"); + $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use); + } + } + } else { + if (isset($attrs['type'])) { + // serialize schema-defined type + $xml .= $this->serializeType($eName, $attrs['type'], $v, $use, $encodingStyle); + } else { + // serialize generic type + $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use"); + $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use); + } + } + } + } + } else { + //echo 'got here'; + } + $xml .= ""; + } elseif ($phpType == 'array') { + if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) { + $elementNS = " xmlns=\"$ns\""; + } else { + $elementNS = ''; + } + if (is_null($value)) { + if ($use == 'literal') { + // TODO: depends on nillable + return "<$name$elementNS/>"; + } else { + return "<$name$elementNS xsi:nil=\"true\"/>"; + } + } + if (isset($typeDef['multidimensional'])) { + $nv = array(); + foreach($value as $v) { + $cols = ',' . sizeof($v); + $nv = array_merge($nv, $v); + } + $value = $nv; + } else { + $cols = ''; + } + if (is_array($value) && sizeof($value) >= 1) { + $rows = sizeof($value); + $contents = ''; + foreach($value as $k => $v) { + $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]"); + //if (strpos($typeDef['arrayType'], ':') ) { + if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) { + $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use); + } else { + $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use); + } + } + $this->debug('contents: '.$this->varDump($contents)); + } else { + $rows = 0; + $contents = null; + } + // TODO: for now, an empty value will be serialized as a zero element + // array. Revisit this when coding the handling of null/nil values. + if ($use == 'literal') { + $xml = "<$name$elementNS>" + .$contents + .""; + } else { + $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '. + $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') + .':arrayType="' + .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) + .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">" + .$contents + .""; + } + } elseif ($phpType == 'scalar') { + if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) { + $elementNS = " xmlns=\"$ns\""; + } else { + $elementNS = ''; + } + if ($use == 'literal') { + if ($forceType) { + return "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value"; + } else { + return "<$name$elementNS>$value"; + } + } else { + return "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value"; + } + } + $this->debug('returning: '.$this->varDump($xml)); + return $xml; + } + + /** + * adds an XML Schema complex type to the WSDL types + * + * @param name + * @param typeClass (complexType|simpleType|attribute) + * @param phpType: currently supported are array and struct (php assoc array) + * @param compositor (all|sequence|choice) + * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param elements = array ( name = array(name=>'',type=>'') ) + * @param attrs = array( + * array( + * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType", + * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]" + * ) + * ) + * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string) + * @see xmlschema + * + */ + function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') { + if (count($elements) > 0) { + foreach($elements as $n => $e){ + // expand each element + foreach ($e as $k => $v) { + $k = strpos($k,':') ? $this->expandQname($k) : $k; + $v = strpos($v,':') ? $this->expandQname($v) : $v; + $ee[$k] = $v; + } + $eElements[$n] = $ee; + } + $elements = $eElements; + } + + if (count($attrs) > 0) { + foreach($attrs as $n => $a){ + // expand each attribute + foreach ($a as $k => $v) { + $k = strpos($k,':') ? $this->expandQname($k) : $k; + $v = strpos($v,':') ? $this->expandQname($v) : $v; + $aa[$k] = $v; + } + $eAttrs[$n] = $aa; + } + $attrs = $eAttrs; + } + + $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase; + $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType; + + $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns']; + $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType); + } + + /** + * adds an XML Schema simple type to the WSDL types + * + * @param name + * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param typeClass (simpleType) + * @param phpType: (scalar) + * @see xmlschema + * + */ + function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar') { + $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase; + + $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns']; + $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType); + } + + /** + * register a service with the server + * + * @param string $methodname + * @param string $in assoc array of input values: key = param name, value = param type + * @param string $out assoc array of output values: key = param name, value = param type + * @param string $namespace optional The namespace for the operation + * @param string $soapaction optional The soapaction for the operation + * @param string $style (rpc|document) optional The style for the operation + * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now) + * @param string $documentation optional The description to include in the WSDL + * @access public + */ + function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = ''){ + if ($style == 'rpc' && $use == 'encoded') { + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + } else { + $encodingStyle = ''; + } + // get binding + $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] = + array( + 'name' => $name, + 'binding' => $this->serviceName . 'Binding', + 'endpoint' => $this->endpoint, + 'soapAction' => $soapaction, + 'style' => $style, + 'input' => array( + 'use' => $use, + 'namespace' => $namespace, + 'encodingStyle' => $encodingStyle, + 'message' => $name . 'Request', + 'parts' => $in), + 'output' => array( + 'use' => $use, + 'namespace' => $namespace, + 'encodingStyle' => $encodingStyle, + 'message' => $name . 'Response', + 'parts' => $out), + 'namespace' => $namespace, + 'transport' => 'http://schemas.xmlsoap.org/soap/http', + 'documentation' => $documentation); + // add portTypes + // add messages + if($in) + { + foreach($in as $pName => $pType) + { + if(strpos($pType,':')) { + $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType); + } + $this->messages[$name.'Request'][$pName] = $pType; + } + } else { + $this->messages[$name.'Request']= '0'; + } + if($out) + { + foreach($out as $pName => $pType) + { + if(strpos($pType,':')) { + $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType); + } + $this->messages[$name.'Response'][$pName] = $pType; + } + } else { + $this->messages[$name.'Response']= '0'; + } + return true; + } +} +?> +* @version $Id$ +* @access public +*/ +class soap_parser extends nusoap_base { + + var $xml = ''; + var $xml_encoding = ''; + var $method = ''; + var $root_struct = ''; + var $root_struct_name = ''; + var $root_struct_namespace = ''; + var $root_header = ''; + var $document = ''; // incoming SOAP body (text) + // determines where in the message we are (envelope,header,body,method) + var $status = ''; + var $position = 0; + var $depth = 0; + var $default_namespace = ''; + var $namespaces = array(); + var $message = array(); + var $parent = ''; + var $fault = false; + var $fault_code = ''; + var $fault_str = ''; + var $fault_detail = ''; + var $depth_array = array(); + var $debug_flag = true; + var $soapresponse = NULL; + var $responseHeaders = ''; // incoming SOAP headers (text) + var $body_position = 0; + // for multiref parsing: + // array of id => pos + var $ids = array(); + // array of id => hrefs => pos + var $multirefs = array(); + // toggle for auto-decoding element content + var $decode_utf8 = true; + + /** + * constructor + * + * @param string $xml SOAP message + * @param string $encoding character encoding scheme of message + * @param string $method + * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1 + * @access public + */ + function soap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){ + $this->xml = $xml; + $this->xml_encoding = $encoding; + $this->method = $method; + $this->decode_utf8 = $decode_utf8; + + // Check whether content has been read. + if(!empty($xml)){ + $this->debug('Entering soap_parser(), length='.strlen($xml).', encoding='.$encoding); + // Create an XML parser - why not xml_parser_create_ns? + $this->parser = xml_parser_create($this->xml_encoding); + // Set the options for parsing the XML data. + //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding); + // Set the object for the parser. + xml_set_object($this->parser, $this); + // Set the element handlers for the parser. + xml_set_element_handler($this->parser, 'start_element','end_element'); + xml_set_character_data_handler($this->parser,'character_data'); + + // Parse the XML file. + if(!xml_parse($this->parser,$xml,true)){ + // Display an error message. + $err = sprintf('XML error parsing SOAP payload on line %d: %s', + xml_get_current_line_number($this->parser), + xml_error_string(xml_get_error_code($this->parser))); + $this->debug($err); + $this->debug("XML payload:\n" . $xml); + $this->setError($err); + } else { + $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name); + // get final value + $this->soapresponse = $this->message[$this->root_struct]['result']; + // get header value: no, because this is documented as XML string +// if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){ +// $this->responseHeaders = $this->message[$this->root_header]['result']; +// } + // resolve hrefs/ids + if(sizeof($this->multirefs) > 0){ + foreach($this->multirefs as $id => $hrefs){ + $this->debug('resolving multirefs for id: '.$id); + $idVal = $this->buildVal($this->ids[$id]); + foreach($hrefs as $refPos => $ref){ + $this->debug('resolving href at pos '.$refPos); + $this->multirefs[$id][$refPos] = $idVal; + } + } + } + } + xml_parser_free($this->parser); + } else { + $this->debug('xml was empty, didn\'t parse!'); + $this->setError('xml was empty, didn\'t parse!'); + } + } + + /** + * start-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @param string $attrs associative array of attributes + * @access private + */ + function start_element($parser, $name, $attrs) { + // position in a total number of elements, starting from 0 + // update class level pos + $pos = $this->position++; + // and set mine + $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>''); + // depth = how many levels removed from root? + // set mine as current global depth and increment global depth value + $this->message[$pos]['depth'] = $this->depth++; + + // else add self as child to whoever the current parent is + if($pos != 0){ + $this->message[$this->parent]['children'] .= '|'.$pos; + } + // set my parent + $this->message[$pos]['parent'] = $this->parent; + // set self as current parent + $this->parent = $pos; + // set self as current value for this depth + $this->depth_array[$this->depth] = $pos; + // get element prefix + if(strpos($name,':')){ + // get ns prefix + $prefix = substr($name,0,strpos($name,':')); + // get unqualified name + $name = substr(strstr($name,':'),1); + } + // set status + if($name == 'Envelope'){ + $this->status = 'envelope'; + } elseif($name == 'Header'){ + $this->root_header = $pos; + $this->status = 'header'; + } elseif($name == 'Body'){ + $this->status = 'body'; + $this->body_position = $pos; + // set method + } elseif($this->status == 'body' && $pos == ($this->body_position+1)){ + $this->status = 'method'; + $this->root_struct_name = $name; + $this->root_struct = $pos; + $this->message[$pos]['type'] = 'struct'; + $this->debug("found root struct $this->root_struct_name, pos $this->root_struct"); + } + // set my status + $this->message[$pos]['status'] = $this->status; + // set name + $this->message[$pos]['name'] = htmlspecialchars($name); + // set attrs + $this->message[$pos]['attrs'] = $attrs; + + // loop through atts, logging ns and type declarations + $attstr = ''; + foreach($attrs as $key => $value){ + $key_prefix = $this->getPrefix($key); + $key_localpart = $this->getLocalPart($key); + // if ns declarations, add to class level array of valid namespaces + if($key_prefix == 'xmlns'){ + if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){ + $this->XMLSchemaVersion = $value; + $this->namespaces['xsd'] = $this->XMLSchemaVersion; + $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance'; + } + $this->namespaces[$key_localpart] = $value; + // set method namespace + if($name == $this->root_struct_name){ + $this->methodNamespace = $value; + } + // if it's a type declaration, set type + } elseif($key_localpart == 'type'){ + $value_prefix = $this->getPrefix($value); + $value_localpart = $this->getLocalPart($value); + $this->message[$pos]['type'] = $value_localpart; + $this->message[$pos]['typePrefix'] = $value_prefix; + if(isset($this->namespaces[$value_prefix])){ + $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix]; + } else if(isset($attrs['xmlns:'.$value_prefix])) { + $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix]; + } + // should do something here with the namespace of specified type? + } elseif($key_localpart == 'arrayType'){ + $this->message[$pos]['type'] = 'array'; + /* do arrayType ereg here + [1] arrayTypeValue ::= atype asize + [2] atype ::= QName rank* + [3] rank ::= '[' (',')* ']' + [4] asize ::= '[' length~ ']' + [5] length ::= nextDimension* Digit+ + [6] nextDimension ::= Digit+ ',' + */ + $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]'; + if(ereg($expr,$value,$regs)){ + $this->message[$pos]['typePrefix'] = $regs[1]; + $this->message[$pos]['arrayTypePrefix'] = $regs[1]; + if (isset($this->namespaces[$regs[1]])) { + $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]]; + } else if (isset($attrs['xmlns:'.$regs[1]])) { + $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]]; + } + $this->message[$pos]['arrayType'] = $regs[2]; + $this->message[$pos]['arraySize'] = $regs[3]; + $this->message[$pos]['arrayCols'] = $regs[4]; + } + } + // log id + if($key == 'id'){ + $this->ids[$value] = $pos; + } + // root + if($key_localpart == 'root' && $value == 1){ + $this->status = 'method'; + $this->root_struct_name = $name; + $this->root_struct = $pos; + $this->debug("found root struct $this->root_struct_name, pos $pos"); + } + // for doclit + $attstr .= " $key=\"$value\""; + } + // get namespace - must be done after namespace atts are processed + if(isset($prefix)){ + $this->message[$pos]['namespace'] = $this->namespaces[$prefix]; + $this->default_namespace = $this->namespaces[$prefix]; + } else { + $this->message[$pos]['namespace'] = $this->default_namespace; + } + if($this->status == 'header'){ + if ($this->root_header != $pos) { + $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; + } + } elseif($this->root_struct_name != ''){ + $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; + } + } + + /** + * end-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @access private + */ + function end_element($parser, $name) { + // position of current element is equal to the last value left in depth_array for my depth + $pos = $this->depth_array[$this->depth--]; + + // get element prefix + if(strpos($name,':')){ + // get ns prefix + $prefix = substr($name,0,strpos($name,':')); + // get unqualified name + $name = substr(strstr($name,':'),1); + } + + // build to native type + if(isset($this->body_position) && $pos > $this->body_position){ + // deal w/ multirefs + if(isset($this->message[$pos]['attrs']['href'])){ + // get id + $id = substr($this->message[$pos]['attrs']['href'],1); + // add placeholder to href array + $this->multirefs[$id][$pos] = 'placeholder'; + // add set a reference to it as the result value + $this->message[$pos]['result'] =& $this->multirefs[$id][$pos]; + // build complex values + } elseif($this->message[$pos]['children'] != ''){ + + // if result has already been generated (struct/array + if(!isset($this->message[$pos]['result'])){ + $this->message[$pos]['result'] = $this->buildVal($pos); + } + + // set value of simple type + } else { + //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']); + if (isset($this->message[$pos]['type'])) { + $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); + } else { + $parent = $this->message[$pos]['parent']; + if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { + $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); + } else { + $this->message[$pos]['result'] = $this->message[$pos]['cdata']; + } + } + + /* add value to parent's result, if parent is struct/array + $parent = $this->message[$pos]['parent']; + if($this->message[$parent]['type'] != 'map'){ + if(strtolower($this->message[$parent]['type']) == 'array'){ + $this->message[$parent]['result'][] = $this->message[$pos]['result']; + } else { + $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result']; + } + } + */ + } + } + + // for doclit + if($this->status == 'header'){ + if ($this->root_header != $pos) { + $this->responseHeaders .= ""; + } + } elseif($pos >= $this->root_struct){ + $this->document .= ""; + } + // switch status + if($pos == $this->root_struct){ + $this->status = 'body'; + $this->root_struct_namespace = $this->message[$pos]['namespace']; + } elseif($name == 'Body'){ + $this->status = 'envelope'; + } elseif($name == 'Header'){ + $this->status = 'envelope'; + } elseif($name == 'Envelope'){ + // + } + // set parent back to my parent + $this->parent = $this->message[$pos]['parent']; + } + + /** + * element content handler + * + * @param string $parser XML parser object + * @param string $data element content + * @access private + */ + function character_data($parser, $data){ + $pos = $this->depth_array[$this->depth]; + if ($this->xml_encoding=='UTF-8'){ + // TODO: add an option to disable this for folks who want + // raw UTF-8 that, e.g., might not map to iso-8859-1 + // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1"); + if($this->decode_utf8){ + $data = utf8_decode($data); + } + } + $this->message[$pos]['cdata'] .= $data; + // for doclit + if($this->status == 'header'){ + $this->responseHeaders .= $data; + } else { + $this->document .= $data; + } + } + + /** + * get the parsed message + * + * @return mixed + * @access public + */ + function get_response(){ + return $this->soapresponse; + } + + /** + * get the parsed headers + * + * @return string XML or empty if no headers + * @access public + */ + function getHeaders(){ + return $this->responseHeaders; + } + + /** + * decodes entities + * + * @param string $text string to translate + * @access private + */ + function decode_entities($text){ + foreach($this->entities as $entity => $encoded){ + $text = str_replace($encoded,$entity,$text); + } + return $text; + } + + /** + * decodes simple types into PHP variables + * + * @param string $value value to decode + * @param string $type XML type to decode + * @param string $typens XML type namespace to decode + * @access private + */ + function decodeSimple($value, $type, $typens) { + // TODO: use the namespace! + if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') { + return (string) $value; + } + if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') { + return (int) $value; + } + if ($type == 'float' || $type == 'double' || $type == 'decimal') { + return (double) $value; + } + if ($type == 'boolean') { + if (strtolower($value) == 'false' || strtolower($value) == 'f') { + return false; + } + return (boolean) $value; + } + if ($type == 'base64' || $type == 'base64Binary') { + return base64_decode($value); + } + // obscure numeric types + if ($type == 'nonPositiveInteger' || $type == 'negativeInteger' + || $type == 'nonNegativeInteger' || $type == 'positiveInteger' + || $type == 'unsignedInt' + || $type == 'unsignedShort' || $type == 'unsignedByte') { + return (int) $value; + } + // everything else + return (string) $value; + } + + /** + * builds response structures for compound values (arrays/structs) + * + * @param string $pos position in node tree + * @access private + */ + function buildVal($pos){ + if(!isset($this->message[$pos]['type'])){ + $this->message[$pos]['type'] = ''; + } + $this->debug('inside buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']); + // if there are children... + if($this->message[$pos]['children'] != ''){ + $children = explode('|',$this->message[$pos]['children']); + array_shift($children); // knock off empty + // md array + if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){ + $r=0; // rowcount + $c=0; // colcount + foreach($children as $child_pos){ + $this->debug("got an MD array element: $r, $c"); + $params[$r][] = $this->message[$child_pos]['result']; + $c++; + if($c == $this->message[$pos]['arrayCols']){ + $c = 0; + $r++; + } + } + // array + } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){ + $this->debug('adding array '.$this->message[$pos]['name']); + foreach($children as $child_pos){ + $params[] = &$this->message[$child_pos]['result']; + } + // apache Map type: java hashtable + } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){ + foreach($children as $child_pos){ + $kv = explode("|",$this->message[$child_pos]['children']); + $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result']; + } + // generic compound type + //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') { + } else { + // Apache Vector type: treat as an array + if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') { + $notstruct = 1; + } else { + // is array or struct? + foreach($children as $child_pos){ + if(isset($keys) && isset($keys[$this->message[$child_pos]['name']])){ + $notstruct = 1; + break; + } + $keys[$this->message[$child_pos]['name']] = 1; + } + } + // + foreach($children as $child_pos){ + if(isset($notstruct)){ + $params[] = &$this->message[$child_pos]['result']; + } else { + if (isset($params[$this->message[$child_pos]['name']])) { + // de-serialize repeated element name into an array + if (!is_array($params[$this->message[$child_pos]['name']])) { + $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]); + } + $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result']; + } else { + $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result']; + } + } + } + } + return is_array($params) ? $params : array(); + } else { + $this->debug('no children'); + if(strpos($this->message[$pos]['cdata'],'&')){ + return strtr($this->message[$pos]['cdata'],array_flip($this->entities)); + } else { + return $this->message[$pos]['cdata']; + } + } + } +} + + + +?>call( string methodname [ ,array parameters] ); +* +* // bye bye client +* unset($soapclient); +* +* @author Dietrich Ayala +* @version $Id$ +* @access public +*/ +class nusoapclient extends nusoap_base { + + var $username = ''; + var $password = ''; + var $requestHeaders = false; // SOAP headers in request (text) + var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text) + var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text) + var $endpoint; + var $error_str = false; + var $proxyhost = ''; + var $proxyport = ''; + var $proxyusername = ''; + var $proxypassword = ''; + var $xml_encoding = ''; // character set encoding of incoming (response) messages + var $http_encoding = false; + var $timeout = 0; + var $response_timeout = 30; + var $endpointType = ''; + var $persistentConnection = false; + var $defaultRpcParams = false; + var $request = ''; // HTTP request + var $response = ''; // HTTP response + var $responseData = ''; // SOAP payload of response + // toggles whether the parser decodes element content w/ utf8_decode() + var $decode_utf8 = true; + + /** + * fault related variables + * + * @var fault + * @var faultcode + * @var faultstring + * @var faultdetail + * @access public + */ + var $fault, $faultcode, $faultstring, $faultdetail; + + /** + * constructor + * + * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object) + * @param bool $wsdl optional, set to true if using WSDL + * @param int $portName optional portName in WSDL document + * @param string $proxyhost + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @param integer $timeout set the connection timeout + * @param integer $response_timeout set the response timeout + * @access public + */ + function nusoapclient($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30){ + $this->endpoint = $endpoint; + $this->proxyhost = $proxyhost; + $this->proxyport = $proxyport; + $this->proxyusername = $proxyusername; + $this->proxypassword = $proxypassword; + $this->timeout = $timeout; + $this->response_timeout = $response_timeout; + + // make values + if($wsdl){ + $this->endpointType = 'wsdl'; + if (is_object($endpoint) && is_a($endpoint, 'wsdl')) { + $this->wsdl = $endpoint; + $this->endpoint = $this->wsdl->wsdl; + $this->wsdlFile = $this->endpoint; + $this->debug('existing wsdl instance created from ' . $this->endpoint); + } else { + $this->wsdlFile = $this->endpoint; + + // instantiate wsdl object and parse wsdl file + $this->debug('instantiating wsdl class with doc: '.$endpoint); + $this->wsdl = new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout); + } + $this->debug("wsdl debug...\n".$this->wsdl->debug_str); + $this->wsdl->debug_str = ''; + // catch errors + if($errstr = $this->wsdl->getError()){ + $this->debug('got wsdl error: '.$errstr); + $this->setError('wsdl error: '.$errstr); + } elseif($this->operations = $this->wsdl->getOperations()){ + $this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile); + } else { + $this->debug( 'getOperations returned false'); + $this->setError('no operations defined in the WSDL document!'); + } + } + } + + /** + * calls method, returns PHP native type + * + * @param string $method SOAP server URL or path + * @param array $params For RPC, array of parameters, can be associative or not. + * For literal, either the stringized XML for the body, + * or an array of parameters like the RPC case. The + * $rpcParams parameter controls this treatment, or + * the $defaultRpcParams field if $rpcParams is not + * specified. IMPORTANT: most services with literal + * parameters have document style, in which case there + * is really one parameter, the root of the fragment + * used in the call, which encloses what programmers + * normally think of parameters. A parameter array + * *must* include the wrapper. + * @param string $namespace optional method namespace (WSDL can override) + * @param string $soapAction optional SOAPAction value (WSDL can override) + * @param boolean $headers optional array of soapval objects for headers + * @param boolean $rpcParams optional treat params as RPC for use="literal" + * This can be used on a per-call basis to overrider defaultRpcParams. + * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override) + * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override) + * @return mixed + * @access public + */ + function call($operation,$params=array(),$namespace='',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){ + $this->operation = $operation; + $this->fault = false; + $this->error_str = ''; + $this->request = ''; + $this->response = ''; + $this->responseData = ''; + $this->faultstring = ''; + $this->faultcode = ''; + $this->opData = array(); + + $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $rpcParams"); + $this->debug("endpointType: $this->endpointType"); + if ($headers) { + $this->requestHeaders = $headers; + } + // if wsdl, get operation data and process parameters + if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){ + + $this->opData = $opData; + foreach($opData as $key => $value){ + $this->debug("$key -> $value"); + } + if (isset($opData['soapAction'])) { + $soapAction = $opData['soapAction']; + } + $this->endpoint = $opData['endpoint']; + $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : ($namespace != '' ? $namespace : 'http://testuri.org'); + $style = $opData['style']; + // add ns to ns array + if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){ + $this->wsdl->namespaces['nu'] = $namespace; + } + // serialize payload + + if($opData['input']['use'] == 'literal') { + if (is_null($rpcParams)) { + $rpcParams = $this->defaultRpcParams; + } + if ($rpcParams) { + $this->debug("serializing literal params for operation $operation"); + $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params); + $defaultNamespace = $this->wsdl->wsdl_info['targetNamespace']; + //$this->debug($this->varDump($params)); + } else { + // TODO: what? We want to treat $params as a scalar.... + $this->debug("serializing literal document for operation $operation"); + //$payload = is_array($params) ? array_shift($params) : $params; + $payload = $this->wsdl->serializeParameters($operation,'input',$params); + } + } else { + $this->debug("serializing encoded params for operation $operation"); + + // Partial fix for multiple encoding styles in the same function call + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + $payload = "<".$this->wsdl->getPrefixFromNamespace($namespace).":$operation"; + if(isset($opData['output']['encodingStyle']) && $encodingStyle != $opData['output']['encodingStyle']) { + $payload .= (' SOAP-ENV:encodingStyle="' . $opData['output']['encodingStyle'] . '"'); + } + $payload .= ('>' . $this->wsdl->serializeRPCParameters($operation,'input',$params). + 'wsdl->getPrefixFromNamespace($namespace).":$operation>"); + } + $this->debug('payload size: '.strlen($payload)); + // serialize envelope + $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$this->wsdl->usedNamespaces,$style); + $this->debug("wsdl debug: \n".$this->wsdl->debug_str); + $this->wsdl->debug_str = ''; + if ($errstr = $this->wsdl->getError()) { + $this->debug('got wsdl error: '.$errstr); + $this->setError('wsdl error: '.$errstr); + return false; + } + } elseif($this->endpointType == 'wsdl') { + $this->setError( 'operation '.$operation.' not present.'); + $this->debug("operation '$operation' not present."); + $this->debug("wsdl debug: \n".$this->wsdl->debug_str); + $this->wsdl->debug_str = ''; + return false; + // no wsdl + } else { + // make message + if($namespace == ''){ + $namespace = 'http://testuri.org'; + $this->wsdl->namespaces['ns1'] = $namespace; + } + // serialize envelope + // note: + $payload = ''; + if(is_array($params)){ + foreach($params as $k => $v){ + $payload .= $this->serialize_val($v,$k,false,false,false,false,$use); + } + } + $payload = "".$payload.""; + $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,array(),$style,$use); + } + $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace, style: $style"); + // send + $this->debug('sending msg (len: '.strlen($soapmsg).") w/ soapaction '$soapAction'..."); + $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout); + if($errstr = $this->getError()){ + $this->debug('Error: '.$errstr); + return false; + } else { + $this->return = $return; + $this->debug('sent message successfully and got a(n) '.gettype($return).' back'); + + // fault? + if(is_array($return) && isset($return['faultcode'])){ + $this->debug('got fault'); + $this->setError($return['faultcode'].': '.$return['faultstring']); + $this->fault = true; + foreach($return as $k => $v){ + $this->$k = $v; + $this->debug("$k = $v
    "); + } + return $return; + } else { + // array of return values + if(is_array($return)){ + // multiple 'out' parameters + if(sizeof($return) > 1){ + return $return; + } + // single 'out' parameter + return array_shift($return); + // nothing returned (ie, echoVoid) + } else { + return ""; + } + } + } + } + + /** + * get available data pertaining to an operation + * + * @param string $operation operation name + * @return array array of data pertaining to the operation + * @access public + */ + function getOperationData($operation){ + if(isset($this->operations[$operation])){ + return $this->operations[$operation]; + } + $this->debug("No data for operation: $operation"); + } + + /** + * send the SOAP message + * + * Note: if the operation has multiple return values + * the return value of this method will be an array + * of those values. + * + * @param string $msg a SOAPx4 soapmsg object + * @param string $soapaction SOAPAction value + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @return mixed native PHP types. + * @access private + */ + function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) { + // detect transport + switch(true){ + // http(s) + case ereg('^http',$this->endpoint): + $this->debug('transporting via HTTP'); + if($this->persistentConnection == true && is_object($this->persistentConnection)){ + $http =& $this->persistentConnection; + } else { + $http = new soap_transport_http($this->endpoint); + if ($this->persistentConnection) { + $http->usePersistentConnection(); + } + } + $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset()); + $http->setSOAPAction($soapaction); + if($this->proxyhost && $this->proxyport){ + $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword); + } + if($this->username != '' && $this->password != '') { + $http->setCredentials($this->username,$this->password); + } + if($this->http_encoding != ''){ + $http->setEncoding($this->http_encoding); + } + $this->debug('sending message, length: '.strlen($msg)); + if(ereg('^http:',$this->endpoint)){ + //if(strpos($this->endpoint,'http:')){ + $this->responseData = $http->send($msg,$timeout,$response_timeout); + } elseif(ereg('^https',$this->endpoint)){ + //} elseif(strpos($this->endpoint,'https:')){ + //if(phpversion() == '4.3.0-dev'){ + //$response = $http->send($msg,$timeout,$response_timeout); + //$this->request = $http->outgoing_payload; + //$this->response = $http->incoming_payload; + //} else + if (extension_loaded('curl')) { + $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout); + } else { + $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS'); + } + } else { + $this->setError('no http/s in endpoint url'); + } + $this->request = $http->outgoing_payload; + $this->response = $http->incoming_payload; + $this->debug("transport debug data...\n".$http->debug_str); + + // save transport object if using persistent connections + if ($this->persistentConnection) { + $http->debug_str = ''; + if (!is_object($this->persistentConnection)) { + $this->persistentConnection = $http; + } + } + + if($err = $http->getError()){ + $this->setError('HTTP Error: '.$err); + return false; + } elseif($this->getError()){ + return false; + } else { + $this->debug('got response, length: '. strlen($this->responseData).' type: '.$http->incoming_headers['content-type']); + return $this->parseResponse($http->incoming_headers, $this->responseData); + } + break; + default: + $this->setError('no transport found, or selected transport is not yet supported!'); + return false; + break; + } + } + + /** + * processes SOAP message returned from server + * + * @param array $headers The HTTP headers + * @param string $data unprocessed response data from server + * @return mixed value of the message, decoded into a PHP type + * @access protected + */ + function parseResponse($headers, $data) { + $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']); + if (!strstr($headers['content-type'], 'text/xml')) { + $this->setError('Response not of type text/xml'); + return false; + } + if (strpos($headers['content-type'], '=')) { + $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1)); + $this->debug('Got response encoding: ' . $enc); + if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){ + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common + $this->xml_encoding = 'UTF-8'; + } + $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser'); + $parser = new soap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8); + // add parser debug data to our debug + $this->debug($parser->debug_str); + // if parse errors + if($errstr = $parser->getError()){ + $this->setError( $errstr); + // destroy the parser object + unset($parser); + return false; + } else { + // get SOAP headers + $this->responseHeaders = $parser->getHeaders(); + // get decoded message + $return = $parser->get_response(); + // add document for doclit support + $this->document = $parser->document; + // destroy the parser object + unset($parser); + // return decode message + return $return; + } + } + + /** + * set the SOAP headers + * + * @param $headers string XML + * @access public + */ + function setHeaders($headers){ + $this->requestHeaders = $headers; + } + + /** + * get the response headers + * + * @return mixed object SOAPx4 soapval object or empty if no headers + * @access public + */ + function getHeaders(){ + if($this->responseHeaders != '') { + return $this->responseHeaders; + } + } + + /** + * set proxy info here + * + * @param string $proxyhost + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @access public + */ + function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') { + $this->proxyhost = $proxyhost; + $this->proxyport = $proxyport; + $this->proxyusername = $proxyusername; + $this->proxypassword = $proxypassword; + } + + /** + * if authenticating, set user credentials here + * + * @param string $username + * @param string $password + * @access public + */ + function setCredentials($username, $password) { + $this->username = $username; + $this->password = $password; + } + + /** + * use HTTP encoding + * + * @param string $enc + * @access public + */ + function setHTTPEncoding($enc='gzip, deflate'){ + $this->http_encoding = $enc; + } + + /** + * use HTTP persistent connections if possible + * + * @access public + */ + function useHTTPPersistentConnection(){ + $this->persistentConnection = true; + } + + /** + * gets the default RPC parameter setting. + * If true, default is that call params are like RPC even for document style. + * Each call() can override this value. + * + * @access public + */ + function getDefaultRpcParams() { + return $this->defaultRpcParams; + } + + /** + * sets the default RPC parameter setting. + * If true, default is that call params are like RPC even for document style + * Each call() can override this value. + * + * @param boolean $rpcParams + * @access public + */ + function setDefaultRpcParams($rpcParams) { + $this->defaultRpcParams = $rpcParams; + } + + /** + * dynamically creates proxy class, allowing user to directly call methods from wsdl + * + * @return object soap_proxy object + * @access public + */ + function getProxy(){ + $evalStr = ''; + foreach($this->operations as $operation => $opData){ + if($operation != ''){ + // create param string + $paramStr = ''; + if(sizeof($opData['input']['parts']) > 0){ + foreach($opData['input']['parts'] as $name => $type){ + $paramStr .= "\$$name,"; + } + $paramStr = substr($paramStr,0,strlen($paramStr)-1); + } + $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace']; + $evalStr .= "function $operation ($paramStr){ + // load params into array + \$params = array($paramStr); + return \$this->call('$operation',\$params,'".$opData['namespace']."','".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."'); + }"; + unset($paramStr); + } + } + $r = rand(); + $evalStr = 'class soap_proxy_'.$r.' extends nusoapclient { + '.$evalStr.' + }'; + //print "proxy class:
    $evalStr
    "; + // eval the class + eval($evalStr); + // instantiate proxy object + eval("\$proxy = new soap_proxy_$r('');"); + // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice + $proxy->endpointType = 'wsdl'; + $proxy->wsdlFile = $this->wsdlFile; + $proxy->wsdl = $this->wsdl; + $proxy->operations = $this->operations; + $proxy->defaultRpcParams = $this->defaultRpcParams; + // transfer other state + $proxy->username = $this->username; + $proxy->password = $this->password; + $proxy->proxyhost = $this->proxyhost; + $proxy->proxyport = $this->proxyport; + $proxy->proxyusername = $this->proxyusername; + $proxy->proxypassword = $this->proxypassword; + $proxy->timeout = $this->timeout; + $proxy->response_timeout = $this->response_timeout; + $proxy->http_encoding = $this->http_encoding; + $proxy->persistentConnection = $this->persistentConnection; + return $proxy; + } + + /** + * gets the HTTP body for the current request. + * + * @param string $soapmsg The SOAP payload + * @return string The HTTP body, which includes the SOAP payload + * @access protected + */ + function getHTTPBody($soapmsg) { + return $soapmsg; + } + + /** + * gets the HTTP content type for the current request. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type for the current request. + * @access protected + */ + function getHTTPContentType() { + return 'text/xml'; + } + + /** + * gets the HTTP content type charset for the current request. + * returns false for non-text content types. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type charset for the current request. + * @access protected + */ + function getHTTPContentTypeCharset() { + return $this->soap_defencoding; + } + + /* + * whether or not parser should decode utf8 element content + * + * @return always returns true + * @access public + */ + function decodeUTF8($bool){ + $this->decode_utf8 = $bool; + return true; + } +} +?> \ No newline at end of file diff --git a/include/classes/pclzip.lib.php b/include/classes/pclzip.lib.php new file mode 100644 index 000000000..89e862fbc --- /dev/null +++ b/include/classes/pclzip.lib.php @@ -0,0 +1,5694 @@ +zipname = $p_zipname; + $this->zip_fd = 0; + $this->magic_quotes_status = -1; + + // ----- Return + return; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // create($p_filelist, $p_add_dir="", $p_remove_dir="") + // create($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two different synopsis. The first one is historical. + // This method creates a Zip Archive. The Zip file is created in the + // filesystem. The files and directories indicated in $p_filelist + // are added in the archive. See the parameters description for the + // supported format of $p_filelist. + // When a directory is in the list, the directory and its content is added + // in the archive. + // In this synopsis, the function takes an optional variable list of + // options. See bellow the supported options. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function create($p_filelist) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove from the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } + else if ($v_size > 2) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Invalid number / type of arguments"); + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); + + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + } + + // ----- The list is a list of string names + else { + $v_string_list = $p_filelist; + } + } + + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + } + + // ----- Invalid variable type for $p_filelist + else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); + return 0; + } + + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + if ($v_string != '') { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } + else { + } + } + } + + // ----- For each file in the list check the attributes + $v_supported_attributes + = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' + ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' + ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' + ,PCLZIP_ATT_FILE_MTIME => 'optional' + ,PCLZIP_ATT_FILE_CONTENT => 'optional' + ,PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, + $v_filedescr_list[], + $v_options, + $v_supported_attributes); + if ($v_result != 1) { + return 0; + } + } + + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Call the create fct + $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Return + return $p_result_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // add($p_filelist, $p_add_dir="", $p_remove_dir="") + // add($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two synopsis. The first one is historical. + // This methods add the list of files in an existing archive. + // If a file with the same name already exists, it is added at the end of the + // archive, the first one is still present. + // If the archive does not exist, it is created. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_OPT_ADD_COMMENT : + // PCLZIP_OPT_PREPEND_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function add($p_filelist) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_ADD_COMMENT => 'optional', + PCLZIP_OPT_PREPEND_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); + + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + } + + // ----- The list is a list of string names + else { + $v_string_list = $p_filelist; + } + } + + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + } + + // ----- Invalid variable type for $p_filelist + else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist"); + return 0; + } + + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } + } + + // ----- For each file in the list check the attributes + $v_supported_attributes + = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' + ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' + ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' + ,PCLZIP_ATT_FILE_MTIME => 'optional' + ,PCLZIP_ATT_FILE_CONTENT => 'optional' + ,PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, + $v_filedescr_list[], + $v_options, + $v_supported_attributes); + if ($v_result != 1) { + return 0; + } + } + + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Call the create fct + $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Return + return $p_result_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : listContent() + // Description : + // This public method, gives the list of the files and directories, with their + // properties. + // The properties of each entries in the list are (used also in other functions) : + // filename : Name of the file. For a create or add action it is the filename + // given by the user. For an extract function it is the filename + // of the extracted file. + // stored_filename : Name of the file / directory stored in the archive. + // size : Size of the stored file. + // compressed_size : Size of the file's data compressed in the archive + // (without the headers overhead) + // mtime : Last known modification date of the file (UNIX timestamp) + // comment : Comment associated with the file + // folder : true | false + // index : index of the file in the archive + // status : status of the action (depending of the action) : + // Values are : + // ok : OK ! + // filtered : the file / dir is not extracted (filtered by user) + // already_a_directory : the file can not be extracted because a + // directory with the same name already exists + // write_protected : the file can not be extracted because a file + // with the same name already exists and is + // write protected + // newer_exist : the file was not extracted because a newer file exists + // path_creation_fail : the file is not extracted because the folder + // does not exist and can not be created + // write_error : the file was not extracted because there was a + // error while writing the file + // read_error : the file was not extracted because there was a error + // while reading the file + // invalid_header : the file was not extracted because of an archive + // format error (bad file header) + // Note that each time a method can continue operating when there + // is an action error on a file, the error is only logged in the file status. + // Return Values : + // 0 on an unrecoverable failure, + // The list of the files in the archive. + // -------------------------------------------------------------------------------- + function listContent() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Call the extracting fct + $p_list = array(); + if (($v_result = $this->privList($p_list)) != 1) + { + unset($p_list); + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // extract($p_path="./", $p_remove_path="") + // extract([$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method extract all the files / directories from the archive to the + // folder indicated in $p_path. + // If you want to ignore the 'root' part of path of the memorized files + // you can indicate this in the optional $p_remove_path parameter. + // By default, if a newer file with the same name already exists, the + // file is not extracted. + // + // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions + // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append + // at the end of the path value of PCLZIP_OPT_PATH. + // Parameters : + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 or a negative value on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function extract() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); +// $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional' + ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' + ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Trace + + // ----- Call the extracting fct + $p_list = array(); + $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, + $v_remove_all_path, $v_options); + if ($v_result < 1) { + unset($p_list); + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + + // -------------------------------------------------------------------------------- + // Function : + // extractByIndex($p_index, $p_path="./", $p_remove_path="") + // extractByIndex($p_index, [$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method is doing a partial extract of the archive. + // The extracted files or folders are identified by their index in the + // archive (from 0 to n). + // Note that if the index identify a folder, only the folder entry is + // extracted, not all the files included in the archive. + // Parameters : + // $p_index : A single index (integer) or a string of indexes of files to + // extract. The form of the string is "0,4-6,8-12" with only numbers + // and '-' for range or ',' to separate ranges. No spaces or ';' + // are allowed. + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and + // not as files. + // The resulting content is in a new field 'content' in the file + // structure. + // This option must be used alone (any other options are ignored). + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + //function extractByIndex($p_index, options...) + function extractByIndex($p_index) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); +// $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional' + ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' + ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + } + else { + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Trace + + // ----- Trick + // Here I want to reuse extractByRule(), so I need to parse the $p_index + // with privParseOptions() + $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index); + $v_options_trick = array(); + $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, + array (PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + return 0; + } + $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Call the extracting fct + if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) { + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // delete([$p_option, $p_option_value, ...]) + // Description : + // This method removes files from the archive. + // If no parameters are given, then all the archive is emptied. + // Parameters : + // None or optional arguments. + // Options : + // PCLZIP_OPT_BY_INDEX : + // PCLZIP_OPT_BY_NAME : + // PCLZIP_OPT_BY_EREG : + // PCLZIP_OPT_BY_PREG : + // Return Values : + // 0 on failure, + // The list of the files which are still present in the archive. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function delete() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Call the delete fct + $v_list = array(); + if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) { + $this->privSwapBackMagicQuotes(); + unset($v_list); + return(0); + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : deleteByIndex() + // Description : + // ***** Deprecated ***** + // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. + // -------------------------------------------------------------------------------- + function deleteByIndex($p_index) + { + + $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : properties() + // Description : + // This method gives the properties of the archive. + // The properties are : + // nb : Number of files in the archive + // comment : Comment associated with the archive file + // status : not_exist, ok + // Parameters : + // None + // Return Values : + // 0 on failure, + // An array with the archive properties. + // -------------------------------------------------------------------------------- + function properties() + { + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + $this->privSwapBackMagicQuotes(); + return(0); + } + + // ----- Default properties + $v_prop = array(); + $v_prop['comment'] = ''; + $v_prop['nb'] = 0; + $v_prop['status'] = 'not_exist'; + + // ----- Look if file exists + if (@is_file($this->zipname)) + { + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + + // ----- Return + return 0; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privSwapBackMagicQuotes(); + return 0; + } + + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Set the user attributes + $v_prop['comment'] = $v_central_dir['comment']; + $v_prop['nb'] = $v_central_dir['entries']; + $v_prop['status'] = 'ok'; + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_prop; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : duplicate() + // Description : + // This method creates an archive by copying the content of an other one. If + // the archive already exist, it is replaced by the new one without any warning. + // Parameters : + // $p_archive : The filename of a valid archive, or + // a valid PclZip object. + // Return Values : + // 1 on success. + // 0 or a negative value on error (error code). + // -------------------------------------------------------------------------------- + function duplicate($p_archive) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the $p_archive is a PclZip object + if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) + { + + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive->zipname); + } + + // ----- Look if the $p_archive is a string (so a filename) + else if (is_string($p_archive)) + { + + // ----- Check that $p_archive is a valid zip file + // TBC : Should also check the archive format + if (!is_file($p_archive)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); + $v_result = PCLZIP_ERR_MISSING_FILE; + } + else { + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive); + } + } + + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : merge() + // Description : + // This method merge the $p_archive_to_add archive at the end of the current + // one ($this). + // If the archive ($this) does not exist, the merge becomes a duplicate. + // If the $p_archive_to_add archive does not exist, the merge is a success. + // Parameters : + // $p_archive_to_add : It can be directly the filename of a valid zip archive, + // or a PclZip object archive. + // Return Values : + // 1 on success, + // 0 or negative values on error (see below). + // -------------------------------------------------------------------------------- + function merge($p_archive_to_add) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Look if the $p_archive_to_add is a PclZip object + if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) + { + + // ----- Merge the archive + $v_result = $this->privMerge($p_archive_to_add); + } + + // ----- Look if the $p_archive_to_add is a string (so a filename) + else if (is_string($p_archive_to_add)) + { + + // ----- Create a temporary archive + $v_object_archive = new PclZip($p_archive_to_add); + + // ----- Merge the archive + $v_result = $this->privMerge($v_object_archive); + } + + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + + + // -------------------------------------------------------------------------------- + // Function : errorCode() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorCode() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorCode()); + } + else { + return($this->error_code); + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorName() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorName($p_with_code=false) + { + $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', + PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', + PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', + PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', + PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', + PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', + PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', + PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', + PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', + PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', + PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', + PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', + PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', + PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', + PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', + PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', + PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE', + PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', + PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION' + ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE' + ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION' + ); + + if (isset($v_name[$this->error_code])) { + $v_value = $v_name[$this->error_code]; + } + else { + $v_value = 'NoName'; + } + + if ($p_with_code) { + return($v_value.' ('.$this->error_code.')'); + } + else { + return($v_value); + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorInfo() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorInfo($p_full=false) + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorString()); + } + else { + if ($p_full) { + return($this->errorName(true)." : ".$this->error_string); + } + else { + return($this->error_string." [code ".$this->error_code."]"); + } + } + } + // -------------------------------------------------------------------------------- + + +// -------------------------------------------------------------------------------- +// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** +// ***** ***** +// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** +// -------------------------------------------------------------------------------- + + + + // -------------------------------------------------------------------------------- + // Function : privCheckFormat() + // Description : + // This method check that the archive exists and is a valid zip archive. + // Several level of check exists. (futur) + // Parameters : + // $p_level : Level of check. Default 0. + // 0 : Check the first bytes (magic codes) (default value)) + // 1 : 0 + Check the central directory (futur) + // 2 : 1 + Check each file header (futur) + // Return Values : + // true on success, + // false on error, the error code is set. + // -------------------------------------------------------------------------------- + function privCheckFormat($p_level=0) + { + $v_result = true; + + // ----- Reset the file system cache + clearstatcache(); + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the file exits + if (!is_file($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'"); + return(false); + } + + // ----- Check that the file is readeable + if (!is_readable($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'"); + return(false); + } + + // ----- Check the magic code + // TBC + + // ----- Check the central header + // TBC + + // ----- Check each file header + // TBC + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privParseOptions() + // Description : + // This internal methods reads the variable list of arguments ($p_options_list, + // $p_size) and generate an array with the options and values ($v_result_list). + // $v_requested_options contains the options that can be present and those that + // must be present. + // $v_requested_options is an array, with the option value as key, and 'optional', + // or 'mandatory' as value. + // Parameters : + // See above. + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false) + { + $v_result=1; + + // ----- Read the options + $i=0; + while ($i<$p_size) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$p_options_list[$i]])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for next option + switch ($p_options_list[$i]) { + // ----- Look for options that request a path value + case PCLZIP_OPT_PATH : + case PCLZIP_OPT_REMOVE_PATH : + case PCLZIP_OPT_ADD_PATH : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_THRESHOLD : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + return PclZip::errorCode(); + } + + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + return PclZip::errorCode(); + } + + // ----- Check the value + $v_value = $p_options_list[$i+1]; + if ((!is_integer($v_value)) || ($v_value<0)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + return PclZip::errorCode(); + } + + // ----- Get the value (and convert it in bytes) + $v_result_list[$p_options_list[$i]] = $v_value*1048576; + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_ON : + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_TEMP_FILE_OFF : + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'"); + return PclZip::errorCode(); + } + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if ( is_string($p_options_list[$i+1]) + && ($p_options_list[$i+1] != '')) { + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); + $i++; + } + else { + } + break; + + // ----- Look for options that request an array of string for value + case PCLZIP_OPT_BY_NAME : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an EREG or PREG expression + case PCLZIP_OPT_BY_EREG : + // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG + // to PCLZIP_OPT_BY_PREG + $p_options_list[$i] = PCLZIP_OPT_BY_PREG; + case PCLZIP_OPT_BY_PREG : + //case PCLZIP_OPT_CRYPT : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that takes a string + case PCLZIP_OPT_COMMENT : + case PCLZIP_OPT_ADD_COMMENT : + case PCLZIP_OPT_PREPEND_COMMENT : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, + "Missing parameter value for option '" + .PclZipUtilOptionText($p_options_list[$i]) + ."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, + "Wrong parameter value for option '" + .PclZipUtilOptionText($p_options_list[$i]) + ."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an array of index + case PCLZIP_OPT_BY_INDEX : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_work_list = array(); + if (is_string($p_options_list[$i+1])) { + + // ----- Remove spaces + $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', ''); + + // ----- Parse items + $v_work_list = explode(",", $p_options_list[$i+1]); + } + else if (is_integer($p_options_list[$i+1])) { + $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + $v_work_list = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Reduce the index list + // each index item in the list must be a couple with a start and + // an end value : [0,3], [5-5], [8-10], ... + // ----- Check the format of each item + $v_sort_flag=false; + $v_sort_value=0; + for ($j=0; $j= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + $i++; + break; + + // ----- Look for options that request a call-back + case PCLZIP_CB_PRE_EXTRACT : + case PCLZIP_CB_POST_EXTRACT : + case PCLZIP_CB_PRE_ADD : + case PCLZIP_CB_POST_ADD : + /* for futur use + case PCLZIP_CB_PRE_DELETE : + case PCLZIP_CB_POST_DELETE : + case PCLZIP_CB_PRE_LIST : + case PCLZIP_CB_POST_LIST : + */ + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_function_name = $p_options_list[$i+1]; + + // ----- Check that the value is a valid existing function + if (!function_exists($v_function_name)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Set the attribute + $v_result_list[$p_options_list[$i]] = $v_function_name; + $i++; + break; + + default : + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Unknown parameter '" + .$p_options_list[$i]."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Next options + $i++; + } + + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($v_result_list[$key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); + + // ----- Return + return PclZip::errorCode(); + } + } + } + } + + // ----- Look for default values + if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { + + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privOptionDefaultThreshold() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privOptionDefaultThreshold(&$p_options) + { + $v_result=1; + + if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { + return $v_result; + } + + // ----- Get 'memory_limit' configuration value + $v_memory_limit = ini_get('memory_limit'); + $v_memory_limit = trim($v_memory_limit); + $last = strtolower(substr($v_memory_limit, -1)); + + if($last == 'g') + //$v_memory_limit = $v_memory_limit*1024*1024*1024; + $v_memory_limit = $v_memory_limit*1073741824; + if($last == 'm') + //$v_memory_limit = $v_memory_limit*1024*1024; + $v_memory_limit = $v_memory_limit*1048576; + if($last == 'k') + $v_memory_limit = $v_memory_limit*1024; + + $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO); + + + // ----- Sanity check : No threshold if value lower than 1M + if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { + unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privFileDescrParseAtt() + // Description : + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false) + { + $v_result=1; + + // ----- For each file in the list check the attributes + foreach ($p_file_list as $v_key => $v_value) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$v_key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for attribute + switch ($v_key) { + case PCLZIP_ATT_FILE_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['filename'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + break; + + case PCLZIP_ATT_FILE_NEW_SHORT_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_short_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + break; + + case PCLZIP_ATT_FILE_NEW_FULL_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_full_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + break; + + // ----- Look for options that takes a string + case PCLZIP_ATT_FILE_COMMENT : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['comment'] = $v_value; + break; + + case PCLZIP_ATT_FILE_MTIME : + if (!is_integer($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['mtime'] = $v_value; + break; + + case PCLZIP_ATT_FILE_CONTENT : + $p_filedescr['content'] = $v_value; + break; + + default : + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Unknown parameter '".$v_key."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($p_file_list[$key])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); + return PclZip::errorCode(); + } + } + } + } + + // end foreach + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privFileDescrExpand() + // Description : + // This method look for each item of the list to see if its a file, a folder + // or a string to be added as file. For any other type of files (link, other) + // just ignore the item. + // Then prepare the information that will be stored for that file. + // When its a folder, expand the folder with all the files that are in that + // folder (recursively). + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privFileDescrExpand(&$p_filedescr_list, &$p_options) + { + $v_result=1; + + // ----- Create a result list + $v_result_list = array(); + + // ----- Look each entry + for ($i=0; $iprivCalculateStoredFilename($v_descr, $p_options); + + // ----- Add the descriptor in result list + $v_result_list[sizeof($v_result_list)] = $v_descr; + + // ----- Look for folder + if ($v_descr['type'] == 'folder') { + // ----- List of items in folder + $v_dirlist_descr = array(); + $v_dirlist_nb = 0; + if ($v_folder_handler = @opendir($v_descr['filename'])) { + while (($v_item_handler = @readdir($v_folder_handler)) !== false) { + + // ----- Skip '.' and '..' + if (($v_item_handler == '.') || ($v_item_handler == '..')) { + continue; + } + + // ----- Compose the full filename + $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler; + + // ----- Look for different stored filename + // Because the name of the folder was changed, the name of the + // files/sub-folders also change + if (($v_descr['stored_filename'] != $v_descr['filename']) + && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { + if ($v_descr['stored_filename'] != '') { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler; + } + else { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; + } + } + + $v_dirlist_nb++; + } + + @closedir($v_folder_handler); + } + else { + // TBC : unable to open folder in read mode + } + + // ----- Expand each element of the list + if ($v_dirlist_nb != 0) { + // ----- Expand + if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { + return $v_result; + } + + // ----- Concat the resulting list + $v_result_list = array_merge($v_result_list, $v_dirlist_descr); + } + else { + } + + // ----- Free local array + unset($v_dirlist_descr); + } + } + + // ----- Get the result list + $p_filedescr_list = $v_result_list; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCreate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privCreate($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the file in write mode + if (($v_result = $this->privOpenFd('wb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Add the list of files + $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options); + + // ----- Close + $this->privCloseFd(); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAdd() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAdd($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Look if the archive exists or is empty + if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) + { + + // ----- Do a create + $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options); + + // ----- Return + return $v_result; + } + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) + { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Create the Central Dir files header + for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = $v_central_dir['comment']; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) { + $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Close + $this->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privOpenFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privOpenFd($p_mode) + { + $v_result=1; + + // ----- Look if already open + if ($this->zip_fd != 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCloseFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privCloseFd() + { + $v_result=1; + + if ($this->zip_fd != 0) + @fclose($this->zip_fd); + $this->zip_fd = 0; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddList() + // Description : + // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is + // different from the real path of the file. This is usefull if you want to have PclTar + // running in any directory, and memorize relative path from an other directory. + // Parameters : + // $p_list : An array containing the file or directory names to add in the tar + // $p_result_list : list of added files with their properties (specially the status field) + // $p_add_dir : Path to add in the filename path archived + // $p_remove_dir : Path to remove in the filename path archived + // Return Values : + // -------------------------------------------------------------------------------- +// function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + function privAddList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Create the Central Dir files header + for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileList() + // Description : + // Parameters : + // $p_filedescr_list : An array containing the file description + // or directory names to add in the zip + // $p_result_list : list of added files with their properties (specially the status field) + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_header = array(); + + // ----- Recuperate the current number of elt in list + $v_nb = sizeof($p_result_list); + + // ----- Loop on the files + for ($j=0; ($jprivAddFile($p_filedescr_list[$j], $v_header, + $p_options); + if ($v_result != 1) { + return $v_result; + } + + // ----- Store the file infos + $p_result_list[$v_nb++] = $v_header; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFile($p_filedescr, &$p_header, &$p_options) + { + $v_result=1; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + // TBC : Already done in the fileAtt check ... ? + if ($p_filename == "") { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for a stored different filename + /* TBC : Removed + if (isset($p_filedescr['stored_filename'])) { + $v_stored_filename = $p_filedescr['stored_filename']; + } + else { + $v_stored_filename = $p_filedescr['stored_filename']; + } + */ + + // ----- Set the file properties + clearstatcache(); + $p_header['version'] = 20; + $p_header['version_extracted'] = 10; + $p_header['flag'] = 0; + $p_header['compression'] = 0; + $p_header['crc'] = 0; + $p_header['compressed_size'] = 0; + $p_header['filename_len'] = strlen($p_filename); + $p_header['extra_len'] = 0; + $p_header['disk'] = 0; + $p_header['internal'] = 0; + $p_header['offset'] = 0; + $p_header['filename'] = $p_filename; +// TBC : Removed $p_header['stored_filename'] = $v_stored_filename; + $p_header['stored_filename'] = $p_filedescr['stored_filename']; + $p_header['extra'] = ''; + $p_header['status'] = 'ok'; + $p_header['index'] = -1; + + // ----- Look for regular file + if ($p_filedescr['type']=='file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = filesize($p_filename); + } + + // ----- Look for regular folder + else if ($p_filedescr['type']=='folder') { + $p_header['external'] = 0x00000010; + $p_header['mtime'] = filemtime($p_filename); + $p_header['size'] = filesize($p_filename); + } + + // ----- Look for virtual file + else if ($p_filedescr['type'] == 'virtual_file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = strlen($p_filedescr['content']); + } + + + // ----- Look for filetime + if (isset($p_filedescr['mtime'])) { + $p_header['mtime'] = $p_filedescr['mtime']; + } + else if ($p_filedescr['type'] == 'virtual_file') { + $p_header['mtime'] = time(); + } + else { + $p_header['mtime'] = filemtime($p_filename); + } + + // ------ Look for file comment + if (isset($p_filedescr['comment'])) { + $p_header['comment_len'] = strlen($p_filedescr['comment']); + $p_header['comment'] = $p_filedescr['comment']; + } + else { + $p_header['comment_len'] = 0; + $p_header['comment'] = ''; + } + + // ----- Look for pre-add callback + if (isset($p_options[PCLZIP_CB_PRE_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCLZIP_CB_PRE_ADD, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_header['status'] = "skipped"; + $v_result = 1; + } + + // ----- Update the informations + // Only some fields can be modified + if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { + $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); + } + } + + // ----- Look for empty stored filename + if ($p_header['stored_filename'] == "") { + $p_header['status'] = "filtered"; + } + + // ----- Check the path length + if (strlen($p_header['stored_filename']) > 0xFF) { + $p_header['status'] = 'filename_too_long'; + } + + // ----- Look if no error, or file not skipped + if ($p_header['status'] == 'ok') { + + // ----- Look for a file + if ($p_filedescr['type'] == 'file') { + // ----- Look for using temporary file to zip + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) + || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) { + $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + } + + // ----- Use "in memory" zip algo + else { + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + return PclZip::errorCode(); + } + + // ----- Read the file content + $v_content = @fread($v_file, $p_header['size']); + + // ----- Close the file + @fclose($v_file); + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + } + + // ----- Look for normal compression + else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + + } + + } + + // ----- Look for a virtual file (a file from string) + else if ($p_filedescr['type'] == 'virtual_file') { + + $v_content = $p_filedescr['content']; + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + } + + // ----- Look for normal compression + else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + } + + // ----- Look for a directory + else if ($p_filedescr['type'] == 'folder') { + // ----- Look for directory last '/' + if (@substr($p_header['stored_filename'], -1) != '/') { + $p_header['stored_filename'] .= '/'; + } + + // ----- Set the file properties + $p_header['size'] = 0; + //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked + $p_header['external'] = 0x00000010; // Value for a folder : to be checked + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) + { + return $v_result; + } + } + } + + // ----- Look for post-add callback + if (isset($p_options[PCLZIP_CB_POST_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PCLZIP_CB_POST_ADD, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Ignored + $v_result = 1; + } + + // ----- Update the informations + // Nothing can be modified + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) + { + $v_result=PCLZIP_ERR_NO_ERROR; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + return PclZip::errorCode(); + } + + // ----- Creates a compressed temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; + if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = filesize($p_filename); + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @gzputs($v_file_compressed, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file); + @gzclose($v_file_compressed); + + // ----- Check the minimum file size + if (filesize($v_gzip_temp_name) < 18) { + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes'); + return PclZip::errorCode(); + } + + // ----- Extract the compressed attributes + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + // ----- Read the gzip file header + $v_binary_data = @fread($v_file_compressed, 10); + $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data); + + // ----- Check some parameters + $v_data_header['os'] = bin2hex($v_data_header['os']); + + // ----- Read the gzip file footer + @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8); + $v_binary_data = @fread($v_file_compressed, 8); + $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data); + + // ----- Set the attributes + $p_header['compression'] = ord($v_data_header['cm']); + //$p_header['mtime'] = $v_data_header['mtime']; + $p_header['crc'] = $v_data_footer['crc']; + $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18; + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + return $v_result; + } + + // ----- Add the compressed data + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) + { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + fseek($v_file_compressed, 10); + $v_size = $p_header['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file_compressed, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Unlink the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCalculateStoredFilename() + // Description : + // Based on file descriptor properties and global options, this method + // calculate the filename that will be stored in the archive. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privCalculateStoredFilename(&$p_filedescr, &$p_options) + { + $v_result=1; + + // ----- Working variables + $p_filename = $p_filedescr['filename']; + if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { + $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH]; + } + else { + $p_add_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) { + $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH]; + } + else { + $p_remove_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + else { + $p_remove_all_dir = 0; + } + + + // ----- Look for full name change + if (isset($p_filedescr['new_full_name'])) { + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); + } + + // ----- Look for path and/or short name change + else { + + // ----- Look for short name change + // Its when we cahnge just the filename but not the path + if (isset($p_filedescr['new_short_name'])) { + $v_path_info = pathinfo($p_filename); + $v_dir = ''; + if ($v_path_info['dirname'] != '') { + $v_dir = $v_path_info['dirname'].'/'; + } + $v_stored_filename = $v_dir.$p_filedescr['new_short_name']; + } + else { + // ----- Calculate the stored filename + $v_stored_filename = $p_filename; + } + + // ----- Look for all path to remove + if ($p_remove_all_dir) { + $v_stored_filename = basename($p_filename); + } + // ----- Look for partial path remove + else if ($p_remove_dir != "") { + if (substr($p_remove_dir, -1) != '/') + $p_remove_dir .= "/"; + + if ( (substr($p_filename, 0, 2) == "./") + || (substr($p_remove_dir, 0, 2) == "./")) { + + if ( (substr($p_filename, 0, 2) == "./") + && (substr($p_remove_dir, 0, 2) != "./")) { + $p_remove_dir = "./".$p_remove_dir; + } + if ( (substr($p_filename, 0, 2) != "./") + && (substr($p_remove_dir, 0, 2) == "./")) { + $p_remove_dir = substr($p_remove_dir, 2); + } + } + + $v_compare = PclZipUtilPathInclusion($p_remove_dir, + $v_stored_filename); + if ($v_compare > 0) { + if ($v_compare == 2) { + $v_stored_filename = ""; + } + else { + $v_stored_filename = substr($v_stored_filename, + strlen($p_remove_dir)); + } + } + } + + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); + + // ----- Look for path to add + if ($p_add_dir != "") { + if (substr($p_add_dir, -1) == "/") + $v_stored_filename = $p_add_dir.$v_stored_filename; + else + $v_stored_filename = $p_add_dir."/".$v_stored_filename; + } + } + + // ----- Filename (reduce the path of stored name) + $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); + $p_filedescr['stored_filename'] = $v_stored_filename; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteFileHeader(&$p_header) + { + $v_result=1; + + // ----- Store the offset position of the file + $p_header['offset'] = ftell($this->zip_fd); + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + // ----- Packed data + $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, + $p_header['version_extracted'], $p_header['flag'], + $p_header['compression'], $v_mtime, $v_mdate, + $p_header['crc'], $p_header['compressed_size'], + $p_header['size'], + strlen($p_header['stored_filename']), + $p_header['extra_len']); + + // ----- Write the first 148 bytes of the header in the archive + fputs($this->zip_fd, $v_binary_data, 30); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralFileHeader(&$p_header) + { + $v_result=1; + + // TBC + //for(reset($p_header); $key = key($p_header); next($p_header)) { + //} + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + + // ----- Packed data + $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, + $p_header['version'], $p_header['version_extracted'], + $p_header['flag'], $p_header['compression'], + $v_mtime, $v_mdate, $p_header['crc'], + $p_header['compressed_size'], $p_header['size'], + strlen($p_header['stored_filename']), + $p_header['extra_len'], $p_header['comment_len'], + $p_header['disk'], $p_header['internal'], + $p_header['external'], $p_header['offset']); + + // ----- Write the 42 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 46); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + if ($p_header['comment_len'] != 0) + { + fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) + { + $v_result=1; + + // ----- Packed data + $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, + $p_nb_entries, $p_size, + $p_offset, strlen($p_comment)); + + // ----- Write the 22 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 22); + + // ----- Write the variable fields + if (strlen($p_comment) != 0) + { + fputs($this->zip_fd, $p_comment, strlen($p_comment)); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privList() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privList(&$p_list) + { + $v_result=1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Go to beginning of Central Dir + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_central_dir['offset'])) + { + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + for ($i=0; $i<$v_central_dir['entries']; $i++) + { + // ----- Read the file header + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + $v_header['index'] = $i; + + // ----- Get the only interesting attributes + $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); + unset($v_header); + } + + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privConvertHeader2FileInfo() + // Description : + // This function takes the file informations from the central directory + // entries and extract the interesting parameters that will be given back. + // The resulting file infos are set in the array $p_info + // $p_info['filename'] : Filename with full path. Given by user (add), + // extracted in the filesystem (extract). + // $p_info['stored_filename'] : Stored filename in the archive. + // $p_info['size'] = Size of the file. + // $p_info['compressed_size'] = Compressed size of the file. + // $p_info['mtime'] = Last modification date of the file. + // $p_info['comment'] = Comment associated with the file. + // $p_info['folder'] = true/false : indicates if the entry is a folder or not. + // $p_info['status'] = status of the action on the file. + // $p_info['crc'] = CRC of the file content. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privConvertHeader2FileInfo($p_header, &$p_info) + { + $v_result=1; + + // ----- Get the interesting attributes + $v_temp_path = PclZipUtilPathReduction($p_header['filename']); + $p_info['filename'] = $v_temp_path; + $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']); + $p_info['stored_filename'] = $v_temp_path; + $p_info['size'] = $p_header['size']; + $p_info['compressed_size'] = $p_header['compressed_size']; + $p_info['mtime'] = $p_header['mtime']; + $p_info['comment'] = $p_header['comment']; + $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); + $p_info['index'] = $p_header['index']; + $p_info['status'] = $p_header['status']; + $p_info['crc'] = $p_header['crc']; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractByRule() + // Description : + // Extract a file or directory depending of rules (by index, by name, ...) + // Parameters : + // $p_file_list : An array where will be placed the properties of each + // extracted file + // $p_path : Path to add while writing the extracted files + // $p_remove_path : Path to remove (from the file memorized path) while writing the + // extracted files. If the path does not match the file path, + // the file is extracted with its memorized path. + // $p_remove_path does not apply to 'list' mode. + // $p_path and $p_remove_path are commulative. + // Return Values : + // 1 on success,0 or less on error (see error code list) + // -------------------------------------------------------------------------------- + function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result=1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check the path + if ( ($p_path == "") + || ( (substr($p_path, 0, 1) != "/") + && (substr($p_path, 0, 3) != "../") + && (substr($p_path,1,2)!=":/"))) + $p_path = "./".$p_path; + + // ----- Reduce the path last (and duplicated) '/' + if (($p_path != "./") && ($p_path != "/")) + { + // ----- Look for the path end '/' + while (substr($p_path, -1) == "/") + { + $p_path = substr($p_path, 0, strlen($p_path)-1); + } + } + + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) + { + $p_remove_path .= '/'; + } + $p_remove_path_size = strlen($p_remove_path); + + // ----- Open the zip file + if (($v_result = $this->privOpenFd('rb')) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + + // ----- Read each entry + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + + // ----- Read next Central dir entry + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Store the index + $v_header['index'] = $i; + + // ----- Store the file position + $v_pos_entry = ftell($this->zip_fd); + + // ----- Look for the specific extract rules + $v_extract = false; + + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_extract = true; + } + } + // ----- Look for a filename + elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_extract = true; + } + } + } + + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { + $v_extract = true; + } + } + */ + + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { + $v_extract = true; + } + } + + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_extract = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j+1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + break; + } + } + } + + // ----- Look for no rule, which means extract all the archive + else { + $v_extract = true; + } + + // ----- Check compression method + if ( ($v_extract) + && ( ($v_header['compression'] != 8) + && ($v_header['compression'] != 0))) { + $v_header['status'] = 'unsupported_compression'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, + "Filename '".$v_header['stored_filename']."' is " + ."compressed by an unsupported compression " + ."method (".$v_header['compression'].") "); + + return PclZip::errorCode(); + } + } + + // ----- Check encrypted files + if (($v_extract) && (($v_header['flag'] & 1) == 1)) { + $v_header['status'] = 'unsupported_encryption'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, + "Unsupported encryption for " + ." filename '".$v_header['stored_filename'] + ."'"); + + return PclZip::errorCode(); + } + } + + // ----- Look for real extraction + if (($v_extract) && ($v_header['status'] != 'ok')) { + $v_result = $this->privConvertHeader2FileInfo($v_header, + $p_file_list[$v_nb_extracted++]); + if ($v_result != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + $v_extract = false; + } + + // ----- Look for real extraction + if ($v_extract) + { + + // ----- Go to the file position + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_header['offset'])) + { + // ----- Close the zip file + $this->privCloseFd(); + + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for extraction as string + if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { + + $v_string = ''; + + // ----- Extracting the file + $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Set the file content + $p_file_list[$v_nb_extracted]['content'] = $v_string; + + // ----- Next extracted file + $v_nb_extracted++; + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + // ----- Look for extraction in standard output + elseif ( (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) + && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) { + // ----- Extracting the file in standard output + $v_result1 = $this->privExtractFileInOutput($v_header, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + // ----- Look for normal extraction + else { + // ----- Extracting the file + $v_result1 = $this->privExtractFile($v_header, + $p_path, $p_remove_path, + $p_remove_all_path, + $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + } + } + + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFile() + // Description : + // Parameters : + // Return Values : + // + // 1 : ... ? + // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback + // -------------------------------------------------------------------------------- + function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result=1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for all path to remove + if ($p_remove_all_path == true) { + // ----- Look for folder entry that not need to be extracted + if (($p_entry['external']&0x00000010)==0x00000010) { + + $p_entry['status'] = "filtered"; + + return $v_result; + } + + // ----- Get the basename of the path + $p_entry['filename'] = basename($p_entry['filename']); + } + + // ----- Look for path to remove + else if ($p_remove_path != "") + { + if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) + { + + // ----- Change the file status + $p_entry['status'] = "filtered"; + + // ----- Return + return $v_result; + } + + $p_remove_path_size = strlen($p_remove_path); + if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) + { + + // ----- Remove the path + $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); + + } + } + + // ----- Add the path + if ($p_path != '') { + $p_entry['filename'] = $p_path."/".$p_entry['filename']; + } + + // ----- Check a base_dir_restriction + if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { + $v_inclusion + = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], + $p_entry['filename']); + if ($v_inclusion == 0) { + + PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, + "Filename '".$p_entry['filename']."' is " + ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION"); + + return PclZip::errorCode(); + } + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Look for specific actions while the file exist + if (file_exists($p_entry['filename'])) + { + + // ----- Look if file is a directory + if (is_dir($p_entry['filename'])) + { + + // ----- Change the file status + $p_entry['status'] = "already_a_directory"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, + "Filename '".$p_entry['filename']."' is " + ."already used by an existing directory"); + + return PclZip::errorCode(); + } + } + // ----- Look if file is write protected + else if (!is_writeable($p_entry['filename'])) + { + + // ----- Change the file status + $p_entry['status'] = "write_protected"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, + "Filename '".$p_entry['filename']."' exists " + ."and is write protected"); + + return PclZip::errorCode(); + } + } + + // ----- Look if the extracted file is older + else if (filemtime($p_entry['filename']) > $p_entry['mtime']) + { + // ----- Change the file status + if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER])) + && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) { + } + else { + $p_entry['status'] = "newer_exist"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, + "Newer version of '".$p_entry['filename']."' exists " + ."and option PCLZIP_OPT_REPLACE_NEWER is not selected"); + + return PclZip::errorCode(); + } + } + } + else { + } + } + + // ----- Check the directory availability and create it if necessary + else { + if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) + $v_dir_to_check = $p_entry['filename']; + else if (!strstr($p_entry['filename'], "/")) + $v_dir_to_check = ""; + else + $v_dir_to_check = dirname($p_entry['filename']); + + if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { + + // ----- Change the file status + $p_entry['status'] = "path_creation_fail"; + + // ----- Return + //return $v_result; + $v_result = 1; + } + } + } + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) + { + // ----- Look for not compressed file + if ($p_entry['compression'] == 0) { + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) + { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + // ----- Return + return $v_result; + } + + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + /* Try to speed up the code + $v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_binary_data, $v_read_size); + */ + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Closing the destination file + fclose($v_dest_file); + + // ----- Change the file mtime + touch($p_entry['filename'], $p_entry['mtime']); + + + } + else { + // ----- TBC + // Need to be finished + if (($p_entry['flag'] & 1) == 1) { + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.'); + return PclZip::errorCode(); + } + + + // ----- Look for using temporary file to unzip + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) + || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) { + $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + } + + // ----- Look for extract in memory + else { + + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = @gzinflate($v_buffer); + unset($v_buffer); + if ($v_file_content === FALSE) { + + // ----- Change the file status + // TBC + $p_entry['status'] = "error"; + + return $v_result; + } + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + return $v_result; + } + + // ----- Write the uncompressed data + @fwrite($v_dest_file, $v_file_content, $p_entry['size']); + unset($v_file_content); + + // ----- Closing the destination file + @fclose($v_dest_file); + + } + + // ----- Change the file mtime + @touch($p_entry['filename'], $p_entry['mtime']); + } + + // ----- Look for chmod option + if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { + + // ----- Change the mode of the file + @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); + } + + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileUsingTempFile(&$p_entry, &$p_options) + { + $v_result=1; + + // ----- Creates a temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; + if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); + return PclZip::errorCode(); + } + + + // ----- Write gz file format header + $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3)); + @fwrite($v_dest_file, $v_binary_data, 10); + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Write gz file format footer + $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']); + @fwrite($v_dest_file, $v_binary_data, 8); + + // ----- Close the temporary file + @fclose($v_dest_file); + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + $p_entry['status'] = "write_error"; + return $v_result; + } + + // ----- Open the temporary gz file + if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) { + @fclose($v_dest_file); + $p_entry['status'] = "read_error"; + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($v_src_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + @fclose($v_dest_file); + @gzclose($v_src_file); + + // ----- Delete the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileInOutput() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileInOutput(&$p_entry, &$p_options) + { + $v_result=1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) { + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + // ----- Trace + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) { + // ----- Look for not compressed file + if ($p_entry['compressed_size'] == $p_entry['size']) { + + // ----- Read the file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Send the file to the output + echo $v_buffer; + unset($v_buffer); + } + else { + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = gzinflate($v_buffer); + unset($v_buffer); + + // ----- Send the file to the output + echo $v_file_content; + unset($v_file_content); + } + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileAsString() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileAsString(&$p_entry, &$p_string, &$p_options) + { + $v_result=1; + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) { + // ----- Look for not compressed file + // if ($p_entry['compressed_size'] == $p_entry['size']) + if ($p_entry['compression'] == 0) { + + // ----- Reading the file + $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); + } + else { + + // ----- Reading the file + $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + if (($p_string = @gzinflate($v_data)) === FALSE) { + // TBC + } + } + + // ----- Trace + } + else { + // TBC : error : can not extract a folder in a string + } + + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Swap the content to header + $v_local_header['content'] = $p_string; + $p_string = ''; + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT].'(PCLZIP_CB_POST_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Swap back the content to header + $p_string = $v_local_header['content']; + unset($v_local_header['content']); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadFileHeader(&$p_header) + { + $v_result=1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x04034b50) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 26); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 26) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); + + // ----- Get filename + $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); + + // ----- Get extra_fields + if ($v_data['extra_len'] != 0) { + $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); + } + else { + $p_header['extra'] = ''; + } + + // ----- Extract properties + $p_header['version_extracted'] = $v_data['version']; + $p_header['compression'] = $v_data['compression']; + $p_header['size'] = $v_data['size']; + $p_header['compressed_size'] = $v_data['compressed_size']; + $p_header['crc'] = $v_data['crc']; + $p_header['flag'] = $v_data['flag']; + $p_header['filename_len'] = $v_data['filename_len']; + + // ----- Recuperate date in UNIX format + $p_header['mdate'] = $v_data['mdate']; + $p_header['mtime'] = $v_data['mtime']; + if ($p_header['mdate'] && $p_header['mtime']) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } + else + { + $p_header['mtime'] = time(); + } + + // TBC + //for(reset($v_data); $key = key($v_data); next($v_data)) { + //} + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set the status field + $p_header['status'] = "ok"; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadCentralFileHeader(&$p_header) + { + $v_result=1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x02014b50) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 42); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 42) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); + + // ----- Get filename + if ($p_header['filename_len'] != 0) + $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); + else + $p_header['filename'] = ''; + + // ----- Get extra + if ($p_header['extra_len'] != 0) + $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); + else + $p_header['extra'] = ''; + + // ----- Get comment + if ($p_header['comment_len'] != 0) + $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); + else + $p_header['comment'] = ''; + + // ----- Extract properties + + // ----- Recuperate date in UNIX format + //if ($p_header['mdate'] && $p_header['mtime']) + // TBC : bug : this was ignoring time with 0/0/0 + if (1) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } + else + { + $p_header['mtime'] = time(); + } + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set default status to ok + $p_header['status'] = 'ok'; + + // ----- Look if it is a directory + if (substr($p_header['filename'], -1) == '/') { + //$p_header['external'] = 0x41FF0010; + $p_header['external'] = 0x00000010; + } + + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCheckFileHeaders() + // Description : + // Parameters : + // Return Values : + // 1 on success, + // 0 on error; + // -------------------------------------------------------------------------------- + function privCheckFileHeaders(&$p_local_header, &$p_central_header) + { + $v_result=1; + + // ----- Check the static values + // TBC + if ($p_local_header['filename'] != $p_central_header['filename']) { + } + if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) { + } + if ($p_local_header['flag'] != $p_central_header['flag']) { + } + if ($p_local_header['compression'] != $p_central_header['compression']) { + } + if ($p_local_header['mtime'] != $p_central_header['mtime']) { + } + if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { + } + + // ----- Look for flag bit 3 + if (($p_local_header['flag'] & 8) == 8) { + $p_local_header['size'] = $p_central_header['size']; + $p_local_header['compressed_size'] = $p_central_header['compressed_size']; + $p_local_header['crc'] = $p_central_header['crc']; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadEndCentralDir() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadEndCentralDir(&$p_central_dir) + { + $v_result=1; + + // ----- Go to the end of the zip file + $v_size = filesize($this->zipname); + @fseek($this->zip_fd, $v_size); + if (@ftell($this->zip_fd) != $v_size) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- First try : look if this is an archive with no commentaries (most of the time) + // in this case the end of central dir is at 22 bytes of the file end + $v_found = 0; + if ($v_size > 26) { + @fseek($this->zip_fd, $v_size-22); + if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read for bytes + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = @unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] == 0x06054b50) { + $v_found = 1; + } + + $v_pos = ftell($this->zip_fd); + } + + // ----- Go back to the maximum possible size of the Central Dir End Record + if (!$v_found) { + $v_maximum_size = 65557; // 0xFFFF + 22; + if ($v_maximum_size > $v_size) + $v_maximum_size = $v_size; + @fseek($this->zip_fd, $v_size-$v_maximum_size); + if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read byte per byte in order to find the signature + $v_pos = ftell($this->zip_fd); + $v_bytes = 0x00000000; + while ($v_pos < $v_size) + { + // ----- Read a byte + $v_byte = @fread($this->zip_fd, 1); + + // ----- Add the byte + //$v_bytes = ($v_bytes << 8) | Ord($v_byte); + // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number + // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. + $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); + + // ----- Compare the bytes + if ($v_bytes == 0x504b0506) + { + $v_pos++; + break; + } + + $v_pos++; + } + + // ----- Look if not found end of central dir + if ($v_pos == $v_size) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Read the first 18 bytes of the header + $v_binary_data = fread($this->zip_fd, 18); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 18) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); + + // ----- Check the global size + if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { + + // ----- Removed in release 2.2 see readme file + // The check of the file size is a little too strict. + // Some bugs where found when a zip is encrypted/decrypted with 'crypt'. + // While decrypted, zip has training 0 bytes + if (0) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, + 'The central dir is not at the end of the archive.' + .' Some trailing bytes exists after the archive.'); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Get comment + if ($v_data['comment_size'] != 0) { + $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); + } + else + $p_central_dir['comment'] = ''; + + $p_central_dir['entries'] = $v_data['entries']; + $p_central_dir['disk_entries'] = $v_data['disk_entries']; + $p_central_dir['offset'] = $v_data['offset']; + $p_central_dir['size'] = $v_data['size']; + $p_central_dir['disk'] = $v_data['disk']; + $p_central_dir['disk_start'] = $v_data['disk_start']; + + // TBC + //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { + //} + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDeleteByRule() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDeleteByRule(&$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Scan all the files + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + $v_header_list = array(); + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + + // ----- Read the file header + $v_header_list[$v_nb_extracted] = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + + return $v_result; + } + + + // ----- Store the index + $v_header_list[$v_nb_extracted]['index'] = $i; + + // ----- Look for the specific extract rules + $v_found = false; + + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } + elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */ + && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } + } + // ----- Look for a filename + elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_found = true; + } + } + } + + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + } + */ + + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + } + + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_found = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j+1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + break; + } + } + } + else { + $v_found = true; + } + + // ----- Look for deletion + if ($v_found) + { + unset($v_header_list[$v_nb_extracted]); + } + else + { + $v_nb_extracted++; + } + } + + // ----- Look if something need to be deleted + if ($v_nb_extracted > 0) { + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Creates a temporary zip archive + $v_temp_zip = new PclZip($v_zip_temp_name); + + // ----- Open the temporary zip file in write mode + if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Look which file need to be kept + for ($i=0; $izip_fd); + if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_local_header = array(); + if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Check that local file header is same as central file header + if ($this->privCheckFileHeaders($v_local_header, + $v_header_list[$i]) != 1) { + // TBC + } + unset($v_local_header); + + // ----- Write the file header + if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Read/write the data block + if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_temp_zip->zip_fd); + + // ----- Re-Create the Central Dir files header + for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Transform the header to a 'usable' info + $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { + // ----- Reset the file list + unset($v_header_list); + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Close + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Destroy the temporary archive + unset($v_temp_zip); + } + + // ----- Remove every files : reset the file + else if ($v_central_dir['entries'] != 0) { + $this->privCloseFd(); + + if (($v_result = $this->privOpenFd('wb')) != 1) { + return $v_result; + } + + if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) { + return $v_result; + } + + $this->privCloseFd(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDirCheck() + // Description : + // Check if a directory exists, if not it creates it and all the parents directory + // which may be useful. + // Parameters : + // $p_dir : Directory path to check. + // Return Values : + // 1 : OK + // -1 : Unable to create directory + // -------------------------------------------------------------------------------- + function privDirCheck($p_dir, $p_is_dir=false) + { + $v_result = 1; + + + // ----- Remove the final '/' + if (($p_is_dir) && (substr($p_dir, -1)=='/')) + { + $p_dir = substr($p_dir, 0, strlen($p_dir)-1); + } + + // ----- Check the directory availability + if ((is_dir($p_dir)) || ($p_dir == "")) + { + return 1; + } + + // ----- Extract parent directory + $p_parent_dir = dirname($p_dir); + + // ----- Just a check + if ($p_parent_dir != $p_dir) + { + // ----- Look for parent directory + if ($p_parent_dir != "") + { + if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) + { + return $v_result; + } + } + } + + // ----- Create the directory + if (!@mkdir($p_dir, 0777)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privMerge() + // Description : + // If $p_archive_to_add does not exist, the function exit with a success result. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privMerge(&$p_archive_to_add) + { + $v_result=1; + + // ----- Look if the archive_to_add exists + if (!is_file($p_archive_to_add->zipname)) + { + + // ----- Nothing to merge, so merge is a success + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Look if the archive exists + if (!is_file($this->zipname)) + { + + // ----- Do a duplicate + $v_result = $this->privDuplicate($p_archive_to_add->zipname); + + // ----- Return + return $v_result; + } + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Open the archive_to_add file + if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1) + { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir_to_add = array(); + if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + return $v_result; + } + + // ----- Go to beginning of File + @rewind($p_archive_to_add->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the files from the archive_to_add into the temporary file + $v_size = $v_central_dir_to_add['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_zip_temp_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the block of file headers from the archive_to_add + $v_size = $v_central_dir_to_add['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Merge the file comments + $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment']; + + // ----- Calculate the size of the (new) central header + $v_size = @ftell($v_zip_temp_fd)-$v_offset; + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive fd + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + @fclose($v_zip_temp_fd); + $this->zip_fd = null; + + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Close + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDuplicate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDuplicate($p_archive_filename) + { + $v_result=1; + + // ----- Look if the $p_archive_filename exists + if (!is_file($p_archive_filename)) + { + + // ----- Nothing to duplicate, so duplicate is a success. + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('wb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) + { + $this->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = filesize($p_archive_filename); + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close + $this->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privErrorLog() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorLog($p_error_code=0, $p_error_string='') + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclError($p_error_code, $p_error_string); + } + else { + $this->error_code = $p_error_code; + $this->error_string = $p_error_string; + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privErrorReset() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorReset() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclErrorReset(); + } + else { + $this->error_code = 0; + $this->error_string = ''; + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDisableMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDisableMagicQuotes() + { + $v_result=1; + + // ----- Look if function exists + if ( (!function_exists("get_magic_quotes_runtime")) + || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } + + // ----- Look if already done + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Get and memorize the magic_quote value + $this->magic_quotes_status = @get_magic_quotes_runtime(); + + // ----- Disable magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime(0); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privSwapBackMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privSwapBackMagicQuotes() + { + $v_result=1; + + // ----- Look if function exists + if ( (!function_exists("get_magic_quotes_runtime")) + || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } + + // ----- Look if something to do + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Swap back magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime($this->magic_quotes_status); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + } + // End of class + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathReduction() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilPathReduction($p_dir) + { + $v_result = ""; + + // ----- Look for not empty path + if ($p_dir != "") { + // ----- Explode path by directory names + $v_list = explode("/", $p_dir); + + // ----- Study directories from last to first + $v_skip = 0; + for ($i=sizeof($v_list)-1; $i>=0; $i--) { + // ----- Look for current path + if ($v_list[$i] == ".") { + // ----- Ignore this directory + // Should be the first $i=0, but no check is done + } + else if ($v_list[$i] == "..") { + $v_skip++; + } + else if ($v_list[$i] == "") { + // ----- First '/' i.e. root slash + if ($i == 0) { + $v_result = "/".$v_result; + if ($v_skip > 0) { + // ----- It is an invalid path, so the path is not modified + // TBC + $v_result = $p_dir; + $v_skip = 0; + } + } + // ----- Last '/' i.e. indicates a directory + else if ($i == (sizeof($v_list)-1)) { + $v_result = $v_list[$i]; + } + // ----- Double '/' inside the path + else { + // ----- Ignore only the double '//' in path, + // but not the first and last '/' + } + } + else { + // ----- Look for item to skip + if ($v_skip > 0) { + $v_skip--; + } + else { + $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:""); + } + } + } + + // ----- Look for skip + if ($v_skip > 0) { + while ($v_skip > 0) { + $v_result = '../'.$v_result; + $v_skip--; + } + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathInclusion() + // Description : + // This function indicates if the path $p_path is under the $p_dir tree. Or, + // said in an other way, if the file or sub-dir $p_path is inside the dir + // $p_dir. + // The function indicates also if the path is exactly the same as the dir. + // This function supports path with duplicated '/' like '//', but does not + // support '.' or '..' statements. + // Parameters : + // Return Values : + // 0 if $p_path is not inside directory $p_dir + // 1 if $p_path is inside directory $p_dir + // 2 if $p_path is exactly the same as $p_dir + // -------------------------------------------------------------------------------- + function PclZipUtilPathInclusion($p_dir, $p_path) + { + $v_result = 1; + + // ----- Look for path beginning by ./ + if ( ($p_dir == '.') + || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) { + $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1); + } + if ( ($p_path == '.') + || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) { + $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1); + } + + // ----- Explode dir and path by directory separator + $v_list_dir = explode("/", $p_dir); + $v_list_dir_size = sizeof($v_list_dir); + $v_list_path = explode("/", $p_path); + $v_list_path_size = sizeof($v_list_path); + + // ----- Study directories paths + $i = 0; + $j = 0; + while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { + + // ----- Look for empty dir (path reduction) + if ($v_list_dir[$i] == '') { + $i++; + continue; + } + if ($v_list_path[$j] == '') { + $j++; + continue; + } + + // ----- Compare the items + if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { + $v_result = 0; + } + + // ----- Next items + $i++; + $j++; + } + + // ----- Look if everything seems to be the same + if ($v_result) { + // ----- Skip all the empty items + while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; + while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; + + if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { + // ----- There are exactly the same + $v_result = 2; + } + else if ($i < $v_list_dir_size) { + // ----- The path is shorter than the dir + $v_result = 0; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilCopyBlock() + // Description : + // Parameters : + // $p_mode : read/write compression mode + // 0 : src & dest normal + // 1 : src gzip, dest normal + // 2 : src normal, dest gzip + // 3 : src & dest gzip + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0) + { + $v_result = 1; + + if ($p_mode==0) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==1) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==2) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==3) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilRename() + // Description : + // This function tries to do a simple rename() function. If it fails, it + // tries to copy the $p_src file in a new $p_dest file and then unlink the + // first one. + // Parameters : + // $p_src : Old filename + // $p_dest : New filename + // Return Values : + // 1 on success, 0 on failure. + // -------------------------------------------------------------------------------- + function PclZipUtilRename($p_src, $p_dest) + { + $v_result = 1; + + // ----- Try to rename the files + if (!@rename($p_src, $p_dest)) { + + // ----- Try to copy & unlink the src + if (!@copy($p_src, $p_dest)) { + $v_result = 0; + } + else if (!@unlink($p_src)) { + $v_result = 0; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilOptionText() + // Description : + // Translate option value in text. Mainly for debug purpose. + // Parameters : + // $p_option : the option value. + // Return Values : + // The option text value. + // -------------------------------------------------------------------------------- + function PclZipUtilOptionText($p_option) + { + + $v_list = get_defined_constants(); + for (reset($v_list); $v_key = key($v_list); next($v_list)) { + $v_prefix = substr($v_key, 0, 10); + if (( ($v_prefix == 'PCLZIP_OPT') + || ($v_prefix == 'PCLZIP_CB_') + || ($v_prefix == 'PCLZIP_ATT')) + && ($v_list[$v_key] == $p_option)) { + return $v_key; + } + } + + $v_result = 'Unknown'; + + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilTranslateWinPath() + // Description : + // Translate windows path by replacing '\' by '/' and optionally removing + // drive letter. + // Parameters : + // $p_path : path to translate. + // $p_remove_disk_letter : true | false + // Return Values : + // The path translated. + // -------------------------------------------------------------------------------- + function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true) + { + if (stristr(php_uname(), 'windows')) { + // ----- Look for potential disk letter + if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { + $p_path = substr($p_path, $v_position+1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } + } + return $p_path; + } + // -------------------------------------------------------------------------------- + + +?> diff --git a/include/classes/phpmailer/atutormailer.class.php b/include/classes/phpmailer/atutormailer.class.php new file mode 100644 index 000000000..3c06ecd84 --- /dev/null +++ b/include/classes/phpmailer/atutormailer.class.php @@ -0,0 +1,144 @@ +IsSMTP(); // set mailer to use SMTP + $this->Host = ini_get('SMTP'); // specify main and backup server + } else { + $this->IsSendmail(); // use sendmail + $this->Sendmail = ini_get('sendmail_path'); + } + + $this->SMTPAuth = false; // turn on SMTP authentication + $this->IsHTML(false); + + // send the email in the current encoding: + global $myLang; + $this->CharSet = $myLang->getCharacterSet(); + } + + /** + * Appends a custom ATutor footer to all outgoing email then sends the email. + * If mail_queue is enabled then instead of sending the mail out right away, it + * places it in the database and waits for the cron to send it using SendQueue(). + * The mail queue does not support reply-to, or attachments, and converts all BCCs + * to regular To emails. + * @access public + * @return boolean whether or not the mail was sent (or queued) successfully. + * @see parent::send() + * @since ATutor 1.4.1 + * @author Joel Kronenberg + */ + function Send() { + global $_config; + + // attach the ATutor footer to the body first: + $this->Body .= "\n\n".'----------------------------------------------'."\n"; + $this->Body .= _AT('sent_via_atutor', AT_BASE_HREF); + if ($_SESSION['course_id'] > 0) { + $this->Body .= 'login.php?course='.$_SESSION['course_id'].' | ' . $_SESSION['course_title']; + } + + $this->Body .= "\n"._AT('atutor_home').': http://atutor.ca'; + + // if this email has been queued then don't send it. instead insert it in the db + // for each bcc or to or cc + if ($_config['enable_mail_queue'] && !$this->attachment) { + global $db; + for ($i = 0; $i < count($this->to); $i++) { + $this->QueueMail(addslashes($this->to[$i][0]), addslashes($this->to[$i][1]), addslashes($this->From), addslashes($this->FromName), addslashes($this->Subject), addslashes($this->Body)); + } + for($i = 0; $i < count($this->cc); $i++) { + $this->QueueMail(addslashes($this->cc[$i][0]), addslashes($this->cc[$i][1]), addslashes($this->From), addslashes($this->FromName), addslashes($this->Subject), addslashes($this->Body)); + } + for($i = 0; $i < count($this->bcc); $i++) { + $this->QueueMail(addslashes($this->bcc[$i][0]), addslashes($this->bcc[$i][1]), addslashes($this->From), addslashes($this->FromName), addslashes($this->Subject), addslashes($this->Body)); + } + return true; + } else { + return parent::Send(); + } + } + + /** + * Adds the mail to the queue. + * @access private + * @return boolean whether the mail was queued successfully. + * @since ATutor 1.5.3 + * @author Joel Kronenberg + */ + function QueueMail($to_email, $to_name, $from_email, $from_name, $subject, $body) { + global $db; + $sql = "INSERT INTO ".TABLE_PREFIX."mail_queue VALUES (NULL, '$to_email', '$to_name', '$from_email', '$from_name', '".addslashes($this->CharSet)."', '$subject', '$body')"; + return mysql_query($sql, $db); + } + + /** + * Sends all the queued mail. Called by ./admin/cron.php. + * @access public + * @return void + * @since ATutor 1.5.3 + * @author Joel Kronenberg + */ + function SendQueue() { + global $db; + + $mail_ids = ''; + $sql = "SELECT * FROM ".TABLE_PREFIX."mail_queue"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $this->ClearAllRecipients(); + + $this->AddAddress($row['to_email'], $row['to_name']); + $this->From = $row['from_email']; + $this->FromName = $row['from_name']; + $this->CharSet = $row['char_set']; + $this->Subject = $row['subject']; + $this->Body = $row['body']; + + parent::Send(); + + $mail_ids .= $row['mail_id'].','; + } + if ($mail_ids) { + $mail_ids = substr($mail_ids, 0, -1); // remove the last comma + $sql = "DELETE FROM ".TABLE_PREFIX."mail_queue WHERE mail_id IN ($mail_ids)"; + mysql_query($sql, $db); + } + } + +} + +?> diff --git a/include/classes/phpmailer/class.phpmailer.php b/include/classes/phpmailer/class.phpmailer.php new file mode 100644 index 000000000..8d955b2cb --- /dev/null +++ b/include/classes/phpmailer/class.phpmailer.php @@ -0,0 +1,1499 @@ +ContentType = "text/html"; + else + $this->ContentType = "text/plain"; + } + + /** + * Sets Mailer to send message using SMTP. + * @return void + */ + function IsSMTP() { + $this->Mailer = "smtp"; + } + + /** + * Sets Mailer to send message using PHP mail() function. + * @return void + */ + function IsMail() { + $this->Mailer = "mail"; + } + + /** + * Sets Mailer to send message using the $Sendmail program. + * @return void + */ + function IsSendmail() { + $this->Mailer = "sendmail"; + } + + /** + * Sets Mailer to send message using the qmail MTA. + * @return void + */ + function IsQmail() { + $this->Sendmail = "/var/qmail/bin/sendmail"; + $this->Mailer = "sendmail"; + } + + + ///////////////////////////////////////////////// + // RECIPIENT METHODS + ///////////////////////////////////////////////// + + /** + * Adds a "To" address. + * @param string $address + * @param string $name + * @return void + */ + function AddAddress($address, $name = "") { + $cur = count($this->to); + $this->to[$cur][0] = trim($address); + $this->to[$cur][1] = $name; + } + + /** + * Adds a "Cc" address. Note: this function works + * with the SMTP mailer on win32, not with the "mail" + * mailer. + * @param string $address + * @param string $name + * @return void + */ + function AddCC($address, $name = "") { + $cur = count($this->cc); + $this->cc[$cur][0] = trim($address); + $this->cc[$cur][1] = $name; + } + + /** + * Adds a "Bcc" address. Note: this function works + * with the SMTP mailer on win32, not with the "mail" + * mailer. + * @param string $address + * @param string $name + * @return void + */ + function AddBCC($address, $name = "") { + $cur = count($this->bcc); + $this->bcc[$cur][0] = trim($address); + $this->bcc[$cur][1] = $name; + } + + /** + * Adds a "Reply-to" address. + * @param string $address + * @param string $name + * @return void + */ + function AddReplyTo($address, $name = "") { + $cur = count($this->ReplyTo); + $this->ReplyTo[$cur][0] = trim($address); + $this->ReplyTo[$cur][1] = $name; + } + + + ///////////////////////////////////////////////// + // MAIL SENDING METHODS + ///////////////////////////////////////////////// + + /** + * Creates message and assigns Mailer. If the message is + * not sent successfully then it returns false. Use the ErrorInfo + * variable to view description of the error. + * @return bool + */ + function Send() { + $header = ""; + $body = ""; + $result = true; + + if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) + { + $this->SetError($this->Lang("provide_address")); + return false; + } + + // Set whether the message is multipart/alternative + if(!empty($this->AltBody)) + $this->ContentType = "multipart/alternative"; + + $this->error_count = 0; // reset errors + $this->SetMessageType(); + $header .= $this->CreateHeader(); + $body = $this->CreateBody(); + + if($body == "") { return false; } + + // Choose the mailer + switch($this->Mailer) + { + case "sendmail": + $result = $this->SendmailSend($header, $body); + break; + case "mail": + $result = $this->MailSend($header, $body); + break; + case "smtp": + $result = $this->SmtpSend($header, $body); + break; + default: + $this->SetError($this->Mailer . $this->Lang("mailer_not_supported")); + $result = false; + break; + } + + return $result; + } + + /** + * Sends mail using the $Sendmail program. + * @access private + * @return bool + */ + function SendmailSend($header, $body) { + if ($this->Sender != "") + $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + else + $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); + + if(!@$mail = popen($sendmail, "w")) + { + $this->SetError($this->Lang("execute") . $this->Sendmail); + return false; + } + + fputs($mail, $header); + fputs($mail, $body); + + $result = pclose($mail) >> 8 & 0xFF; + if($result != 0) + { + $this->SetError($this->Lang("execute") . $this->Sendmail); + return false; + } + + return true; + } + + /** + * Sends mail using the PHP mail() function. + * @access private + * @return bool + */ + function MailSend($header, $body) { + $to = ""; + for($i = 0; $i < count($this->to); $i++) + { + if($i != 0) { $to .= ", "; } + $to .= $this->to[$i][0]; + } + + if ($this->Sender != "" && strlen(ini_get("safe_mode"))< 1) + { + $old_from = ini_get("sendmail_from"); + ini_set("sendmail_from", $this->Sender); + $params = sprintf("-oi -f %s", $this->Sender); + $rt = @mail($to, $this->EncodeHeader($this->Subject), $body, + $header, $params); + } + else + $rt = @mail($to, $this->EncodeHeader($this->Subject), $body, $header); + + if (isset($old_from)) + ini_set("sendmail_from", $old_from); + + if(!$rt) + { + $this->SetError($this->Lang("instantiate")); + return false; + } + + return true; + } + + /** + * Sends mail via SMTP using PhpSMTP (Author: + * Chris Ryan). Returns bool. Returns false if there is a + * bad MAIL FROM, RCPT, or DATA input. + * @access private + * @return bool + */ + function SmtpSend($header, $body) { + include_once($this->PluginDir . "class.smtp.php"); + $error = ""; + $bad_rcpt = array(); + + if(!$this->SmtpConnect()) + return false; + + $smtp_from = ($this->Sender == "") ? $this->From : $this->Sender; + if(!$this->smtp->Mail($smtp_from)) + { + $error = $this->Lang("from_failed") . $smtp_from; + $this->SetError($error); + $this->smtp->Reset(); + return false; + } + + // Attempt to send attach all recipients + for($i = 0; $i < count($this->to); $i++) + { + if(!$this->smtp->Recipient($this->to[$i][0])) + $bad_rcpt[] = $this->to[$i][0]; + } + for($i = 0; $i < count($this->cc); $i++) + { + if(!$this->smtp->Recipient($this->cc[$i][0])) + $bad_rcpt[] = $this->cc[$i][0]; + } + for($i = 0; $i < count($this->bcc); $i++) + { + if(!$this->smtp->Recipient($this->bcc[$i][0])) + $bad_rcpt[] = $this->bcc[$i][0]; + } + + if(count($bad_rcpt) > 0) // Create error message + { + for($i = 0; $i < count($bad_rcpt); $i++) + { + if($i != 0) { $error .= ", "; } + $error .= $bad_rcpt[$i]; + } + $error = $this->Lang("recipients_failed") . $error; + $this->SetError($error); + $this->smtp->Reset(); + return false; + } + + if(!$this->smtp->Data($header . $body)) + { + $this->SetError($this->Lang("data_not_accepted")); + $this->smtp->Reset(); + return false; + } + if($this->SMTPKeepAlive == true) + $this->smtp->Reset(); + else + $this->SmtpClose(); + + return true; + } + + /** + * Initiates a connection to an SMTP server. Returns false if the + * operation failed. + * @access private + * @return bool + */ + function SmtpConnect() { + if($this->smtp == NULL) { $this->smtp = new SMTP(); } + + $this->smtp->do_debug = $this->SMTPDebug; + $hosts = explode(";", $this->Host); + $index = 0; + $connection = ($this->smtp->Connected()); + + // Retry while there is no connection + while($index < count($hosts) && $connection == false) + { + if(strstr($hosts[$index], ":")) + list($host, $port) = explode(":", $hosts[$index]); + else + { + $host = $hosts[$index]; + $port = $this->Port; + } + + if($this->smtp->Connect($host, $port, $this->Timeout)) + { + if ($this->Helo != '') + $this->smtp->Hello($this->Helo); + else + $this->smtp->Hello($this->ServerHostname()); + + if($this->SMTPAuth) + { + if(!$this->smtp->Authenticate($this->Username, + $this->Password)) + { + $this->SetError($this->Lang("authenticate")); + $this->smtp->Reset(); + $connection = false; + } + } + $connection = true; + } + $index++; + } + if(!$connection) + $this->SetError($this->Lang("connect_host")); + + return $connection; + } + + /** + * Closes the active SMTP session if one exists. + * @return void + */ + function SmtpClose() { + if($this->smtp != NULL) + { + if($this->smtp->Connected()) + { + $this->smtp->Quit(); + $this->smtp->Close(); + } + } + } + + /** + * Sets the language for all class error messages. Returns false + * if it cannot load the language file. The default language type + * is English. + * @param string $lang_type Type of language (e.g. Portuguese: "br") + * @param string $lang_path Path to the language file directory + * @access public + * @return bool + */ + function SetLanguage($lang_type, $lang_path = "language/") { + if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php')) + include($lang_path.'phpmailer.lang-'.$lang_type.'.php'); + else if(file_exists($lang_path.'phpmailer.lang-en.php')) + include($lang_path.'phpmailer.lang-en.php'); + else + { + $this->SetError("Could not load language file"); + return false; + } + $this->language = $PHPMAILER_LANG; + + return true; + } + + ///////////////////////////////////////////////// + // MESSAGE CREATION METHODS + ///////////////////////////////////////////////// + + /** + * Creates recipient headers. + * @access private + * @return string + */ + function AddrAppend($type, $addr) { + $addr_str = $type . ": "; + $addr_str .= $this->AddrFormat($addr[0]); + if(count($addr) > 1) + { + for($i = 1; $i < count($addr); $i++) + $addr_str .= ", " . $this->AddrFormat($addr[$i]); + } + $addr_str .= $this->LE; + + return $addr_str; + } + + /** + * Formats an address correctly. + * @access private + * @return string + */ + function AddrFormat($addr) { + if(empty($addr[1])) + $formatted = $addr[0]; + else + { + $formatted = $this->EncodeHeader($addr[1], 'phrase') . " <" . + $addr[0] . ">"; + } + + return $formatted; + } + + /** + * Wraps message for use with mailers that do not + * automatically perform wrapping and for quoted-printable. + * Original written by philippe. + * @access private + * @return string + */ + function WrapText($message, $length, $qp_mode = false) { + $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; + + $message = $this->FixEOL($message); + if (substr($message, -1) == $this->LE) + $message = substr($message, 0, -1); + + $line = explode($this->LE, $message); + $message = ""; + for ($i=0 ;$i < count($line); $i++) + { + $line_part = explode(" ", $line[$i]); + $buf = ""; + for ($e = 0; $e $length)) + { + $space_left = $length - strlen($buf) - 1; + if ($e != 0) + { + if ($space_left > 20) + { + $len = $space_left; + if (substr($word, $len - 1, 1) == "=") + $len--; + elseif (substr($word, $len - 2, 1) == "=") + $len -= 2; + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= " " . $part; + $message .= $buf . sprintf("=%s", $this->LE); + } + else + { + $message .= $buf . $soft_break; + } + $buf = ""; + } + while (strlen($word) > 0) + { + $len = $length; + if (substr($word, $len - 1, 1) == "=") + $len--; + elseif (substr($word, $len - 2, 1) == "=") + $len -= 2; + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) + $message .= $part . sprintf("=%s", $this->LE); + else + $buf = $part; + } + } + else + { + $buf_o = $buf; + $buf .= ($e == 0) ? $word : (" " . $word); + + if (strlen($buf) > $length and $buf_o != "") + { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + } + $message .= $buf . $this->LE; + } + + return $message; + } + + /** + * Set the body wrapping. + * @access private + * @return void + */ + function SetWordWrap() { + if($this->WordWrap < 1) + return; + + switch($this->message_type) + { + case "alt": + // fall through + case "alt_attachments": + $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); + break; + default: + $this->Body = $this->WrapText($this->Body, $this->WordWrap); + break; + } + } + + /** + * Assembles message header. + * @access private + * @return string + */ + function CreateHeader() { + $result = ""; + + // Set the boundaries + $uniq_id = md5(uniqid(time())); + $this->boundary[1] = "b1_" . $uniq_id; + $this->boundary[2] = "b2_" . $uniq_id; + + $result .= $this->HeaderLine("Date", $this->RFCDate()); + if($this->Sender == "") + $result .= $this->HeaderLine("Return-Path", trim($this->From)); + else + $result .= $this->HeaderLine("Return-Path", trim($this->Sender)); + + // To be created automatically by mail() + if($this->Mailer != "mail") + { + if(count($this->to) > 0) + $result .= $this->AddrAppend("To", $this->to); + else if (count($this->cc) == 0) + $result .= $this->HeaderLine("To", "undisclosed-recipients:;"); + if(count($this->cc) > 0) + $result .= $this->AddrAppend("Cc", $this->cc); + } + + $from = array(); + $from[0][0] = trim($this->From); + $from[0][1] = $this->FromName; + $result .= $this->AddrAppend("From", $from); + + // sendmail and mail() extract Bcc from the header before sending + if((($this->Mailer == "sendmail") || ($this->Mailer == "mail")) && (count($this->bcc) > 0)) + $result .= $this->AddrAppend("Bcc", $this->bcc); + + if(count($this->ReplyTo) > 0) + $result .= $this->AddrAppend("Reply-to", $this->ReplyTo); + + // mail() sets the subject itself + if($this->Mailer != "mail") + $result .= $this->HeaderLine("Subject", $this->EncodeHeader(trim($this->Subject))); + + $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); + $result .= $this->HeaderLine("X-Priority", $this->Priority); + $result .= $this->HeaderLine("X-Mailer", "PHPMailer [version " . $this->Version . "]"); + + if($this->ConfirmReadingTo != "") + { + $result .= $this->HeaderLine("Disposition-Notification-To", + "<" . trim($this->ConfirmReadingTo) . ">"); + } + + // Add custom headers + for($index = 0; $index < count($this->CustomHeader); $index++) + { + $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), + $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); + } + $result .= $this->HeaderLine("MIME-Version", "1.0"); + + switch($this->message_type) + { + case "plain": + $result .= $this->HeaderLine("Content-Transfer-Encoding", $this->Encoding); + $result .= sprintf("Content-Type: %s; charset=\"%s\"", + $this->ContentType, $this->CharSet); + break; + case "attachments": + // fall through + case "alt_attachments": + if($this->InlineImageExists()) + { + $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", + "multipart/related", $this->LE, $this->LE, + $this->boundary[1], $this->LE); + } + else + { + $result .= $this->HeaderLine("Content-Type", "multipart/mixed;"); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + } + break; + case "alt": + $result .= $this->HeaderLine("Content-Type", "multipart/alternative;"); + $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + } + + if($this->Mailer != "mail") + $result .= $this->LE.$this->LE; + + return $result; + } + + /** + * Assembles the message body. Returns an empty string on failure. + * @access private + * @return string + */ + function CreateBody() { + $result = ""; + + $this->SetWordWrap(); + + switch($this->message_type) + { + case "alt": + $result .= $this->GetBoundary($this->boundary[1], "", + "text/plain", ""); + $result .= $this->EncodeString($this->AltBody, $this->Encoding); + $result .= $this->LE.$this->LE; + $result .= $this->GetBoundary($this->boundary[1], "", + "text/html", ""); + + $result .= $this->EncodeString($this->Body, $this->Encoding); + $result .= $this->LE.$this->LE; + + $result .= $this->EndBoundary($this->boundary[1]); + break; + case "plain": + $result .= $this->EncodeString($this->Body, $this->Encoding); + break; + case "attachments": + $result .= $this->GetBoundary($this->boundary[1], "", "", ""); + $result .= $this->EncodeString($this->Body, $this->Encoding); + $result .= $this->LE; + + $result .= $this->AttachAll(); + break; + case "alt_attachments": + $result .= sprintf("--%s%s", $this->boundary[1], $this->LE); + $result .= sprintf("Content-Type: %s;%s" . + "\tboundary=\"%s\"%s", + "multipart/alternative", $this->LE, + $this->boundary[2], $this->LE.$this->LE); + + // Create text body + $result .= $this->GetBoundary($this->boundary[2], "", + "text/plain", "") . $this->LE; + + $result .= $this->EncodeString($this->AltBody, $this->Encoding); + $result .= $this->LE.$this->LE; + + // Create the HTML body + $result .= $this->GetBoundary($this->boundary[2], "", + "text/html", "") . $this->LE; + + $result .= $this->EncodeString($this->Body, $this->Encoding); + $result .= $this->LE.$this->LE; + + $result .= $this->EndBoundary($this->boundary[2]); + + $result .= $this->AttachAll(); + break; + } + if($this->IsError()) + $result = ""; + + return $result; + } + + /** + * Returns the start of a message boundary. + * @access private + */ + function GetBoundary($boundary, $charSet, $contentType, $encoding) { + $result = ""; + if($charSet == "") { $charSet = $this->CharSet; } + if($contentType == "") { $contentType = $this->ContentType; } + if($encoding == "") { $encoding = $this->Encoding; } + + $result .= $this->TextLine("--" . $boundary); + $result .= sprintf("Content-Type: %s; charset = \"%s\"", + $contentType, $charSet); + $result .= $this->LE; + $result .= $this->HeaderLine("Content-Transfer-Encoding", $encoding); + $result .= $this->LE; + + return $result; + } + + /** + * Returns the end of a message boundary. + * @access private + */ + function EndBoundary($boundary) { + return $this->LE . "--" . $boundary . "--" . $this->LE; + } + + /** + * Sets the message type. + * @access private + * @return void + */ + function SetMessageType() { + if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) + $this->message_type = "plain"; + else + { + if(count($this->attachment) > 0) + $this->message_type = "attachments"; + if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) + $this->message_type = "alt"; + if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) + $this->message_type = "alt_attachments"; + } + } + + /** + * Returns a formatted header line. + * @access private + * @return string + */ + function HeaderLine($name, $value) { + return $name . ": " . $value . $this->LE; + } + + /** + * Returns a formatted mail line. + * @access private + * @return string + */ + function TextLine($value) { + return $value . $this->LE; + } + + ///////////////////////////////////////////////// + // ATTACHMENT METHODS + ///////////////////////////////////////////////// + + /** + * Adds an attachment from a path on the filesystem. + * Returns false if the file could not be found + * or accessed. + * @param string $path Path to the attachment. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return bool + */ + function AddAttachment($path, $name = "", $encoding = "base64", + $type = "application/octet-stream") { + if(!@is_file($path)) + { + $this->SetError($this->Lang("file_access") . $path); + return false; + } + + $filename = basename($path); + if($name == "") + $name = $filename; + + $cur = count($this->attachment); + $this->attachment[$cur][0] = $path; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $name; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + $this->attachment[$cur][5] = false; // isStringAttachment + $this->attachment[$cur][6] = "attachment"; + $this->attachment[$cur][7] = 0; + + return true; + } + + /** + * Attaches all fs, string, and binary attachments to the message. + * Returns an empty string on failure. + * @access private + * @return string + */ + function AttachAll() { + // Return text of body + $mime = array(); + + // Add all attachments + for($i = 0; $i < count($this->attachment); $i++) + { + // Check for string attachment + $bString = $this->attachment[$i][5]; + if ($bString) + $string = $this->attachment[$i][0]; + else + $path = $this->attachment[$i][0]; + + $filename = $this->attachment[$i][1]; + $name = $this->attachment[$i][2]; + $encoding = $this->attachment[$i][3]; + $type = $this->attachment[$i][4]; + $disposition = $this->attachment[$i][6]; + $cid = $this->attachment[$i][7]; + + $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); + $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE); + $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); + + if($disposition == "inline") + $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); + + $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", + $disposition, $name, $this->LE.$this->LE); + + // Encode as string attachment + if($bString) + { + $mime[] = $this->EncodeString($string, $encoding); + if($this->IsError()) { return ""; } + $mime[] = $this->LE.$this->LE; + } + else + { + $mime[] = $this->EncodeFile($path, $encoding); + if($this->IsError()) { return ""; } + $mime[] = $this->LE.$this->LE; + } + } + + $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE); + + return join("", $mime); + } + + /** + * Encodes attachment in requested format. Returns an + * empty string on failure. + * @access private + * @return string + */ + function EncodeFile ($path, $encoding = "base64") { + if(!@$fd = fopen($path, "rb")) + { + $this->SetError($this->Lang("file_open") . $path); + return ""; + } + $magic_quotes = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + $file_buffer = fread($fd, filesize($path)); + $file_buffer = $this->EncodeString($file_buffer, $encoding); + fclose($fd); + set_magic_quotes_runtime($magic_quotes); + + return $file_buffer; + } + + /** + * Encodes string to requested format. Returns an + * empty string on failure. + * @access private + * @return string + */ + function EncodeString ($str, $encoding = "base64") { + $encoded = ""; + switch(strtolower($encoding)) { + case "base64": + // chunk_split is found in PHP >= 3.0.6 + $encoded = chunk_split(base64_encode($str), 76, $this->LE); + break; + case "7bit": + case "8bit": + $encoded = $this->FixEOL($str); + if (substr($encoded, -(strlen($this->LE))) != $this->LE) + $encoded .= $this->LE; + break; + case "binary": + $encoded = $str; + break; + case "quoted-printable": + $encoded = $this->EncodeQP($str); + break; + default: + $this->SetError($this->Lang("encoding") . $encoding); + break; + } + return $encoded; + } + + /** + * Encode a header string to best of Q, B, quoted or none. + * @access private + * @return string + */ + function EncodeHeader ($str, $position = 'text') { + $x = 0; + + switch (strtolower($position)) { + case 'phrase': + if (!preg_match('/[\200-\377]/', $str)) { + // Can't use addslashes as we don't know what value has magic_quotes_sybase. + $encoded = addcslashes($str, "\0..\37\177\\\""); + + if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) + return ($encoded); + else + return ("\"$encoded\""); + } + $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + break; + case 'comment': + $x = preg_match_all('/[()"]/', $str, $matches); + // Fall-through + case 'text': + default: + $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + break; + } + + if ($x == 0) + return ($str); + + $maxlen = 75 - 7 - strlen($this->CharSet); + // Try to select the encoding which should produce the shortest output + if (strlen($str)/3 < $x) { + $encoding = 'B'; + $encoded = base64_encode($str); + $maxlen -= $maxlen % 4; + $encoded = trim(chunk_split($encoded, $maxlen, "\n")); + } else { + $encoding = 'Q'; + $encoded = $this->EncodeQ($str, $position); + $encoded = $this->WrapText($encoded, $maxlen, true); + $encoded = str_replace("=".$this->LE, "\n", trim($encoded)); + } + + $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); + $encoded = trim(str_replace("\n", $this->LE, $encoded)); + + return $encoded; + } + + /** + * Encode string to quoted-printable. + * @access private + * @return string + */ + function EncodeQP ($str) { + $encoded = $this->FixEOL($str); + if (substr($encoded, -(strlen($this->LE))) != $this->LE) + $encoded .= $this->LE; + + // Replace every high ascii, control and = characters + $encoded = preg_replace('/([\000-\010\013\014\016-\037\075\177-\377])/e', + "'='.sprintf('%02X', ord('\\1'))", $encoded); + // Replace every spaces and tabs when it's the last character on a line + $encoded = preg_replace("/([\011\040])".$this->LE."/e", + "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded); + + // Maximum line length of 76 characters before CRLF (74 + space + '=') + $encoded = $this->WrapText($encoded, 74, true); + + return $encoded; + } + + /** + * Encode string to q encoding. + * @access private + * @return string + */ + function EncodeQ ($str, $position = "text") { + // There should not be any EOL in the string + $encoded = preg_replace("[\r\n]", "", $str); + + switch (strtolower($position)) { + case "phrase": + $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + break; + case "comment": + $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + case "text": + default: + // Replace every high ascii, control =, ? and _ characters + $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', + "'='.sprintf('%02X', ord('\\1'))", $encoded); + break; + } + + // Replace every spaces to _ (more readable than =20) + $encoded = str_replace(" ", "_", $encoded); + + return $encoded; + } + + /** + * Adds a string or binary attachment (non-filesystem) to the list. + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * @param string $string String attachment data. + * @param string $filename Name of the attachment. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return void + */ + function AddStringAttachment($string, $filename, $encoding = "base64", + $type = "application/octet-stream") { + // Append to $attachment array + $cur = count($this->attachment); + $this->attachment[$cur][0] = $string; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $filename; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + $this->attachment[$cur][5] = true; // isString + $this->attachment[$cur][6] = "attachment"; + $this->attachment[$cur][7] = 0; + } + + /** + * Adds an embedded attachment. This can include images, sounds, and + * just about any other document. Make sure to set the $type to an + * image type. For JPEG images use "image/jpeg" and for GIF images + * use "image/gif". + * @param string $path Path to the attachment. + * @param string $cid Content ID of the attachment. Use this to identify + * the Id for accessing the image in an HTML form. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @return bool + */ + function AddEmbeddedImage($path, $cid, $name = "", $encoding = "base64", + $type = "application/octet-stream") { + + if(!@is_file($path)) + { + $this->SetError($this->Lang("file_access") . $path); + return false; + } + + $filename = basename($path); + if($name == "") + $name = $filename; + + // Append to $attachment array + $cur = count($this->attachment); + $this->attachment[$cur][0] = $path; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $name; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + $this->attachment[$cur][5] = false; // isStringAttachment + $this->attachment[$cur][6] = "inline"; + $this->attachment[$cur][7] = $cid; + + return true; + } + + /** + * Returns true if an inline attachment is present. + * @access private + * @return bool + */ + function InlineImageExists() { + $result = false; + for($i = 0; $i < count($this->attachment); $i++) + { + if($this->attachment[$i][6] == "inline") + { + $result = true; + break; + } + } + + return $result; + } + + ///////////////////////////////////////////////// + // MESSAGE RESET METHODS + ///////////////////////////////////////////////// + + /** + * Clears all recipients assigned in the TO array. Returns void. + * @return void + */ + function ClearAddresses() { + $this->to = array(); + } + + /** + * Clears all recipients assigned in the CC array. Returns void. + * @return void + */ + function ClearCCs() { + $this->cc = array(); + } + + /** + * Clears all recipients assigned in the BCC array. Returns void. + * @return void + */ + function ClearBCCs() { + $this->bcc = array(); + } + + /** + * Clears all recipients assigned in the ReplyTo array. Returns void. + * @return void + */ + function ClearReplyTos() { + $this->ReplyTo = array(); + } + + /** + * Clears all recipients assigned in the TO, CC and BCC + * array. Returns void. + * @return void + */ + function ClearAllRecipients() { + $this->to = array(); + $this->cc = array(); + $this->bcc = array(); + } + + /** + * Clears all previously set filesystem, string, and binary + * attachments. Returns void. + * @return void + */ + function ClearAttachments() { + $this->attachment = array(); + } + + /** + * Clears all custom headers. Returns void. + * @return void + */ + function ClearCustomHeaders() { + $this->CustomHeader = array(); + } + + + ///////////////////////////////////////////////// + // MISCELLANEOUS METHODS + ///////////////////////////////////////////////// + + /** + * Adds the error message to the error container. + * Returns void. + * @access private + * @return void + */ + function SetError($msg) { + $this->error_count++; + $this->ErrorInfo = $msg; + } + + /** + * Returns the proper RFC 822 formatted date. + * @access private + * @return string + */ + function RFCDate() { + $tz = date("Z"); + $tzs = ($tz < 0) ? "-" : "+"; + $tz = abs($tz); + $tz = ($tz/3600)*100 + ($tz%3600)/60; + $result = sprintf("%s %s%04d", date("D, j M Y H:i:s"), $tzs, $tz); + + return $result; + } + + /** + * Returns the appropriate server variable. Should work with both + * PHP 4.1.0+ as well as older versions. Returns an empty string + * if nothing is found. + * @access private + * @return mixed + */ + function ServerVar($varName) { + global $HTTP_SERVER_VARS; + global $HTTP_ENV_VARS; + + if(!isset($_SERVER)) + { + $_SERVER = $HTTP_SERVER_VARS; + if(!isset($_SERVER["REMOTE_ADDR"])) + $_SERVER = $HTTP_ENV_VARS; // must be Apache + } + + if(isset($_SERVER[$varName])) + return $_SERVER[$varName]; + else + return ""; + } + + /** + * Returns the server hostname or 'localhost.localdomain' if unknown. + * @access private + * @return string + */ + function ServerHostname() { + if ($this->Hostname != "") + $result = $this->Hostname; + elseif ($this->ServerVar('SERVER_NAME') != "") + $result = $this->ServerVar('SERVER_NAME'); + else + $result = "localhost.localdomain"; + + return $result; + } + + /** + * Returns a message in the appropriate language. + * @access private + * @return string + */ + function Lang($key) { + if(count($this->language) < 1) + $this->SetLanguage("en"); // set the default language + + if(isset($this->language[$key])) + return $this->language[$key]; + else + return "Language string failed to load: " . $key; + } + + /** + * Returns true if an error occurred. + * @return bool + */ + function IsError() { + return ($this->error_count > 0); + } + + /** + * Changes every end of line from CR or LF to CRLF. + * @access private + * @return string + */ + function FixEOL($str) { + $str = str_replace("\r\n", "\n", $str); + $str = str_replace("\r", "\n", $str); + $str = str_replace("\n", $this->LE, $str); + return $str; + } + + /** + * Adds a custom header. + * @return void + */ + function AddCustomHeader($custom_header) { + $this->CustomHeader[] = explode(":", $custom_header, 2); + } +} + +?> \ No newline at end of file diff --git a/include/classes/phpmailer/class.smtp.php b/include/classes/phpmailer/class.smtp.php new file mode 100644 index 000000000..ad0fb52c8 --- /dev/null +++ b/include/classes/phpmailer/class.smtp.php @@ -0,0 +1,1045 @@ +smtp_conn = 0; + $this->error = null; + $this->helo_rply = null; + + $this->do_debug = 0; + } + + /************************************************************* + * CONNECTION FUNCTIONS * + ***********************************************************/ + + /** + * Connect to the server specified on the port specified. + * If the port is not specified use the default SMTP_PORT. + * If tval is specified then a connection will try and be + * established with the server for that number of seconds. + * If tval is not specified the default is 30 seconds to + * try on the connection. + * + * SMTP CODE SUCCESS: 220 + * SMTP CODE FAILURE: 421 + * @access public + * @return bool + */ + function Connect($host,$port=0,$tval=30) { + # set the error val to null so there is no confusion + $this->error = null; + + # make sure we are __not__ connected + if($this->connected()) { + # ok we are connected! what should we do? + # for now we will just give an error saying we + # are already connected + $this->error = + array("error" => "Already connected to a server"); + return false; + } + + if(empty($port)) { + $port = $this->SMTP_PORT; + } + + #connect to the smtp server + $this->smtp_conn = @fsockopen($host, # the host of the server + $port, # the port to use + $errno, # error number if any + $errstr, # error message if any + $tval); # give up after ? secs + # verify we connected properly + if(empty($this->smtp_conn)) { + $this->error = array("error" => "Failed to connect to server", + "errno" => $errno, + "errstr" => $errstr); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": $errstr ($errno)" . $this->CRLF; + } + return false; + } + + # sometimes the SMTP server takes a little longer to respond + # so we will give it a longer timeout for the first read + // Windows still does not have support for this timeout function + if(substr(PHP_OS, 0, 3) != "WIN") + socket_set_timeout($this->smtp_conn, $tval, 0); + + # get any announcement stuff + $announce = $this->get_lines(); + + # set the timeout of any socket functions at 1/10 of a second + //if(function_exists("socket_set_timeout")) + // socket_set_timeout($this->smtp_conn, 0, 100000); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce; + } + + return true; + } + + /** + * Performs SMTP authentication. Must be run after running the + * Hello() method. Returns true if successfully authenticated. + * @access public + * @return bool + */ + function Authenticate($username, $password) { + // Start authentication + fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "AUTH not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + // Send encoded username + fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "Username not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + // Send encoded password + fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 235) { + $this->error = + array("error" => "Password not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + return true; + } + + /** + * Returns true if connected to a server otherwise false + * @access private + * @return bool + */ + function Connected() { + if(!empty($this->smtp_conn)) { + $sock_status = socket_get_status($this->smtp_conn); + if($sock_status["eof"]) { + # hmm this is an odd situation... the socket is + # valid but we aren't connected anymore + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE:" . $this->CRLF . + "EOF caught while checking if connected"; + } + $this->Close(); + return false; + } + return true; # everything looks good + } + return false; + } + + /** + * Closes the socket and cleans up the state of the class. + * It is not considered good to use this function without + * first trying to use QUIT. + * @access public + * @return void + */ + function Close() { + $this->error = null; # so there is no confusion + $this->helo_rply = null; + if(!empty($this->smtp_conn)) { + # close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = 0; + } + } + + + /*************************************************************** + * SMTP COMMANDS * + *************************************************************/ + + /** + * Issues a data command and sends the msg_data to the server + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being seperated by and additional . + * + * Implements rfc 821: DATA + * + * SMTP CODE INTERMEDIATE: 354 + * [data] + * . + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 552,554,451,452 + * SMTP CODE FAILURE: 451,554 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + function Data($msg_data) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Data() without being connected"); + return false; + } + + fputs($this->smtp_conn,"DATA" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 354) { + $this->error = + array("error" => "DATA command not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + # the server is ready to accept data! + # according to rfc 821 we should not send more than 1000 + # including the CRLF + # characters on a single line so we will break the data up + # into lines by \r and/or \n then if needed we will break + # each of those into smaller lines to fit within the limit. + # in addition we will be looking for lines that start with + # a period '.' and append and additional period '.' to that + # line. NOTE: this does not count towards are limit. + + # normalize the line breaks so we know the explode works + $msg_data = str_replace("\r\n","\n",$msg_data); + $msg_data = str_replace("\r","\n",$msg_data); + $lines = explode("\n",$msg_data); + + # we need to find a good way to determine is headers are + # in the msg_data or if it is a straight msg body + # currently I'm assuming rfc 822 definitions of msg headers + # and if the first field of the first line (':' sperated) + # does not contain a space then it _should_ be a header + # and we can process all lines before a blank "" line as + # headers. + $field = substr($lines[0],0,strpos($lines[0],":")); + $in_headers = false; + if(!empty($field) && !strstr($field," ")) { + $in_headers = true; + } + + $max_line_length = 998; # used below; set here for ease in change + + while(list(,$line) = @each($lines)) { + $lines_out = null; + if($line == "" && $in_headers) { + $in_headers = false; + } + # ok we need to break this line up into several + # smaller lines + while(strlen($line) > $max_line_length) { + $pos = strrpos(substr($line,0,$max_line_length)," "); + + # Patch to fix DOS attack + if(!$pos) { + $pos = $max_line_length - 1; + } + + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos + 1); + # if we are processing headers we need to + # add a LWSP-char to the front of the new line + # rfc 822 on long msg headers + if($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + # now send the lines to the server + while(list(,$line_out) = @each($lines_out)) { + if(strlen($line_out) > 0) + { + if(substr($line_out, 0, 1) == ".") { + $line_out = "." . $line_out; + } + } + fputs($this->smtp_conn,$line_out . $this->CRLF); + } + } + + # ok all the message data has been sent so lets get this + # over with aleady + fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "DATA not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Expand takes the name and asks the server to list all the + * people who are members of the _list_. Expand will return + * back and array of the result or false if an error occurs. + * Each value in the array returned has the format of: + * [ ] + * The definition of is defined in rfc 821 + * + * Implements rfc 821: EXPN + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 550 + * SMTP CODE ERROR : 500,501,502,504,421 + * @access public + * @return string array + */ + function Expand($name) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Expand() without being connected"); + return false; + } + + fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "EXPN not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + # parse the reply and place in our array to return to user + $entries = explode($this->CRLF,$rply); + while(list(,$l) = @each($entries)) { + $list[] = substr($l,4); + } + + return $list; + } + + /** + * Sends the HELO command to the smtp server. + * This makes sure that we and the server are in + * the same known state. + * + * Implements from rfc 821: HELO + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 501, 504, 421 + * @access public + * @return bool + */ + function Hello($host="") { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Hello() without being connected"); + return false; + } + + # if a hostname for the HELO wasn't specified determine + # a suitable one to send + if(empty($host)) { + # we need to determine some sort of appopiate default + # to send to the server + $host = "localhost"; + } + + // Send extended hello first (RFC 2821) + if(!$this->SendHello("EHLO", $host)) + { + if(!$this->SendHello("HELO", $host)) + return false; + } + + return true; + } + + /** + * Sends a HELO/EHLO command. + * @access private + * @return bool + */ + function SendHello($hello, $host) { + fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => $hello . " not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + $this->helo_rply = $rply; + + return true; + } + + /** + * Gets help information on the keyword specified. If the keyword + * is not specified then returns generic help, ussually contianing + * A list of keywords that help is available on. This function + * returns the results back to the user. It is up to the user to + * handle the returned data. If an error occurs then false is + * returned with $this->error set appropiately. + * + * Implements rfc 821: HELP [ ] + * + * SMTP CODE SUCCESS: 211,214 + * SMTP CODE ERROR : 500,501,502,504,421 + * @access public + * @return string + */ + function Help($keyword="") { + $this->error = null; # to avoid confusion + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Help() without being connected"); + return false; + } + + $extra = ""; + if(!empty($keyword)) { + $extra = " " . $keyword; + } + + fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 211 && $code != 214) { + $this->error = + array("error" => "HELP not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + return $rply; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. + * + * Implements rfc 821: MAIL FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,421 + * @access public + * @return bool + */ + function Mail($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Mail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "MAIL not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Sends the command NOOP to the SMTP server. + * + * Implements from rfc 821: NOOP + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 421 + * @access public + * @return bool + */ + function Noop() { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Noop() without being connected"); + return false; + } + + fputs($this->smtp_conn,"NOOP" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "NOOP not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Sends the quit command to the server and then closes the socket + * if there is no error or the $close_on_error argument is true. + * + * Implements from rfc 821: QUIT + * + * SMTP CODE SUCCESS: 221 + * SMTP CODE ERROR : 500 + * @access public + * @return bool + */ + function Quit($close_on_error=true) { + $this->error = null; # so there is no confusion + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Quit() without being connected"); + return false; + } + + # send the quit command to the server + fputs($this->smtp_conn,"quit" . $this->CRLF); + + # get any good-bye messages + $byemsg = $this->get_lines(); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg; + } + + $rval = true; + $e = null; + + $code = substr($byemsg,0,3); + if($code != 221) { + # use e as a tmp var cause Close will overwrite $this->error + $e = array("error" => "SMTP server rejected quit command", + "smtp_code" => $code, + "smtp_rply" => substr($byemsg,4)); + $rval = false; + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $e["error"] . ": " . + $byemsg . $this->CRLF; + } + } + + if(empty($e) || $close_on_error) { + $this->Close(); + } + + return $rval; + } + + /** + * Sends the command RCPT to the SMTP server with the TO: argument of $to. + * Returns true if the recipient was accepted false if it was rejected. + * + * Implements from rfc 821: RCPT TO: + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,552,553,450,451,452 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + function Recipient($to) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Recipient() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250 && $code != 251) { + $this->error = + array("error" => "RCPT not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Sends the RSET command to abort and transaction that is + * currently in progress. Returns true if successful false + * otherwise. + * + * Implements rfc 821: RSET + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500,501,504,421 + * @access public + * @return bool + */ + function Reset() { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Reset() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RSET" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "RSET failed", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in. + * + * Implements rfc 821: SEND FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + * @access public + * @return bool + */ + function Send($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Send() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "SEND not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * + * Implements rfc 821: SAML FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + * @access public + * @return bool + */ + function SendAndMail($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called SendAndMail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "SAML not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in or mail it to them if they are not. + * + * Implements rfc 821: SOML FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + * @access public + * @return bool + */ + function SendOrMail($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called SendOrMail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "SOML not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /** + * This is an optional command for SMTP that this class does not + * support. This method is here to make the RFC821 Definition + * complete for this class and __may__ be implimented in the future + * + * Implements from rfc 821: TURN + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 502 + * SMTP CODE ERROR : 500, 503 + * @access public + * @return bool + */ + function Turn() { + $this->error = array("error" => "This method, TURN, of the SMTP ". + "is not implemented"); + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF; + } + return false; + } + + /** + * Verifies that the name is recognized by the server. + * Returns false if the name could not be verified otherwise + * the response from the server is returned. + * + * Implements rfc 821: VRFY + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,553 + * SMTP CODE ERROR : 500,501,502,421 + * @access public + * @return int + */ + function Verify($name) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Verify() without being connected"); + return false; + } + + fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250 && $code != 251) { + $this->error = + array("error" => "VRFY failed on name '$name'", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return $rply; + } + + /******************************************************************* + * INTERNAL FUNCTIONS * + ******************************************************************/ + + /** + * Read in as many lines as possible + * either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * @access private + * @return string + */ + function get_lines() { + $data = ""; + while($str = fgets($this->smtp_conn,515)) { + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data was \"$data\"" . + $this->CRLF; + echo "SMTP -> get_lines(): \$str is \"$str\"" . + $this->CRLF; + } + $data .= $str; + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF; + } + # if the 4th character is a space then we are done reading + # so just break the loop + if(substr($str,3,1) == " ") { break; } + } + return $data; + } + +} + + + ?> diff --git a/include/classes/phpmailer/phpmailer.lang-en.php b/include/classes/phpmailer/phpmailer.lang-en.php new file mode 100644 index 000000000..2f45383fe --- /dev/null +++ b/include/classes/phpmailer/phpmailer.lang-en.php @@ -0,0 +1,23 @@ + diff --git a/include/classes/sqlutility.class.php b/include/classes/sqlutility.class.php new file mode 100644 index 000000000..afcb42cc1 --- /dev/null +++ b/include/classes/sqlutility.class.php @@ -0,0 +1,229 @@ + add the current + // substring to the returned array + if (!$i) { + $ret[] = $sql; + return true; + } + // Backquotes or no backslashes before + // quotes: it's indeed the end of the + // string -> exit the loop + else if ($string_start == '`' || $sql[$i-1] != '\\') { + $string_start = ''; + $in_string = false; + break; + } + // one or more Backslashes before the presumed + // end of string... + else { + // first checks for escaped backslashes + $j = 2; + $escaped_backslash = false; + while ($i-$j > 0 && $sql[$i-$j] == '\\') { + $escaped_backslash = !$escaped_backslash; + $j++; + } + // ... if escaped backslashes: it's really the + // end of the string -> exit the loop + if ($escaped_backslash) { + $string_start = ''; + $in_string = false; + break; + } + // ... else loop + else { + $i++; + } + } // end if...elseif...else + } // end for + } // end if (in string) + // We are not in a string, first check for delimiter... + else if ($char == ';') { + // if delimiter found, add the parsed part to the returned array + $ret[] = substr($sql, 0, $i); + $sql = ltrim(substr($sql, min($i + 1, $sql_len))); + $sql_len = strlen($sql); + if ($sql_len) { + $i = -1; + } else { + // The submited statement(s) end(s) here + return true; + } + } // end else if (is delimiter) + // ... then check for start of a string,... + else if (($char == '"') || ($char == '\'') || ($char == '`')) { + $in_string = true; + $string_start = $char; + } // end else if (is start of string) + + // for start of a comment (and remove this comment if found)... + // the comment signs can be "# ", "/*", "--". The comment start signs must be at the begining of a line. + else if ($char == '#' || ($char == '/' && $sql[$i+1] == '*') || ($char == ' ' && $i > 1 && $sql[$i-2] . $sql[$i-1] == '--')) { + // starting position of the comment depends on the comment type + $start_of_comment = (($char == '#' || ($char == '/' && $sql[$i+1] == '*')) ? $i : $i-2); + // if no "\n" exits in the remaining string, checks for "\r" + // (Mac eol style) + if ($char == '/' && $sql[$i+1] == '*') + $end_of_comment = strpos($sql, "*/", $i+2) + 2; + else + $end_of_comment = (strpos(' ' . $sql, "\012", $i+2)) + ? strpos(' ' . $sql, "\012", $i+2) + : strpos(' ' . $sql, "\015", $i+2); + if (!$end_of_comment) { + // no eol found after '#', add the parsed part to the returned + // array and exit + $ret[] = trim(substr($sql, 0, $i-1)); + return true; + } else { + $sql = substr($sql, 0, $start_of_comment) . ltrim(substr($sql, $end_of_comment)); + $sql_len = strlen($sql); + $i--; + } // end if...else + } // end else if (is comment) + } // end for + + // add any rest to the returned array + if (!empty($sql) && trim($sql) != '') { + $ret[] = $sql; + } + return true; + } + + /** + * add a prefix.'_' to all tablenames in a query + * + * @param string $query valid MySQL query string + * @param string $prefix prefix to add to all table names + * @return mixed FALSE on failure + */ + function prefixQuery($query, $prefix) + { + $pattern = "/^(INSERT INTO|CREATE TABLE|ALTER TABLE|UPDATE)(\s)+([`]?)([^`\s]+)\\3(\s)+/siU"; + $pattern2 = "/^(DROP TABLE)(\s)+([`]?)([^`\s]+)\\3(\s)?$/siU"; + if (preg_match($pattern, $query, $matches) || preg_match($pattern2, $query, $matches)) { + $replace = "\\1 ".$prefix."\\4\\5"; + $matches[0] = preg_replace($pattern, $replace, $query); + return $matches; + } + return false; + } + + function queryFromFile($sql_file_path, $table_prefix) + { + global $db, $progress, $errors; + + $tables = array(); + + if (!file_exists($sql_file_path)) + return false; + + $sql_query = trim(fread(fopen($sql_file_path, 'r'), filesize($sql_file_path))); + SqlUtility::splitSqlFile($pieces, $sql_query); + + foreach ($pieces as $piece) + { + $piece = trim($piece); + + // [0] contains the prefixed query + // [4] contains unprefixed table name + if ($table_prefix || ($table_prefix == '')) + $prefixed_query = SqlUtility::prefixQuery($piece, $table_prefix); + else + $prefixed_query = $piece; + + if ($prefixed_query != false ) + { + $table = $table_prefix.$prefixed_query[4]; + if($prefixed_query[1] == 'CREATE TABLE') + { + if (mysql_query($prefixed_query[0],$db) !== false) + $progress[] = 'Table '.$table . ' created successfully.'; + else + { + if (mysql_errno($db) == 1050) + $progress[] = 'Table '.$table . ' already exists. Skipping.'; + else + $errors[] = 'Table ' . $table . ' creation failed.'; + } + } + elseif($prefixed_query[1] == 'INSERT INTO') + mysql_query($prefixed_query[0],$db); + elseif($prefixed_query[1] == 'REPLACE INTO') + mysql_query($prefixed_query[0],$db); + elseif($prefixed_query[1] == 'ALTER TABLE') + mysql_query($prefixed_query[0],$db); + elseif($prefixed_query[1] == 'DROP TABLE') + mysql_query($prefixed_query[1] . ' ' .$table,$db); + } + } + return TRUE; + } + + // This function only revert queries on "CREATE TABLE" and "INSERT INTO language_text" + function revertQueryFromFile($sql_file_path, $table_prefix) + { + global $db, $progress, $errors; + + $tables = array(); + + if (!file_exists($sql_file_path)) + return false; + + $sql_query = trim(fread(fopen($sql_file_path, 'r'), filesize($sql_file_path))); + SqlUtility::splitSqlFile($pieces, $sql_query); + + foreach ($pieces as $piece) + { + $piece = trim($piece); + + $pattern_create_table = "/^CREATE TABLE\s+([`]?)([^`\s]+)\\1(\s)+/siU"; + if (preg_match($pattern_create_table, $piece, $matches)) + { + $sql = 'DROP TABLE '. $table_prefix . $matches[2]; + mysql_query($sql, $db); + } + + $pattern_insert_lang = "/^INSERT INTO\s+([`]?)language_text\\1\s+.*VALUES.*'.*'.*'(.*)'.*'(.*)'/siU"; + if (preg_match($pattern_insert_lang, $piece, $matches)) + { + $sql = "DELETE FROM ".$table_prefix."language_text WHERE variable='".$matches[2]."' AND term='".$matches[3]."'"; + mysql_query($sql, $db); + } + } + + return TRUE; + } +} +?> \ No newline at end of file diff --git a/include/classes/subscribe.class.php b/include/classes/subscribe.class.php new file mode 100644 index 000000000..0b51bac8d --- /dev/null +++ b/include/classes/subscribe.class.php @@ -0,0 +1,187 @@ + +* @access public +*/ + +class subscription { + + public $member_id; + public $entity_id; + public $entity_type; + private $ent_param = array(); + + // Constructor. Does nothing at the moment. + public function subscription() { + return true; + } + + + // Checks if user is subscribed to feed. + public function is_subscribed($entity_type, $member_id, $entity_id) { + + // Get appropriate sql parameters and write sql query + $ent_param = $this->entity_switch($entity_type); + $sql = ($ent_param) ? "SELECT COUNT(*) FROM $ent_param[sub_table] WHERE member_id = '$member_id' AND $ent_param[sub_id] = '$entity_id'" : false; + + // Run SQL and check if table is populated for given member id and entity id + if ($sql){ + $result = mysql_fetch_array(mysql_query($sql)); + return (empty($result[0]))?false:true; + } else { + return false; + } + } + + // Gets group name for blog posts + private function get_group_title(){ + if (isset($_GET['oid'])){ + $oid = $_GET['oid']; + } elseif (isset($_POST['oid'])){ + $oid = $_POST['oid']; + } + + $gid = (is_array($oid))?$oid[0]:$oid; + if (!empty($gid)){ + $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id='$gid'"; + $result = mysql_fetch_row(mysql_query($sql)); + return $result[0]; + } else { + return false; + } + } + + // Gets email and site name + private function get_system_email (){ + $sql = "SELECT * FROM ".TABLE_PREFIX."config WHERE name = 'site_name' OR name = 'contact_email'"; + $result = mysql_query($sql); + while($row = mysql_fetch_row($result)){ + if ($row[0] == 'site_name'){ + $sysinfo['site_name'] = $row[1]; + } elseif ($row[0] == 'contact_email'){ + $sysinfo['contact_email'] = $row[1]; + } + } + return $sysinfo; + } + // Subscribes user to feed + public function set_subscription($entity_type, $member_id, $entity_id){ + + //Checks subscribability (only for blogs) + if ($entity_type == 'blog' && !$this->check_blog_subscribability($entity_id,$member_id)){ + return false; + } + + $ent_param = $this->entity_switch($entity_type); + $sql = ($ent_param) ? "INSERT INTO $ent_param[sub_table] (member_id, $ent_param[sub_id]) VALUES('$member_id','$entity_id')" : false; + return (mysql_query($sql))?true:false; + } + + // Unsubscribes user to feed + public function unset_subscription($entity_type, $member_id, $entity_id){ + $ent_param = $this->entity_switch($entity_type); + $sql = ($ent_param) ? "DELETE FROM $ent_param[sub_table] WHERE member_id = '$member_id' AND $ent_param[sub_id] = '$entity_id'" : false; + return (mysql_query($sql))?true:false; + } + + // Sends mail to all subscribed users + public function send_mail($entity_type,$entity_id,$post_id){ + // We need the automailer + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + //Also, we need to know what ballpark we're in + $ent_param = $this->entity_switch($entity_type); + + // Now, what are we going to send? + $fetch = (!empty($ent_param[content_head]))?$ent_param[content_head].",".$ent_param[content_body]:$ent_param[content_body]; + $sql = "SELECT $fetch FROM $ent_param[content_table] WHERE $ent_param[content_id] = '$post_id'"; + $post = mysql_fetch_array(mysql_query($sql)); + + //Get all subscribers + $sql = "SELECT t1.email, t1.member_id FROM ".TABLE_PREFIX."members t1, $ent_param[sub_table] t2 WHERE t2.$ent_param[sub_id] = '$entity_id' AND t1.member_id=t2.member_id"; + $result = mysql_query($sql); + + //get system email + $sysinfo = $this->get_system_email(); + + //Send lots of mails + while ($subscriber = mysql_fetch_array($result)){ + $mail = new ATutorMailer; + $mail->AddAddress($subscriber['email'], get_display_name($subscriber['member_id'])); + $body = $ent_param[mail_header]; + $body .= "
    "; + $body .= _AT('posted_by').": ".get_display_name($_SESSION['member_id'])."
    "; + $body .= (!empty($ent_param[content_head]))?"

    ".$post[$ent_param[content_head]]."


    ":''; + $body .= format_content($post[$ent_param[content_body]],$_POST['formatting'],$glossary)."
    "; + $mail->CharSet = 'utf-8'; + $mail->ContentType = 'text/html'; + $mail->FromName = $sysinfo['site_name']; + $mail->From = $sysinfo['contact_email']; + $mail->Subject = $ent_param[mail_subject]; + $mail->Body = $body; + + if(!$mail->Send()) { + $msg->addError('SENDING_ERROR'); + } + + unset($mail); + } + + } + + // Internal function used to set appropriate SQL parameters for a given entity type + private function entity_switch($entity_type){ + switch($entity_type){ + case "blog": + $param[sub_table] = TABLE_PREFIX.'blog_subscription'; + $param[sub_id] = 'group_id'; + $param[content_table] = TABLE_PREFIX.'blog_posts'; + $param[content_id] = 'post_id'; + $param[content_head] = 'title'; + $param[content_body] = 'body'; + $param[group_title] = $this->get_group_title(); + $param[mail_subject] = _AT('blog_notify_subject'); + $param[mail_header] = _AT('blog_notify_body', $param[group_title], AT_BASE_HREF.'bounce.php?course='.$_SESSION['course_id']); + break; + case "blogcomment": + $param[sub_table] = TABLE_PREFIX.'blog_subscription'; + $param[sub_id] = 'group_id'; + $param[content_table] = TABLE_PREFIX.'blog_posts_comments'; + $param[content_id] = 'comment_id'; + //$param[content_head] = 'date'; + $param[content_body] = 'comment'; + $param[group_title] = $this->get_group_title(); + $param[mail_subject] = _AT('blogcomment_notify_subject'); + $param[mail_header] = _AT('blogcomment_notify_body', $param[group_title], AT_BASE_HREF.'bounce.php?course='.$_SESSION['course_id']); + + break; + case "course": + break; + + case "forum": + break; + + case "thread": + break; + + default: //If unknown entity type, return false + return false; + } + return $param; + } + + private function check_blog_subscribability($group_id,$member_id){ + $sql="SELECT COUNT(*) FROM ".TABLE_PREFIX."groups t1 LEFT JOIN ".TABLE_PREFIX."groups_types t2 ON t1.type_id=t2.type_id LEFT JOIN ".TABLE_PREFIX."course_enrollment t3 ON t2.course_id=t3.course_id WHERE group_id='".$group_id."' AND member_id='".$member_id."'"; + $result = mysql_fetch_row(mysql_query($sql)); + return (empty($result[0]))?false:true; + } +} +?> \ No newline at end of file diff --git a/include/classes/vcard.php b/include/classes/vcard.php new file mode 100644 index 000000000..052740dec --- /dev/null +++ b/include/classes/vcard.php @@ -0,0 +1,160 @@ + 126) ) { // always encode "\t", which is *not* required + $h2 = floor($dec/16); $h1 = floor($dec%16); + $c = $escape.$hex["$h2"].$hex["$h1"]; + } + if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted + $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay + $newline = " "; + } + $newline .= $c; + } // end of for + $output .= $newline; + if ($jproperties[$key] = quoted_printable_encode($number); + } + + // UNTESTED !!! + function setPhoto($type, $photo) { // $type = "GIF" | "JPEG" + $this->properties["PHOTO;TYPE=$type;ENCODING=BASE64"] = base64_encode($photo); + } + + function setFormattedName($name) { + $this->properties["FN"] = quoted_printable_encode($name); + } + + function setName($family="", $first="", $additional="", $prefix="", $suffix="") { + $this->properties["N"] = "$family;$first;$additional;$prefix;$suffix"; + $this->filename = "$first%20$family.vcf"; + if ($this->properties["FN"]=="") $this->setFormattedName(trim("$prefix $first $additional $family $suffix")); + } + + function setBirthday($date) { // $date format is YYYY-MM-DD + $this->properties["BDAY"] = $date; + } + + function setAddress($postoffice="", $extended="", $street="", $city="", $region="", $zip="", $country="", $type="HOME;POSTAL") { + // $type may be DOM | INTL | POSTAL | PARCEL | HOME | WORK or any combination of these: e.g. "WORK;PARCEL;POSTAL" + $key = "ADR"; + if ($type!="") $key.= ";$type"; + $key.= ";ENCODING=QUOTED-PRINTABLE"; + $this->properties[$key] = encode($name).";".encode($extended).";".encode($street).";".encode($city).";".encode($region).";".encode($zip).";".encode($country); + + if ($this->properties["LABEL;$type;ENCODING=QUOTED-PRINTABLE"] == "") { + //$this->setLabel($postoffice, $extended, $street, $city, $region, $zip, $country, $type); + } + } + + function setLabel($postoffice="", $extended="", $street="", $city="", $region="", $zip="", $country="", $type="HOME;POSTAL") { + $label = ""; + if ($postoffice!="") $label.= "$postoffice\r\n"; + if ($extended!="") $label.= "$extended\r\n"; + if ($street!="") $label.= "$street\r\n"; + if ($zip!="") $label.= "$zip "; + if ($city!="") $label.= "$city\r\n"; + if ($region!="") $label.= "$region\r\n"; + if ($country!="") $country.= "$country\r\n"; + + $this->properties["LABEL;$type;ENCODING=QUOTED-PRINTABLE"] = quoted_printable_encode($label); + } + + function setEmail($address) { + $this->properties["EMAIL;INTERNET"] = $address; + } + + function setNote($note) { + $this->properties["NOTE;ENCODING=QUOTED-PRINTABLE"] = quoted_printable_encode($note); + } + + function setURL($url, $type="") { + // $type may be WORK | HOME + $key = "URL"; + if ($type!="") $key.= ";$type"; + $this->properties[$key] = $url; + } + + function getVCard() { + $text = "BEGIN:VCARD\r\n"; + $text.= "VERSION:2.1\r\n"; + foreach($this->properties as $key => $value) { + $text.= "$key:$value\r\n"; + } + $text.= "REV:".date("Y-m-d")."T".date("H:i:s")."Z\r\n"; + //$text.= "MAILER:PHP vCard class by Kai Blankenhorn\r\n"; + $text.= "END:VCARD\r\n"; + return $text; + } + + function getFileName() { + return $this->filename; + } +} + + +?> \ No newline at end of file diff --git a/include/classes/zipfile.class.php b/include/classes/zipfile.class.php new file mode 100644 index 000000000..01fc07431 --- /dev/null +++ b/include/classes/zipfile.class.php @@ -0,0 +1,263 @@ +filename = substr(md5(rand()), 0, 5); + + //create a temporary folder for this zip instance + $this->zipfile_dir = PCLZIP_TEMPORARY_DIR.$this->filename.DIRECTORY_SEPARATOR; + mkdir($this->zipfile_dir); + $this->is_closed = false; + } + + + /** + * Public interface for adding a dir and its contents recursively to zip file + * @access public + * @param string $dir the real system directory that contains the files to add to the zip + * @param string $zip_prefix_dir the zip dir where the contents of $dir will be put in + * @param string $pre_pend_dir used during the recursion to keep track of the path, default='' + * @see $_base_path in include/vitals.inc.php + * @see priv_add_dir() in include/classes/zipfile.class.php + * @see add_file() in include/classes/zipfile.class.php + * @author Joel Kronenberg + */ + function add_dir($dir, $zip_prefix_dir, $pre_pend_dir='') { + if (!($dh = @opendir($dir.$pre_pend_dir))) { + echo 'cant open dir: '.$dir.$pre_pend_dir; + exit; + } + //copy folder recursively into the temp folder. + copys($dir, $this->zipfile_dir.DIRECTORY_SEPARATOR.$zip_prefix_dir); + } + + /** + * Adding a dir to the archive + * @access private + * @param string $name directory name + * @param string $timestamp time, default='' + * @author Joel Kronenberg + */ + function priv_add_dir($name, $timestamp = '') { + //deprecated as of ATutor 2.0 + } + + /** + * Public interface to create a directory in the archive. + * @access public + * @param string $name directory name + * @param string $timestamp time of creation, default='' + * @see $_base_path in include/vitals.inc.php + * @see priv_add_dir() in include/zipfile.class.php + * @author Joel Kronenberg + */ + function create_dir($name, $timestamp='') { + $name = trim($name); + //don't create a folder if it is itself + if ($name=='' || $name=='.'){ + return; + } + + $parent_folder = dirname($name); + if (!is_dir($this->zipfile_dir.$name) && ($parent_folder=='.' || $parent_folder=='')){ + //base case + mkdir($this->zipfile_dir.$name); + return; + } else { + //recursion step + $this->create_dir(dirname($name)); + } + + //returned stack. continue from where it left off. + if (!is_dir($this->zipfile_dir.$name)){ + //the parent folder should be created at this point, create itself + mkdir($this->zipfile_dir.$name); + } + } + + + /** + * Adds a file to the archive. + * @access public + * @param string $file_data file contents + * @param string $name name of file in archive (add path if your want) + * @param string $timestamp time of creation, default='' + * @see $_base_path in include/vitals.inc.php + * @see priv_add_dir() in include/zipfile.class.php + * @author Joel Kronenberg + */ + function add_file($file_data, $name, $timestamp = '') { + $name = str_replace("\\", "/", $name); + + //check if folder exists, if not, create it. + if (!is_dir($this->zipfile_dir.dirname($name))){ + $this->create_dir(dirname($name)); + } + + //write to file + $fp = fopen($this->zipfile_dir.$name, 'w'); + fwrite($fp, $file_data); + fclose($fp); + } + + /** + * Closes archive, sets $is_closed to true + * @access public + * @param none + * @author Joel Kronenberg + */ + function close() { + //use pclzip to compress the file, and save it in the temp folder. + $archive = new PclZip($this->zipfile_dir.$this->filename.'.zip'); + $v_list = $archive->create($this->zipfile_dir, + PCLZIP_OPT_REMOVE_PATH, $this->zipfile_dir); + + //error info + if ($v_list == 0) { + die ("Error: " . $archive->errorInfo(true)); + } + + $this->is_closed = true; + } + + /** + * Gets size of new archive + * Only call this after calling close() - will return false if the zip wasn't close()d yet + * @access public + * @return int size of file in byte. + * @author Joel Kronenberg + */ + function get_size() { + if (!$this->is_closed) { + return false; + } + + //file path + $filepath = $this->zipfile_dir.$this->filename.'.zip'; + if (file_exists($filepath)){ + return filesize($filepath); + } + + return false; + } + + + /** + * Returns binary file + * @access public + * @see get_size() in include/classes/zipfile.class.php + * @author Joel Kronenberg + */ + function get_file() { + if (!$this->is_closed) { + $this->close(); + } + return file_get_contents($this->zipfile_dir.$this->filename.'.zip'); + } + + /** + * Writes the file to disk. + * Similar to get_file(), but instead of returning the file, it saves it to disk. + * @access public + * @author Joel Kronenberg + * @param $file The full path and file name of the destination file. + */ + function write_file($file) { + if (!$this->is_closed) { + $this->close(); + } + copy($this->zipfile_dir.$this->filename.'.zip', $file); + } + + + /** + * Outputs the file - sends headers to browser to force download + * Only call this after calling close() - will return false if the zip wasn't close()d yet + * @access public + * @see get_size() in include/classes/zipfile.class.php + * @author Joel Kronenberg + */ + function send_file($file_name) { + if (!$this->is_closed) { + $this->close(); + } + $file_name = str_replace(array('"', '<', '>', '|', '?', '*', ':', '/', '\\'), '', $file_name); + + header("Content-type: application/octet-stream"); + header("Content-disposition: attachment; filename=$file_name.zip"); + readfile($this->zipfile_dir.$this->filename.'.zip'); + exit; + } + + /** + * Destructor - removes temporary folder and its content. + * Should self-destruct automatically for PHP 5.0+; otherwise developers should call this function + * to clean up. + * @access public + * @author Harris Wong + */ + function __destruct(){ + clr_dir($this->zipfile_dir); + } +} + +?> \ No newline at end of file diff --git a/include/classes/zipfile.class.php.bak b/include/classes/zipfile.class.php.bak new file mode 100644 index 000000000..690cdcb7e --- /dev/null +++ b/include/classes/zipfile.class.php.bak @@ -0,0 +1,368 @@ +files_data = ''; + $this->central_directory_headers = ''; + $this->num_entries = 0; + $this->is_closed = false; + } + + /** + * Public interface for adding a dir and its contents recursively to zip file + * @access public + * @param string $dir the real system directory that contains the files to add to the zip + * @param string $zip_prefix_dir the zip dir where the contents of $dir will be put in + * @param string $pre_pend_dir used during the recursion to keep track of the path, default='' + * @see $_base_path in include/vitals.inc.php + * @see priv_add_dir() in include/classes/zipfile.class.php + * @see add_file() in include/classes/zipfile.class.php + * @author Joel Kronenberg + */ + function add_dir($dir, $zip_prefix_dir, $pre_pend_dir='') { + if (!($dh = @opendir($dir.$pre_pend_dir))) { + echo 'cant open dir: '.$dir.$pre_pend_dir; + exit; + } + + while (($file = readdir($dh)) !== false) { + /* skip directories */ + if ($file == '.' || $file == '..') { + continue; + } + /* skip potential harmful files/directories */ + if ( (strpos($file, '..') !== false) || (strpos($file, '/') !== false)) { + continue; + } + + $file_info = stat( $dir . $pre_pend_dir . $file ); + + if (is_dir( $dir . $pre_pend_dir . $file )) { + /* create this dir in the zip */ + $this->priv_add_dir( $zip_prefix_dir . $pre_pend_dir . $file . '/', + $file_info['mtime'] ); + + /* continue recursion, going down this dir */ + $this->add_dir( $dir, + $zip_prefix_dir, + $pre_pend_dir . $file . '/' ); + + } else { + /* add this file to the zip */ + $this-> add_file( file_get_contents($dir . $pre_pend_dir . $file), + $zip_prefix_dir . $pre_pend_dir . $file, + $file_info['mtime'] ); + } + } + closedir($dh); + } + + /** + * Adding a dir to the archive + * @access private + * @param string $name directory name + * @param string $timestamp time, default='' + * @author Joel Kronenberg + */ + function priv_add_dir($name, $timestamp = '') { + $name = str_replace("\\", "/", $name); + $old_offset = strlen($this->files_data); + + $local_file_header = "\x50\x4b\x03\x04"; // local file header signature 4 bytes (0x04034b50) + $local_file_header .= "\x0a\x00"; // ver needed to extract // version needed to extract 2 bytes + $local_file_header .= "\x00\x00"; // gen purpose bit flag // general purpose bit flag 2 bytes + $local_file_header .= "\x00\x00"; // compression method // compression method 2 bytes + $local_file_header .= "\x00\x00\x00\x00"; // last mod time and date // last mod file time 2 bytes & last mod file date 2 bytes + $local_file_header .= pack("V",0); // crc32 // crc-32 4 bytes + $local_file_header .= pack("V",0); //compressed filesize // compressed size 4 bytes + $local_file_header .= pack("V",0); //uncompressed filesize // uncompressed size 4 bytes + $local_file_header .= pack("v", strlen($name) ); //length of pathname // file name length 2 bytes + $local_file_header .= pack("v", 0 ); //extra field length // extra field length 2 bytes + $local_file_header .= $name; // file name (variable size) & extra field (variable size) + // end of "local file header" segment + + // no "file data" segment for path + + // add this entry to array + $this->files_data .= $local_file_header; + + // ext. file attributes mirrors MS-DOS directory attr byte, detailed + // at http://support.microsoft.com/support/kb/articles/Q125/0/19.asp + + if ($timestamp) { + $v_date = getdate($timestamp); + } else { + $v_date = getdate(); + } + $time = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $date = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + // now add to central record + $central_directory = "\x50\x4b\x01\x02"; // central file header signature 4 bytes (0x02014b50) + $central_directory .="\x14\x00"; // version made by // version made by 2 bytes + $central_directory .="\x14\x00"; // version needed to extract // version needed to extract 2 bytes + $central_directory .="\x00\x00"; // gen purpose bit flag // general purpose bit flag 2 bytes + $central_directory .="\x00\x00"; // compression method // compression method 2 bytes + $central_directory .= pack("v",$time); // time // last mod file time 2 bytes + $central_directory .= pack("v",$date); // date // last mod file date 2 bytes + $central_directory .= pack("V", 0); // crc32 // crc-32 4 bytes + $central_directory .= pack("V", 0); // compressed filesize // compressed size 4 bytes + $central_directory .= pack("V", 0); // uncompressed filesize // uncompressed size 4 bytes + $central_directory .= pack("v", strlen($name) ); //length of filename // file name length 2 bytes + $central_directory .= pack("v", 0); // extra field length // extra field length 2 bytes + $central_directory .= pack("v", 0); // file comment length // file comment length 2 bytes + $central_directory .= pack("v", 0); // disk number start // disk number start 2 bytes + $central_directory .= pack("v", 0); // internal file attributes // internal file attributes 2 bytes + $central_directory .= pack("V", 16+32); //external file attributes - 'directory' 'archive' bit set // external file attributes 4 bytes + $central_directory .= pack("V", $old_offset); //relative offset of local header // relative offset of local header 4 bytes + $central_directory .= $name; // file name (variable size) + + $this->central_directory_headers .= $central_directory; + + $this->num_entries++; + } + + /** + * Public interface to create a directory in the archive. + * @access public + * @param string $name directory name + * @param string $timestamp time of creation, default='' + * @see $_base_path in include/vitals.inc.php + * @see priv_add_dir() in include/zipfile.class.php + * @author Joel Kronenberg + */ + function create_dir($name, $timestamp='') { + $name = trim($name); + + if (substr($name, -1) != '/') { + /* add the trailing slash */ + $name .= '/'; + } + + $this->priv_add_dir($name, $timestamp = ''); + } + + /** + * Adds a file to the archive. + * @access public + * @param string $file_data file contents + * @param string $name name of file in archive (add path if your want) + * @param string $timestamp time of creation, default='' + * @see $_base_path in include/vitals.inc.php + * @see priv_add_dir() in include/zipfile.class.php + * @author Joel Kronenberg + */ + function add_file($file_data, $name, $timestamp = '') { + $name = str_replace("\\", "/", $name); + $crc = crc32($file_data); + $uncompressed_size = strlen($file_data); + $file_data = substr(gzcompress($file_data, 9), 2, -4); + $compressed_size = strlen($file_data); + $old_offset = strlen($this->files_data); + + /* local file header */ + $local_file_header = "\x50\x4b\x03\x04"; // local file header signature 4 bytes (0x04034b50) + $local_file_header .= "\x14\x00"; // ver needed to extract // version needed to extract 2 bytes + $local_file_header .= "\x00\x00"; // gen purpose bit flag // general purpose bit flag 2 bytes + $local_file_header .= "\x08\x00"; // compression method // compression method 2 bytes + $local_file_header .= "\x00\x00\x00\x00"; // last mod time and date // last mod file time 2 bytes & last mod file date 2 bytes + $local_file_header .= pack("V",$crc); // crc32 // crc-32 4 bytes + $local_file_header .= pack("V",$compressed_size); //compressed filesize // compressed size 4 bytes + $local_file_header .= pack("V",$uncompressed_size); //uncompressed filesize // uncompressed size 4 bytes + $local_file_header .= pack("v", strlen($name) ); //length of filename // file name length 2 bytes + $local_file_header .= "\x00\x00"; //extra field length // extra field length 2 bytes + $local_file_header .= $name; // file name (variable size) & extra field (variable size) + /* end of local file header */ + + $this->files_data .= $local_file_header . $file_data; // . $data_descriptor;; + + /* create the central directory */ + $central_directory = ''; + if ($timestamp) { + $v_date = getdate($timestamp); + } else { + $v_date = getdate(); + } + $time = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $date = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + // now add to central directory record + $central_directory = "\x50\x4b\x01\x02"; // central file header signature 4 bytes (0x02014b50) + $central_directory .="\x14\x00"; // version made by // version made by 2 bytes + $central_directory .="\x14\x00"; // version needed to extract // version needed to extract 2 bytes + $central_directory .="\x00\x00"; // gen purpose bit flag // general purpose bit flag 2 bytes + $central_directory .="\x08\x00"; // compression method // compression method 2 bytes + $central_directory .= pack("v",$time); // time // last mod file time 2 bytes + $central_directory .= pack("v",$date); // date // last mod file date 2 bytes + $central_directory .= pack("V",$crc); // crc32 // crc-32 4 bytes + $central_directory .= pack("V",$compressed_size); //compressed filesize // compressed size 4 bytes + $central_directory .= pack("V",$uncompressed_size); //uncompressed filesize // uncompressed size 4 bytes + $central_directory .= pack("v", strlen($name) ); //length of filename // file name length 2 bytes + $central_directory .= "\x00\x00"; //extra field length // extra field length 2 bytes + $central_directory .= "\x00\x00"; //file comment length // file comment length 2 bytes + $central_directory .= "\x00\x00"; //disk number start // disk number start 2 bytes + $central_directory .= "\x00\x00"; //internal file attributes // internal file attributes 2 bytes + $central_directory .= pack("V", 32); //external file attributes - 'archive' bit set // external file attributes 4 bytes + $central_directory .= pack("V", $old_offset); + + $central_directory .= $name; // file name (variable size) + + $this->central_directory_headers .= $central_directory; + + $this->num_entries++; + } + + /** + * Closes archive, sets $is_closed to true + * @access public + * @param none + * @author Joel Kronenberg + */ + function close() { + $this->files_data .= $this->central_directory_headers . "\x50\x4b\x05\x06\x00\x00\x00\x00" . + pack("v", $this->num_entries). // total # of entries "on this disk" + pack("v", $this->num_entries). // total # of entries overall + pack("V", strlen($this->central_directory_headers)). // size of central dir + pack("V", strlen($this->files_data)). // offset to start of central dir + "\x00\x00"; + + unset($this->central_directory_headers); + unset($this->num_entries); + + $this->zip_file =& $this->files_data; + $this->is_closed = true; + } + + /** + * Gets size of new archive + * Only call this after calling close() - will return false if the zip wasn't close()d yet + * @access public + * @return int size of file + * @author Joel Kronenberg + */ + function get_size() { + if (!$this->is_closed) { + return false; + } + return strlen($this->zip_file); + } + + + /** + * Returns binary file + * @access public + * @see get_size() in include/classes/zipfile.class.php + * @author Joel Kronenberg + */ + function get_file() { + if (!$this->is_closed) { + $this->close(); + } + return $this->zip_file; + } + + /** + * Writes the file to disk. + * Similar to get_file(), but instead of returning the file, it saves it to disk. + * @access public + * @author Joel Kronenberg + * @param $file The full path and file name of the destination file. + */ + function write_file($file) { + if (!$this->is_closed) { + $this->close(); + } + if (function_exists('file_put_contents')) { + file_put_contents($file, $this->zip_file); + } else { + $fp = fopen($file, 'wb+'); + fwrite($fp, $this->zip_file); + fclose($fp); + } + } + + + /** + * Outputs the file - sends headers to browser to force download + * Only call this after calling close() - will return false if the zip wasn't close()d yet + * @access public + * @see get_size() in include/classes/zipfile.class.php + * @author Joel Kronenberg + */ + function send_file($file_name) { + if (!$this->is_closed) { + $this->close(); + } + $file_name = str_replace(array('"', '<', '>', '|', '?', '*', ':', '/', '\\'), '', $file_name); + + header('Content-Type: application/x-zip'); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="'.htmlspecialchars($file_name).'.zip"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: '.$this->get_size()); + + echo $this->get_file(); + + exit; + } +} + +?> \ No newline at end of file diff --git a/include/footer.inc.php b/include/footer.inc.php new file mode 100644 index 000000000..699185303 --- /dev/null +++ b/include/footer.inc.php @@ -0,0 +1,101 @@ + 0) { + $savant->assign('my_uri', $_my_uri); + + $savant->assign('right_menu_open', TRUE); + $savant->assign('popup_help', 'MAIN_MENU'); + $savant->assign('menu_url', ''); + $savant->assign('close_menu_url', htmlspecialchars($_my_uri).'disable=PREF_MAIN_MENU'); + $savant->assign('close_menus', _AT('close_menus')); + + //copyright can be found in include/html/copyright.inc.php + + $side_menu = explode('|', $system_courses[$_SESSION['course_id']]['side_menu']); + + foreach ($side_menu as $side) { + if (isset($_stacks[$side])) { + $stack_files[] = $_stacks[$side]['file']; + } + } +} + +$theme_img = $_base_path . 'themes/'. $_SESSION['prefs']['PREF_THEME'] . '/images/'; +$savant->assign('img', $theme_img); + +if (isset($err)) { + $err->showErrors(); // print all the errors caught on this page +} +$savant->assign('side_menu', $stack_files); + +// this js is indep of the theme used: +?> + +display('include/tm_footer.tmpl.php'); + else + $savant->display('include/fm_footer.tmpl.php'); +} else { + $savant->display('include/footer.tmpl.php'); +} + +//Harris Timer + $mtime = microtime(); + $mtime = explode(" ", $mtime); + $mtime = $mtime[1] + $mtime[0]; + $endtime = $mtime; + $totaltime = ($endtime - $starttime); + debug ($totaltime. ' seconds.', "TIME USED"); +//Harris Timer Ends + +if (defined('AT_DEVEL') && AT_DEVEL) { + debug(TABLE_PREFIX, 'TABLE_PREFIX'); + debug(DB_NAME, 'DB_NAME'); + debug(VERSION, 'VERSION'); + debug($_SESSION); +} + +?> \ No newline at end of file diff --git a/include/header.inc.php b/include/header.inc.php new file mode 100644 index 000000000..0ff115b9a --- /dev/null +++ b/include/header.inc.php @@ -0,0 +1,295 @@ +assign('lang_code', $_SESSION['lang']); +$savant->assign('lang_charset', $myLang->getCharacterSet()); +$savant->assign('base_path', $_base_path); +$savant->assign('base_tmpl_path', $_SERVER['HTTP_HOST']); +$savant->assign('theme', $_SESSION['prefs']['PREF_THEME']); +$savant->assign('current_date', AT_date(_AT('announcement_date_format'))); +$savant->assign('just_social', $_config['just_social']); + +$theme_img = $_base_path . 'themes/'. $_SESSION['prefs']['PREF_THEME'] . '/images/'; +$savant->assign('img', $theme_img); + +$_tmp_base_href = AT_BASE_HREF; +if (isset($course_base_href) || isset($content_base_href)) { + $_tmp_base_href .= $course_base_href; + if ($content_base_href) { + $_tmp_base_href .= $content_base_href; + } +} + +$savant->assign('content_base_href', $_tmp_base_href); +$savant->assign('base_href', AT_BASE_HREF); + +//Handle pretty url pages +if ((($_config['course_dir_name'] + $_config['pretty_url']) > 0) && ($temp = strpos($_SERVER['PHP_SELF'], AT_PRETTY_URL_HANDLER)) > 0){ + $current_page = $pretty_current_page; //this is set in AT_PRETTY_URL_HANDLER +} + +if ($myLang->isRTL()) { + $savant->assign('rtl_css', ''); +} else { + $savant->assign('rtl_css', ''); +} + +$custom_head = ''; +if (isset($_custom_css)) { + $custom_head = ''; +} + +if (isset($_custom_head)) { + $custom_head .= ' +' . $_custom_head; +} + +$savant->assign('custom_css', $custom_head); + +if ($onload && ($_SESSION['prefs']['PREF_FORM_FOCUS'] || ($substr($onload, -8) != 'focus();'))) { + $savant->assign('onload', $onload); +} + +if (isset($_SESSION['valid_user']) && $_SESSION['valid_user'] === true) { + if (!empty($_SESSION['member_id'])) { + $savant->assign('user_name', get_display_name($_SESSION['member_id'])); + } else { + $savant->assign('user_name', $_SESSION['login']); + } +} else { + $savant->assign('user_name', _AT('guest')); +} + +if (!isset($_pages[$current_page])) { + global $msg; + $msg->addError('PAGE_NOT_FOUND'); // probably the wrong error + header('location: '.AT_BASE_HREF.'index.php'); + exit; +} + +$_top_level_pages = get_main_navigation($current_page); + +$_current_top_level_page = get_current_main_page($current_page); + +if (empty($_top_level_pages)) { + if (!$_SESSION['member_id'] && !$_SESSION['course_id']) { + $_top_level_pages = get_main_navigation($_pages[AT_NAV_PUBLIC][0]); + } else if ($_SESSION['course_id'] < 0) { + $_top_level_pages = get_main_navigation($_pages[AT_NAV_ADMIN][0]); + } else if (!$_SESSION['course_id']) { + $_top_level_pages = get_main_navigation($_pages[AT_NAV_START][0]); + } else { + $_top_level_pages = get_main_navigation($_pages[AT_NAV_COURSE][0]); + } +} +$_sub_level_pages = get_sub_navigation($current_page); + +$_current_sub_level_page = get_current_sub_navigation_page($current_page); + +$_path = get_path($current_page); + +unset($_path[0]); +if (isset($_path[2]['url'], $_sub_level_pages[0]['url']) && $_path[2]['url'] == $_sub_level_pages[0]['url']) { + $back_to_page = $_path[3]; +} else if (isset($_path[1]['url'], $_sub_level_pages[0]['url']) && $_path[1]['url'] == $_sub_level_pages[0]['url']) { + $back_to_page = isset($_path[2]) ? $_path[2] : null; +} else if (isset($_path[1])) { + $back_to_page = $_path[1]; +} + +if (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0) { + $_path[] = array('url' => $_base_path . url_rewrite('index.php'), 'title' => $_SESSION['course_title']); +} else if (isset($_SESSION['course_id']) && $_SESSION['course_id'] < 0) { + $_path[] = array('url' => $_base_path . 'admin/index.php', 'title' => _AT('administration')); +} + +if (isset($_SESSION['member_id']) && $_SESSION['member_id']) { + $_path[] = array('url' => $_base_path . 'bounce.php?course=0', 'title' => _AT('my_start_page')); +} else if (!isset($_SESSION['course_id']) || !$_SESSION['course_id']) { + $_path[] = array('url' => $_base_path . 'login.php', 'title' => SITE_NAME); +} + +$_path = array_reverse($_path); + +if (isset($_pages[$current_page]['title'])) { + $_page_title = $_pages[$current_page]['title']; +} else { + $_page_title = _AT($_pages[$current_page]['title_var']); +} + + + +/* calculate the section_title: */ +if (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0) { + //Truncate course title if it's > 45. + $session_course_title = htmlentities($_SESSION['course_title'], ENT_QUOTES, 'UTF-8'); + $section_title = validate_length($session_course_title, 100, VALIDATE_LENGTH_FOR_DISPLAY); + // If there is an icon, display it on the header + $sql = 'SELECT icon FROM '.TABLE_PREFIX.'courses WHERE course_id='.$_SESSION['course_id']; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + if (!empty($row['icon'])){ + //Check if this is a custom icon, if so, use get_course_icon.php to get it + //Otherwise, simply link it from the images/ + $custom_icon_path = AT_CONTENT_DIR.$_SESSION['course_id']."/custom_icons/"; + if (file_exists($custom_icon_path.$row['icon'])) { + if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $course_icon = $_base_path.'get_course_icon.php/?id='.$_SESSION['course_id']; + } else { + $course_icon = $_base_path.'content/' . $_SESSION['course_id'] . '/'; + } + } else { + $course_icon = $_base_path.'images/courses/'.$row['icon']; + } + $savant->assign('icon', $course_icon); + } +} else if (!isset($_SESSION['valid_user']) || !$_SESSION['valid_user']) { + $section_title = SITE_NAME; + if (defined('HOME_URL') && HOME_URL) { + $_top_level_pages[] = array('url' => HOME_URL, 'title' => _AT('home')); + } +} else if ($_SESSION['course_id'] < 0) { + $section_title = _AT('administration'); +} else if (!$_SESSION['course_id']) { + $section_title = _AT('my_start_page'); +} +$savant->assign('current_top_level_page', $_current_top_level_page); +$savant->assign('sub_level_pages', $_sub_level_pages); +$savant->assign('current_sub_level_page', $_current_sub_level_page); + +$savant->assign('path', $_path); +$savant->assign('back_to_page', isset($back_to_page) ? $back_to_page : null); +$savant->assign('page_title', htmlspecialchars($_page_title, ENT_COMPAT, "UTF-8")); +$savant->assign('top_level_pages', $_top_level_pages); +$savant->assign('section_title', $section_title); + +if (isset($_pages[$current_page]['guide'])) { + $savant->assign('guide', AT_GUIDES_PATH . $_pages[$current_page]['guide']); +} + +$myLang->sendContentTypeHeader(); + +if (isset($_SESSION['course_id']) && $_SESSION['course_id'] > -1) { + + /* the list of our courses: */ + /* used for the courses drop down */ + global $system_courses; + if ($_SESSION['valid_user']) { + $sql = "SELECT E.course_id FROM ".TABLE_PREFIX."course_enrollment E WHERE E.member_id=$_SESSION[member_id] AND E.approved<>'n'"; + $result = @mysql_query($sql, $db); + + $nav_courses = array(); /* the list of courses we're enrolled in or own */ + while ($row = @mysql_fetch_assoc($result)) { + //Truncate course title if it's > 45. + $system_courses[$row['course_id']]['title'] = htmlentities($system_courses[$row['course_id']]['title'], ENT_QUOTES, 'UTF-8'); + $nav_courses[$row['course_id']] = validate_length($system_courses[$row['course_id']]['title'], 45, VALIDATE_LENGTH_FOR_DISPLAY); + } + + natcasesort($nav_courses); + reset($nav_courses); + $savant->assign('nav_courses', $nav_courses); + } + + if (($_SESSION['course_id'] > 0) && isset($_SESSION['prefs']['PREF_JUMP_REDIRECT']) && $_SESSION['prefs']['PREF_JUMP_REDIRECT']) { + $savant->assign('rel_url', $_rel_url); + } else { + $savant->assign('rel_url', ''); + } + + /* course specific elements: */ + /* != 'public' special case for the about.php page, which is available from a course but hides the content menu */ + $sequence_links = array(); + if ($_SESSION['course_id'] > 0) { + $sequence_links = $contentManager->generateSequenceCrumbs($cid); + $savant->assign('sequence_links', $sequence_links); + } + + //side menu array + if ($_SESSION['course_id'] > 0) { + $side_menu = array(); + $side_menu = explode('|', $system_courses[$_SESSION['course_id']]['side_menu']); + $side_menu = array_intersect($side_menu, $_stacks); + $savant->assign('side_menu', $side_menu); + } +} + +// array of content tools for shortcuts tool bar. +if (isset($_tool_shortcuts)) $savant->assign('shortcuts', $_tool_shortcuts); + +/* Register our Errorhandler on everypage */ +//require_once(AT_INCLUDE_PATH . 'classes/ErrorHandler/ErrorHandler.class.php'); +//$err = new ErrorHandler(); + + +//TODO*******************BOLOGNA*******************REMOVE ME*******************/ +// if filemanager is a inside a popup or a frame +// i don't like this code. i don't know were these two variables are coming from +// anyone can add ?framed=1 to a URL to alter the behaviour. + +// global $_course_id is set when a guest accessing a public course. +// This is to solve the issue that the google indexing fails as the session vars are lost. +if (isset($_SESSION['course_id'])) + $_course_id = $_SESSION['course_id']; +else if (isset($_GET['p_course'])) // p_course is set when pretty url is turned on and public course is accessed + $_course_id = $_GET['p_course']; + +$savant->assign('course_id', $_course_id); +$savant->assign('is_mobile_device', is_mobile_device()); +$savant->assign('mobile_device_type', get_mobile_device_type()); + +if ((isset($_REQUEST['framed']) && $_REQUEST['framed']) || (isset($_REQUEST['popup']) && $_REQUEST['popup'])) { + $savant->assign('framed', 1); + $savant->assign('popup', 1); + + if(isset($tool_flag) && ($tool_flag)) + $savant->display('include/tm_header.tmpl.php'); //header for toolmanager + else + $savant->display('include/fm_header.tmpl.php'); + +} else { + //$savant->assign('opensocial', open_social_libs($_base_href)); + $savant->display('include/header.tmpl.php'); +} + + +?> diff --git a/include/html/auto_enroll_courses.inc.php b/include/html/auto_enroll_courses.inc.php new file mode 100644 index 000000000..5d34c2c58 --- /dev/null +++ b/include/html/auto_enroll_courses.inc.php @@ -0,0 +1,101 @@ + "") +{ + + $associate_string = $_REQUEST["en_id"]; + + $sql_courses = "SELECT aec.course_id + FROM " . TABLE_PREFIX."auto_enroll a, " . + TABLE_PREFIX."auto_enroll_courses aec + where a.associate_string='".$associate_string ."' + and a.auto_enroll_id = aec.auto_enroll_id"; + + $result_courses = mysql_query($sql_courses, $db) or die(mysql_error()); + + if (mysql_num_rows($result_courses) > 0) $_SESSION['enroll'] = AT_ENROLL_YES; + + while ($row_courses = mysql_fetch_assoc($result_courses)) + { + $course = $row_courses["course_id"]; + + $sql = "SELECT access, member_id FROM ".TABLE_PREFIX."courses WHERE course_id=$course"; + $result = mysql_query($sql, $db); + $course_info = mysql_fetch_assoc($result); + + if ($course_info['access'] == 'private') + { + $sql = "INSERT INTO ".TABLE_PREFIX."course_enrollment VALUES ($member_id, $course, 'n', 0, '"._AT('student')."', 0)"; + $result = mysql_query($sql, $db); + + // send the email - if needed + if ($system_courses[$course]['notify'] == 1) { + $mail_list = array(); //initialize an array to store all the pending emails + + //Get the list of students with enrollment privilege + $module =& $moduleFactory->getModule('_core/enrolment'); + $sql = "SELECT email, first_name, last_name, `privileges` FROM ".TABLE_PREFIX."members m INNER JOIN ".TABLE_PREFIX."course_enrollment ce ON m.member_id=ce.member_id WHERE ce.privileges > 0 AND ce.course_id=$course"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) + { + if (query_bit($row['privileges'], $module->getPrivilege())) + { + unset($row['privileges']); //we don't need the privilege to flow around + $mail_list[] = $row; + } + } + + //Get instructor information + $ins_id = $system_courses[$course]['member_id']; + $sql = "SELECT email, first_name, last_name FROM ".TABLE_PREFIX."members WHERE member_id=$ins_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $mail_list[] = $row; + + //Send email notification to both assistants with privileges & Instructor + foreach ($mail_list as $row) + { + $to_email = $row['email']; + $tmp_message = $row['first_name'] .' ' . $row['last_name']."\n\n"; + $tmp_message .= _AT('enrol_messagenew', $system_courses[$course]['title'], AT_BASE_HREF ); + if ($to_email != '') { + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + $mail = new ATutorMailer; + $mail->From = $_config['contact_email']; + $mail->FromName = $_config['site_name']; + $mail->AddAddress($to_email); + $mail->Subject = _AT('enrol_message3'); + $mail->Body = $tmp_message; + + if (!$mail->Send()) + { + $msg->addError('SENDING_ERROR'); + } + unset($mail); + } + } + } + } else { + $sql = "INSERT INTO ".TABLE_PREFIX."course_enrollment VALUES ($member_id, $course, 'y', 0, '"._AT('student')."', 0)"; + $result = mysql_query($sql, $db); + } + } + +} +?> \ No newline at end of file diff --git a/include/html/auto_enroll_list_courses.inc.php b/include/html/auto_enroll_list_courses.inc.php new file mode 100644 index 000000000..9e7f93410 --- /dev/null +++ b/include/html/auto_enroll_list_courses.inc.php @@ -0,0 +1,85 @@ + "") +{ + $associate_string = $_REQUEST["en_id"]; + + $cats = array(); + $cats[0] = _AT('cats_uncategorized'); + + $sql = "SELECT cat_id, cat_name FROM ".TABLE_PREFIX."course_cats"; + $result = mysql_query($sql,$db); + while($row = mysql_fetch_array($result)) { + $cats[$row['cat_id']] = $row['cat_name']; + } + + $sql_courses = "SELECT aec.auto_enroll_courses_id auto_enroll_courses_id, + aec.course_id, + c.cat_id, + c.title title + FROM " . TABLE_PREFIX."auto_enroll a, " . + TABLE_PREFIX."auto_enroll_courses aec, " . + TABLE_PREFIX ."courses c + where a.associate_string='".$associate_string ."' + and a.auto_enroll_id = aec.auto_enroll_id + and aec.course_id = c.course_id"; + + $result_courses = mysql_query($sql_courses, $db) or die(mysql_error()); + + if (mysql_num_rows($result_courses) > 0) + { +?> + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + + diff --git a/include/html/browse.inc.php b/include/html/browse.inc.php new file mode 100644 index 000000000..3351e1dfb --- /dev/null +++ b/include/html/browse.inc.php @@ -0,0 +1,131 @@ +-1'; + $_GET['access'] = ''; +} + +if (isset($_GET['category']) && ($_GET['category'] > -1)) { + $_GET['category'] = intval($_GET['category']); + $page_string .= SEP.'category='.$_GET['category']; + $sql_category = '='.$_GET['category']; +} else { + $sql_category = '<>-1'; + $_GET['category'] = -1; // all (because 0 = uncategorized) +} + +if (isset($_GET['include']) && $_GET['include'] == 'one') { + $checked_include_one = ' checked="checked"'; + $page_string .= SEP.'include=one'; +} else { + $_GET['include'] = 'all'; + $checked_include_all = ' checked="checked"'; + $page_string .= SEP.'include=all'; +} + +if (!empty($_GET['search'])) { + $page_string .= SEP.'search='.urlencode($stripslashes($_GET['search'])); + $search = $addslashes($_GET['search']); + $search = explode(' ', $search); + + if ($_GET['include'] == 'all') { + $predicate = 'AND '; + } else { + $predicate = 'OR '; + } + + $sql_search = ''; + foreach ($search as $term) { + $term = trim($term); + $term = str_replace(array('%','_'), array('\%', '\_'), $term); + if ($term) { + $term = '%'.$term.'%'; + $sql_search .= "((title LIKE '$term') OR (description LIKE '$term')) $predicate"; + } + } + $sql_search = '('.substr($sql_search, 0, -strlen($predicate)).')'; +} else { + $sql_search = '1'; +} + +$sql = "SELECT COUNT(course_id) AS cnt FROM ".TABLE_PREFIX."courses WHERE access $sql_access AND cat_id $sql_category AND $sql_search AND hide=0"; +$result = mysql_query($sql, $db); +$row = mysql_fetch_assoc($result); +$num_results = $row['cnt']; + +$sql = "SELECT * FROM ".TABLE_PREFIX."courses WHERE access $sql_access AND cat_id $sql_category AND $sql_search AND hide=0 ORDER BY title"; +$courses_result = mysql_query($sql, $db); + +// add "enroll me" link if the user is not the course owner and is not enrolled +while ($row = mysql_fetch_assoc($courses_result)) { + if ($_SESSION['member_id'] > 0) { + $sql = "SELECT * FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$_SESSION[member_id] AND course_id=".$row['course_id']; + $result = mysql_query($sql, $db); + + if ($row['access'] == 'private') { + $enroll_link = ''. _AT('enroll_me').''; + } else { + $enroll_link = ''. _AT('enroll_me').''; + } + + if (mysql_num_rows($result) == 0 && $_SESSION['member_id'] <> $row['member_id']) { + $row['enroll_link'] = $enroll_link; + } else if ($row['access'] == 'private') { + $enrollment_row = mysql_fetch_assoc($courses_result); + if ($enrollment_row['approved'] == 'n') $row['enroll_link'] = $enroll_link; + } + } + $courses_rows[] = $row; +} + +// get the categories + + + + + +
    +
    + + + :
    + />
    + + />
    +
    +
    + + + +assign('dropdown_contents', ob_get_contents()); +ob_end_clean(); + +$savant->assign('title', _AT('search')); + +$savant->display('include/box.tmpl.php'); +?> \ No newline at end of file diff --git a/include/html/dropdowns/users_online.inc.php b/include/html/dropdowns/users_online.inc.php new file mode 100644 index 000000000..35485542b --- /dev/null +++ b/include/html/dropdowns/users_online.inc.php @@ -0,0 +1,49 @@ +".time()." ORDER BY login"; +$result = mysql_query($sql, $db); +if ($row = mysql_fetch_assoc($result)) { + echo '
      '; + do { + $type = 'class="user"'; + if ($system_courses[$_course_id]['member_id'] == $row['member_id']) { + $type = 'class="user instructor" title="'._AT('instructor').'"'; + } + echo '
    • '.AT_print($row['login'], 'members.login').'
    • '; + } while ($row = mysql_fetch_assoc($result)); + echo '
    '; +} else { + echo ''._AT('none_found').'
    '; +} + +echo ''._AT('guests_not_listed').''; + +$savant->assign('dropdown_contents', ob_get_contents()); +ob_end_clean(); +$savant->assign('title', _AT('users_online')); +$savant->display('include/box.tmpl.php'); +?> \ No newline at end of file diff --git a/include/html/frameset/footer.inc.php b/include/html/frameset/footer.inc.php new file mode 100644 index 000000000..691287b6e --- /dev/null +++ b/include/html/frameset/footer.inc.php @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/include/html/frameset/header.inc.php b/include/html/frameset/header.inc.php new file mode 100644 index 000000000..10d4c47e2 --- /dev/null +++ b/include/html/frameset/header.inc.php @@ -0,0 +1,34 @@ + + + + ATutor - <?php echo $_SESSION['course_title']; + if ($cid != 0) { + $myPath = $path; + $num_path = count($myPath); + for ($i =0; $i<$num_path; $i++) { + echo ' - '; + echo $myPath[$i]['title']; + } + } else if (is_array($_section) ) { + $num_sections = count($_section); + for($i = 0; $i < $num_sections; $i++) { + echo ' - '; + echo $_section[$i][0]; + } + } + ?> + + + + isRTL()) { + echo ''."\n"; + } + ?> + + + diff --git a/include/html/languages.inc.php b/include/html/languages.inc.php new file mode 100644 index 000000000..460a6babe --- /dev/null +++ b/include/html/languages.inc.php @@ -0,0 +1,33 @@ +getNumLanguages() < 2){ + return; +} +?> +

    getNumLanguages() > 5) { + echo '
    '; + echo ''; + $languageManager->printDropdown($_SESSION['lang'], 'lang', 'lang'); + echo ' '; + echo '
    '; + } else { + echo ''; + $languageManager->printList($_SESSION['lang'], 'lang', 'lang', htmlspecialchars($_my_uri)); + } +?>
    \ No newline at end of file diff --git a/include/html/release_date.inc.php b/include/html/release_date.inc.php new file mode 100644 index 000000000..aa8734fdd --- /dev/null +++ b/include/html/release_date.inc.php @@ -0,0 +1,72 @@ +'; + for ($i = 1; $i <= 31; $i++) { + echo ''; + } + echo ''; + + echo ''; + + echo ' '; + + echo _AT('at').' :'; + + echo ' '._AT('hours_24').''; +?> \ No newline at end of file diff --git a/include/html/search.inc.php b/include/html/search.inc.php new file mode 100644 index 000000000..2d5708c8b --- /dev/null +++ b/include/html/search.inc.php @@ -0,0 +1,340 @@ + 0) { + $checked_find_in_course = ' checked="checked"'; + $checked_display_as_pages = ' checked="checked"'; + } else if ($_SESSION['valid_user']) { + $checked_find_in_my_courses = ' checked="checked"'; + $checked_display_as_courses = ' checked="checked"'; + } else { + $checked_find_in_all_courses = ' checked="checked"'; + $checked_display_as_summaries = ' checked="checked"'; + } +} +if (isset($_GET['search']) && !$_GET['words']) { + $msg->printErrors('SEARCH_TERM_REQUIRED'); + $_GET = array(); +} + +?> + +
    + +
    +
    + *
    + +
    + +
    +
    + />
    + /> +
    + +
    +
    + 0) : ?> + />
    + + + -1)) : ?> + />
    + + + /> +
    + +
    +
    + /> + /> + /> +
    + +
    +
    + />
    + + />
    + + />

    +
    + +
    + +
    +
    +
    + + 0)) { + if ($_GET['display_as'] == 'pages') { + $search_results = get_search_result($_GET['words'], $predicate, $_SESSION['course_id'], $num_found, $total_score); + } else { // 'courses' or 'summaries' : + $search_results[$_SESSION['course_id']] = get_search_result($_GET['words'], $predicate, $_SESSION['course_id'], $num_found, $total_score); + $search_totals[$_SESSION['course_id']] = $total_score; + } + } else { + if ($_GET['find_in'] == 'my') { + $my_courses = get_my_courses($_SESSION['member_id']); + } else { // $_GET['find_in'] == 'all' (or other). always safe to perform. + $my_courses = get_all_courses($_SESSION['member_id']); + } + + foreach ($my_courses as $tmp_course_id) { + if ($_GET['display_as'] == 'pages') { + // merge all the content results together + $search_results = array_merge($search_results, get_search_result($_GET['words'], $predicate, $tmp_course_id, $num_found, $total_score)); + } else { + // group by Course + $total_score = 0; + $search_results[$tmp_course_id] = get_search_result($_GET['words'], $predicate, $tmp_course_id, $num_found, $total_score); + if ($total_score) { + $search_totals[$tmp_course_id] = $total_score; + } // else: no content found in this course. + } + } + } + + if ($_GET['display_as'] == 'summaries') { + $num_found = count($search_totals); + } + + echo '

    '.$num_found.' '._AT('search_results').'

    '; + + if (!$num_found) { + $msg->printInfos('NO_SEARCH_RESULTS'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } else if (!$num_found && count($search_totals)) { + // meaning: no pages were found, just courses: + $num_found = count($search_totals); + } + + $num_pages = ceil($num_found / $results_per_page); + + $page = isset($_GET['p']) ? intval($_GET['p']) : 0; + if (!$page) { + $page = 1; + } + + $count = (($page-1) * $results_per_page) + 1; + + $pages_text = '
    '; + $pages_text .= '
      '; + for ($i=1; $i<=$num_pages; $i++) { + $pages_text .= '
    • '; + if ($i == $page) { + $pages_text .= ''.$i.''; + } else { + $pages_text .= ''.$i.''; + } + $pages_text .= '
    • '; + } + $pages_text .= '
    '; + $pages_text .= '
    '; + + echo $pages_text; + + if ($_GET['display_as'] == 'pages') { + uasort($search_results, 'score_cmp'); + + $search_results = array_slice($search_results, ($page-1)*$results_per_page, $results_per_page); + + echo '
    '; + print_search_pages($search_results); + echo '
    '."\n"; + } else { + arsort($search_totals); + reset($search_totals); + + $skipped = 0; // number that have been skipped + $printed_so_far = 0; // number printed on this page + + foreach ($search_totals as $tmp_course_id => $score) { + $total_here = 0; + if ($printed_so_far == $results_per_page) { + break; + } + + $increment_count = false; + if (count($search_results[$tmp_course_id]) && ($_GET['display_as'] == 'courses')) { + uasort($search_results[$tmp_course_id], 'score_cmp'); + reset($search_results[$tmp_course_id]); + + $num_available = count($search_results[$tmp_course_id]); // total number available for this course + + if ($printed_so_far == $results_per_page) { + break; + } + + if ($skipped < $count) { + // this course is being truncated + // implies that it's at the start of the page + $start = ($page -1) * $results_per_page - $skipped; + + $total_here = count($search_results[$tmp_course_id]); + + $search_results[$tmp_course_id] = array_slice($search_results[$tmp_course_id], $start, $results_per_page - $printed_so_far); + + $num_printing = count($search_results[$tmp_course_id]); + + $printed_so_far += $num_printing; + $skipped += ($num_available - $num_printing); + + if ($num_printing == 0) { + continue; + } + $increment_count = true; + } + } else { + if ($printed_so_far == $results_per_page) { + break; + } + + $total_here = count($search_results[$tmp_course_id]); + if (($total_here == 0) || ($_GET['display_as'] == 'summaries')) { + if ($skipped < ($page-1) * $results_per_page) { + $skipped++; + continue; + } + $printed_so_far ++; + $increment_count = true; + } else { + $printed_so_far += $total_here; + } + } + echo '
    '._AT('results_from', ''.$highlight_system_courses[$tmp_course_id]['title'] .'').' - '._AT('pages_found', $total_here) . '
    '; + + + echo '

    '; + if ($highlight_system_courses[$tmp_course_id]['description']) { + echo $highlight_system_courses[$tmp_course_id]['description']; + } else { + echo ''._AT('no_description').''; + } + + echo '
    ['._AT('Access').': '; + + switch ($highlight_system_courses[$tmp_course_id]['access']){ + case 'public': + echo _AT('public'); + break; + case 'protected': + echo _AT('protected'); + break; + case 'private': + echo _AT('private'); + break; + } + $language =& $languageManager->getLanguage($highlight_system_courses[$tmp_course_id]['primary_language']); + + echo '. '._AT('primary_language').': ' . $language->getTranslatedName(); + + echo ']'; + + echo '

    '; + + if ($_GET['display_as'] != 'summaries') { + echo '
    '; + print_search_pages($search_results[$tmp_course_id]); + echo '
    '; + } + + } + } + + echo $pages_text; +} + +?> \ No newline at end of file diff --git a/include/index.html b/include/index.html new file mode 100644 index 000000000..bbd0b351b --- /dev/null +++ b/include/index.html @@ -0,0 +1 @@ +this is a blank page \ No newline at end of file diff --git a/include/lib/constants.inc.php b/include/lib/constants.inc.php new file mode 100644 index 000000000..1cfdea02c --- /dev/null +++ b/include/lib/constants.inc.php @@ -0,0 +1,427 @@ + 0){ + $endpos = $temp; +} else { + $endpos = strlen($_base_href); + +} +$_base_href = substr($_base_href, 0, $endpos); +$_base_path = substr($_base_href, strlen($server_protocol . $_SERVER['HTTP_HOST'])); + +define('AT_BASE_HREF', $_base_href); + +/* relative uri */ +$_rel_url = '/'.implode('/', array_slice($url_parts, count($url_parts) - $dir_deep-1)); + +/* where the gudes are (could be a full URL if needed): */ +define('AT_GUIDES_PATH', $_base_path . 'documentation/'); + +define('AT_BACKUP_DIR', AT_CONTENT_DIR . 'backups/'); // where the backups get stored + +define('VERSION', '2.0'); +define('ONLINE_UPDATE', 3); /* update the user expiry every 3 min */ + +/* valid date format_types: */ +/* @see ./include/lib/date_functions.inc.php */ +define('AT_DATE_MYSQL_DATETIME', 1); /* YYYY-MM-DD HH:MM:SS */ +define('AT_DATE_MYSQL_TIMESTAMP_14', 2); /* YYYYMMDDHHMMSS */ +define('AT_DATE_UNIX_TIMESTAMP', 3); /* seconds since epoch */ +define('AT_DATE_INDEX_VALUE', 4); /* index to the date arrays */ + +define('AT_ROLE_STUDENT', 0); +define('AT_ROLE_INSTRUCTOR', 1); + +define('AT_KBYTE_SIZE', 1024); + +define('AT_COURSESIZE_UNLIMITED', -1); +define('AT_COURSESIZE_DEFAULT', -2); /* can be changed in config.inc.php */ +define('AT_FILESIZE_DEFAULT', -3); /* this too */ +define('AT_FILESIZE_SYSTEM_MAX', -4); +$editable_file_types = array('txt', 'html', 'htm', 'xml', 'css', 'asc', 'csv', 'sql'); + +/* how many poll choices are available: */ +define('AT_NUM_POLL_CHOICES', 7); + +/* ways of releasing a test */ +define('AT_RELEASE_NEVER', 0); // do not release +define('AT_RELEASE_IMMEDIATE', 1); // release after submitted +define('AT_RELEASE_MARKED', 2); // release after all q's marked + +/* QPROP = question property: */ +define('AT_TESTS_QPROP_WORD', 1); +define('AT_TESTS_QPROP_SENTENCE', 2); +define('AT_TESTS_QPROP_PARAGRAPH', 3); +define('AT_TESTS_QPROP_PAGE', 4); +define('AT_TESTS_QPROP_ALIGN_VERT', 5); // align question options vertically +define('AT_TESTS_QPROP_ALIGN_HORT', 6); // align question options horizontally + +/* enrollment types for $_SESSION['enroll'] */ +define('AT_ENROLL_NO', 0); +define('AT_ENROLL_YES', 1); +define('AT_ENROLL_ALUMNUS', 2); + +// constants for reading list module +define ('RL_TYPE_BOOK', 1); +define ('RL_TYPE_URL', 2); +define ('RL_TYPE_HANDOUT', 3); +define ('RL_TYPE_AV', 4); +define ('RL_TYPE_FILE', 5); + +// content type +define ('CONTENT_TYPE_CONTENT', 0); +define ('CONTENT_TYPE_FOLDER', 1); +define ('CONTENT_TYPE_WEBLINK', 2); + +// content pre-requisite type +define ('CONTENT_PRE_TEST', 'test'); + +// the types of mobile devices +define('IPOD_DEVICE', 'ipod'); +define('BLACKBERRY_DEVICE', 'blackberry'); +define('ANDROID_DEVICE', 'android'); +define('UNKNOWN_DEVICE', 'unknown'); + +// machine type +define('DESKTOP_DEVICE', 'Desktop'); +define('MOBILE_DEVICE', 'Mobile'); + +$_rl_types = array (); +$_rl_types[RL_TYPE_BOOK] = 'rl_book'; +$_rl_types[RL_TYPE_URL] = 'rl_url'; +$_rl_types[RL_TYPE_HANDOUT] = 'rl_handout'; +$_rl_types[RL_TYPE_AV] = 'rl_av'; +$_rl_types[RL_TYPE_FILE] = 'rl_file'; + +/* control how user inputs get formatted on output: */ +/* note: v131 not all formatting options are available on each section. */ + +define('AT_FORMAT_NONE', 0); /* LEQ to ~AT_FORMAT_ALL */ +define('AT_FORMAT_EMOTICONS', 1); +define('AT_FORMAT_LINKS', 2); +define('AT_FORMAT_IMAGES', 4); +define('AT_FORMAT_HTML', 8); +define('AT_FORMAT_GLOSSARY', 16); +define('AT_FORMAT_ATCODES', 32); +define('AT_FORMAT_CONTENT_DIR', 64); /* remove CONTENT_DIR */ +define('AT_FORMAT_QUOTES', 128); /* remove double quotes (does this get used?) */ +define('AT_FORMAT_ALL', AT_FORMAT_EMOTICONS + + AT_FORMAT_LINKS + + AT_FORMAT_IMAGES + + AT_FORMAT_HTML + + AT_FORMAT_GLOSSARY + + AT_FORMAT_ATCODES + + AT_FORMAT_CONTENT_DIR); + +$_field_formatting = array(); + +$_field_formatting['content.keywords'] = AT_FORMAT_NONE; +$_field_formatting['content.title'] = AT_FORMAT_ALL & ~AT_FORMAT_HTML | AT_FORMAT_QUOTES; +$_field_formatting['content.text'] = AT_FORMAT_ALL; + +$_field_formatting['course_cats.cat_name'] = AT_FORMAT_NONE; + +$_field_formatting['courses.*'] = AT_FORMAT_ALL & ~AT_FORMAT_EMOTICONS & ~AT_FORMAT_ATCODES & ~AT_FORMAT_LINKS & ~AT_FORMAT_IMAGES; + +$_field_formatting['forums.title'] = AT_FORMAT_NONE; +$_field_formatting['forums.description'] = AT_FORMAT_ALL; + +$_field_formatting['forums_threads.subject'] = AT_FORMAT_ALL & ~AT_FORMAT_HTML; +$_field_formatting['forums_threads.body'] = AT_FORMAT_ALL & ~AT_FORMAT_HTML; + +$_field_formatting['glossary.word'] = AT_FORMAT_NONE; +$_field_formatting['glossary.definition'] = AT_FORMAT_ALL & ~AT_FORMAT_HTML; + +$_field_formatting['instructor_approvals.notes']= AT_FORMAT_NONE; + +$_field_formatting['members.*'] = AT_FORMAT_NONE; /* wildcards are okay */ + +$_field_formatting['messages.subject'] = AT_FORMAT_EMOTICONS + AT_FORMAT_IMAGES; +$_field_formatting['messages.body'] = AT_FORMAT_EMOTICONS + AT_FORMAT_LINKS + AT_FORMAT_IMAGES + AT_FORMAT_ATCODES; + +$_field_formatting['news.title'] = AT_FORMAT_EMOTICONS | AT_FORMAT_LINKS & ~AT_FORMAT_HTML; +$_field_formatting['news.body'] = AT_FORMAT_ALL; + +$_field_formatting['resource_categories.CatName']= AT_FORMAT_NONE; +$_field_formatting['resource_categories.Url'] = AT_FORMAT_NONE; +$_field_formatting['resource_links.LinkName'] = AT_FORMAT_NONE; +$_field_formatting['resource_links.Description']= AT_FORMAT_NONE; +$_field_formatting['resource_links.SubmitName']= AT_FORMAT_NONE; + +$_field_formatting['tests.title'] = AT_FORMAT_ALL; +$_field_formatting['tests.instructions'] = AT_FORMAT_ALL; + +$_field_formatting['themes.title'] = AT_FORMAT_NONE; + +$_field_formatting['tests_answers.answer'] = AT_FORMAT_NONE; +$_field_formatting['tests_answers.notes'] = AT_FORMAT_ALL; +$_field_formatting['tests_questions.*'] = AT_FORMAT_ALL; + +$_field_formatting['tests_questions_categories.title'] = AT_FORMAT_NONE; + +$_field_formatting['polls.*'] = AT_FORMAT_ALL; + +$_field_formatting['blog_posts.body'] = AT_FORMAT_ALL & ~AT_FORMAT_HTML; +$_field_formatting['blog_posts.title'] = AT_FORMAT_NONE; + +$_field_formatting['blog_posts_comments.comment'] = AT_FORMAT_ALL & ~AT_FORMAT_HTML; + +$_field_formatting['courses.banner'] = AT_FORMAT_ALL; + +if (isset($_GET['cid'])) { + $cid = intval($_GET['cid']); +} else if (isset($_POST['cid'])) { + $cid = intval($_POST['cid']); +} + + +/* google type constants - @author Harris */ +define('GOOGLE_TYPE_SOAP', 0); //The original soap search with key generated before Dec 2005. +define('GOOGLE_TYPE_AJAX', 1); //The new AJAX search by google + +/* flags for validate_length in vitals. - @author Harris*/ +define('VALIDATE_LENGTH_FOR_DISPLAY', 1); + +/* the length of sublink text display in the course index page, detail view */ +define('SUBLINK_TEXT_LEN', 38); + +/* The lock out time for max login attempts */ +define('LOGIN_ATTEMPT_LOCKED_TIME', 60); //in minutes, default an hour, 60 minutes + +/* UTC Timezones array */ +/* When the PHP requirement is 5.3, replace this with PHP timezone functions */ + +$utc_timezones = array( +array("UTC-12, Y","-12","Pacific/Fiji"), +array("UTC-11, X","-11","Pacific/Midway"), +array("UTC-10, W","-10","Pacific/Honolulu"), +array("UTC-9:30, V","-9.5","Pacific/Polynesia"), +array("UTC-9, V","-9","America/Anchorage"), +array("UTC-8, U","-8"," America/Los_Angeles"), +array("UTC-7, T","-7","America/Denver"), +array("UTC-6, S","-6","America/Chicago"), +array("UTC-5, R","-5","America/New_York"), +array("UTC-4:30, Q","-4.5","America/Caracas"), +array("UTC-4, Q","-4"," America/Antigua"), +array("UTC-3:30, P","-3.5","America/St_Johns"), +array("UTC-3, P","-3","Antarctica/Rothera"), +array("UTC-2, O","-2","Atlantic/South_Georgia"), +array("UTC-1, N","-1","America/Scoresbysund"), +array("UTC+0, Z","0","Europe/Dublin"), +array("UTC+1, A","1","Europe/Andorra"), +array("UTC+2, B","2","Europe/Mariehamn"), +array("UTC+3, C","3","Asia/Baghdad"), +array("UTC+3:30, C","3.5","Asia/Tehran"), +array("UTC+4, D","4","Asia/Dubai"), +array("UTC+4:30, D","4.5","Asia/Kabul"), +array("UTC+5, E","5","Asia/Aqtobe"), +array("UTC+5:30, E","5.5","Asia/Kolkata"), +array("UTC+5:45, E","5.75","Asia/Katmandu"), +array("UTC+6, F","6","Antarctica/Mawson"), +array("UTC+6:30, F","6.5","Asia/Rangoon"), +array("UTC+7, G","7","Antarctica/Davis"), +array("UTC+8, H","8","Australia/Perth"), +array("UTC+8:45, H","8.75","Australia/Eucla"), +array("UTC+9, I","9","Asia/Seoul"), +array("UTC+9:30, I","9.5","Australia/Darwin"), +array("UTC+10, K","10","Australia/Lindeman"), +array("UTC+10:30, K","10.5"," Australia/Lord_Howe"), +array("UTC+11, L","11","Pacific/Ponape"), +array("UTC+11:30, L","11.5","Pacific/Norfolk"), +array("UTC+12, M","12","Antarctica/McMurdo"), +array("UTC+12:45, M","12.75","Pacific/Chatham"), +array("UTC+13, M","13","Pacific/Tongatapu"), +array("UTC+14, M","14","Pacific/Kiritimati")); +/* End of Timezones array */ +?> diff --git a/include/lib/html_resource_parser.inc.php b/include/lib/html_resource_parser.inc.php new file mode 100644 index 000000000..309aa3d9f --- /dev/null +++ b/include/lib/html_resource_parser.inc.php @@ -0,0 +1,111 @@ +set_object($handler); + $parser->set_element_handler('openHandler','closeHandler'); + + $parser->parse($text); + + foreach ($handler->resources as $resource) { + $url_parts = @parse_url($resource); + + if (isset($url_parts['scheme'])) { + // we don't want full urls + continue; + } + + if ((substr($resource, 0, 1) == '/')) { + // we don't want absolute urls + continue; + } + + // make sure this resource exists in this course's content directory: + $resource_server_path = realpath(AT_CONTENT_DIR . $_SESSION['course_id']. '/' . $resource); + if (file_exists($resource_server_path) && is_file($resource_server_path)) { + $resources[$resource] = $resource_server_path; + } + } + + return $resources; +} + +/* + the following resources are to be identified: + even if some of these can't be images, they can still be files in the content dir. + theoretically the only urls we wouldn't deal with would be for a + + img => src + a => href // ignore if href doesn't exist (ie. ) + object => data | classid // probably only want data + applet => classid | archive // whatever these two are should double check to see if it's a valid file (not a dir) + link => href + script => src + form => action + input => src + iframe => src +*/ +class XML_HTMLSax_Handler { + var $elements = array( 'img' => 'src', + 'a' => 'href', + 'object' => array('data', 'classid'), + 'applet' => array('classid', 'archive'), + 'link' => 'href', + 'script' => 'src', + 'form' => 'action', + 'input' => 'src', + 'iframe' => 'src', + 'embed' => 'src', + 'param' => 'value'); + var $resources = array(); + + function XML_HTMLSax_Handler() { + $this->resources = array(); + } + + function openHandler(& $parser,$name,$attrs) { + $name = strtolower($name); + $attrs = array_change_key_case($attrs, CASE_LOWER); + + /* check if this attribute specifies the files in different ways: (ie. java) */ + if (is_array($this->elements[$name])) { + $items = $this->elements[$name]; + + foreach ($items as $item) { + if ($attrs[$item] != '') { + /* some attributes allow a listing of files to include seperated by commas (ie. applet->archive). */ + if (strpos($attrs[$item], ',') !== false) { + $files = explode(',', $attrs[$item]); + foreach ($files as $file) { + $this->resources[] = trim($file); + } + } else { + $this->resources[] = $attrs[$item]; + } + } + } + } else if (isset($this->elements[$name]) && ($attrs[$this->elements[$name]] != '')) { + /* we know exactly which attribute contains the reference to the file. */ + $this->resources[] = $attrs[$this->elements[$name]]; + } + } + function closeHandler(& $parser,$name) { } +} +?> \ No newline at end of file diff --git a/include/lib/json.inc.php b/include/lib/json.inc.php new file mode 100644 index 000000000..e888e309a --- /dev/null +++ b/include/lib/json.inc.php @@ -0,0 +1,806 @@ + + * @author Matt Knapp + * @author Brett Stimmerman + * @copyright 2005 Michal Migurski + * @version CVS: $Id$ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + /*************************************************************************/ + // $Id$ + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_SLICE', 1); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_STR', 2); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_ARR', 3); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_OBJ', 4); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_CMT', 5); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** + * Converts to and from JSON format. + * + * Brief example of use: + * + * + * // create a new instance of Services_JSON + * $json = new Services_JSON(); + * + * // convert a complexe value to JSON notation, and send it to the browser + * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); + * $output = $json->encode($value); + * + * print($output); + * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] + * + * // accept incoming POST data, assumed to be in JSON notation + * $input = file_get_contents('php://input', 1000000); + * $value = $json->decode($input); + * + */ +class Services_JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + */ + function Services_JSON($use = 0) + { + $this->use = $use; + } + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var) + { + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + // treat it like a regular array + $elements = array_map(array($this, 'encode'), $var); + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = get_object_vars($var); + + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->encode($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->encode(strval($name)) . ':' . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { + // found a quote, we're in a string, and it's not escaped + // we know that it's not escaped becase there is _not_ an + // odd number of backslashes at the end of the string so far + array_pop($stk); + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } +} + +if (class_exists('PEAR_Error')) { + + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } + +} else { + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + +} + +?> diff --git a/include/lib/menu_pages.php b/include/lib/menu_pages.php new file mode 100644 index 000000000..7d4b081a9 --- /dev/null +++ b/include/lib/menu_pages.php @@ -0,0 +1,504 @@ + 0) { + $main_links = $home_links = $side_menu = array(); + + if ($system_courses[$_SESSION['course_id']]['main_links']) { + $main_links = explode('|', $system_courses[$_SESSION['course_id']]['main_links']); + foreach ($main_links as $link) { + if (isset($_pages[$link])) { + $_pages[$link]['parent'] = AT_NAV_COURSE; + } + } + $_pages[AT_NAV_COURSE] = array_merge($_pages[AT_NAV_COURSE], $main_links); + } + + if ($system_courses[$_SESSION['course_id']]['home_links']) { + $home_links = explode('|', $system_courses[$_SESSION['course_id']]['home_links']); + foreach ($home_links as $link) { + if (isset($_pages[$link])) { + $_pages[AT_NAV_HOME][] = $link; + } + } + } + + if (authenticate(AT_PRIV_ADMIN, AT_PRIV_RETURN)) { + $_pages[AT_NAV_COURSE][] = 'tools/index.php'; + } else if ($_SESSION['privileges']) { + + /** + * the loop and all this module priv checking is done to hide the Manage tab + * when this student has privileges, but no items linked from the Manage tab. + * Example: the File Storage privilege does not have a Manage tab item. + * In the best case it stops after the first found link. + * In the worst case it goes through all the modules and doesn't find a link. + */ + $module_list = $moduleFactory->getModules(AT_MODULE_STATUS_ENABLED, 0, TRUE); + $keys = array_keys($module_list); + + foreach ($keys as $module_name) { + $module =& $module_list[$module_name]; + if ($module->getPrivilege() && authenticate($module->getPrivilege(), AT_PRIV_RETURN) && ($module->getChildPage('tools/index.php'))) { + $_pages[AT_NAV_COURSE][] = 'tools/index.php'; + break; + } + } + } +} else if (isset($_SESSION['course_id']) && $_SESSION['course_id'] == -1) { + /* admin pages */ + $_pages['admin/index.php']['title_var'] = 'home'; + $_pages['admin/index.php']['parent'] = AT_NAV_ADMIN; + $_pages['admin/index.php']['guide'] = 'admin/?p=configuration.php'; + $_pages['admin/index.php']['children'] = array_merge(array('mods/_core/users/admins/my_edit.php', 'mods/_core/users/admins/my_password.php'), isset($_pages['mods/_core/users/index.php']['children']) ? $_pages['admin/index.php']['children'] : array()); + + $_pages['mods/_core/users/admins/my_edit.php']['title_var'] = 'my_account'; + $_pages['mods/_core/users/admins/my_edit.php']['parent'] = 'admin/index.php'; + $_pages['mods/_core/users/admins/my_edit.php']['guide'] = 'admin/?p=my_account.php'; + + $_pages['mods/_core/users/admins/my_password.php']['title_var'] = 'change_password'; + $_pages['mods/_core/users/admins/my_password.php']['parent'] = 'admin/index.php'; + + if (admin_authenticate(AT_ADMIN_PRIV_ADMIN, AT_PRIV_RETURN)) { + //hide system preference from non-super admins + $_pages[AT_NAV_ADMIN][] = 'admin/config_edit.php'; + + $_pages['admin/config_edit.php']['title_var'] = 'system_preferences'; + $_pages['admin/config_edit.php']['parent'] = AT_NAV_ADMIN; + $_pages['admin/config_edit.php']['guide'] = 'admin/?p=system_preferences.php'; + $_pages['admin/config_edit.php']['children'] = array_merge((array) $_pages['admin/config_edit.php']['children'], array('admin/error_logging.php','mods/_standard/social/index_admin.php')); + } + $_pages['admin/fix_content.php']['title_var'] = 'fix_content_ordering'; + $_pages['admin/fix_content.php']['parent'] = 'admin/index.php'; + + $_pages['admin/error_logging.php']['title_var'] = 'error_logging'; + $_pages['admin/error_logging.php']['parent'] = 'admin/config_edit.php'; + $_pages['admin/error_logging.php']['guide'] = 'admin/?p=error_logging.php'; + $_pages['admin/error_logging.php']['children'] = array_merge(array('admin/error_logging_bundle.php', 'admin/error_logging_reset.php'), isset($_pages['admin/error_logging.php']['children']) ? $_pages['admin/error_logging.php']['children'] : array()); + + $_pages['admin/error_logging_reset.php']['title_var'] = 'reset_log'; + $_pages['admin/error_logging_reset.php']['parent'] = 'admin/error_logging.php'; + + $_pages['admin/error_logging_bundle.php']['title_var'] = 'report_errors'; + $_pages['admin/error_logging_bundle.php']['parent'] = 'admin/error_logging.php'; + + $_pages['admin/error_logging_details.php']['title_var'] = 'viewing_profile_bugs'; + $_pages['admin/error_logging_details.php']['parent'] = 'admin/error_logging.php'; + + $_pages['admin/error_logging_view.php']['title_var'] = 'viewing_errors'; + $_pages['admin/error_logging_view.php']['parent'] = 'admin/error_logging_details.php'; + + if (admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + // hide modules from non-super admins + + $_pages['mods/_core/modules/index.php']['title_var'] = 'modules'; + $_pages['mods/_core/modules/index.php']['parent'] = AT_NAV_ADMIN; + $_pages['mods/_core/modules/index.php']['guide'] = 'admin/?p=modules.php'; + $_pages['mods/_core/modules/index.php']['children'] = array('mods/_core/modules/install_modules.php'); + + $_pages['mods/_core/modules/details.php']['title_var'] = 'details'; + $_pages['mods/_core/modules/details.php']['parent'] = 'mods/_core/modules/index.php'; + + $_pages['mods/_core/modules/module_uninstall_step_1.php']['title_var'] = 'module_uninstall'; + $_pages['mods/_core/modules/module_uninstall_step_1.php']['parent'] = 'mods/_core/modules/index.php'; + + $_pages['mods/_core/modules/module_uninstall_step_2.php']['title_var'] = 'module_uninstall'; + $_pages['mods/_core/modules/module_uninstall_step_2.php']['parent'] = 'mods/_core/modules/index.php'; + + $_pages['mods/_core/modules/module_uninstall_step_3.php']['title_var'] = 'module_uninstall'; + $_pages['mods/_core/modules/module_uninstall_step_3.php']['parent'] = 'mods/_core/modules/index.php'; + + $_pages['mods/_core/modules/install_modules.php']['title_var'] = 'install_modules'; + $_pages['mods/_core/modules/install_modules.php']['parent'] = 'mods/_core/modules/index.php'; + $_pages['mods/_core/modules/install_modules.php']['guide'] = 'admin/?p=modules.php'; + + $_pages['mods/_core/modules/version_history.php']['title_var'] = 'version_history'; + $_pages['mods/_core/modules/version_history.php']['parent'] = 'mods/_core/modules/install_modules.php'; + + $_pages['mods/_core/modules/module_install_step_1.php']['title_var'] = 'details'; + $_pages['mods/_core/modules/module_install_step_1.php']['parent'] = 'mods/_core/modules/install_modules.php'; + + $_pages['mods/_core/modules/module_install_step_2.php']['title_var'] = 'details'; + $_pages['mods/_core/modules/module_install_step_2.php']['parent'] = 'mods/_core/modules/install_modules.php'; + + $_pages['mods/_core/modules/module_install_step_3.php']['title_var'] = 'details'; + $_pages['mods/_core/modules/module_install_step_3.php']['parent'] = 'mods/_core/modules/install_modules.php'; + + $_pages['mods/_core/modules/confirm.php']['title_var'] = 'confirm'; + $_pages['mods/_core/modules/confirm.php']['parent'] = 'mods/_core/modules/add_new.php'; + + $_pages['admin/cron_config.php']['title_var'] = 'cron_config'; + $_pages['admin/cron_config.php']['parent'] = 'admin/config_edit.php'; + $_pages['admin/cron_config.php']['guide'] = 'admin/?p=cron_setup.php'; + $_pages['admin/config_edit.php']['children'] = array_merge((array) $_pages['admin/config_edit.php']['children'], array('admin/cron_config.php')); +/* + $_pages['admin/auto_enroll.php']['title_var'] = 'auto_enroll'; + $_pages['admin/auto_enroll.php']['parent'] = 'admin/config_edit.php'; + $_pages['admin/auto_enroll.php']['guide'] = 'admin/?p=auto_enroll.php'; + $_pages['admin/auto_enroll.php']['children'] = array_merge(array('admin/auto_enroll_edit.php')); + $_pages['admin/config_edit.php']['children'] = array_merge((array) $_pages['admin/config_edit.php']['children'], array('admin/auto_enroll.php')); + + $_pages['admin/auto_enroll_edit.php']['title_var'] = 'auto_enroll_edit'; + $_pages['admin/auto_enroll_edit.php']['parent'] = 'admin/auto_enroll.php'; + $_pages['admin/auto_enroll_edit.php']['guide'] = 'admin/?p=auto_enroll.php'; + + $_pages['admin/auto_enroll_delete.php']['title_var'] = 'auto_enroll_delete'; + $_pages['admin/auto_enroll_delete.php']['parent'] = 'admin/auto_enroll.php'; +*/ + } + } + +/* global pages */ +$_pages['about.php']['title_var'] = 'about_atutor'; + +$_pages['404.php']['title_var'] = '404'; + +$_pages['help/index.php']['title_var'] = 'help'; +$_pages['help/index.php']['children'] = array_merge(array('help/accessibility.php', 'help/contact_support.php'), isset($_pages['help/index.php']['children']) ? $_pages['help/index.php']['children'] : array()); + +$_pages['help/accessibility.php']['title_var'] = 'accessibility'; +$_pages['help/accessibility.php']['parent'] = 'help/index.php'; + +$_pages['help/contact_support.php']['title_var'] = 'contact_support'; +$_pages['help/contact_support.php']['parent'] = 'help/index.php'; + + +$_pages['contact_instructor.php']['title_var'] = 'contact_instructor'; + +/* public pages */ + +$_pages['registration.php']['title_var'] = 'register'; +$_pages['registration.php']['parent'] = AT_NAV_PUBLIC; +$_pages['registration.php']['children'] = isset($_pages['browse.php']['children']) ? $_pages['browse.php']['children'] : array(); +$_pages['registration.php']['guide'] = 'general/?p=register.php'; + +$_pages['browse.php']['title_var'] = 'browse_courses'; +$_pages['browse.php']['parent'] = AT_NAV_PUBLIC; +$_pages['browse.php']['children'] = isset($_pages['browse.php']['children']) ? $_pages['browse.php']['children'] : array(); +$_pages['browse.php']['guide'] = 'general/?p=browse_courses.php'; + +$_pages['login.php']['title_var'] = 'login'; +$_pages['login.php']['parent'] = AT_NAV_PUBLIC; +$_pages['login.php']['children'] = array_merge(array('password_reminder.php'), isset($_pages['login.php']['children']) ? $_pages['login.php']['children'] : array()); +$_pages['login.php']['guide'] = 'general/?p=login.php'; + +$_pages['confirm.php']['title_var'] = 'confirm'; +$_pages['confirm.php']['parent'] = AT_NAV_PUBLIC; + +$_pages['password_reminder.php']['title_var'] = 'password_reminder'; +$_pages['password_reminder.php']['parent'] = 'login.php'; +$_pages['password_reminder.php']['guide'] = 'general/?p=password_reminder.php'; + +$_pages['logout.php']['title_var'] = 'logout'; +$_pages['logout.php']['parent'] = AT_NAV_PUBLIC; + +/* my start page pages */ +$_pages['users/index.php']['title_var'] = 'my_courses'; +$_pages['users/index.php']['parent'] = AT_NAV_START; +$_pages['users/index.php']['guide'] = 'general/?p=my_courses.php'; +if (isset($_SESSION['member_id']) && $_SESSION['member_id'] && (!isset($_SESSION['course_id']) || !$_SESSION['course_id'])) { + $_pages['users/index.php']['children'] = array_merge(array('mods/_core/courses/users/create_course.php'), isset($_pages['users/index.php']['children']) ? $_pages['users/index.php']['children'] : array()); +} + $_pages['users/browse.php']['title_var'] = 'browse_courses'; + //$_pages['users/browse.php']['parent'] = 'users/index.php'; + $_pages['users/browse.php']['parent'] = AT_NAV_START; + $_pages['users/browse.php']['guide'] = 'general/?p=browse_courses.php'; + +$_pages['mods/_core/courses/users/create_course.php']['parent'] = 'users/index.php'; +$_pages['mods/_core/courses/users/create_course.php']['guide'] = 'instructor/?p=creating_courses.php'; +if (isset($_SESSION['member_id']) && get_instructor_status() === TRUE) +{ + $_pages['mods/_core/courses/users/create_course.php']['title_var'] = 'create_course'; +} +else if (isset($_SESSION['member_id']) && defined('ALLOW_INSTRUCTOR_REQUESTS') || ALLOW_INSTRUCTOR_REQUESTS) +{ + $_pages['mods/_core/courses/users/create_course.php']['title_var'] = 'request_instructor_priv'; +} + +$_pages['users/private_enroll.php']['title_var'] = 'enroll'; +$_pages['users/private_enroll.php']['parent'] = 'users/index.php'; + +$_pages['users/remove_course.php']['title_var'] = 'unenroll'; +$_pages['users/remove_course.php']['parent'] = 'users/index.php'; + +$_pages['mods/_standard/profile_pictures/profile_picture.php']['title_var'] = 'picture'; + +$_pages['users/profile.php']['title_var'] = 'profile'; +$_pages['users/profile.php']['parent'] = AT_NAV_START; +$_pages['users/profile.php']['guide'] = 'general/?p=profile.php'; +//$_pages['users/profile.php']['children'] = array_merge(array('users/password_change.php', 'users/email_change.php','mods/_standard/profile_pictures/profile_picture.php'), (array) $_pages['users/profile.php']['children']); +$_pages['users/profile.php']['children'] = array_merge(array('users/password_change.php', 'users/email_change.php','mods/_standard/profile_pictures/profile_picture.php'), (array) $_pages['users/profile.php']['children']); + +$_pages['users/password_change.php']['title_var'] = 'change_password'; +$_pages['users/password_change.php']['parent'] = 'users/profile.php'; +//$_pages['users/password_change.php']['guide'] = 'instructor/?p=creating_courses.php'; + +$_pages['users/email_change.php']['title_var'] = 'change_email'; +$_pages['users/email_change.php']['parent'] = 'users/profile.php'; + +$_pages['users/preferences.php']['title_var'] = 'preferences'; +$_pages['users/preferences.php']['parent'] = AT_NAV_START; +$_pages['users/preferences.php']['guide'] = 'general/?p=preferences.php'; + +$_pages['users/pref_wizard/index.php']['title_var'] = 'preferences'; +$_pages['users/pref_wizard/index.php']['parent'] = AT_NAV_START; + + + +/* course pages */ +$_pages['index.php']['title_var'] = 'home'; +$_pages['index.php']['parent'] = AT_NAV_COURSE; + +$_pages['enroll.php']['title_var'] = 'enroll'; +$_pages['enroll.php']['parent'] = AT_NAV_COURSE; + +/* instructor pages: */ +$_pages['tools/index.php']['title_var'] = 'manage'; +$_pages['tools/index.php']['parent'] = AT_NAV_COURSE; + +$_pages['inbox/index.php']['title_var'] = 'inbox'; +$_pages['inbox/index.php']['children'] = array_merge(array('inbox/sent_messages.php', 'inbox/send_message.php', 'inbox/export.php'), isset($_pages['inbox/index.php']['children']) ? $_pages['inbox/index.php']['children'] : array()); + +$_pages['inbox/sent_messages.php']['title_var'] = 'sent_messages'; +$_pages['inbox/sent_messages.php']['parent'] = 'inbox/index.php'; + +$_pages['inbox/send_message.php']['title_var'] = 'send_message'; +$_pages['inbox/send_message.php']['parent'] = 'inbox/index.php'; + +$_pages['inbox/export.php']['title_var'] = 'export'; +$_pages['inbox/export.php']['parent'] = 'inbox/index.php'; + +$_pages['profile.php']['title_var'] = 'profile'; +$_pages['profile.php']['parent'] = 'index.php'; + + +$current_page = substr($_SERVER['PHP_SELF'], strlen($_base_path)); + +function get_num_new_messages() { + global $db; + static $num_messages; + + if (isset($num_messages)) { + return $num_messages; + } + $sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."messages WHERE to_member_id=$_SESSION[member_id] AND new=1"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $num_messages = $row['cnt']; + + return $num_messages; +} + +//TODO****************BOLOGNA*******************REMOVE ME**********************/ +function get_main_navigation($current_page) { + global $_pages, $_base_path, $_tool; + + $parent_page = $_pages[$current_page]['parent']; + $_top_level_pages = array(); + + $tool_file= $table = ''; + + if (isset($parent_page) && defined($parent_page)) { + foreach($_pages[$parent_page] as $page) { + if (isset($_pages[$page])) { + if (isset($_pages[$page]['title'])) { + $_page_title = $_pages[$page]['title']; + } else { + $_page_title = _AT($_pages[$page]['title_var']); + } + + if(isset($_tool[$_pages[$page]['title_var']])){ //viene prelevato il file nel caso in cui lo strumento sia valodo per essere inserito nella toolbar in fase di editing dei conenuti del corso + $tool_file = $_tool[$_pages[$page]['title_var']]['file']; + $table = $_tool[$_pages[$page]['title_var']]['table']; + } else { + $tool_file = ''; + $table = ''; + } + + $_top_level_pages[] = array('url' => $_base_path . url_rewrite($page), 'title' => $_page_title, 'img' => $_base_path.$_pages[$page]['img'], 'tool_file' => $tool_file, 'table' => $table); + } + } + } else if (isset($parent_page)) { + return get_main_navigation($parent_page); + } + + return $_top_level_pages; +} + +function get_current_main_page($current_page) { + global $_pages, $_base_path; + $parent_page = $_pages[$current_page]['parent']; + + if (isset($parent_page) && defined($parent_page)) { + return $_base_path . url_rewrite($current_page); + } else if (isset($parent_page)) { + return get_current_main_page($parent_page); + } +} + +function get_sub_navigation($current_page) { + global $_pages, $_base_path; + + if (isset($current_page) && defined($current_page)) { + // reached the top + return array(); + } else if (isset($_pages[$current_page]['children'])) { + if (isset($_pages[$current_page]['title'])) { + $_page_title = $_pages[$current_page]['title']; + } else { + $_page_title = _AT($_pages[$current_page]['title_var']); + } + + $_sub_level_pages[] = array('url' => $_base_path . $current_page, 'title' => $_page_title); + foreach ($_pages[$current_page]['children'] as $child) { + + if (isset($_pages[$child]['title'])) { + $_page_title = $_pages[$child]['title']; + } else { + $_page_title = _AT($_pages[$child]['title_var']); + } + + $_sub_level_pages[] = array('url' => $_base_path . $child, 'title' => $_page_title, 'has_children' => isset($_pages[$child]['children'])); + } + } else if (isset($_pages[$current_page]['parent'])) { + // no children + + $parent_page = $_pages[$current_page]['parent']; + return get_sub_navigation($parent_page); + } + + return $_sub_level_pages; +} + +function get_current_sub_navigation_page($current_page) { + global $_pages, $_base_path; + $parent_page = $_pages[$current_page]['parent']; + + if (isset($parent_page) && defined($parent_page)) { + return $_base_path . $current_page; + } else { + return $_base_path . $current_page; + } +} + +function get_path($current_page) { + global $_pages, $_base_path; + + $parent_page = $_pages[$current_page]['parent']; + + if (isset($_pages[$current_page]['title'])) { + $_page_title = $_pages[$current_page]['title']; + } else { + $_page_title = _AT($_pages[$current_page]['title_var']); + } + + if (isset($parent_page) && defined($parent_page)) { + $path[] = array('url' => $_base_path . url_rewrite($current_page), 'title' => $_page_title); + return $path; + } else if (isset($parent_page)) { + $path[] = array('url' => $_base_path . url_rewrite($current_page), 'title' => $_page_title); + $path = array_merge((array) $path, get_path($parent_page)); + } else { + $path[] = array('url' => $_base_path . url_rewrite($current_page), 'title' => $_page_title); + } + + return $path; +} + +//TODO****************BOLOGNA*********************REMOVE ME*****************/ +function get_home_navigation($home_array='') { + global $_pages, $_list, $_base_path, $_tool; + + // set default home_array to course index navigation array + if (!is_array($home_array)) $home_array = $_pages[AT_NAV_HOME]; + + $home_links = array(); + foreach ($home_array as $child) { //esecuzione del ciclo fin quando non saranno terminati i moduli presenti nella home-page del corso + if (isset($_pages[$child])) { + // initialization + $title = $icon = $sub_file = $image = $text = $tool_file = $table =''; + + if (isset($_pages[$child]['title'])) { //viene prelevato il titolo che dovr� poi essere utilizzato nella visualizzazione + $title = $_pages[$child]['title']; + } else { + $title = _AT($_pages[$child]['title_var']); + } + if(isset($_pages[$child]['icon'])) { //si controlla se è presente l'icona inserita nel modulo di rifrimento. si ricorda che l'icona � inserita solo per i moduli che prevedono possibili sottocontenuti. + $icon = $_base_path.$_pages[$child]['icon']; //in caso positivo viene prelevata e inserita in una variabile di appoggio che poi sar� a sua volta inserita all'interno dell'array finale home_links[] + } else if(isset($_pages[$child]['text'])) { //nel caso in cui non sia presente un' icona associata si controlla se � stato settata il testo (per moduli privi di sottocontenuti). + $text = $_pages[$child]['text']; //il testo viene inserito in una variabile d'appoggio e successivamente nell'array. + } + + if (isset($_list[$_pages[$child]['title_var']])) //viene prelevato il path del file che dovr� poi essere richiamato nella visualizzazione dei sottocontenuti. solo i moduli che prevedono sottocontenuti avranno un file di riferimento. + $sub_file = $_list[$_pages[$child]['title_var']]['file']; + + if(isset($_tool[$_pages[$child]['title_var']])){ //viene prelevato il file nel caso in cui lo strumento sia valido per essere inserito nella toolbar in fase di editing dei conenuti del corso + $tool_file = $_tool[$_pages[$child]['title_var']]['file']; + $table = $_tool[$_pages[$child]['title_var']]['table']; + } + + $real_image_in_theme = AT_INCLUDE_PATH.'../themes/'.$_SESSION['prefs']['PREF_THEME'].'/'.$_pages[$child]['img']; + $image_in_theme = $_base_path.'themes/'.$_SESSION['prefs']['PREF_THEME'].'/'.$_pages[$child]['img']; + + // look for images in theme folder. If not exists, use images relative to ATutor root folder + if (file_exists($real_image_in_theme)) + $image = $image_in_theme; + else + $image = $_base_path.$_pages[$child]['img']; + + // inclusion of all data necessary for displaying the modules on the home-page. Set by default to check the visible because the modules will be loaded all visible in the home. + $home_links[] = array('url' => $_base_path . url_rewrite($child), 'title' => $title, 'img' => $image, 'icon' => $icon, 'text' => $text, 'sub_file' => $sub_file, 'tool_file' => $tool_file, 'table' => $table); + } + } + return $home_links; +} + +?> \ No newline at end of file diff --git a/include/lib/mime.inc.php b/include/lib/mime.inc.php new file mode 100644 index 000000000..4fb606f65 --- /dev/null +++ b/include/lib/mime.inc.php @@ -0,0 +1,185 @@ + \ No newline at end of file diff --git a/include/lib/mysql_connect.inc.php b/include/lib/mysql_connect.inc.php new file mode 100644 index 000000000..5aaa6285d --- /dev/null +++ b/include/lib/mysql_connect.inc.php @@ -0,0 +1,42 @@ + \ No newline at end of file diff --git a/include/lib/output.inc.php b/include/lib/output.inc.php new file mode 100644 index 000000000..3c619bf43 --- /dev/null +++ b/include/lib/output.inc.php @@ -0,0 +1,1757 @@ +('. $args[0] .')'; + } + } + + $sql = 'INSERT INTO '.TABLE_PREFIX.'language_pages (`term`, `page`) VALUES ("'.$args[0].'", "'.$_rel_url.'")'; + mysql_query($sql, $db); + + return $msgs; + } + } + + // a template variable + if (!isset($_template)) { + $url_parts = parse_url(AT_BASE_HREF); + $name = substr($_SERVER['PHP_SELF'], strlen($url_parts['path'])-1); + + if ( !($lang_et = cache(120, 'lang', $_SESSION['lang'].'_'.$name)) ) { + global $db; + + /* get $_template from the DB */ + + $sql = "SELECT L.* FROM ".TABLE_PREFIX."language_text L, ".TABLE_PREFIX."language_pages P WHERE L.language_code='{$_SESSION['lang']}' AND L.variable<>'_msgs' AND L.term=P.term AND P.page='$_rel_url' ORDER BY L.variable ASC"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + //Do not overwrite the variable that existed in the cache_template already. + //The edited terms (_c_template) will always be at the top of the resultset + //0003279 + if (isset($_cache_template[$row['term']])){ + continue; + } + + // saves us from doing an ORDER BY + if ($row['language_code'] == $_SESSION['lang']) { + $_cache_template[$row['term']] = stripslashes($row['text']); + } else if (!isset($_cache_template[$row['term']])) { + $_cache_template[$row['term']] = stripslashes($row['text']); + } + } + + cache_variable('_cache_template'); + endcache(true, false); + } + $_template = $_cache_template; + } + $num_args = func_num_args(); + if (is_array($args[0])) { + $args = $args[0]; + $num_args = count($args); + } + $format = array_shift($args); + + if (isset($_template[$format])) { + /* + var_dump($_template); + var_dump($format); + var_dump($args); + exit; + */ + $outString = vsprintf($_template[$format], $args); + $str = ob_get_contents(); + } else { + $outString = ''; + } + + + if ($outString === false) { + return ('[Error parsing language. Variable: '.$format.'. Language: '.$_SESSION['lang'].' ]'); + } + + if (empty($outString)) { + global $db; + $sql = 'SELECT L.* FROM '.TABLE_PREFIX.'language_text L WHERE L.language_code="'.$_SESSION['lang'].'" AND L.variable<>"_msgs" AND L.term="'.$format.'"'; + + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $_template[$row['term']] = stripslashes($row['text']); + $outString = $_template[$row['term']]; + if (empty($outString)) { + return ('[ '.$format.' ]'); + } + $outString = $_template[$row['term']]; + $outString = vsprintf($outString, $args); + + /* update the locations */ + $sql = 'INSERT INTO '.TABLE_PREFIX.'language_pages (`term`, `page`) VALUES ("'.$format.'", "'.$_rel_url.'")'; + mysql_query($sql, $db); + } + + return $outString; +} + +/**********************************************************************************************************/ + /** + * Transforms text based on formatting preferences. Original $input is also changed (passed by reference). + * Can be called as: + * 1) $output = AT_print($input, $name); + * echo $output; + * + * 2) echo AT_print($input, $name); // prefered method + * + * @access public + * @param string $input text being transformed + * @param string $name the unique name of this field (convension: table_name.field_name) + * @param boolean $runtime_html forcefully disables html formatting for $input (only used by fields that + * have the 'formatting' option + * @return string transformed $input + * @see AT_FORMAT constants in include/lib/constants.inc.php + * @see query_bit() in include/vitals.inc.php + * @author Joel Kronenberg + */ + function AT_print($input, $name, $runtime_html = true) { + global $_field_formatting, $_config; + + if (!isset($_field_formatting[$name])) { + /* field not set, check if there's a global setting */ + $parts = explode('.', $name); + + /* check if wildcard is set: */ + if (isset($_field_formatting[$parts[0].'.*'])) { + $name = $parts[0].'.*'; + } else { + /* field not set, and there's no global setting */ + /* same as AT_FORMAT_NONE */ + return $input; + } + } + + if (query_bit($_field_formatting[$name], AT_FORMAT_QUOTES)) { + $input = str_replace('"', '"', $input); + } + + if (query_bit($_field_formatting[$name], AT_FORMAT_CONTENT_DIR)) { + $input = str_replace('CONTENT_DIR/', '', $input); + } + + if (query_bit($_field_formatting[$name], AT_FORMAT_HTML) && $runtime_html) { + /* what special things do we have to do if this is HTML ? remove unwanted HTML? validate? */ + } else { + $input = str_replace('<', '<', $input); + $input = nl2br($input); + } + + if (isset($_config['latex_server']) && $_config['latex_server']) { + $input = preg_replace('/\[tex\](.*?)\[\/tex\]/sie', "'\"'.'$1'.'\"'", $input); + } + + /* this has to be here, only because AT_FORMAT_HTML is the only check that has an else-block */ + if ($_field_formatting[$name] === AT_FORMAT_NONE) { + return $input; + } + + if (query_bit($_field_formatting[$name], AT_FORMAT_EMOTICONS)) { + $input = smile_replace($input); + } + + if (query_bit($_field_formatting[$name], AT_FORMAT_ATCODES)) { + $input = trim(myCodes(' ' . $input . ' ')); + } + + if (query_bit($_field_formatting[$name], AT_FORMAT_LINKS)) { + $input = trim(make_clickable(' ' . $input . ' ')); + } + + if (query_bit($_field_formatting[$name], AT_FORMAT_IMAGES)) { + $input = trim(image_replace(' ' . $input . ' ')); + } + + + return $input; + } + +/********************************************************************************************/ +// Global variables for emoticons + +global $smile_pics; +global $smile_codes; +if (!isset($smile_pics)) { + $smile_pics[0] = $_base_path.'images/forum/smile.gif'; + $smile_pics[1] = $_base_path.'images/forum/wink.gif'; + $smile_pics[2] = $_base_path.'images/forum/frown.gif'; + $smile_pics[3] = $_base_path.'images/forum/ohwell.gif'; + $smile_pics[4] = $_base_path.'images/forum/tongue.gif'; + $smile_pics[5] = $_base_path.'images/forum/51.gif'; + $smile_pics[6] = $_base_path.'images/forum/52.gif'; + $smile_pics[7] = $_base_path.'images/forum/54.gif'; + $smile_pics[8] = $_base_path.'images/forum/27.gif'; + $smile_pics[9] = $_base_path.'images/forum/19.gif'; + $smile_pics[10] = $_base_path.'images/forum/3.gif'; + $smile_pics[11] = $_base_path.'images/forum/56.gif'; +} + +if (!isset($smile_codes)) { + $smile_codes[0] = ':)'; + $smile_codes[1] = ';)'; + $smile_codes[2] = ':('; + $smile_codes[3] = '::ohwell::'; + $smile_codes[4] = ':P'; + $smile_codes[5] = '::evil::'; + $smile_codes[6] = '::angry::'; + $smile_codes[7] = '::lol::'; + $smile_codes[8] = '::crazy::'; + $smile_codes[9] = '::tired::'; + $smile_codes[10] = '::confused::'; + $smile_codes[11] = '::muah::'; +} + +/** +* Replaces smile-code text into smilie image. +* @access public +* @param string $text smile text to be transformed +* @return string transformed $text +* @see $smile_pics in include/lib/output.inc.php (above) +* @see $smile_codes in include/lib/output.inc.php (above) +* @author Joel Kronenberg +*/ +function smile_replace($text) { + global $smile_pics; + global $smile_codes; + static $smiles; + + $smiles[0] = ''._AT('smile_smile').''; + $smiles[1] = ''._AT('smile_wink').''; + $smiles[2] = ''._AT('smile_frown').''; + $smiles[3]= ''._AT('smile_oh_well').''; + $smiles[4]= ''._AT('smile_tongue').''; + $smiles[5]= ''._AT('smile_evil').''; + $smiles[6]= ''._AT('smile_angry').''; + $smiles[7]= ''._AT('smile_lol').''; + $smiles[8]= ''._AT('smile_crazy').''; + $smiles[9]= ''._AT('smile_tired').''; + $smiles[10]= ''._AT('smile_confused').''; + $smiles[11]= ''._AT('smile_muah').''; + + $text = str_replace($smile_codes[0],$smiles[0],$text); + $text = str_replace($smile_codes[1],$smiles[1],$text); + $text = str_replace($smile_codes[2],$smiles[2],$text); + $text = str_replace($smile_codes[3],$smiles[3],$text); + $text = str_replace($smile_codes[4],$smiles[4],$text); + $text = str_replace($smile_codes[5],$smiles[5],$text); + $text = str_replace($smile_codes[6],$smiles[6],$text); + $text = str_replace($smile_codes[7],$smiles[7],$text); + $text = str_replace($smile_codes[8],$smiles[8],$text); + $text = str_replace($smile_codes[9],$smiles[9],$text); + $text = str_replace($smile_codes[10],$smiles[10],$text); + $text = str_replace($smile_codes[11],$smiles[11],$text); + + return $text; +} + + +/* Used specifically for the visual editor +*/ +function smile_javascript () { + global $_base_path; + global $smile_pics; + global $smile_codes; + + static $i = 0; + + while ($smile_pics [$i]) { + echo 'case "'.$smile_codes[$i].'":'."\n"; + echo 'pic = "'.$smile_pics[$i].'";'."\n"; + echo 'break;'."\n"; + $i++; + } +} + +function myCodes($text, $html = false) { + global $_base_path; + global $HTTP_USER_AGENT; + + if (substr($HTTP_USER_AGENT,0,11) == 'Mozilla/4.7') { + $text = str_replace('[quote]','

    ',$text); + $text = str_replace('[/quote]','

    ',$text); + + $text = str_replace('[reply]','

    ',$text); + $text = str_replace('[/reply]','

    ',$text); + } else { + $text = str_replace('[quote]','

    ',$text); + $text = str_replace('[/quote]','

    ',$text); + + $text = str_replace('[reply]','

    ',$text); + $text = str_replace('[/reply]','

    ',$text); + } + + $text = str_replace('[b]','',$text); + $text = str_replace('[/b]','',$text); + + $text = str_replace('[i]','',$text); + $text = str_replace('[/i]','',$text); + + $text = str_replace('[u]','',$text); + $text = str_replace('[/u]','',$text); + + $text = str_replace('[center]','

    ',$text); + $text = str_replace('[/center]','

    ',$text); + + /* colours */ + $text = str_replace('[blue]','',$text); + $text = str_replace('[/blue]','',$text); + + $text = str_replace('[orange]','',$text); + $text = str_replace('[/orange]','',$text); + + $text = str_replace('[red]','',$text); + $text = str_replace('[/red]','',$text); + + $text = str_replace('[purple]','',$text); + $text = str_replace('[/purple]','',$text); + + $text = str_replace('[green]','',$text); + $text = str_replace('[/green]','',$text); + + $text = str_replace('[gray]','',$text); + $text = str_replace('[/gray]','',$text); + + $text = str_replace('[op]',' '._AT('view_entire_post').'',$text); + + $text = str_replace('[head1]','

    ',$text); + $text = str_replace('[/head1]','

    ',$text); + + $text = str_replace('[head2]','

    ',$text); + $text = str_replace('[/head2]','

    ',$text); + + $text = str_replace('[cid]',$_base_path.'content.php?cid='.$_SESSION['s_cid'],$text); + + // fix for http://www.atutor.ca/atutor/mantis/view.php?id=4104 + global $sequence_links; + if ($_SESSION['course_id'] > 0 && !isset($sequence_links) && $_REQUEST['cid'] > 0) { + global $contentManager; + $sequence_links = $contentManager->generateSequenceCrumbs($_REQUEST['cid']); + } + if (isset($sequence_links['previous']) && $sequence_links['previous']['url']) { + $text = str_replace('[pid]', $sequence_links['previous']['url'], $text); + } + if (isset($sequence_links['next']) && $sequence_links['next']['url']) { + $text = str_replace('[nid]', $sequence_links['next']['url'], $text); + } + if (isset($sequence_links['resume']) && $sequence_links['resume']['url']) { + $text = str_replace('[nid]', $sequence_links['resume']['url'], $text); + } + if (isset($sequence_links['first']) && $sequence_links['first']['url']) { + $text = str_replace('[fid]', $sequence_links['first']['url'], $text); + } + +//LAW - replace

    tags in [code] tags with
    +//http://www.atutor.ca/atutor/mantis/view.php?id=4134 - attempt to fix this bug - does not work as required +// $outputUtils = new ContentOutputUtils(); +// $text = $outputUtils ->stripPtags($text); + + /* contributed by Thomas M. Duffey */ + $html = !$html ? 0 : 1; + + // little hack added by greg to add syntax highlighting without using + + $text = str_replace("[code]","[code][/code]",$text); + + $text = preg_replace("/\[code\]\s*(.*)\s*\[\\/code\]/Usei", "highlight_code(fix_quotes('\\1'), $html)", $text); + // now remove the */ +function highlight_code($code, $html) { + // XHTMLize PHP highlight_string output until it gets fixed in PHP + static $search = array( + '
    ', + '', + 'color="'); + + static $replace = array( + '
    ', + '', + 'style="color:'); + if (!$html) { + $code = str_replace('<', '<', $code); + $code = str_replace("\r", '', $code); + } + + return str_replace($search, $replace, highlight_string($code, true)); +} + +/* contributed by Thomas M. Duffey */ +function fix_quotes($text){ + return str_replace('\\"', '"', $text); +} + +function embed_media($text) { + if (preg_match("/\[media(\|[0-9]+\|[0-9]+)?\]*/", $text)==0){ + return $text; + } + + $media_matches = Array(); + + /* + First, we search though the text for all different kinds of media defined by media tags and store the results in $media_matches. + + Then the different replacements for the different media tags are stored in $media_replace. + + Lastly, we loop through all $media_matches / $media_replaces. (We choose $media_replace as index because $media_matches is multi-dimensioned.) It is important that for each $media_matches there is a $media_replace with the same index. For each media match we check the width/height, or we use the default value of 425x350. We then replace the height/width/media1/media2 parameter placeholders in $media_replace with the correct ones, before running a str_replace on $text, replacing the given media with its correct replacement. + + */ + + // youtube videos + preg_match_all("#\[media[0-9a-z\|]*\]http://([a-z0-9\.]*)?youtube.com/watch\?v=([a-z0-9_-]+)\[/media\]#i",$text,$media_matches[1],PREG_SET_ORDER); + $media_replace[1] = ''; + + // .mpg + preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).mpg\[/media\]#i",$text,$media_matches[2],PREG_SET_ORDER); + $media_replace[2] = "##MEDIA1##.mpg"; + + // .avi + preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).avi\[/media\]#i",$text,$media_matches[3],PREG_SET_ORDER); + $media_replace[3] = "##MEDIA1##.avi"; + + // .wmv + preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).wmv\[/media\]#i",$text,$media_matches[4],PREG_SET_ORDER); + $media_replace[4] = "##MEDIA1##.wmv"; + + // .mov + preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).mov\[/media\]#i",$text,$media_matches[5],PREG_SET_ORDER); + $media_replace[5] = "##MEDIA1##.mov"; + + // .swf + preg_match_all("#\[media[0-9a-z\|]*\]([.\w\d]+[^\s\"]+).swf\[/media\]#i",$text,$media_matches[6],PREG_SET_ORDER); + $media_replace[6] = " ##MEDIA1##.swf"; + + // .mp3 + preg_match_all("#\[media[0-9a-z\|]*\](.+[^\s\"]+).mp3\[/media\]#i",$text,$media_matches[7],PREG_SET_ORDER); + $media_replace[7] = "##MEDIA1##.mp3"; + + // .wav + preg_match_all("#\[media[0-9a-z\|]*\](.+[^\s\"]+).wav\[/media\]#i",$text,$media_matches[8],PREG_SET_ORDER); + $media_replace[8] ="##MEDIA1##.wav"; + + // .ogg + preg_match_all("#\[media[0-9a-z\|]*\](.+[^\s\"]+).ogg\[/media\]#i",$text,$media_matches[9],PREG_SET_ORDER); + $media_replace[9] ="##MEDIA1##.ogg"; + + // .mid + preg_match_all("#\[media[0-9a-z\|]*\](.+[^\s\"]+).mid\[/media\]#i",$text,$media_matches[10],PREG_SET_ORDER); + $media_replace[10] ="##MEDIA1##.mid"; + + $text = preg_replace("#\[media[0-9a-z\|]*\](.+[^\s\"]+).mid\[/media\]#i", "\\1.mid", $text); + + // Executing the replace + for ($i=1;$i<=count($media_replace);$i++){ + foreach($media_matches[$i] as $media) + { + + //find width and height for each matched media + if (preg_match("/\[media\|([0-9]*)\|([0-9]*)\]*/", $media[0], $matches)) + { + $width = $matches[1]; + $height = $matches[2]; + } + else + { + $width = 425; + $height = 350; + } + + //replace media tags with embedded media for each media tag + $media_input = $media_replace[$i]; + $media_input = str_replace("##WIDTH##","$width",$media_input); + $media_input = str_replace("##HEIGHT##","$height",$media_input); + $media_input = str_replace("##MEDIA1##","$media[1]",$media_input); + $media_input = str_replace("##MEDIA2##","$media[2]",$media_input); + $text = str_replace($media[0],$media_input,$text); + } + } + + return $text; +} + +function make_clickable($text) { + $text = embed_media($text); + +// $text = eregi_replace("([[:space:]])(http[s]?)://([^[:space:]<]*)([[:alnum:]#?/&=])", "\\1\\3\\4", $text); +// +// $text = eregi_replace( '([_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'. +// '\@'.'[_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'.'(\.[a-zA-Z]{1,6})+)', +// "\\1", +// $text); + + $text = preg_replace("/([\s])(http[s]?):\/\/(.*)(\s|\$|)(.*)/U", + "\\1\\3\\4\\5", $text); + + $text = preg_replace('/([_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'. + '\@'.'[_a-zA-Z0-9\-]+(\.[_a-zA-Z0-9\-]+)*'.'(\.[a-zA-Z]{1,6})+)/i', + "\\1", + $text); + return $text; +} + +function image_replace($text) { + /* image urls do not require http:// */ + +// $text = eregi_replace("\[image(\|)?([[:alnum:][:space:]]*)\]" . +// "[:space:]*" . +// "([[:alnum:]#?/&=:\"'_.-]+)" . +// "[:space:]*" . +// "((\[/image\])|(.*\[/image\]))", +// "\"\\2\"", +// $text); + + $text = preg_replace("/\[image(\|)?([a-zA-Z0-9\s]*)\]". + "[\s]*". + "([a-zA-Z0-9\#\?\/\&\=\:\\\"\'\_\.\-]+)[\s]*". + "((\[\/image\])|(.*\[\/image\]))/i", + "\"\\2\"", + $text); + + return $text; +} + +function format_final_output($text, $nl2br = true) { + global $_base_path; + + $text = str_replace('CONTENT_DIR/', '', $text); + if ($nl2br) { + return nl2br(image_replace(make_clickable(myCodes(' '.$text, false)))); + } + + return image_replace(make_clickable(myCodes(' '.$text, true))); +} + +// +function apply_customized_format($input) { + global $_input, $moduleFactory, $content_base_href, $_content_base_href; + + $_input = $input; + $_content_base_href = $content_base_href; + + $enabled_modules = $moduleFactory->getModules(AT_MODULE_STATUS_ENABLED); + + if (is_array($enabled_modules)) + { + foreach ($enabled_modules as $dir_name => $mod) + { + $module_content_format_file = AT_INCLUDE_PATH . '../mods/'.$dir_name.'/module_format_content.php'; + if (file_exists($module_content_format_file)) + { + include($module_content_format_file); + } + } + } + + return $_input; +} +/****************************************************************************************/ +/* @See: ./user/search.php & ./index.php */ +function highlight($input, $var) {//$input is the string, $var is the text to be highlighted + if ($var != "") { + $xtemp = ""; + $i=0; + /* + The following 'if' statement is a check to ensure that the search term is not part of the tag, ''. Words within this string are avoided in case a previously highlighted string is used for the haystack, $input. To avoid any html breaks in the highlighted string, the search word is avoided completely. + */ + if (strpos('', $var) !== false) { + return $input; + } + while($i'; + $i += strlen($var); + } + else { + $xtemp .= $input{$i}; + $i++; + } + } + $input = $xtemp; + } + return $input; +} + + +/* @See: ./index.php */ +function format_content($input, $html = 0, $glossary, $simple = false) { + global $_base_path, $_config; + + if (!$html) { + $input = str_replace('<', '<', $input); + $input = str_replace('<?php', ''; + $output .= ''; + return $output; + } + + /* do the glossary search and replace: */ + if (is_array($glossary)) { + foreach ($glossary as $k => $v) { + $k = urldecode($k); + $v = str_replace("\n", '
    ', $v); + $v = str_replace("\r", '', $v); + + /* escape special characters */ + $k = preg_quote($k); + + $k = str_replace('<', '<', $k); + $k = str_replace('/', '\/', $k); + + $original_term = $k; + $term = $original_term; + + $term = '(\s*'.$term.'\s*)'; + $term = str_replace(' ','((
    )*\s*)', $term); + + $def = htmlspecialchars($v, ENT_QUOTES, 'UTF-8'); + if ($simple) { + $input = preg_replace + ("/(\[\?\])$term(\[\/\?\])/i", + '\\2', + $input); + } else { + $input = preg_replace + ("/(\[\?\])$term(\[\/\?\])/i", + '\\2?',$input); + } + } + } else if (!$user_glossary) { + $input = str_replace(array('[?]','[/?]'), '', $input); + } + + $input = str_replace('CONTENT_DIR', '', $input); + + if (isset($_config['latex_server']) && $_config['latex_server']) { + $input = preg_replace('/\[tex\](.*?)\[\/tex\]/sie', "'\"'.'$1'.'\"'", $input); + } + + if ($html) { + $x = apply_customized_format(format_final_output($input, false)); + return $x; + } + + $output = apply_customized_format(format_final_output($input)); + + $output = '

    '.$output.'

    '; + + return $output; +} + +function get_content_table($content) +{ + preg_match_all("/<(h[\d]+)[^>]*>(.*)<\/(\s*)\\1(\s*)>/i", $content, $found_headers, PREG_SET_ORDER); + + if (count($found_headers) == 0) return array("", $content); + else + { + $num_of_headers = 0; + + for ($i = 0; $i < count($found_headers); $i++) + { + $div_id = "_header_" . $num_of_headers++; + + if ($i == 0) + { + $content_table = "
    \n
    ". _AT("table_of_contents")."\n"; + } + + $content = str_replace($found_headers[$i][0], '
    '.$found_headers[$i][0].'
    ', $content); + $content_table .= ''. $found_headers[$i][2]."\n"; + + if ($i == count($found_headers) - 1) + { + $content_table .= "

    "; + } + } + return array($content_table, $content); + } +} + +function find_terms($find_text) { + preg_match_all("/(\[\?\])(.[^\?]*)(\[\/\?\])/i", $find_text, $found_terms, PREG_PATTERN_ORDER); + return $found_terms; +} + +/*********************************************************************** + @See /include/Classes/Message/Message.class.php + Jacek Materna +*/ + +/** +* Take a code as input and grab its language specific message. Also cache the resulting +* message. Return the message. Same as get_message but key value in cache is string +* @access public +* @param string $codes Message Code to translate - > 'term' field in DB +* @return string The translated language specific message for code $code +* @author Jacek Materna +*/ +function getTranslatedCodeStr($codes) { + + /* this is where we want to get the msgs from the database inside a static variable */ + global $_cache_msgs_new; + static $_msgs_new; + + if (!isset($_msgs_new)) { + if ( !($lang_et = cache(120, 'msgs_new', $_SESSION['lang'])) ) { + global $db, $_base_path; + + $parent = Language::getParentCode($_SESSION['lang']); + + /* get $_msgs_new from the DB */ + $sql = 'SELECT * FROM '.TABLE_PREFIX.'language_text WHERE variable="_msgs" AND (language_code="'.$_SESSION['lang'].'" OR language_code="'.$parent.'")'; + $result = @mysql_query($sql, $db); + $i = 1; + while ($row = @mysql_fetch_assoc($result)) { + // do not cache key as a digit (no contstant(), use string) + $_cache_msgs_new[$row['term']] = str_replace('SITE_URL/', $_base_path, $row['text']); + if (AT_DEVEL) { + $_cache_msgs_new[$row['term']] .= ' ('.$row['term'].')'; + } + } + + cache_variable('_cache_msgs_new'); + endcache(true, false); + } + $_msgs_new = $_cache_msgs_new; + } + + if (is_array($codes)) { + /* this is an array with terms to replace */ + $code = array_shift($codes); + + $message = $_msgs_new[$code]; + $terms = $codes; + + /* replace the tokens with the terms */ + $message = vsprintf($message, $terms); + + } else { + $message = $_msgs_new[$codes]; + + if ($message == '') { + /* the language for this msg is missing: */ + + $sql = 'SELECT * FROM '.TABLE_PREFIX.'language_text WHERE variable="_msgs"'; + $result = @mysql_query($sql, $db); + $i = 1; + while ($row = @mysql_fetch_assoc($result)) { + if (($row['term']) === $codes) { + $message = '['.$row['term'].']'; + break; + } + } + } + $code = $codes; + } + return $message; +} + +function html_get_list($array) { + $list = ''; + foreach ($array as $value) { + $list .= '
  • '.$value.'
  • '; + } + return $list; +} + +/** + * print_paginator + * + * print out list of page links + */ +function print_paginator($current_page, $num_rows, $request_args, $rows_per_page = 50, $window = 5) { + $num_pages = ceil($num_rows / $rows_per_page); + $request_args = '?'.$request_args; + + if ($num_rows) { + echo '
    '; + echo '
      '; + + $i=max($current_page-$window - max($window-$num_pages+$current_page,0), 1); + + if ($i > 1) { + echo '
    • 1
    • '; + if ($i > 2) { + echo '
    • '; + } + } + + for ($i; $i<= min($current_page+$window -min($current_page-$window,0),$num_pages); $i++) { + if ($current_page == $i) { + echo '
    • '.$current_page.'
    • '; + } else { + echo '
    • '.$i.'
    • '; + } + } + if ($i <= $num_pages) { + if ($i < $num_pages) { + echo '
    • '; + } + echo '
    • '.$num_pages.'
    • '; + } + echo '
    '; + echo '
    '; + } +} + + +/** +* According to user's preferences, it provides appropriated resources in the content page. +* @access public +* @param $cid: content id. +* @param $content_page: the original content page ($content_row['text'], from content.php). +* @return string|array $content: the content page with the appropriated resources. +* @see $db in include/vitals.inc.php +* @author Silvia Mirri +*/ +function provide_alternatives1($cid, $content_page){ + global $db; + + $vidoe_exts = array("mpg", "avi", "wmv", "mov", "swf", "mp3", "wav", "ogg", "mid"); + + $content = $content_page; + + if (($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_TEXT']==0) && ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_AUDIO']==0) && ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_VISUAL']==0)) + { + //No user's preferences related to content format are declared + return $content; + } + /*else if ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_TEXT']==1){ + + $sql_primary = "SELECT * FROM ".TABLE_PREFIX."primary_resources WHERE content_id=".$cid." and resource='".mysql_real_escape_string($content_page)."'"; + + $result = mysql_query($sql_primary, $db); + if (mysql_num_rows($result) > 0) { + while ($row = mysql_fetch_assoc($result)) { + $sql_type = "SELECT * FROM ".TABLE_PREFIX."primary_resources_types WHERE primary_resource_id=$row[primary_resource_id]"; + $result_type = mysql_query($sql_type, $db); + if (mysql_num_rows($result_type) > 0) { + while ($row_type = mysql_fetch_assoc($result_type)){ + if (($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TEXT']==1) && ($row_type[type_id]==3)){ + $sql_text = "SELECT * FROM ".TABLE_PREFIX."secondary_resources WHERE primary_resource_id=$row[primary_resource_id] and language_code='".$_SESSION['prefs']['PREF_ALT_TEXT_PREFER_LANG']."'"; + $result_text = mysql_query($sql_text, $db); + if (mysql_num_rows($result_text) > 0) { + while ($row_text = mysql_fetch_assoc($result_text)){ + $sql_text_alt = "SELECT * FROM ".TABLE_PREFIX."secondary_resources_types WHERE secondary_resource_id=$row_text[secondary_resource_id]"; + $result_text_alt = mysql_query($sql_text_alt, $db); + if (mysql_num_rows($result_text_alt) > 0) { + while ($row_text_alt = mysql_fetch_assoc($result_text_alt)){ + if ((($_SESSION['prefs']['PREF_ALT_TO_TEXT']==visual) && ($row_text_alt[type_id]==4)) || (($_SESSION['prefs']['PREF_ALT_TO_TEXT']==audio) && ($row_audio_alt[type_id]==1)) || (($_SESSION['prefs']['PREF_ALT_TO_TEXT']==sign_lang) && ($row_text_alt[type_id]==2))) { + if (($_SESSION['prefs']['PREF_ALT_TO_TEXT_APPEND_OR_REPLACE']=='replace')) + $content = $row_text_alt['secondary_resource']; + else + $content = $content.'
    '.$row_text_alt['secondary_resource']; + } + } + } + } + } + } + } + } + } + + } + return $content; + }*/ + else + { + $sql_primary = "SELECT * FROM ".TABLE_PREFIX."primary_resources WHERE content_id=".$cid." ORDER BY primary_resource_id"; + $result = mysql_query($sql_primary, $db); + + if (mysql_num_rows($result) > 0) + { + while ($row = mysql_fetch_assoc($result)) + { + $sql_type = "SELECT * FROM ".TABLE_PREFIX."primary_resources_types WHERE primary_resource_id=$row[primary_resource_id]"; + $result_type = mysql_query($sql_type, $db); + + if (mysql_num_rows($result_type) > 0) + { + while ($row_type = mysql_fetch_assoc($result_type)) + { + if (($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_AUDIO']==1) && ($row_type[type_id]==1)) + { + $sql_audio = "SELECT * FROM ".TABLE_PREFIX."secondary_resources WHERE primary_resource_id=$row[primary_resource_id] and language_code='".$_SESSION['prefs']['PREF_ALT_AUDIO_PREFER_LANG']."'"; + $result_audio = mysql_query($sql_audio, $db); + if (mysql_num_rows($result_audio) > 0) + { + while ($row_audio = mysql_fetch_assoc($result_audio)) + { + $sql_audio_alt = "SELECT * FROM ".TABLE_PREFIX."secondary_resources_types WHERE secondary_resource_id=$row_audio[secondary_resource_id]"; + $result_audio_alt = mysql_query($sql_audio_alt, $db); + if (mysql_num_rows($result_audio_alt) > 0) + { + while ($row_audio_alt = mysql_fetch_assoc($result_audio_alt)) + { + if ((($_SESSION['prefs']['PREF_ALT_TO_AUDIO']=="visual") && ($row_audio_alt[type_id]==4)) || (($_SESSION['prefs']['PREF_ALT_TO_AUDIO']==text) && ($row_audio_alt[type_id]==3)) || (($_SESSION['prefs']['PREF_ALT_TO_AUDIO']==sign_lang) && ($row_audio_alt[type_id]==2))) + { + if (($_SESSION['prefs']['PREF_ALT_TO_AUDIO_APPEND_OR_REPLACE']=='replace')) + { + $before = explode($row['resource'], $content); + $last_c = substr($before[0], -1, 1); + if ($last_c=="]") + $shift = strripos($before[0], '['); + else + $shift = strripos($before[0], '<'); + + $len = strlen($before[0]); + $shift = $len-$shift; + $first = substr($before[0], 0, -$shift); + $ext = substr($row_audio['secondary_resource'], -3); + if (in_array($ext, $vidoe_exts)) + { + $content = $first.'[media]'.$row_audio['secondary_resource']; + if ($last_c=="]") + { + $after = substr($before[1], 8); + $after = '[/media]'.$after; + } + else + { + $shift = strpos($before[1], ''.$row_audio['secondary_resource']; + if ($last_c=="]") + { + $after = substr($before[1], 8); + $after = ''.$after; + } + else + { + $shift = strpos($before[1], ''); + $alt_shift = $len-$shift; + $res = substr($before[1], 0, -$alt_shift); + $shift = $shift+4; + $after = substr($before[1], $shift); + $content = $content.$res.'
    [media]'.$row_audio['secondary_resource'].'[/media]'.$after; + } + } + else + { + if ($last_c=="]") + { + $after = substr($before[1], 8); + $content = $content.'[/media]'.'

    '.$row_audio['secondary_resource'].'

    '.$after; + } + else + { + $shift = strpos($before[1], ''); + $alt_shift = $len-$shift; + $res = substr($before[1], 0, -$alt_shift); + $shift = $shift+4; + $after = substr($before[1], $shift); + $content = $content.$res.'

    '.$row_audio['secondary_resource'].'

    '.$after; + } + } + } + } + } + } + } + } + } + if (($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_TEXT']==1) && ($row_type[type_id]==3)) + { + $sql_text = "SELECT * FROM ".TABLE_PREFIX."secondary_resources WHERE primary_resource_id=$row[primary_resource_id] and language_code='".$_SESSION['prefs']['PREF_ALT_VISUAL_PREFER_LANG']."'"; + $result_text = mysql_query($sql_text, $db); + if (mysql_num_rows($result_text) > 0) + { + while ($row_text = mysql_fetch_assoc($result_text)) + { + $sql_text_alt = "SELECT * FROM ".TABLE_PREFIX."secondary_resources_types WHERE secondary_resource_id=$row_text[secondary_resource_id]"; + $result_text_alt = mysql_query($sql_text_alt, $db); + if (mysql_num_rows($result_text_alt) > 0) + { + while ($row_text_alt = mysql_fetch_assoc($result_text_alt)) + { + if ((($_SESSION['prefs']['PREF_ALT_TO_TEXT']==audio) && ($row_text_alt[type_id]==1)) || + (($_SESSION['prefs']['PREF_ALT_TO_TEXT']==visual) && ($row_text_alt[type_id]==4)) || + (($_SESSION['prefs']['PREF_ALT_TO_TEXT']==sign_lang) && ($row_text_alt[type_id]==2))) + { + if ($_SESSION['prefs']['PREF_ALT_TO_TEXT_APPEND_OR_REPLACE']=='replace') + { + $before = explode($row['resource'], $content); + $shift = strripos($before[0], '<'); + $len = strlen($before[0]); + $shift = $len-$shift; + $first = substr($before[0], 0, -$shift); + $ext = substr($row_text['secondary_resource'], -3); + if (in_array($ext, $vidoe_exts)) + { + $content = $first.'[media]'.$row_text['secondary_resource']; + if ($last_c=="]") + { + $after = substr($before[1], 8); + $after = '[/media]'.$after; + } + else + { + $shift = strpos($before[1], ''; + $shift = strpos($before[1], ''.$row_text['secondary_resource']; + $shift = strpos($before[1], ''); + $shift = strpos($before[1], '>') + 1; + $alt_shift = $len-$shift; + $res = substr($before[1], 0, -$alt_shift); + //$shift = $shift; + $after = substr($before[1], $shift); + $af = strpos($after, '<'); + $str = substr($after, $af, 4); + if ($str != '') + $content = $content.$res.'
    [media]'.$row_text['secondary_resource'].'[/media]'.$after; + else + { + $shift = strpos($before[1], ''); + $alt_shift = $len-$shift; + $res = substr($before[1], 0, -$alt_shift); + $shift = $shift+4; + $after = substr($before[1], $shift); + $content = $content.$res.'
    [media]'.$row_text['secondary_resource'].'[/media]'.$after; + } + } + else + { + if (($_SESSION['prefs']['PREF_ALT_TO_TEXT']==visual) && ($row_text_alt[type_id]==4)) + { + $shift = strpos($before[1], ''); + $alt_shift = $len-$shift; + $res = substr($before[1], 0, -$alt_shift); + $shift = $shift+4; + $after = substr($before[1], $shift); + $content = $content.$res.'Alternate Text'.$after; + } + else + { + $shift = strpos($before[1], ''); + $alt_shift = $len-$shift; + $res = substr($before[1], 0, -$alt_shift); + $shift = $shift+4; + $after = substr($before[1], $shift); + $content = $content.$res.'

    '.$row_text['secondary_resource'].'

    '.$after; + } + } + } + } + } + } + } + } + } + if (($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_VISUAL']==1) && ($row_type[type_id]==4)) + { + $sql_visual = "SELECT * FROM ".TABLE_PREFIX."secondary_resources WHERE primary_resource_id=$row[primary_resource_id] and language_code='".$_SESSION['prefs']['PREF_ALT_VISUAL_PREFER_LANG']."'"; + $result_visual = mysql_query($sql_visual, $db); + + if (mysql_num_rows($result_visual) > 0) + { + while ($row_visual = mysql_fetch_assoc($result_visual)) + { + $sql_visual_alt = "SELECT * FROM ".TABLE_PREFIX."secondary_resources_types WHERE secondary_resource_id=$row_visual[secondary_resource_id]"; + $result_visual_alt = mysql_query($sql_visual_alt, $db); + + if (mysql_num_rows($result_visual_alt) > 0) + { + while ($row_visual_alt = mysql_fetch_assoc($result_visual_alt)) + { + if ((($_SESSION['prefs']['PREF_ALT_TO_VISUAL']==audio) && ($row_visual_alt[type_id]==1)) || + (($_SESSION['prefs']['PREF_ALT_TO_VISUAL']==text) && ($row_visual_alt[type_id]==3)) || + (($_SESSION['prefs']['PREF_ALT_TO_VISUAL']==sign_lang) && ($row_visual_alt[type_id]==2))) + { + if ($_SESSION['prefs']['PREF_ALT_TO_VISUAL_APPEND_OR_REPLACE']=='replace') + { + $before = explode($row['resource'], $content); + $last_c = substr($before[0], -1, 1); + if ($last_c=="]"){ + $shift = strripos($before[0], '['); + } + else + { + $shift = strripos($before[0], '<'); + } + $len = strlen($before[0]); + $shift = $len-$shift; + $first = substr($before[0], 0, -$shift); + $ext = substr($row_visual['secondary_resource'], -3); + if (in_array($ext, $vidoe_exts)) + { + $content = $first.'[media]'.$row_visual['secondary_resource']; + if ($last_c=="]") + { + $after = substr($before[1], 8); + $after = '[/media]'.$after; + } + else + { + $shift = strpos($before[1], '/>'); + $after = substr($before[1], $shift); + $after = substr($after, 2); + $after = '[/media]'.$after; + } + } + else + { + $new = ''.$row_visual['secondary_resource'].''; + if ($last_c=="]") + { + $after = substr($before[1], 8); + $content = $content.$after; + } + else + { + $shift = strpos($before[1], '/>'); + $alt_shift = $len-$shift; + $res = substr($before[1], 0, -$alt_shift); + $shift = $shift+2; + $after = substr($before[1], $shift); + } + } + $content = $content.$after; + } + else + { + $before = explode($row['resource'], $content); + $content = $before[0].$row['resource']; + $last_c = substr($before[0], -1, 1); + $ext = substr($row_visual['secondary_resource'], -3); + if (in_array($ext, $vidoe_exts)) + { + if ($last_c=="]") + { + $after = substr($before[1], 8); + $content = $content.'[/media][media]'.$row_visual['secondary_resource'].'[/media]'.$after; + } + else + { + $shift = strpos($before[1], '/>'); + $alt_shift = $len-$shift; + $res = substr($before[1], 0, -$alt_shift); + $shift = $shift+2; + $after = substr($before[1], $shift); + $content = $content.$res.'/>[media]'.$row_visual['secondary_resource'].'[/media]'.$after; + } + } + else + { + if ($last_c=="]") + { + $after = substr($before[1], 8); + $content = $content.'[/media]'.'

    '.$row_visual['secondary_resource'].'

    '.$after; + } + else + { + $shift = strpos($before[1], '/>'); + $alt_shift = $len-$shift; + $res = substr($before[1], 0, -$alt_shift); + $shift = $shift+2; + $after = substr($before[1], $shift); + $content = $content.$res.'/>

    '.$row_visual['secondary_resource'].'

    '.$after; + } + } + } + } + } + } + } + } + } + } + } + } + + return $content; + } + else + { + //No alternatives are declared by content authors + $content=$content_page; + return $content; + } + } +} + +/** +* replace source object with alternatives according to user's preferences +* @access public +* @param $cid: content id. +* @param $content: the original content page ($content_row['text'], from content.php). +* @param $info_only: boolean. Default value is "false". When it's "true", returns an array of 4 values: +* $has_text_alternative, $has_audio_alternative, $has_visual_alternative, $has_sign_lang_alternative +* @param $only_on_secondary_type: Default value is "". Accept one of the values: 1(auditory), 2(sign_language), 3(text), 4(visual) +* When the value is given, ignore the alternative preference settings and only replace/append +* (replace or append is still from session preference) the objects with the alternatives with +* the given alternative types. +* @return string $content: the content page with the appropriated resources. +* @see $db from include/vitals.inc.php +* @author Cindy Qi Li +*/ +function provide_alternatives($cid, $content, $info_only = false, $only_on_secondary_type = 0){ + global $db; + + $video_exts = array("mpg", "avi", "wmv", "mov", "swf", "mp3", "wav", "ogg", "mid", "mp4", "flv"); + $txt_exts = array("txt", "html", "htm"); + $image_exts = array("gif", "bmp", "png", "jpg", "jpeg", "png", "tif"); + $only_on_secondary_type = intval($only_on_secondary_type); + + // intialize the 4 returned values when $info_only is on + if ($info_only) + { + $has_text_alternative = false; + $has_audio_alternative = false; + $has_visual_alternative = false; + $has_sign_lang_alternative = false; + } + + if (!$info_only && !$only_on_secondary_type && + ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_TEXT']==0) && + ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_AUDIO']==0) && + ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_VISUAL']==0)) + { + //No user's preferences related to content format are declared + if (!$info_only) { + return $content; + } else { + return array($has_text_alternative, $has_audio_alternative, $has_visual_alternative, $has_sign_lang_alternative); + } + } + + // get all relations between primary resources and their alternatives + $sql = "SELECT c.content_path, pr.resource, prt.type_id primary_type, sr.secondary_resource, srt.type_id secondary_type + FROM ".TABLE_PREFIX."primary_resources pr, ". + TABLE_PREFIX."primary_resources_types prt,". + TABLE_PREFIX."secondary_resources sr,". + TABLE_PREFIX."secondary_resources_types srt,". + TABLE_PREFIX."content c + WHERE pr.content_id=".$cid." + AND pr.primary_resource_id = prt.primary_resource_id + AND pr.primary_resource_id = sr.primary_resource_id + AND sr.language_code='".$_SESSION['lang']."' + AND sr.secondary_resource_id = srt.secondary_resource_id + AND pr.content_id = c.content_id"; + if ($only_on_secondary_type > 0) { + $sql .= " AND srt.type_id=".$only_on_secondary_type; + } + $sql .= " ORDER BY pr.primary_resource_id, prt.type_id"; + + $result = mysql_query($sql, $db); +//debug($sql); + if (mysql_num_rows($result) == 0) { + if (!$info_only) { + return $content; + } else { + return array($has_text_alternative, $has_audio_alternative, $has_visual_alternative, $has_sign_lang_alternative); + } + } + + while ($row = mysql_fetch_assoc($result)) + { + if ($info_only || $only_on_secondary_type || + ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_TEXT']==1 && $row['primary_type']==3 && + ($_SESSION['prefs']['PREF_ALT_TO_TEXT']=="audio" && $row['secondary_type']==1 || + $_SESSION['prefs']['PREF_ALT_TO_TEXT']=="visual" && $row['secondary_type']==4 || + $_SESSION['prefs']['PREF_ALT_TO_TEXT']=="sign_lang" && $row['secondary_type']==2)) || + + ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_AUDIO']==1 && $row['primary_type']==1 && + ($_SESSION['prefs']['PREF_ALT_TO_AUDIO']=="visual" && $row['secondary_type']==4 || + $_SESSION['prefs']['PREF_ALT_TO_AUDIO']=="text" && $row['secondary_type']==3 || + $_SESSION['prefs']['PREF_ALT_TO_AUDIO']=="sign_lang" && $row['secondary_type']==2)) || + + ($_SESSION['prefs']['PREF_USE_ALTERNATIVE_TO_VISUAL']==1 && $row['primary_type']==4 && + ($_SESSION['prefs']['PREF_ALT_TO_VISUAL']=="audio" && $row['secondary_type']==1 || + $_SESSION['prefs']['PREF_ALT_TO_VISUAL']=="text" && $row['secondary_type']==3 || + $_SESSION['prefs']['PREF_ALT_TO_VISUAL']=="sign_lang" && $row['secondary_type']==2)) + ) + { + $ext = substr($row['secondary_resource'], strrpos($row['secondary_resource'], '.')+1); + + // alternative is video + if (in_array($ext, $video_exts)) + $target = '[media]'.$row['secondary_resource'].'[/media]'; + // a text primary to be replaced by a visual alternative + else if (in_array($ext, $txt_exts)) + { + if ($row['content_path'] <> '') + $file_location = $row['content_path'].'/'.$row['secondary_resource']; + else + $file_location = $row['secondary_resource']; + + $file = AT_CONTENT_DIR.$_SESSION['course_id'] . '/'.$file_location; + $target = '
    '.file_get_contents($file); + + // check whether html file + if (preg_match('/.*\.*/s', $target)) + { // is a html file, use iframe to display + // get real path to the text file + if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $course_base_href = 'get.php/'; + } else { + $course_base_href = 'content/' . $_SESSION['course_id'] . '/'; + } + + $file = AT_BASE_HREF . $course_base_href.$file_location; + + $target = ''; + } + else + { // is a text file, insert/replace into content + $target = nl2br($target); + } + } + else if (in_array($ext, $image_exts)) + $target = ''._AT('alternate_text').''; + // otherwise + else + $target = '

    '.$row['secondary_resource'].'

    '; + + // replace or append the target alternative to the source + if (($row['primary_type']==3 && $_SESSION['prefs']['PREF_ALT_TO_TEXT_APPEND_OR_REPLACE'] == 'replace') || + ($row['primary_type']==1 && $_SESSION['prefs']['PREF_ALT_TO_AUDIO_APPEND_OR_REPLACE']=='replace') || + ($row['primary_type']==4 && $_SESSION['prefs']['PREF_ALT_TO_VISUAL_APPEND_OR_REPLACE']=='replace')) + $pattern_replace_to = '${1}'.$target.'${3}'; + else + $pattern_replace_to = '${1}${2}'.$target.'${3}'; + + // *** Alternative replace/append starts from here *** + $img_processed = false; // The indicator to tell the source image is found (or not) + // and processed (or not) in an tag. If found and processed, + // SKIP the found/process for tag because the source is a image + // and is very likely the tag wrapping around + + // append/replace target alternative to [media]source[/media] + if (preg_match("/".preg_quote("[media").".*".preg_quote("]".$row['resource']."[/media]", "/")."/sU", $content)) + { + if (!$info_only) { + $content = preg_replace("/(.*)(".preg_quote("[media").".*".preg_quote("]".$row['resource']."[/media]", "/").")(.*)/sU", + $pattern_replace_to, $content); + } else { + if ($row['secondary_type'] == 1) $has_audio_alternative = true; + if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true; + if ($row['secondary_type'] == 3) $has_text_alternative = true; + if ($row['secondary_type'] == 4) $has_visual_alternative = true; + } + } + + // append/replace target alternative to + if (preg_match("/\/sU", $content)) + { + $img_processed = true; + if (!$info_only) { + $content = preg_replace("/(.*)(\)(.*)/sU", + $pattern_replace_to, $content); + } else { + if ($row['secondary_type'] == 1) $has_audio_alternative = true; + if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true; + if ($row['secondary_type'] == 3) $has_text_alternative = true; + if ($row['secondary_type'] == 4) $has_visual_alternative = true; + } + } + + // append/replace target alternative to ...source... or ... + // skip this "if" when the source object has been processed in aboved tag + if (!$img_processed && preg_match("/\/sU", $content)) + { + if (!$info_only) { + $content = preg_replace("/(.*)(\)(.*)/sU", + $pattern_replace_to, $content); + } else { + if ($row['secondary_type'] == 1) $has_audio_alternative = true; + if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true; + if ($row['secondary_type'] == 3) $has_text_alternative = true; + if ($row['secondary_type'] == 4) $has_visual_alternative = true; + } + } + + // append/replace target alternative to + if (preg_match("/\/sU", $content)) + { + if (!$info_only) { + $content = preg_replace("/(.*)(\)(.*)/sU", + $pattern_replace_to, $content); + } else { + if ($row['secondary_type'] == 1) $has_audio_alternative = true; + if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true; + if ($row['secondary_type'] == 3) $has_text_alternative = true; + if ($row['secondary_type'] == 4) $has_visual_alternative = true; + } + } + + // append/replace target alternative to + if (preg_match("/\/sU", $content)) + { + if (!$info_only) { + $content = preg_replace("/(.*)(\)(.*)/sU", + $pattern_replace_to, $content); + } else { + if ($row['secondary_type'] == 1) $has_audio_alternative = true; + if ($row['secondary_type'] == 2) $has_sign_lang_alternative = true; + if ($row['secondary_type'] == 3) $has_text_alternative = true; + if ($row['secondary_type'] == 4) $has_visual_alternative = true; + } + } + } + } + + if (!$info_only) { + return $content; + } else { + return array($has_text_alternative, $has_audio_alternative, $has_visual_alternative, $has_sign_lang_alternative); + } +} + +/** +* apply_timezone +* converts a unix timestamp into another UNIX timestamp with timezone offset added up. +* Adds the user's timezone offset, then converts back to a MYSQL timestamp +* Available both as a system config option, and a user preference, if both are set +* they are added together +* @param date MYSQL timestamp. +* @return date MYSQL timestamp plus user's and/or system's timezone offset. +* @author Greg Gay . +*/ +function apply_timezone($timestamp){ + global $_config; + + if($_config['time_zone']){ + $timestamp = ($timestamp + ($_config['time_zone']*3600)); + } + + if(isset($_SESSION['prefs']['PREF_TIMEZONE'])){ + $timestamp = ($timestamp + ($_SESSION['prefs']['PREF_TIMEZONE']*3600)); + } + + return $timestamp; +} +?> diff --git a/include/lib/search.inc.php b/include/lib/search.inc.php new file mode 100644 index 000000000..067c49486 --- /dev/null +++ b/include/lib/search.inc.php @@ -0,0 +1,344 @@ + $b['score']) ? -1 : 1; +} + + +function get_search_result($words, $predicate, $course_id, &$num_found, &$total_score){ + global $_pages, $moduleFactory; + + $search_results = array(); + $content_search_results = array(); + $forums_search_results = array(); + $course_score = 0; + + if (isset($_GET['search_within'])){ + if ($_GET['search_within'] == 'content'){ + $content_search_results = get_content_search_result($words, $predicate, $course_id, $total_score, $course_score); + $search_results = $content_search_results; + } elseif ($_GET['search_within'] == 'forums'){ + $forums_search_results = get_forums_search_result($words, $predicate, $course_id, $total_score, $course_score); + // get all enabled modules + $modules = $moduleFactory->getModules(AT_MODULE_STATUS_ENABLED, 0, TRUE); + // if forum has been disabled, don't search in it. + if (isset($_SESSION['course_id']) && $_SESSION['course_id']==0){ + $search_results = $forums_search_results; + } elseif (isset($modules['_standard/forums'])){ + $search_results = $forums_search_results; + } + } else { + $content_search_results = get_content_search_result($words, $predicate, $course_id, $total_score, $course_score); + $forums_search_results = get_forums_search_result($words, $predicate, $course_id, $total_score, $course_score); + $search_results = array_merge($content_search_results, $forums_search_results); + } + + if ((count($search_results) == 0) && $course_score && ($_GET['display_as'] != 'pages')) { + $num_found++; + } + $num_found += count($search_results); + } + return $search_results; +} + +function get_content_search_result($words, $predicate, $course_id, &$total_score, &$course_score) { + global $addslashes, $db, $highlight_system_courses, $strlen, $substr, $strtolower; + + $search_results = array(); + $lower_words = array(); + + $predicate = " $predicate "; // either 'AND' or 'OR' + + $words = trim($words); + $words = explode(' ',$words); + $words = array_values(array_diff(array_unique($words), array(''))); + $num_words = count($words); + $course_score = 0; + $words_sql = ''; + + for ($i=0; $i<$num_words; $i++) { + $lower_words[$i] = $strtolower($words[$i]); + + if ($words_sql) { + $words_sql .= $predicate; + } + $words[$i] = $addslashes($words[$i]); + $words_sql .= ' (C.title LIKE "%'.$words[$i].'%" OR C.text LIKE "%'.$words[$i].'%" OR C.keywords LIKE "%'.$words[$i].'%")'; + + /* search through the course title and description keeping track of its total */ + $course_score += 15 * substr_count($strtolower($highlight_system_courses[$course_id]['title']), $lower_words[$i]); + $course_score += 12 * substr_count($strtolower($highlight_system_courses[$course_id]['description']), $lower_words[$i]); + + $highlight_system_courses[$course_id]['title'] = highlight($highlight_system_courses[$course_id]['title'], $words[$i]); + $highlight_system_courses[$course_id]['description'] = highlight($highlight_system_courses[$course_id]['description'], $words[$i]); + } + if (!$words_sql) { + return; + } + + $sql = 'SELECT C.last_modified, C.course_id, C.content_id, C.title, C.text, C.keywords FROM '.TABLE_PREFIX.'content AS C WHERE C.course_id='.$course_id; + $sql .= ' AND ('.$words_sql.') LIMIT 200'; + + $result = mysql_query($sql, $db); + while($row = mysql_fetch_assoc($result)) { + $score = 0; + + $row['title'] = strip_tags($row['title']); + $row['text'] = strip_tags($row['text']); + + $lower_title = $strtolower($row['title']); + $lower_text = $strtolower($row['text']); + $lower_keywords = $strtolower($row['keywords']); + + if ($strlen($row['text']) > 270) { + $row['text'] = $substr($row['text'], 0, 268).'...'; + } + + for ($i=0; $i<$num_words; $i++) { + $score += 8 * substr_count($lower_keywords, $lower_words[$i]); /* keywords are weighed more */ + $score += 4 * substr_count($lower_title, $lower_words[$i]); /* titles are weighed more */ + $score += 1 * substr_count($lower_text, $lower_words[$i]); + + $row['title'] = highlight($row['title'], $words[$i]); + $row['text'] = highlight($row['text'], $words[$i]); + $row['keywords'] = highlight($row['keywords'], $words[$i]); + + } + if ($score != 0) { + $score += $course_score; + } + $row['score'] = $score; + $search_results[] = $row; + + $total_score += $score; + } + + if ($total_score == 0) { + $total_score = $course_score; + } + + return $search_results; +} + +/* + * Get forum search results + * @author Harris Wong + */ +function get_forums_search_result($words, $predicate, $course_id, &$total_score, &$course_score) { + global $addslashes, $db, $highlight_system_courses, $strlen, $substr, $strtolower; + + $search_results = array(); + $lower_words = array(); + + $predicate = " $predicate "; // either 'AND' or 'OR' + + $words = trim($words); + $words = explode(' ',$words); + $words = array_values(array_diff(array_unique($words), array(''))); + $num_words = count($words); + $course_score = 0; + $words_sql = ''; + + for ($i=0; $i<$num_words; $i++) { + $lower_words[$i] = $strtolower($words[$i]); + + if ($words_sql) { + $words_sql .= $predicate; + } + $words[$i] = $addslashes($words[$i]); + $words_sql .= ' (course_group_forums.title LIKE "%'.$words[$i].'%" OR T.subject LIKE "%'.$words[$i].'%" OR T.body LIKE "%'.$words[$i].'%")'; + + /* search through the course title and description keeping track of its total */ + $course_score += 15 * substr_count($strtolower($highlight_system_courses[$course_id]['title']), $lower_words[$i]); + $course_score += 12 * substr_count($strtolower($highlight_system_courses[$course_id]['description']), $lower_words[$i]); + } + + if (!$words_sql) { + return; + } + + //forums sql + // Wants to get course forums + "my" group forums + //if the search is performed outside of a course, do not search in any group forums + // UNION on course_forums and group_forums + // TODO: Simplify the query. + ((isset($_SESSION['is_admin']) && $_SESSION['is_admin'] > 0) ? $is_admin_string = '1 OR ' : $is_admin_string = ''); + (isset($_SESSION['member_id']) ? $member_id = $_SESSION['member_id'] : $member_id = 0); + $sql = 'SELECT course_group_forums.title AS forum_title, course_group_forums.course_id, T.* FROM '.TABLE_PREFIX.'forums_threads T RIGHT JOIN '; + + //course forums + $sql .= '( SELECT forum_id, course_id, title, description, num_topics, num_posts, last_post, mins_to_edit FROM '.TABLE_PREFIX.'forums_courses '; + $sql .= ' NATURAL JOIN '.TABLE_PREFIX.'forums WHERE course_id='.$course_id; + + $sql .= ' UNION '; + + //group forums + $sql .= ' SELECT forum_id, course_id, title, description, num_topics, num_posts, last_post, mins_to_edit FROM '.TABLE_PREFIX.'forums_groups NATURAL JOIN '; + $sql .= ' (SELECT forum_id, num_topics, num_posts, last_post, mins_to_edit FROM '.TABLE_PREFIX.'forums) AS f NATURAL JOIN '; + $sql .= ' '.TABLE_PREFIX.'groups_members NATURAL JOIN '; + $sql .= ' (SELECT g.*, gt.course_id FROM '.TABLE_PREFIX.'groups g INNER JOIN '.TABLE_PREFIX.'groups_types gt USING (type_id) WHERE '; + $sql .= ' course_id='.$course_id.') AS group_course WHERE '.$is_admin_string .'member_id='.$member_id; + $sql .= ') AS course_group_forums '; + + $sql .= 'USING (forum_id) '; + $sql .= 'WHERE ' . $words_sql; + + $result = mysql_query($sql, $db); + while($row = mysql_fetch_assoc($result)) { + $score = 0; + + $row['forum_title'] = strip_tags($row['forum_title']); + $row['subject'] = strip_tags($row['subject']); + $row['body'] = strip_tags($row['body']); + + $lower_forum_title = $strtolower($row['forum_title']); + $lower_subject = $strtolower($row['subject']); + $lower_body = $strtolower($row['body']); + $num_posts = intval($row['num_comments']); + + if ($strlen($row['body']) > 270) { + $row['body'] = $substr($row['body'], 0, 268).'...'; + } + + for ($i=0; $i<$num_words; $i++) { + $score += 8 * substr_count($lower_forum_title, $lower_words[$i]); /* forum's title are weighed more */ + $score += 4 * substr_count($lower_subject, $lower_words[$i]); /* thread subject are weighed more */ + $score += 2 * substr_count($lower_body, $lower_words[$i]); + $score += 1 * $num_posts; + + $row['forum_title'] = highlight($row['forum_title'], $words[$i]); + $row['subject'] = highlight($row['subject'], $words[$i]); + $row['body'] = highlight($row['body'], $words[$i]); + + } + if ($score != 0) { + $score += $course_score; + } + $row['score'] = $score; + $search_results[] = $row; + + $total_score += $score; + } + + return $search_results; + +} + + +// My Courses - All courses you're enrolled in (including hidden) +function get_my_courses($member_id) { + global $db; + + $list = array(); + + $sql = "SELECT course_id FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$member_id AND (approved='y' OR approved='a')"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $list[] = $row['course_id']; // list contains all the Course IDs + } + + return $list; +} + + +// All courses (display hidden too if you're enrolled in it) +function get_all_courses($member_id) { + global $system_courses, $db; + + $list = array(); + + $num_courses = count($system_courses); + + // add all the courses that are not hidden,then find the hidden courses that you're enrolled in and then add that to array + foreach ($system_courses as $course_id => $course_info) { + if (!$course_info['hide']) { + $list[] = $course_id; + } + } + + // if there aren't any hidden courses: + if (count($system_courses) == count($list)) { + return $list; + } + + if ($_SESSION['valid_user']) { + $my_courses = implode(',', get_my_courses($member_id)); + $sql = "SELECT course_id FROM ".TABLE_PREFIX."courses WHERE hide=1 AND course_id IN (0, $my_courses)"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $list[] = $row['course_id']; + } + } + return $list; +} + +function print_search_pages($result) { + global $count; + + foreach ($result as $items) { + uasort($result, 'score_cmp'); + + echo '
    ' . $count . '. '; + + if(isset($items['forum_title'])){ + //Forum + if ($_SESSION['course_id'] != $items['course_id']) { + echo ''.$items['forum_title'].' - '.$items['subject'].' '; + } else { + echo ''.$items['forum_title'].' - '.$items['subject'].' '; + } + echo '
    '."\n"; + + echo '

    '.$items['body']; + } else { + //Content + if ($_SESSION['course_id'] != $items['course_id']) { + echo ''.$items['title'].' '; + } else { + echo ''.$items['title'].' '; + } + echo ''."\n"; + + echo '

    '.$items['text']; + } + + echo '
    ['._AT('keywords').': '; + if (isset($items['keywords'])) { + echo $items['keywords']; + } else { + echo ''._AT('none').''; + } + echo '. '._AT('author').': '; + if (isset($items['member_id'])) { + echo AT_print(get_display_name($items['member_id']), 'members.login'); + } else { + echo ''._AT('none').''; + } + echo '. '._AT('updated').': '; + echo AT_date(_AT('inbox_date_format'), (isset($items['last_modified']) && $items['last_modified']!='')?$items['last_modified']:$items['last_comment'], AT_DATE_MYSQL_DATETIME); + + echo ']'; + + echo '

    '."\n"; + $count++; + } +} + +?> \ No newline at end of file diff --git a/include/lib/tinymce.inc.php b/include/lib/tinymce.inc.php new file mode 100644 index 000000000..3f81d30a6 --- /dev/null +++ b/include/lib/tinymce.inc.php @@ -0,0 +1,123 @@ + +'; +} + +?> \ No newline at end of file diff --git a/include/lib/tinymce_styles.css b/include/lib/tinymce_styles.css new file mode 100644 index 000000000..931ce7170 --- /dev/null +++ b/include/lib/tinymce_styles.css @@ -0,0 +1,28 @@ +/* styles listed in TinyMCE editor styles dropdown box */ + +.focusbox { +background: #eee; +padding: 10px; +border: solid 1px gray; +margin-top: 20px; +} + +.float-left { + +padding: 5px; +border: solid 1px gray; +float: left; +margin-right: 10px; +} + +.float-right { +padding: 5px; +border: solid 1px gray; +float: right; +margin-left: 10px; +} + +.clear { +clear:both; +} + diff --git a/include/lib/upload.php b/include/lib/upload.php new file mode 100644 index 000000000..78cb3892d --- /dev/null +++ b/include/lib/upload.php @@ -0,0 +1,191 @@ + $multiplier*(int)$POST_MAX_SIZE && $POST_MAX_SIZE) { + header("HTTP/1.1 500 Internal Server Error"); + echo "POST exceeded maximum allowed size."; + exit(0); + } + +// Settings +// $save_path = "/home/elicochr/public_html/sos/uploads/"; + $save_path = urldecode($_GET['path']); + $content_path = AT_CONTENT_DIR . $_SESSION['course_id']; + $pos = strpos(realpath($save_path), $content_path); + if ($pos === false){ + HandleError("Error uploading a file."); + exit(0); + } + $upload_name = "Filedata"; + $max_file_size_in_bytes = 2147483647; // 2GB in bytes + //$extension_whitelist = array("jpg", "gif", "png"); // Allowed file extensions + $valid_chars_regex = '.A-Z0-9_ !@#$%^&()+={}\[\]\',~`-'; // Characters allowed in the file name (in a Regular Expression format) + +// Other variables + $MAX_FILENAME_LENGTH = 260; + $file_name = ""; + $file_extension = ""; + $uploadErrors = array( + 0=>"There is no error, the file uploaded with success", + 1=>"The uploaded file exceeds the upload_max_filesize directive in php.ini", + 2=>"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form", + 3=>"The uploaded file was only partially uploaded", + 4=>"No file was uploaded", + 6=>"Missing a temporary folder" + ); + + +// Validate the upload + if (!isset($_FILES[$upload_name])) { + HandleError("No upload found in \$_FILES for " . $upload_name); + exit(0); + } else if (isset($_FILES[$upload_name]["error"]) && $_FILES[$upload_name]["error"] != 0) { + HandleError($uploadErrors[$_FILES[$upload_name]["error"]]); + exit(0); + } else if (!isset($_FILES[$upload_name]["tmp_name"]) || !@is_uploaded_file($_FILES[$upload_name]["tmp_name"])) { + HandleError("Upload failed is_uploaded_file test."); + exit(0); + } else if (!isset($_FILES[$upload_name]['name'])) { + HandleError("File has no name."); + exit(0); + } + +// Validate the file size (Warning: the largest files supported by this code is 2GB) + $file_size = @filesize($_FILES[$upload_name]["tmp_name"]); + if (!$file_size || $file_size > $max_file_size_in_bytes) { + HandleError("File exceeds the maximum allowed size"); + exit(0); + } + + if ($file_size <= 0) { + HandleError("File size outside allowed lower bound"); + exit(0); + } + + +// Validate file name (for our purposes we'll just remove invalid characters) + $file_name = preg_replace('/[^'.$valid_chars_regex.']|\.+$/i', "", basename($_FILES[$upload_name]['name'])); + if (strlen($file_name) == 0 || strlen($file_name) > $MAX_FILENAME_LENGTH) { + HandleError("Invalid file name"); + exit(0); + } + +// Validate that we won't over-write an existing file + if (file_exists($save_path . $file_name)) { + HandleError("File with this name already exists"); + exit(0); + } + +// Validate file extension +$path_parts = pathinfo($_FILES[$upload_name]['name']); +$ext = $path_parts['extension']; + +/* check if this file extension is allowed: */ +/* $IllegalExtentions is defined in ./include/config.inc.php */ +if (in_array($ext, $IllegalExtentions)) { + HandleError("Invalid file extension"); + exit(0); +} + + +// Validate file contents (extension and mime-type can't be trusted) + /* + Validating the file contents is OS and web server configuration dependant. Also, it may not be reliable. + See the comments on this page: http://us2.php.net/fileinfo + + Also see http://72.14.253.104/search?q=cache:3YGZfcnKDrYJ:www.scanit.be/uploads/php-file-upload.pdf+php+file+command&hl=en&ct=clnk&cd=8&gl=us&client=firefox-a + which describes how a PHP script can be embedded within a GIF image file. + + Therefore, no sample code will be provided here. Research the issue, decide how much security is + needed, and implement a solution that meets the needs. + */ + + +// Process the file + /* + At this point we are ready to process the valid file. This sample code shows how to save the file. Other tasks + could be done such as creating an entry in a database or generating a thumbnail. + + Depending on your server OS and needs you may need to set the Security Permissions on the file after it has + been saved. + */ + if (!@move_uploaded_file($_FILES[$upload_name]["tmp_name"], $save_path.$file_name)) { + HandleError("File could not be saved."); + exit(0); + } + +// Return output to the browser (only supported by SWFUpload for Flash Player 9) + + echo "File Received"; + exit(0); + + +/* Handles the error output. This function was written for SWFUpload for Flash Player 8 which +cannot return data to the server, so it just returns a 500 error. For Flash Player 9 you will +want to change this to return the server data you want to indicate an error and then use SWFUpload's +uploadSuccess to check the server_data for your error indicator. */ +function HandleError($message) { + header("HTTP/1.1 500 Internal Server Error"); + echo $message; +} +?> \ No newline at end of file diff --git a/include/lib/utf8.php b/include/lib/utf8.php new file mode 100644 index 000000000..9a6268e43 --- /dev/null +++ b/include/lib/utf8.php @@ -0,0 +1,1363 @@ + + */ + +/** + * check for mb_string support + */ +if(!defined('UTF8_MBSTRING')){ + if(function_exists('mb_substr') && !defined('UTF8_NOMBSTRING')){ + define('UTF8_MBSTRING',1); + }else{ + define('UTF8_MBSTRING',0); + } +} + +if(UTF8_MBSTRING){ mb_internal_encoding('UTF-8'); } + + +/** + * URL-Encode a filename to allow unicodecharacters + * + * Slashes are not encoded + * + * When the second parameter is true the string will + * be encoded only if non ASCII characters are detected - + * This makes it safe to run it multiple times on the + * same string (default is true) + * + * @author Andreas Gohr + * @see urlencode + */ +function utf8_encodeFN($file,$safe=true){ + if($safe && preg_match('#^[a-zA-Z0-9/_\-.%]+$#',$file)){ + return $file; + } + $file = urlencode($file); + $file = str_replace('%2F','/',$file); + return $file; +} + +/** + * URL-Decode a filename + * + * This is just a wrapper around urldecode + * + * @author Andreas Gohr + * @see urldecode + */ +function utf8_decodeFN($file){ + $file = urldecode($file); + return $file; +} + +/** + * Checks if a string contains 7bit ASCII only + * + * @author Andreas Gohr + */ +function utf8_isASCII($str){ + for($i=0; $i127) return false; + } + return true; +} + +/** + * Strips all highbyte chars + * + * Returns a pure ASCII7 string + * + * @author Andreas Gohr + */ +function utf8_strip($str){ + $ascii = ''; + for($i=0; $i + * @link http://www.php.net/manual/en/function.utf8-encode.php + */ +function utf8_check($Str) { + for ($i=0; $i + * @see strlen() + * @see utf8_decode() + */ +function utf8_strlen($string){ + return strlen(utf8_decode($string)); +} + +/** + * UTF-8 aware alternative to substr + * + * Return part of a string given character offset (and optionally length) + * + * @author Harry Fuecks + * @author Chris Smith + * @param string + * @param integer number of UTF-8 characters offset (from left) + * @param integer (optional) length in UTF-8 characters from offset + * @return mixed string or false if failure + */ +function utf8_substr($str, $offset, $length = null) { + if(UTF8_MBSTRING){ + if( $length === null ){ + return mb_substr($str, $offset); + }else{ + return mb_substr($str, $offset, $length); + } + } + + /* + * Notes: + * + * no mb string support, so we'll use pcre regex's with 'u' flag + * pcre only supports repetitions of less than 65536, in order to accept up to MAXINT values for + * offset and length, we'll repeat a group of 65535 characters when needed (ok, up to MAXINT-65536) + * + * substr documentation states false can be returned in some cases (e.g. offset > string length) + * mb_substr never returns false, it will return an empty string instead. + * + * calculating the number of characters in the string is a relatively expensive operation, so + * we only carry it out when necessary. It isn't necessary for +ve offsets and no specified length + */ + + // cast parameters to appropriate types to avoid multiple notices/warnings + $str = (string)$str; // generates E_NOTICE for PHP4 objects, but not PHP5 objects + $offset = (int)$offset; + if (!is_null($length)) $length = (int)$length; + + // handle trivial cases + if ($length === 0) return ''; + if ($offset < 0 && $length < 0 && $length < $offset) return ''; + + $offset_pattern = ''; + $length_pattern = ''; + + // normalise -ve offsets (we could use a tail anchored pattern, but they are horribly slow!) + if ($offset < 0) { + $strlen = strlen(utf8_decode($str)); // see notes + $offset = $strlen + $offset; + if ($offset < 0) $offset = 0; + } + + // establish a pattern for offset, a non-captured group equal in length to offset + if ($offset > 0) { + $Ox = (int)($offset/65535); + $Oy = $offset%65535; + + if ($Ox) $offset_pattern = '(?:.{65535}){'.$Ox.'}'; + $offset_pattern = '^(?:'.$offset_pattern.'.{'.$Oy.'})'; + } else { + $offset_pattern = '^'; // offset == 0; just anchor the pattern + } + + // establish a pattern for length + if (is_null($length)) { + $length_pattern = '(.*)$'; // the rest of the string + } else { + + if (!isset($strlen)) $strlen = strlen(utf8_decode($str)); // see notes + if ($offset > $strlen) return ''; // another trivial case + + if ($length > 0) { + + $length = min($strlen-$offset, $length); // reduce any length that would go passed the end of the string + + $Lx = (int)($length/65535); + $Ly = $length%65535; + + // +ve length requires ... a captured group of length characters + if ($Lx) $length_pattern = '(?:.{65535}){'.$Lx.'}'; + $length_pattern = '('.$length_pattern.'.{'.$Ly.'})'; + + } else if ($length < 0) { + + if ($length < ($offset - $strlen)) return ''; + + $Lx = (int)((-$length)/65535); + $Ly = (-$length)%65535; + + // -ve length requires ... capture everything except a group of -length characters + // anchored at the tail-end of the string + if ($Lx) $length_pattern = '(?:.{65535}){'.$Lx.'}'; + $length_pattern = '(.*)(?:'.$length_pattern.'.{'.$Ly.'})$'; + } + } + + if (!preg_match('#'.$offset_pattern.$length_pattern.'#us',$str,$match)) return ''; + return $match[1]; +} + +/** + * Unicode aware replacement for substr_replace() + * + * @author Andreas Gohr + * @see substr_replace() + */ +function utf8_substr_replace($string, $replacement, $start , $length=0 ){ + $ret = ''; + if($start>0) $ret .= utf8_substr($string, 0, $start); + $ret .= $replacement; + $ret .= utf8_substr($string, $start+$length); + return $ret; +} + + +/** + * Unicode aware replacement for ltrim() + * + * @author Andreas Gohr + * @see ltrim() + * @return string + */ +function utf8_ltrim($str,$charlist=''){ + if($charlist == '') return ltrim($str); + + //quote charlist for use in a characterclass + $charlist = preg_replace('!([\\\\\\-\\]\\[/])!','\\\${1}',$charlist); + + return preg_replace('/^['.$charlist.']+/u','',$str); +} + +/** + * Unicode aware replacement for rtrim() + * + * @author Andreas Gohr + * @see rtrim() + * @return string + */ +function utf8_rtrim($str,$charlist=''){ + if($charlist == '') return rtrim($str); + + //quote charlist for use in a characterclass + $charlist = preg_replace('!([\\\\\\-\\]\\[/])!','\\\${1}',$charlist); + + return preg_replace('/['.$charlist.']+$/u','',$str); +} + +/** + * Unicode aware replacement for trim() + * + * @author Andreas Gohr + * @see trim() + * @return string + */ +function utf8_trim($str,$charlist='') { + if($charlist == '') return trim($str); + + return utf8_ltrim(utf8_rtrim($str)); +} + + +/** + * This is a unicode aware replacement for strtolower() + * + * Uses mb_string extension if available + * + * @author Leo Feyer + * @see strtolower() + * @see utf8_strtoupper() + */ +function utf8_strtolower($string){ + if(UTF8_MBSTRING) return mb_strtolower($string,'utf-8'); + + global $UTF8_UPPER_TO_LOWER; + return strtr($string,$UTF8_UPPER_TO_LOWER); +} + +/** + * This is a unicode aware replacement for strtoupper() + * + * Uses mb_string extension if available + * + * @author Leo Feyer + * @see strtoupper() + * @see utf8_strtoupper() + */ +function utf8_strtoupper($string){ + if(UTF8_MBSTRING) return mb_strtoupper($string,'utf-8'); + + global $UTF8_LOWER_TO_UPPER; + return strtr($string,$UTF8_LOWER_TO_UPPER); +} + +/** + * Replace accented UTF-8 characters by unaccented ASCII-7 equivalents + * + * Use the optional parameter to just deaccent lower ($case = -1) or upper ($case = 1) + * letters. Default is to deaccent both cases ($case = 0) + * + * @author Andreas Gohr + */ +function utf8_deaccent($string,$case=0){ + if($case <= 0){ + global $UTF8_LOWER_ACCENTS; + $string = strtr($string,$UTF8_LOWER_ACCENTS); + } + if($case >= 0){ + global $UTF8_UPPER_ACCENTS; + $string = strtr($string,$UTF8_UPPER_ACCENTS); + } + return $string; +} + +/** + * Romanize a non-latin string + * + * @author Andreas Gohr + */ +function utf8_romanize($string){ + if(utf8_isASCII($string)) return $string; //nothing to do + + global $UTF8_ROMANIZATION; + return strtr($string,$UTF8_ROMANIZATION); +} + +/** + * Removes special characters (nonalphanumeric) from a UTF-8 string + * + * This function adds the controlchars 0x00 to 0x19 to the array of + * stripped chars (they are not included in $UTF8_SPECIAL_CHARS) + * + * @author Andreas Gohr + * @param string $string The UTF8 string to strip of special chars + * @param string $repl Replace special with this string + * @param string $additional Additional chars to strip (used in regexp char class) + */ +function utf8_stripspecials($string,$repl='',$additional=''){ + global $UTF8_SPECIAL_CHARS; + global $UTF8_SPECIAL_CHARS2; + + static $specials = null; + if(is_null($specials)){ +# $specials = preg_quote(unicode_to_utf8($UTF8_SPECIAL_CHARS), '/'); + $specials = preg_quote($UTF8_SPECIAL_CHARS2, '/'); + } + + return preg_replace('/['.$additional.'\x00-\x19'.$specials.']/u',$repl,$string); +} + +/** + * This is an Unicode aware replacement for strpos + * + * @author Leo Feyer + * @see strpos() + * @param string + * @param string + * @param integer + * @return integer + */ +function utf8_strpos($haystack, $needle, $offset=0){ + $comp = 0; + $length = null; + + while (is_null($length) || $length < $offset) { + $pos = strpos($haystack, $needle, $offset + $comp); + + if ($pos === false) + return false; + + $length = utf8_strlen(substr($haystack, 0, $pos)); + + if ($length < $offset) + $comp = $pos - $length; + } + + return $length; +} + + +/** + * This is an Unicode aware replacement for strrpos. + * Based on utf8_strpos written by Leo + * + * @author Harris Wong + * @see strrpos() + * @param string + * @param string + * @param integer + * @return integer + */ +function utf8_strrpos($haystack, $needle, $offset=0){ + $comp = 0; + $length = null; + + while (is_null($length) || $length < $offset) { + $pos = strrpos($haystack, $needle, $offset + $comp); + + if ($pos === false) + return false; + + $length = utf8_strlen(substr($haystack, 0, $pos)); + + if ($length < $offset) + $comp = $pos - $length; + } + + return $length; +} + + +/** + * Encodes UTF-8 characters to HTML entities + * + * @author Tom N Harris + * @author + * @link http://www.php.net/manual/en/function.utf8-decode.php + */ +function utf8_tohtml ($str) { + $ret = ''; + foreach (utf8_to_unicode($str) as $cp) { + if ($cp < 0x80) + $ret .= chr($cp); + elseif ($cp < 0x100) + $ret .= "&#$cp;"; + else + $ret .= '&#x'.dechex($cp).';'; + } + return $ret; +} + +/** + * Decodes HTML entities to UTF-8 characters + * + * Convert any &#..; entity to a codepoint, + * The entities flag defaults to only decoding numeric entities. + * Pass HTML_ENTITIES and named entities, including & < etc. + * are handled as well. Avoids the problem that would occur if you + * had to decode "&#38;&amp;#38;" + * + * unhtmlspecialchars(utf8_unhtml($s)) -> "&&" + * utf8_unhtml(unhtmlspecialchars($s)) -> "&&#38;" + * what it should be -> "&&#38;" + * + * @author Tom N Harris + * @param string $str UTF-8 encoded string + * @param boolean $entities Flag controlling decoding of named entities. + * @return UTF-8 encoded string with numeric (and named) entities replaced. + */ +function utf8_unhtml($str, $entities=null) { + static $decoder = null; + if (is_null($decoder)) + $decoder = new utf8_entity_decoder(); + if (is_null($entities)) + return preg_replace_callback('/(&#([Xx])?([0-9A-Za-z]+);)/m', + 'utf8_decode_numeric', $str); + else + return preg_replace_callback('/&(#)?([Xx])?([0-9A-Za-z]+);/m', + array(&$decoder, 'decode'), $str); +} +function utf8_decode_numeric($ent) { + switch ($ent[2]) { + case 'X': + case 'x': + $cp = hexdec($ent[3]); + break; + default: + $cp = intval($ent[3]); + break; + } + return unicode_to_utf8(array($cp)); +} +class utf8_entity_decoder { + var $table; + function utf8_entity_decoder() { + $table = get_html_translation_table(HTML_ENTITIES); + $table = array_flip($table); + $this->table = array_map(array(&$this,'makeutf8'), $table); + } + function makeutf8($c) { + return unicode_to_utf8(array(ord($c))); + } + function decode($ent) { + if ($ent[1] == '#') { + return utf8_decode_numeric($ent); + } elseif (array_key_exists($ent[0],$this->table)) { + return $this->table[$ent[0]]; + } else { + return $ent[0]; + } + } +} + +/** + * Takes an UTF-8 string and returns an array of ints representing the + * Unicode characters. Astral planes are supported ie. the ints in the + * output can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates + * are not allowed. + * + * If $strict is set to true the function returns false if the input + * string isn't a valid UTF-8 octet sequence and raises a PHP error at + * level E_USER_WARNING + * + * Note: this function has been modified slightly in this library to + * trigger errors on encountering bad bytes + * + * @author + * @author Harry Fuecks + * @param string UTF-8 encoded string + * @param boolean Check for invalid sequences? + * @return mixed array of unicode code points or false if UTF-8 invalid + * @see unicode_to_utf8 + * @link http://hsivonen.iki.fi/php-utf8/ + * @link http://sourceforge.net/projects/phputf8/ + */ +function utf8_to_unicode($str,$strict=false) { + $mState = 0; // cached expected number of octets after the current octet + // until the beginning of the next UTF8 character sequence + $mUcs4 = 0; // cached Unicode character + $mBytes = 1; // cached expected number of octets in the current sequence + + $out = array(); + + $len = strlen($str); + + for($i = 0; $i < $len; $i++) { + + $in = ord($str{$i}); + + if ( $mState == 0) { + + // When mState is zero we expect either a US-ASCII character or a + // multi-octet sequence. + if (0 == (0x80 & ($in))) { + // US-ASCII, pass straight through. + $out[] = $in; + $mBytes = 1; + + } else if (0xC0 == (0xE0 & ($in))) { + // First octet of 2 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x1F) << 6; + $mState = 1; + $mBytes = 2; + + } else if (0xE0 == (0xF0 & ($in))) { + // First octet of 3 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x0F) << 12; + $mState = 2; + $mBytes = 3; + + } else if (0xF0 == (0xF8 & ($in))) { + // First octet of 4 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x07) << 18; + $mState = 3; + $mBytes = 4; + + } else if (0xF8 == (0xFC & ($in))) { + /* First octet of 5 octet sequence. + * + * This is illegal because the encoded codepoint must be either + * (a) not the shortest form or + * (b) outside the Unicode range of 0-0x10FFFF. + * Rather than trying to resynchronize, we will carry on until the end + * of the sequence and let the later error handling code catch it. + */ + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x03) << 24; + $mState = 4; + $mBytes = 5; + + } else if (0xFC == (0xFE & ($in))) { + // First octet of 6 octet sequence, see comments for 5 octet sequence. + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 1) << 30; + $mState = 5; + $mBytes = 6; + + } elseif($strict) { + /* Current octet is neither in the US-ASCII range nor a legal first + * octet of a multi-octet sequence. + */ + trigger_error( + 'utf8_to_unicode: Illegal sequence identifier '. + 'in UTF-8 at byte '.$i, + E_USER_WARNING + ); + return false; + + } + + } else { + + // When mState is non-zero, we expect a continuation of the multi-octet + // sequence + if (0x80 == (0xC0 & ($in))) { + + // Legal continuation. + $shift = ($mState - 1) * 6; + $tmp = $in; + $tmp = ($tmp & 0x0000003F) << $shift; + $mUcs4 |= $tmp; + + /** + * End of the multi-octet sequence. mUcs4 now contains the final + * Unicode codepoint to be output + */ + if (0 == --$mState) { + + /* + * Check for illegal sequences and codepoints. + */ + // From Unicode 3.1, non-shortest form is illegal + if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || + ((3 == $mBytes) && ($mUcs4 < 0x0800)) || + ((4 == $mBytes) && ($mUcs4 < 0x10000)) || + (4 < $mBytes) || + // From Unicode 3.2, surrogate characters are illegal + (($mUcs4 & 0xFFFFF800) == 0xD800) || + // Codepoints outside the Unicode range are illegal + ($mUcs4 > 0x10FFFF)) { + + if($strict){ + trigger_error( + 'utf8_to_unicode: Illegal sequence or codepoint '. + 'in UTF-8 at byte '.$i, + E_USER_WARNING + ); + + return false; + } + + } + + if (0xFEFF != $mUcs4) { + // BOM is legal but we don't want to output it + $out[] = $mUcs4; + } + + //initialize UTF8 cache + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + } + + } elseif($strict) { + /** + *((0xC0 & (*in) != 0x80) && (mState != 0)) + * Incomplete multi-octet sequence. + */ + trigger_error( + 'utf8_to_unicode: Incomplete multi-octet '. + ' sequence in UTF-8 at byte '.$i, + E_USER_WARNING + ); + + return false; + } + } + } + return $out; +} + +/** + * Takes an array of ints representing the Unicode characters and returns + * a UTF-8 string. Astral planes are supported ie. the ints in the + * input can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates + * are not allowed. + * + * If $strict is set to true the function returns false if the input + * array contains ints that represent surrogates or are outside the + * Unicode range and raises a PHP error at level E_USER_WARNING + * + * Note: this function has been modified slightly in this library to use + * output buffering to concatenate the UTF-8 string (faster) as well as + * reference the array by it's keys + * + * @param array of unicode code points representing a string + * @param boolean Check for invalid sequences? + * @return mixed UTF-8 string or false if array contains invalid code points + * @author + * @author Harry Fuecks + * @see utf8_to_unicode + * @link http://hsivonen.iki.fi/php-utf8/ + * @link http://sourceforge.net/projects/phputf8/ + */ +function unicode_to_utf8($arr,$strict=false) { + if (!is_array($arr)) return ''; + ob_start(); + + foreach (array_keys($arr) as $k) { + + # ASCII range (including control chars) + if ( ($arr[$k] >= 0) && ($arr[$k] <= 0x007f) ) { + + echo chr($arr[$k]); + + # 2 byte sequence + } else if ($arr[$k] <= 0x07ff) { + + echo chr(0xc0 | ($arr[$k] >> 6)); + echo chr(0x80 | ($arr[$k] & 0x003f)); + + # Byte order mark (skip) + } else if($arr[$k] == 0xFEFF) { + + // nop -- zap the BOM + + # Test for illegal surrogates + } else if ($arr[$k] >= 0xD800 && $arr[$k] <= 0xDFFF) { + + // found a surrogate + if($strict){ + trigger_error( + 'unicode_to_utf8: Illegal surrogate '. + 'at index: '.$k.', value: '.$arr[$k], + E_USER_WARNING + ); + return false; + } + + # 3 byte sequence + } else if ($arr[$k] <= 0xffff) { + + echo chr(0xe0 | ($arr[$k] >> 12)); + echo chr(0x80 | (($arr[$k] >> 6) & 0x003f)); + echo chr(0x80 | ($arr[$k] & 0x003f)); + + # 4 byte sequence + } else if ($arr[$k] <= 0x10ffff) { + + echo chr(0xf0 | ($arr[$k] >> 18)); + echo chr(0x80 | (($arr[$k] >> 12) & 0x3f)); + echo chr(0x80 | (($arr[$k] >> 6) & 0x3f)); + echo chr(0x80 | ($arr[$k] & 0x3f)); + + } elseif($strict) { + + trigger_error( + 'unicode_to_utf8: Codepoint out of Unicode range '. + 'at index: '.$k.', value: '.$arr[$k], + E_USER_WARNING + ); + + // out of range + return false; + } + } + + $result = ob_get_contents(); + ob_end_clean(); + return $result; +} + +/** + * UTF-8 to UTF-16BE conversion. + * + * Maybe really UCS-2 without mb_string due to utf8_to_unicode limits + */ +function utf8_to_utf16be(&$str, $bom = false) { + $out = $bom ? "\xFE\xFF" : ''; + if(UTF8_MBSTRING) return $out.mb_convert_encoding($str,'UTF-16BE','UTF-8'); + + $uni = utf8_to_unicode($str); + foreach($uni as $cp){ + $out .= pack('n',$cp); + } + return $out; +} + +/** + * UTF-8 to UTF-16BE conversion. + * + * Maybe really UCS-2 without mb_string due to utf8_to_unicode limits + */ +function utf16be_to_utf8(&$str) { + $uni = unpack('n*',$str); + return unicode_to_utf8($uni); +} + +/** + * Replace bad bytes with an alternative character + * + * ASCII character is recommended for replacement char + * + * PCRE Pattern to locate bad bytes in a UTF-8 string + * Comes from W3 FAQ: Multilingual Forms + * Note: modified to include full ASCII range including control chars + * + * @author Harry Fuecks + * @see http://www.w3.org/International/questions/qa-forms-utf-8 + * @param string to search + * @param string to replace bad bytes with (defaults to '?') - use ASCII + * @return string + */ +function utf8_bad_replace($str, $replace = '') { + $UTF8_BAD = + '([\x00-\x7F]'. # ASCII (including control chars) + '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte + '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs + '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte + '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates + '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 + '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 + '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 + '|(.{1}))'; # invalid byte + ob_start(); + while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches)) { + if ( !isset($matches[2])) { + echo $matches[0]; + } else { + echo $replace; + } + $str = substr($str,strlen($matches[0])); + } + $result = ob_get_contents(); + ob_end_clean(); + return $result; +} + +/** + * adjust a byte index into a utf8 string to a utf8 character boundary + * + * @param $str string utf8 character string + * @param $i int byte index into $str + * @param $next bool direction to search for boundary, + * false = up (current character) + * true = down (next character) + * + * @return int byte index into $str now pointing to a utf8 character boundary + * + * @author chris smith + */ +function utf8_correctIdx(&$str,$i,$next=false) { + + if ($i <= 0) return 0; + + $limit = strlen($str); + if ($i>=$limit) return $limit; + + if ($next) { + while (($i<$limit) && ((ord($str[$i]) & 0xC0) == 0x80)) $i++; + } else { + while ($i && ((ord($str[$i]) & 0xC0) == 0x80)) $i--; + } + + return $i; +} + +// only needed if no mb_string available +if(!UTF8_MBSTRING){ + /** + * UTF-8 Case lookup table + * + * This lookuptable defines the upper case letters to their correspponding + * lower case letter in UTF-8 + * + * @author Andreas Gohr + */ + global $UTF8_LOWER_TO_UPPER; + $UTF8_LOWER_TO_UPPER = array( + "z"=>"Z","y"=>"Y","x"=>"X","w"=>"W","v"=>"V","u"=>"U","t"=>"T","s"=>"S","r"=>"R","q"=>"Q", + "p"=>"P","o"=>"O","n"=>"N","m"=>"M","l"=>"L","k"=>"K","j"=>"J","i"=>"I","h"=>"H","g"=>"G", + "f"=>"F","e"=>"E","d"=>"D","c"=>"C","b"=>"B","a"=>"A","ῳ"=>"ῼ","ῥ"=>"Ῥ","ῡ"=>"Ῡ","ῑ"=>"Ῑ", + "ῐ"=>"Ῐ","ῃ"=>"ῌ","ι"=>"Ι","ᾳ"=>"ᾼ","ᾱ"=>"Ᾱ","ᾰ"=>"Ᾰ","ᾧ"=>"ᾯ","ᾦ"=>"ᾮ","ᾥ"=>"ᾭ","ᾤ"=>"ᾬ", + "ᾣ"=>"ᾫ","ᾢ"=>"ᾪ","ᾡ"=>"ᾩ","ᾗ"=>"ᾟ","ᾖ"=>"ᾞ","ᾕ"=>"ᾝ","ᾔ"=>"ᾜ","ᾓ"=>"ᾛ","ᾒ"=>"ᾚ","ᾑ"=>"ᾙ", + "ᾐ"=>"ᾘ","ᾇ"=>"ᾏ","ᾆ"=>"ᾎ","ᾅ"=>"ᾍ","ᾄ"=>"ᾌ","ᾃ"=>"ᾋ","ᾂ"=>"ᾊ","ᾁ"=>"ᾉ","ᾀ"=>"ᾈ","ώ"=>"Ώ", + "ὼ"=>"Ὼ","ύ"=>"Ύ","ὺ"=>"Ὺ","ό"=>"Ό","ὸ"=>"Ὸ","ί"=>"Ί","ὶ"=>"Ὶ","ή"=>"Ή","ὴ"=>"Ὴ","έ"=>"Έ", + "ὲ"=>"Ὲ","ά"=>"Ά","ὰ"=>"Ὰ","ὧ"=>"Ὧ","ὦ"=>"Ὦ","ὥ"=>"Ὥ","ὤ"=>"Ὤ","ὣ"=>"Ὣ","ὢ"=>"Ὢ","ὡ"=>"Ὡ", + "ὗ"=>"Ὗ","ὕ"=>"Ὕ","ὓ"=>"Ὓ","ὑ"=>"Ὑ","ὅ"=>"Ὅ","ὄ"=>"Ὄ","ὃ"=>"Ὃ","ὂ"=>"Ὂ","ὁ"=>"Ὁ","ὀ"=>"Ὀ", + "ἷ"=>"Ἷ","ἶ"=>"Ἶ","ἵ"=>"Ἵ","ἴ"=>"Ἴ","ἳ"=>"Ἳ","ἲ"=>"Ἲ","ἱ"=>"Ἱ","ἰ"=>"Ἰ","ἧ"=>"Ἧ","ἦ"=>"Ἦ", + "ἥ"=>"Ἥ","ἤ"=>"Ἤ","ἣ"=>"Ἣ","ἢ"=>"Ἢ","ἡ"=>"Ἡ","ἕ"=>"Ἕ","ἔ"=>"Ἔ","ἓ"=>"Ἓ","ἒ"=>"Ἒ","ἑ"=>"Ἑ", + "ἐ"=>"Ἐ","ἇ"=>"Ἇ","ἆ"=>"Ἆ","ἅ"=>"Ἅ","ἄ"=>"Ἄ","ἃ"=>"Ἃ","ἂ"=>"Ἂ","ἁ"=>"Ἁ","ἀ"=>"Ἀ","ỹ"=>"Ỹ", + "ỷ"=>"Ỷ","ỵ"=>"Ỵ","ỳ"=>"Ỳ","ự"=>"Ự","ữ"=>"Ữ","ử"=>"Ử","ừ"=>"Ừ","ứ"=>"Ứ","ủ"=>"Ủ","ụ"=>"Ụ", + "ợ"=>"Ợ","ỡ"=>"Ỡ","ở"=>"Ở","ờ"=>"Ờ","ớ"=>"Ớ","ộ"=>"Ộ","ỗ"=>"Ỗ","ổ"=>"Ổ","ồ"=>"Ồ","ố"=>"Ố", + "ỏ"=>"Ỏ","ọ"=>"Ọ","ị"=>"Ị","ỉ"=>"Ỉ","ệ"=>"Ệ","ễ"=>"Ễ","ể"=>"Ể","ề"=>"Ề","ế"=>"Ế","ẽ"=>"Ẽ", + "ẻ"=>"Ẻ","ẹ"=>"Ẹ","ặ"=>"Ặ","ẵ"=>"Ẵ","ẳ"=>"Ẳ","ằ"=>"Ằ","ắ"=>"Ắ","ậ"=>"Ậ","ẫ"=>"Ẫ","ẩ"=>"Ẩ", + "ầ"=>"Ầ","ấ"=>"Ấ","ả"=>"Ả","ạ"=>"Ạ","ẛ"=>"Ṡ","ẕ"=>"Ẕ","ẓ"=>"Ẓ","ẑ"=>"Ẑ","ẏ"=>"Ẏ","ẍ"=>"Ẍ", + "ẋ"=>"Ẋ","ẉ"=>"Ẉ","ẇ"=>"Ẇ","ẅ"=>"Ẅ","ẃ"=>"Ẃ","ẁ"=>"Ẁ","ṿ"=>"Ṿ","ṽ"=>"Ṽ","ṻ"=>"Ṻ","ṹ"=>"Ṹ", + "ṷ"=>"Ṷ","ṵ"=>"Ṵ","ṳ"=>"Ṳ","ṱ"=>"Ṱ","ṯ"=>"Ṯ","ṭ"=>"Ṭ","ṫ"=>"Ṫ","ṩ"=>"Ṩ","ṧ"=>"Ṧ","ṥ"=>"Ṥ", + "ṣ"=>"Ṣ","ṡ"=>"Ṡ","ṟ"=>"Ṟ","ṝ"=>"Ṝ","ṛ"=>"Ṛ","ṙ"=>"Ṙ","ṗ"=>"Ṗ","ṕ"=>"Ṕ","ṓ"=>"Ṓ","ṑ"=>"Ṑ", + "ṏ"=>"Ṏ","ṍ"=>"Ṍ","ṋ"=>"Ṋ","ṉ"=>"Ṉ","ṇ"=>"Ṇ","ṅ"=>"Ṅ","ṃ"=>"Ṃ","ṁ"=>"Ṁ","ḿ"=>"Ḿ","ḽ"=>"Ḽ", + "ḻ"=>"Ḻ","ḹ"=>"Ḹ","ḷ"=>"Ḷ","ḵ"=>"Ḵ","ḳ"=>"Ḳ","ḱ"=>"Ḱ","ḯ"=>"Ḯ","ḭ"=>"Ḭ","ḫ"=>"Ḫ","ḩ"=>"Ḩ", + "ḧ"=>"Ḧ","ḥ"=>"Ḥ","ḣ"=>"Ḣ","ḡ"=>"Ḡ","ḟ"=>"Ḟ","ḝ"=>"Ḝ","ḛ"=>"Ḛ","ḙ"=>"Ḙ","ḗ"=>"Ḗ","ḕ"=>"Ḕ", + "ḓ"=>"Ḓ","ḑ"=>"Ḑ","ḏ"=>"Ḏ","ḍ"=>"Ḍ","ḋ"=>"Ḋ","ḉ"=>"Ḉ","ḇ"=>"Ḇ","ḅ"=>"Ḅ","ḃ"=>"Ḃ","ḁ"=>"Ḁ", + "ֆ"=>"Ֆ","օ"=>"Օ","ք"=>"Ք","փ"=>"Փ","ւ"=>"Ւ","ց"=>"Ց","ր"=>"Ր","տ"=>"Տ","վ"=>"Վ","ս"=>"Ս", + "ռ"=>"Ռ","ջ"=>"Ջ","պ"=>"Պ","չ"=>"Չ","ո"=>"Ո","շ"=>"Շ","ն"=>"Ն","յ"=>"Յ","մ"=>"Մ","ճ"=>"Ճ", + "ղ"=>"Ղ","ձ"=>"Ձ","հ"=>"Հ","կ"=>"Կ","ծ"=>"Ծ","խ"=>"Խ","լ"=>"Լ","ի"=>"Ի","ժ"=>"Ժ","թ"=>"Թ", + "ը"=>"Ը","է"=>"Է","զ"=>"Զ","ե"=>"Ե","դ"=>"Դ","գ"=>"Գ","բ"=>"Բ","ա"=>"Ա","ԏ"=>"Ԏ","ԍ"=>"Ԍ", + "ԋ"=>"Ԋ","ԉ"=>"Ԉ","ԇ"=>"Ԇ","ԅ"=>"Ԅ","ԃ"=>"Ԃ","ԁ"=>"Ԁ","ӹ"=>"Ӹ","ӵ"=>"Ӵ","ӳ"=>"Ӳ","ӱ"=>"Ӱ", + "ӯ"=>"Ӯ","ӭ"=>"Ӭ","ӫ"=>"Ӫ","ө"=>"Ө","ӧ"=>"Ӧ","ӥ"=>"Ӥ","ӣ"=>"Ӣ","ӡ"=>"Ӡ","ӟ"=>"Ӟ","ӝ"=>"Ӝ", + "ӛ"=>"Ӛ","ә"=>"Ә","ӗ"=>"Ӗ","ӕ"=>"Ӕ","ӓ"=>"Ӓ","ӑ"=>"Ӑ","ӎ"=>"Ӎ","ӌ"=>"Ӌ","ӊ"=>"Ӊ","ӈ"=>"Ӈ", + "ӆ"=>"Ӆ","ӄ"=>"Ӄ","ӂ"=>"Ӂ","ҿ"=>"Ҿ","ҽ"=>"Ҽ","һ"=>"Һ","ҹ"=>"Ҹ","ҷ"=>"Ҷ","ҵ"=>"Ҵ","ҳ"=>"Ҳ", + "ұ"=>"Ұ","ү"=>"Ү","ҭ"=>"Ҭ","ҫ"=>"Ҫ","ҩ"=>"Ҩ","ҧ"=>"Ҧ","ҥ"=>"Ҥ","ң"=>"Ң","ҡ"=>"Ҡ","ҟ"=>"Ҟ", + "ҝ"=>"Ҝ","қ"=>"Қ","ҙ"=>"Ҙ","җ"=>"Җ","ҕ"=>"Ҕ","ғ"=>"Ғ","ґ"=>"Ґ","ҏ"=>"Ҏ","ҍ"=>"Ҍ","ҋ"=>"Ҋ", + "ҁ"=>"Ҁ","ѿ"=>"Ѿ","ѽ"=>"Ѽ","ѻ"=>"Ѻ","ѹ"=>"Ѹ","ѷ"=>"Ѷ","ѵ"=>"Ѵ","ѳ"=>"Ѳ","ѱ"=>"Ѱ","ѯ"=>"Ѯ", + "ѭ"=>"Ѭ","ѫ"=>"Ѫ","ѩ"=>"Ѩ","ѧ"=>"Ѧ","ѥ"=>"Ѥ","ѣ"=>"Ѣ","ѡ"=>"Ѡ","џ"=>"Џ","ў"=>"Ў","ѝ"=>"Ѝ", + "ќ"=>"Ќ","ћ"=>"Ћ","њ"=>"Њ","љ"=>"Љ","ј"=>"Ј","ї"=>"Ї","і"=>"І","ѕ"=>"Ѕ","є"=>"Є","ѓ"=>"Ѓ", + "ђ"=>"Ђ","ё"=>"Ё","ѐ"=>"Ѐ","я"=>"Я","ю"=>"Ю","э"=>"Э","ь"=>"Ь","ы"=>"Ы","ъ"=>"Ъ","щ"=>"Щ", + "ш"=>"Ш","ч"=>"Ч","ц"=>"Ц","х"=>"Х","ф"=>"Ф","у"=>"У","т"=>"Т","с"=>"С","р"=>"Р","п"=>"П", + "о"=>"О","н"=>"Н","м"=>"М","л"=>"Л","к"=>"К","й"=>"Й","и"=>"И","з"=>"З","ж"=>"Ж","е"=>"Е", + "д"=>"Д","г"=>"Г","в"=>"В","б"=>"Б","а"=>"А","ϵ"=>"Ε","ϲ"=>"Σ","ϱ"=>"Ρ","ϰ"=>"Κ","ϯ"=>"Ϯ", + "ϭ"=>"Ϭ","ϫ"=>"Ϫ","ϩ"=>"Ϩ","ϧ"=>"Ϧ","ϥ"=>"Ϥ","ϣ"=>"Ϣ","ϡ"=>"Ϡ","ϟ"=>"Ϟ","ϝ"=>"Ϝ","ϛ"=>"Ϛ", + "ϙ"=>"Ϙ","ϖ"=>"Π","ϕ"=>"Φ","ϑ"=>"Θ","ϐ"=>"Β","ώ"=>"Ώ","ύ"=>"Ύ","ό"=>"Ό","ϋ"=>"Ϋ","ϊ"=>"Ϊ", + "ω"=>"Ω","ψ"=>"Ψ","χ"=>"Χ","φ"=>"Φ","υ"=>"Υ","τ"=>"Τ","σ"=>"Σ","ς"=>"Σ","ρ"=>"Ρ","π"=>"Π", + "ο"=>"Ο","ξ"=>"Ξ","ν"=>"Ν","μ"=>"Μ","λ"=>"Λ","κ"=>"Κ","ι"=>"Ι","θ"=>"Θ","η"=>"Η","ζ"=>"Ζ", + "ε"=>"Ε","δ"=>"Δ","γ"=>"Γ","β"=>"Β","α"=>"Α","ί"=>"Ί","ή"=>"Ή","έ"=>"Έ","ά"=>"Ά","ʒ"=>"Ʒ", + "ʋ"=>"Ʋ","ʊ"=>"Ʊ","ʈ"=>"Ʈ","ʃ"=>"Ʃ","ʀ"=>"Ʀ","ɵ"=>"Ɵ","ɲ"=>"Ɲ","ɯ"=>"Ɯ","ɩ"=>"Ɩ","ɨ"=>"Ɨ", + "ɣ"=>"Ɣ","ɛ"=>"Ɛ","ə"=>"Ə","ɗ"=>"Ɗ","ɖ"=>"Ɖ","ɔ"=>"Ɔ","ɓ"=>"Ɓ","ȳ"=>"Ȳ","ȱ"=>"Ȱ","ȯ"=>"Ȯ", + "ȭ"=>"Ȭ","ȫ"=>"Ȫ","ȩ"=>"Ȩ","ȧ"=>"Ȧ","ȥ"=>"Ȥ","ȣ"=>"Ȣ","ȟ"=>"Ȟ","ȝ"=>"Ȝ","ț"=>"Ț","ș"=>"Ș", + "ȗ"=>"Ȗ","ȕ"=>"Ȕ","ȓ"=>"Ȓ","ȑ"=>"Ȑ","ȏ"=>"Ȏ","ȍ"=>"Ȍ","ȋ"=>"Ȋ","ȉ"=>"Ȉ","ȇ"=>"Ȇ","ȅ"=>"Ȅ", + "ȃ"=>"Ȃ","ȁ"=>"Ȁ","ǿ"=>"Ǿ","ǽ"=>"Ǽ","ǻ"=>"Ǻ","ǹ"=>"Ǹ","ǵ"=>"Ǵ","dz"=>"Dz","ǯ"=>"Ǯ","ǭ"=>"Ǭ", + "ǫ"=>"Ǫ","ǩ"=>"Ǩ","ǧ"=>"Ǧ","ǥ"=>"Ǥ","ǣ"=>"Ǣ","ǡ"=>"Ǡ","ǟ"=>"Ǟ","ǝ"=>"Ǝ","ǜ"=>"Ǜ","ǚ"=>"Ǚ", + "ǘ"=>"Ǘ","ǖ"=>"Ǖ","ǔ"=>"Ǔ","ǒ"=>"Ǒ","ǐ"=>"Ǐ","ǎ"=>"Ǎ","nj"=>"Nj","lj"=>"Lj","dž"=>"Dž","ƿ"=>"Ƿ", + "ƽ"=>"Ƽ","ƹ"=>"Ƹ","ƶ"=>"Ƶ","ƴ"=>"Ƴ","ư"=>"Ư","ƭ"=>"Ƭ","ƨ"=>"Ƨ","ƥ"=>"Ƥ","ƣ"=>"Ƣ","ơ"=>"Ơ", + "ƞ"=>"Ƞ","ƙ"=>"Ƙ","ƕ"=>"Ƕ","ƒ"=>"Ƒ","ƌ"=>"Ƌ","ƈ"=>"Ƈ","ƅ"=>"Ƅ","ƃ"=>"Ƃ","ſ"=>"S","ž"=>"Ž", + "ż"=>"Ż","ź"=>"Ź","ŷ"=>"Ŷ","ŵ"=>"Ŵ","ų"=>"Ų","ű"=>"Ű","ů"=>"Ů","ŭ"=>"Ŭ","ū"=>"Ū","ũ"=>"Ũ", + "ŧ"=>"Ŧ","ť"=>"Ť","ţ"=>"Ţ","š"=>"Š","ş"=>"Ş","ŝ"=>"Ŝ","ś"=>"Ś","ř"=>"Ř","ŗ"=>"Ŗ","ŕ"=>"Ŕ", + "œ"=>"Œ","ő"=>"Ő","ŏ"=>"Ŏ","ō"=>"Ō","ŋ"=>"Ŋ","ň"=>"Ň","ņ"=>"Ņ","ń"=>"Ń","ł"=>"Ł","ŀ"=>"Ŀ", + "ľ"=>"Ľ","ļ"=>"Ļ","ĺ"=>"Ĺ","ķ"=>"Ķ","ĵ"=>"Ĵ","ij"=>"IJ","ı"=>"I","į"=>"Į","ĭ"=>"Ĭ","ī"=>"Ī", + "ĩ"=>"Ĩ","ħ"=>"Ħ","ĥ"=>"Ĥ","ģ"=>"Ģ","ġ"=>"Ġ","ğ"=>"Ğ","ĝ"=>"Ĝ","ě"=>"Ě","ę"=>"Ę","ė"=>"Ė", + "ĕ"=>"Ĕ","ē"=>"Ē","đ"=>"Đ","ď"=>"Ď","č"=>"Č","ċ"=>"Ċ","ĉ"=>"Ĉ","ć"=>"Ć","ą"=>"Ą","ă"=>"Ă", + "ā"=>"Ā","ÿ"=>"Ÿ","þ"=>"Þ","ý"=>"Ý","ü"=>"Ü","û"=>"Û","ú"=>"Ú","ù"=>"Ù","ø"=>"Ø","ö"=>"Ö", + "õ"=>"Õ","ô"=>"Ô","ó"=>"Ó","ò"=>"Ò","ñ"=>"Ñ","ð"=>"Ð","ï"=>"Ï","î"=>"Î","í"=>"Í","ì"=>"Ì", + "ë"=>"Ë","ê"=>"Ê","é"=>"É","è"=>"È","ç"=>"Ç","æ"=>"Æ","å"=>"Å","ä"=>"Ä","ã"=>"Ã","â"=>"Â", + "á"=>"Á","à"=>"À","µ"=>"Μ","z"=>"Z","y"=>"Y","x"=>"X","w"=>"W","v"=>"V","u"=>"U","t"=>"T", + "s"=>"S","r"=>"R","q"=>"Q","p"=>"P","o"=>"O","n"=>"N","m"=>"M","l"=>"L","k"=>"K","j"=>"J", + "i"=>"I","h"=>"H","g"=>"G","f"=>"F","e"=>"E","d"=>"D","c"=>"C","b"=>"B","a"=>"A" + ); + + /** + * UTF-8 Case lookup table + * + * This lookuptable defines the lower case letters to their correspponding + * upper case letter in UTF-8 + * + * @author Andreas Gohr + */ + global $UTF8_UPPER_TO_LOWER; + $UTF8_UPPER_TO_LOWER = array ( + "Z"=>"z","Y"=>"y","X"=>"x","W"=>"w","V"=>"v","U"=>"u","T"=>"t","S"=>"s","R"=>"r","Q"=>"q", + "P"=>"p","O"=>"o","N"=>"n","M"=>"m","L"=>"l","K"=>"k","J"=>"j","I"=>"i","H"=>"h","G"=>"g", + "F"=>"f","E"=>"e","D"=>"d","C"=>"c","B"=>"b","A"=>"a","ῼ"=>"ῳ","Ῥ"=>"ῥ","Ῡ"=>"ῡ","Ῑ"=>"ῑ", + "Ῐ"=>"ῐ","ῌ"=>"ῃ","Ι"=>"ι","ᾼ"=>"ᾳ","Ᾱ"=>"ᾱ","Ᾰ"=>"ᾰ","ᾯ"=>"ᾧ","ᾮ"=>"ᾦ","ᾭ"=>"ᾥ","ᾬ"=>"ᾤ", + "ᾫ"=>"ᾣ","ᾪ"=>"ᾢ","ᾩ"=>"ᾡ","ᾟ"=>"ᾗ","ᾞ"=>"ᾖ","ᾝ"=>"ᾕ","ᾜ"=>"ᾔ","ᾛ"=>"ᾓ","ᾚ"=>"ᾒ","ᾙ"=>"ᾑ", + "ᾘ"=>"ᾐ","ᾏ"=>"ᾇ","ᾎ"=>"ᾆ","ᾍ"=>"ᾅ","ᾌ"=>"ᾄ","ᾋ"=>"ᾃ","ᾊ"=>"ᾂ","ᾉ"=>"ᾁ","ᾈ"=>"ᾀ","Ώ"=>"ώ", + "Ὼ"=>"ὼ","Ύ"=>"ύ","Ὺ"=>"ὺ","Ό"=>"ό","Ὸ"=>"ὸ","Ί"=>"ί","Ὶ"=>"ὶ","Ή"=>"ή","Ὴ"=>"ὴ","Έ"=>"έ", + "Ὲ"=>"ὲ","Ά"=>"ά","Ὰ"=>"ὰ","Ὧ"=>"ὧ","Ὦ"=>"ὦ","Ὥ"=>"ὥ","Ὤ"=>"ὤ","Ὣ"=>"ὣ","Ὢ"=>"ὢ","Ὡ"=>"ὡ", + "Ὗ"=>"ὗ","Ὕ"=>"ὕ","Ὓ"=>"ὓ","Ὑ"=>"ὑ","Ὅ"=>"ὅ","Ὄ"=>"ὄ","Ὃ"=>"ὃ","Ὂ"=>"ὂ","Ὁ"=>"ὁ","Ὀ"=>"ὀ", + "Ἷ"=>"ἷ","Ἶ"=>"ἶ","Ἵ"=>"ἵ","Ἴ"=>"ἴ","Ἳ"=>"ἳ","Ἲ"=>"ἲ","Ἱ"=>"ἱ","Ἰ"=>"ἰ","Ἧ"=>"ἧ","Ἦ"=>"ἦ", + "Ἥ"=>"ἥ","Ἤ"=>"ἤ","Ἣ"=>"ἣ","Ἢ"=>"ἢ","Ἡ"=>"ἡ","Ἕ"=>"ἕ","Ἔ"=>"ἔ","Ἓ"=>"ἓ","Ἒ"=>"ἒ","Ἑ"=>"ἑ", + "Ἐ"=>"ἐ","Ἇ"=>"ἇ","Ἆ"=>"ἆ","Ἅ"=>"ἅ","Ἄ"=>"ἄ","Ἃ"=>"ἃ","Ἂ"=>"ἂ","Ἁ"=>"ἁ","Ἀ"=>"ἀ","Ỹ"=>"ỹ", + "Ỷ"=>"ỷ","Ỵ"=>"ỵ","Ỳ"=>"ỳ","Ự"=>"ự","Ữ"=>"ữ","Ử"=>"ử","Ừ"=>"ừ","Ứ"=>"ứ","Ủ"=>"ủ","Ụ"=>"ụ", + "Ợ"=>"ợ","Ỡ"=>"ỡ","Ở"=>"ở","Ờ"=>"ờ","Ớ"=>"ớ","Ộ"=>"ộ","Ỗ"=>"ỗ","Ổ"=>"ổ","Ồ"=>"ồ","Ố"=>"ố", + "Ỏ"=>"ỏ","Ọ"=>"ọ","Ị"=>"ị","Ỉ"=>"ỉ","Ệ"=>"ệ","Ễ"=>"ễ","Ể"=>"ể","Ề"=>"ề","Ế"=>"ế","Ẽ"=>"ẽ", + "Ẻ"=>"ẻ","Ẹ"=>"ẹ","Ặ"=>"ặ","Ẵ"=>"ẵ","Ẳ"=>"ẳ","Ằ"=>"ằ","Ắ"=>"ắ","Ậ"=>"ậ","Ẫ"=>"ẫ","Ẩ"=>"ẩ", + "Ầ"=>"ầ","Ấ"=>"ấ","Ả"=>"ả","Ạ"=>"ạ","Ṡ"=>"ẛ","Ẕ"=>"ẕ","Ẓ"=>"ẓ","Ẑ"=>"ẑ","Ẏ"=>"ẏ","Ẍ"=>"ẍ", + "Ẋ"=>"ẋ","Ẉ"=>"ẉ","Ẇ"=>"ẇ","Ẅ"=>"ẅ","Ẃ"=>"ẃ","Ẁ"=>"ẁ","Ṿ"=>"ṿ","Ṽ"=>"ṽ","Ṻ"=>"ṻ","Ṹ"=>"ṹ", + "Ṷ"=>"ṷ","Ṵ"=>"ṵ","Ṳ"=>"ṳ","Ṱ"=>"ṱ","Ṯ"=>"ṯ","Ṭ"=>"ṭ","Ṫ"=>"ṫ","Ṩ"=>"ṩ","Ṧ"=>"ṧ","Ṥ"=>"ṥ", + "Ṣ"=>"ṣ","Ṡ"=>"ṡ","Ṟ"=>"ṟ","Ṝ"=>"ṝ","Ṛ"=>"ṛ","Ṙ"=>"ṙ","Ṗ"=>"ṗ","Ṕ"=>"ṕ","Ṓ"=>"ṓ","Ṑ"=>"ṑ", + "Ṏ"=>"ṏ","Ṍ"=>"ṍ","Ṋ"=>"ṋ","Ṉ"=>"ṉ","Ṇ"=>"ṇ","Ṅ"=>"ṅ","Ṃ"=>"ṃ","Ṁ"=>"ṁ","Ḿ"=>"ḿ","Ḽ"=>"ḽ", + "Ḻ"=>"ḻ","Ḹ"=>"ḹ","Ḷ"=>"ḷ","Ḵ"=>"ḵ","Ḳ"=>"ḳ","Ḱ"=>"ḱ","Ḯ"=>"ḯ","Ḭ"=>"ḭ","Ḫ"=>"ḫ","Ḩ"=>"ḩ", + "Ḧ"=>"ḧ","Ḥ"=>"ḥ","Ḣ"=>"ḣ","Ḡ"=>"ḡ","Ḟ"=>"ḟ","Ḝ"=>"ḝ","Ḛ"=>"ḛ","Ḙ"=>"ḙ","Ḗ"=>"ḗ","Ḕ"=>"ḕ", + "Ḓ"=>"ḓ","Ḑ"=>"ḑ","Ḏ"=>"ḏ","Ḍ"=>"ḍ","Ḋ"=>"ḋ","Ḉ"=>"ḉ","Ḇ"=>"ḇ","Ḅ"=>"ḅ","Ḃ"=>"ḃ","Ḁ"=>"ḁ", + "Ֆ"=>"ֆ","Օ"=>"օ","Ք"=>"ք","Փ"=>"փ","Ւ"=>"ւ","Ց"=>"ց","Ր"=>"ր","Տ"=>"տ","Վ"=>"վ","Ս"=>"ս", + "Ռ"=>"ռ","Ջ"=>"ջ","Պ"=>"պ","Չ"=>"չ","Ո"=>"ո","Շ"=>"շ","Ն"=>"ն","Յ"=>"յ","Մ"=>"մ","Ճ"=>"ճ", + "Ղ"=>"ղ","Ձ"=>"ձ","Հ"=>"հ","Կ"=>"կ","Ծ"=>"ծ","Խ"=>"խ","Լ"=>"լ","Ի"=>"ի","Ժ"=>"ժ","Թ"=>"թ", + "Ը"=>"ը","Է"=>"է","Զ"=>"զ","Ե"=>"ե","Դ"=>"դ","Գ"=>"գ","Բ"=>"բ","Ա"=>"ա","Ԏ"=>"ԏ","Ԍ"=>"ԍ", + "Ԋ"=>"ԋ","Ԉ"=>"ԉ","Ԇ"=>"ԇ","Ԅ"=>"ԅ","Ԃ"=>"ԃ","Ԁ"=>"ԁ","Ӹ"=>"ӹ","Ӵ"=>"ӵ","Ӳ"=>"ӳ","Ӱ"=>"ӱ", + "Ӯ"=>"ӯ","Ӭ"=>"ӭ","Ӫ"=>"ӫ","Ө"=>"ө","Ӧ"=>"ӧ","Ӥ"=>"ӥ","Ӣ"=>"ӣ","Ӡ"=>"ӡ","Ӟ"=>"ӟ","Ӝ"=>"ӝ", + "Ӛ"=>"ӛ","Ә"=>"ә","Ӗ"=>"ӗ","Ӕ"=>"ӕ","Ӓ"=>"ӓ","Ӑ"=>"ӑ","Ӎ"=>"ӎ","Ӌ"=>"ӌ","Ӊ"=>"ӊ","Ӈ"=>"ӈ", + "Ӆ"=>"ӆ","Ӄ"=>"ӄ","Ӂ"=>"ӂ","Ҿ"=>"ҿ","Ҽ"=>"ҽ","Һ"=>"һ","Ҹ"=>"ҹ","Ҷ"=>"ҷ","Ҵ"=>"ҵ","Ҳ"=>"ҳ", + "Ұ"=>"ұ","Ү"=>"ү","Ҭ"=>"ҭ","Ҫ"=>"ҫ","Ҩ"=>"ҩ","Ҧ"=>"ҧ","Ҥ"=>"ҥ","Ң"=>"ң","Ҡ"=>"ҡ","Ҟ"=>"ҟ", + "Ҝ"=>"ҝ","Қ"=>"қ","Ҙ"=>"ҙ","Җ"=>"җ","Ҕ"=>"ҕ","Ғ"=>"ғ","Ґ"=>"ґ","Ҏ"=>"ҏ","Ҍ"=>"ҍ","Ҋ"=>"ҋ", + "Ҁ"=>"ҁ","Ѿ"=>"ѿ","Ѽ"=>"ѽ","Ѻ"=>"ѻ","Ѹ"=>"ѹ","Ѷ"=>"ѷ","Ѵ"=>"ѵ","Ѳ"=>"ѳ","Ѱ"=>"ѱ","Ѯ"=>"ѯ", + "Ѭ"=>"ѭ","Ѫ"=>"ѫ","Ѩ"=>"ѩ","Ѧ"=>"ѧ","Ѥ"=>"ѥ","Ѣ"=>"ѣ","Ѡ"=>"ѡ","Џ"=>"џ","Ў"=>"ў","Ѝ"=>"ѝ", + "Ќ"=>"ќ","Ћ"=>"ћ","Њ"=>"њ","Љ"=>"љ","Ј"=>"ј","Ї"=>"ї","І"=>"і","Ѕ"=>"ѕ","Є"=>"є","Ѓ"=>"ѓ", + "Ђ"=>"ђ","Ё"=>"ё","Ѐ"=>"ѐ","Я"=>"я","Ю"=>"ю","Э"=>"э","Ь"=>"ь","Ы"=>"ы","Ъ"=>"ъ","Щ"=>"щ", + "Ш"=>"ш","Ч"=>"ч","Ц"=>"ц","Х"=>"х","Ф"=>"ф","У"=>"у","Т"=>"т","С"=>"с","Р"=>"р","П"=>"п", + "О"=>"о","Н"=>"н","М"=>"м","Л"=>"л","К"=>"к","Й"=>"й","И"=>"и","З"=>"з","Ж"=>"ж","Е"=>"е", + "Д"=>"д","Г"=>"г","В"=>"в","Б"=>"б","А"=>"а","Ε"=>"ϵ","Σ"=>"ϲ","Ρ"=>"ϱ","Κ"=>"ϰ","Ϯ"=>"ϯ", + "Ϭ"=>"ϭ","Ϫ"=>"ϫ","Ϩ"=>"ϩ","Ϧ"=>"ϧ","Ϥ"=>"ϥ","Ϣ"=>"ϣ","Ϡ"=>"ϡ","Ϟ"=>"ϟ","Ϝ"=>"ϝ","Ϛ"=>"ϛ", + "Ϙ"=>"ϙ","Π"=>"ϖ","Φ"=>"ϕ","Θ"=>"ϑ","Β"=>"ϐ","Ώ"=>"ώ","Ύ"=>"ύ","Ό"=>"ό","Ϋ"=>"ϋ","Ϊ"=>"ϊ", + "Ω"=>"ω","Ψ"=>"ψ","Χ"=>"χ","Φ"=>"φ","Υ"=>"υ","Τ"=>"τ","Σ"=>"σ","Σ"=>"ς","Ρ"=>"ρ","Π"=>"π", + "Ο"=>"ο","Ξ"=>"ξ","Ν"=>"ν","Μ"=>"μ","Λ"=>"λ","Κ"=>"κ","Ι"=>"ι","Θ"=>"θ","Η"=>"η","Ζ"=>"ζ", + "Ε"=>"ε","Δ"=>"δ","Γ"=>"γ","Β"=>"β","Α"=>"α","Ί"=>"ί","Ή"=>"ή","Έ"=>"έ","Ά"=>"ά","Ʒ"=>"ʒ", + "Ʋ"=>"ʋ","Ʊ"=>"ʊ","Ʈ"=>"ʈ","Ʃ"=>"ʃ","Ʀ"=>"ʀ","Ɵ"=>"ɵ","Ɲ"=>"ɲ","Ɯ"=>"ɯ","Ɩ"=>"ɩ","Ɨ"=>"ɨ", + "Ɣ"=>"ɣ","Ɛ"=>"ɛ","Ə"=>"ə","Ɗ"=>"ɗ","Ɖ"=>"ɖ","Ɔ"=>"ɔ","Ɓ"=>"ɓ","Ȳ"=>"ȳ","Ȱ"=>"ȱ","Ȯ"=>"ȯ", + "Ȭ"=>"ȭ","Ȫ"=>"ȫ","Ȩ"=>"ȩ","Ȧ"=>"ȧ","Ȥ"=>"ȥ","Ȣ"=>"ȣ","Ȟ"=>"ȟ","Ȝ"=>"ȝ","Ț"=>"ț","Ș"=>"ș", + "Ȗ"=>"ȗ","Ȕ"=>"ȕ","Ȓ"=>"ȓ","Ȑ"=>"ȑ","Ȏ"=>"ȏ","Ȍ"=>"ȍ","Ȋ"=>"ȋ","Ȉ"=>"ȉ","Ȇ"=>"ȇ","Ȅ"=>"ȅ", + "Ȃ"=>"ȃ","Ȁ"=>"ȁ","Ǿ"=>"ǿ","Ǽ"=>"ǽ","Ǻ"=>"ǻ","Ǹ"=>"ǹ","Ǵ"=>"ǵ","Dz"=>"dz","Ǯ"=>"ǯ","Ǭ"=>"ǭ", + "Ǫ"=>"ǫ","Ǩ"=>"ǩ","Ǧ"=>"ǧ","Ǥ"=>"ǥ","Ǣ"=>"ǣ","Ǡ"=>"ǡ","Ǟ"=>"ǟ","Ǝ"=>"ǝ","Ǜ"=>"ǜ","Ǚ"=>"ǚ", + "Ǘ"=>"ǘ","Ǖ"=>"ǖ","Ǔ"=>"ǔ","Ǒ"=>"ǒ","Ǐ"=>"ǐ","Ǎ"=>"ǎ","Nj"=>"nj","Lj"=>"lj","Dž"=>"dž","Ƿ"=>"ƿ", + "Ƽ"=>"ƽ","Ƹ"=>"ƹ","Ƶ"=>"ƶ","Ƴ"=>"ƴ","Ư"=>"ư","Ƭ"=>"ƭ","Ƨ"=>"ƨ","Ƥ"=>"ƥ","Ƣ"=>"ƣ","Ơ"=>"ơ", + "Ƞ"=>"ƞ","Ƙ"=>"ƙ","Ƕ"=>"ƕ","Ƒ"=>"ƒ","Ƌ"=>"ƌ","Ƈ"=>"ƈ","Ƅ"=>"ƅ","Ƃ"=>"ƃ","S"=>"ſ","Ž"=>"ž", + "Ż"=>"ż","Ź"=>"ź","Ŷ"=>"ŷ","Ŵ"=>"ŵ","Ų"=>"ų","Ű"=>"ű","Ů"=>"ů","Ŭ"=>"ŭ","Ū"=>"ū","Ũ"=>"ũ", + "Ŧ"=>"ŧ","Ť"=>"ť","Ţ"=>"ţ","Š"=>"š","Ş"=>"ş","Ŝ"=>"ŝ","Ś"=>"ś","Ř"=>"ř","Ŗ"=>"ŗ","Ŕ"=>"ŕ", + "Œ"=>"œ","Ő"=>"ő","Ŏ"=>"ŏ","Ō"=>"ō","Ŋ"=>"ŋ","Ň"=>"ň","Ņ"=>"ņ","Ń"=>"ń","Ł"=>"ł","Ŀ"=>"ŀ", + "Ľ"=>"ľ","Ļ"=>"ļ","Ĺ"=>"ĺ","Ķ"=>"ķ","Ĵ"=>"ĵ","IJ"=>"ij","I"=>"ı","Į"=>"į","Ĭ"=>"ĭ","Ī"=>"ī", + "Ĩ"=>"ĩ","Ħ"=>"ħ","Ĥ"=>"ĥ","Ģ"=>"ģ","Ġ"=>"ġ","Ğ"=>"ğ","Ĝ"=>"ĝ","Ě"=>"ě","Ę"=>"ę","Ė"=>"ė", + "Ĕ"=>"ĕ","Ē"=>"ē","Đ"=>"đ","Ď"=>"ď","Č"=>"č","Ċ"=>"ċ","Ĉ"=>"ĉ","Ć"=>"ć","Ą"=>"ą","Ă"=>"ă", + "Ā"=>"ā","Ÿ"=>"ÿ","Þ"=>"þ","Ý"=>"ý","Ü"=>"ü","Û"=>"û","Ú"=>"ú","Ù"=>"ù","Ø"=>"ø","Ö"=>"ö", + "Õ"=>"õ","Ô"=>"ô","Ó"=>"ó","Ò"=>"ò","Ñ"=>"ñ","Ð"=>"ð","Ï"=>"ï","Î"=>"î","Í"=>"í","Ì"=>"ì", + "Ë"=>"ë","Ê"=>"ê","É"=>"é","È"=>"è","Ç"=>"ç","Æ"=>"æ","Å"=>"å","Ä"=>"ä","Ã"=>"ã","Â"=>"â", + "Á"=>"á","À"=>"à","Μ"=>"µ","Z"=>"z","Y"=>"y","X"=>"x","W"=>"w","V"=>"v","U"=>"u","T"=>"t", + "S"=>"s","R"=>"r","Q"=>"q","P"=>"p","O"=>"o","N"=>"n","M"=>"m","L"=>"l","K"=>"k","J"=>"j", + "I"=>"i","H"=>"h","G"=>"g","F"=>"f","E"=>"e","D"=>"d","C"=>"c","B"=>"b","A"=>"a" + ); +}; // end of case lookup tables + +/** + * UTF-8 lookup table for lower case accented letters + * + * This lookuptable defines replacements for accented characters from the ASCII-7 + * range. This are lower case letters only. + * + * @author Andreas Gohr + * @see utf8_deaccent() + */ +global $UTF8_LOWER_ACCENTS; +$UTF8_LOWER_ACCENTS = array( + 'à' => 'a', 'ô' => 'o', 'ď' => 'd', 'ḟ' => 'f', 'ë' => 'e', 'š' => 's', 'ơ' => 'o', + 'ß' => 'ss', 'ă' => 'a', 'ř' => 'r', 'ț' => 't', 'ň' => 'n', 'ā' => 'a', 'ķ' => 'k', + 'ŝ' => 's', 'ỳ' => 'y', 'ņ' => 'n', 'ĺ' => 'l', 'ħ' => 'h', 'ṗ' => 'p', 'ó' => 'o', + 'ú' => 'u', 'ě' => 'e', 'é' => 'e', 'ç' => 'c', 'ẁ' => 'w', 'ċ' => 'c', 'õ' => 'o', + 'ṡ' => 's', 'ø' => 'o', 'ģ' => 'g', 'ŧ' => 't', 'ș' => 's', 'ė' => 'e', 'ĉ' => 'c', + 'ś' => 's', 'î' => 'i', 'ű' => 'u', 'ć' => 'c', 'ę' => 'e', 'ŵ' => 'w', 'ṫ' => 't', + 'ū' => 'u', 'č' => 'c', 'ö' => 'oe', 'è' => 'e', 'ŷ' => 'y', 'ą' => 'a', 'ł' => 'l', + 'ų' => 'u', 'ů' => 'u', 'ş' => 's', 'ğ' => 'g', 'ļ' => 'l', 'ƒ' => 'f', 'ž' => 'z', + 'ẃ' => 'w', 'ḃ' => 'b', 'å' => 'a', 'ì' => 'i', 'ï' => 'i', 'ḋ' => 'd', 'ť' => 't', + 'ŗ' => 'r', 'ä' => 'ae', 'í' => 'i', 'ŕ' => 'r', 'ê' => 'e', 'ü' => 'ue', 'ò' => 'o', + 'ē' => 'e', 'ñ' => 'n', 'ń' => 'n', 'ĥ' => 'h', 'ĝ' => 'g', 'đ' => 'd', 'ĵ' => 'j', + 'ÿ' => 'y', 'ũ' => 'u', 'ŭ' => 'u', 'ư' => 'u', 'ţ' => 't', 'ý' => 'y', 'ő' => 'o', + 'â' => 'a', 'ľ' => 'l', 'ẅ' => 'w', 'ż' => 'z', 'ī' => 'i', 'ã' => 'a', 'ġ' => 'g', + 'ṁ' => 'm', 'ō' => 'o', 'ĩ' => 'i', 'ù' => 'u', 'į' => 'i', 'ź' => 'z', 'á' => 'a', + 'û' => 'u', 'þ' => 'th', 'ð' => 'dh', 'æ' => 'ae', 'µ' => 'u', 'ĕ' => 'e', +); + +/** + * UTF-8 lookup table for upper case accented letters + * + * This lookuptable defines replacements for accented characters from the ASCII-7 + * range. This are upper case letters only. + * + * @author Andreas Gohr + * @see utf8_deaccent() + */ +global $UTF8_UPPER_ACCENTS; +$UTF8_UPPER_ACCENTS = array( + 'À' => 'A', 'Ô' => 'O', 'Ď' => 'D', 'Ḟ' => 'F', 'Ë' => 'E', 'Š' => 'S', 'Ơ' => 'O', + 'Ă' => 'A', 'Ř' => 'R', 'Ț' => 'T', 'Ň' => 'N', 'Ā' => 'A', 'Ķ' => 'K', + 'Ŝ' => 'S', 'Ỳ' => 'Y', 'Ņ' => 'N', 'Ĺ' => 'L', 'Ħ' => 'H', 'Ṗ' => 'P', 'Ó' => 'O', + 'Ú' => 'U', 'Ě' => 'E', 'É' => 'E', 'Ç' => 'C', 'Ẁ' => 'W', 'Ċ' => 'C', 'Õ' => 'O', + 'Ṡ' => 'S', 'Ø' => 'O', 'Ģ' => 'G', 'Ŧ' => 'T', 'Ș' => 'S', 'Ė' => 'E', 'Ĉ' => 'C', + 'Ś' => 'S', 'Î' => 'I', 'Ű' => 'U', 'Ć' => 'C', 'Ę' => 'E', 'Ŵ' => 'W', 'Ṫ' => 'T', + 'Ū' => 'U', 'Č' => 'C', 'Ö' => 'Oe', 'È' => 'E', 'Ŷ' => 'Y', 'Ą' => 'A', 'Ł' => 'L', + 'Ų' => 'U', 'Ů' => 'U', 'Ş' => 'S', 'Ğ' => 'G', 'Ļ' => 'L', 'Ƒ' => 'F', 'Ž' => 'Z', + 'Ẃ' => 'W', 'Ḃ' => 'B', 'Å' => 'A', 'Ì' => 'I', 'Ï' => 'I', 'Ḋ' => 'D', 'Ť' => 'T', + 'Ŗ' => 'R', 'Ä' => 'Ae', 'Í' => 'I', 'Ŕ' => 'R', 'Ê' => 'E', 'Ü' => 'Ue', 'Ò' => 'O', + 'Ē' => 'E', 'Ñ' => 'N', 'Ń' => 'N', 'Ĥ' => 'H', 'Ĝ' => 'G', 'Đ' => 'D', 'Ĵ' => 'J', + 'Ÿ' => 'Y', 'Ũ' => 'U', 'Ŭ' => 'U', 'Ư' => 'U', 'Ţ' => 'T', 'Ý' => 'Y', 'Ő' => 'O', + 'Â' => 'A', 'Ľ' => 'L', 'Ẅ' => 'W', 'Ż' => 'Z', 'Ī' => 'I', 'Ã' => 'A', 'Ġ' => 'G', + 'Ṁ' => 'M', 'Ō' => 'O', 'Ĩ' => 'I', 'Ù' => 'U', 'Į' => 'I', 'Ź' => 'Z', 'Á' => 'A', + 'Û' => 'U', 'Þ' => 'Th', 'Ð' => 'Dh', 'Æ' => 'Ae', 'Ĕ' => 'E', +); + +/** + * UTF-8 array of common special characters + * + * This array should contain all special characters (not a letter or digit) + * defined in the various local charsets - it's not a complete list of non-alphanum + * characters in UTF-8. It's not perfect but should match most cases of special + * chars. + * + * The controlchars 0x00 to 0x19 are _not_ included in this array. The space 0x20 is! + * These chars are _not_ in the array either: _ (0x5f), : 0x3a, . 0x2e, - 0x2d, * 0x2a + * + * @author Andreas Gohr + * @see utf8_stripspecials() + */ +global $UTF8_SPECIAL_CHARS; +$UTF8_SPECIAL_CHARS = array( + 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002b, 0x002c, + 0x002f, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x005b, + 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e, + 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, + 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, + 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, + 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, + 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, + 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, + 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00d7, 0x00f7, 0x02c7, 0x02d8, 0x02d9, + 0x02da, 0x02db, 0x02dc, 0x02dd, 0x0300, 0x0301, 0x0303, 0x0309, 0x0323, 0x0384, + 0x0385, 0x0387, 0x03b2, 0x03c6, 0x03d1, 0x03d2, 0x03d5, 0x03d6, 0x05b0, 0x05b1, + 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, 0x05bb, 0x05bc, + 0x05bd, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05f3, 0x05f4, 0x060c, + 0x061b, 0x061f, 0x0640, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651, + 0x0652, 0x066a, 0x0e3f, 0x200c, 0x200d, 0x200e, 0x200f, 0x2013, 0x2014, 0x2015, + 0x2017, 0x2018, 0x2019, 0x201a, 0x201c, 0x201d, 0x201e, 0x2020, 0x2021, 0x2022, + 0x2026, 0x2030, 0x2032, 0x2033, 0x2039, 0x203a, 0x2044, 0x20a7, 0x20aa, 0x20ab, + 0x20ac, 0x2116, 0x2118, 0x2122, 0x2126, 0x2135, 0x2190, 0x2191, 0x2192, 0x2193, + 0x2194, 0x2195, 0x21b5, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x2200, 0x2202, + 0x2203, 0x2205, 0x2206, 0x2207, 0x2208, 0x2209, 0x220b, 0x220f, 0x2211, 0x2212, + 0x2215, 0x2217, 0x2219, 0x221a, 0x221d, 0x221e, 0x2220, 0x2227, 0x2228, 0x2229, + 0x222a, 0x222b, 0x2234, 0x223c, 0x2245, 0x2248, 0x2260, 0x2261, 0x2264, 0x2265, + 0x2282, 0x2283, 0x2284, 0x2286, 0x2287, 0x2295, 0x2297, 0x22a5, 0x22c5, 0x2310, + 0x2320, 0x2321, 0x2329, 0x232a, 0x2469, 0x2500, 0x2502, 0x250c, 0x2510, 0x2514, + 0x2518, 0x251c, 0x2524, 0x252c, 0x2534, 0x253c, 0x2550, 0x2551, 0x2552, 0x2553, + 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d, + 0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, + 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590, + 0x2591, 0x2592, 0x2593, 0x25a0, 0x25b2, 0x25bc, 0x25c6, 0x25ca, 0x25cf, 0x25d7, + 0x2605, 0x260e, 0x261b, 0x261e, 0x2660, 0x2663, 0x2665, 0x2666, 0x2701, 0x2702, + 0x2703, 0x2704, 0x2706, 0x2707, 0x2708, 0x2709, 0x270c, 0x270d, 0x270e, 0x270f, + 0x2710, 0x2711, 0x2712, 0x2713, 0x2714, 0x2715, 0x2716, 0x2717, 0x2718, 0x2719, + 0x271a, 0x271b, 0x271c, 0x271d, 0x271e, 0x271f, 0x2720, 0x2721, 0x2722, 0x2723, + 0x2724, 0x2725, 0x2726, 0x2727, 0x2729, 0x272a, 0x272b, 0x272c, 0x272d, 0x272e, + 0x272f, 0x2730, 0x2731, 0x2732, 0x2733, 0x2734, 0x2735, 0x2736, 0x2737, 0x2738, + 0x2739, 0x273a, 0x273b, 0x273c, 0x273d, 0x273e, 0x273f, 0x2740, 0x2741, 0x2742, + 0x2743, 0x2744, 0x2745, 0x2746, 0x2747, 0x2748, 0x2749, 0x274a, 0x274b, 0x274d, + 0x274f, 0x2750, 0x2751, 0x2752, 0x2756, 0x2758, 0x2759, 0x275a, 0x275b, 0x275c, + 0x275d, 0x275e, 0x2761, 0x2762, 0x2763, 0x2764, 0x2765, 0x2766, 0x2767, 0x277f, + 0x2789, 0x2793, 0x2794, 0x2798, 0x2799, 0x279a, 0x279b, 0x279c, 0x279d, 0x279e, + 0x279f, 0x27a0, 0x27a1, 0x27a2, 0x27a3, 0x27a4, 0x27a5, 0x27a6, 0x27a7, 0x27a8, + 0x27a9, 0x27aa, 0x27ab, 0x27ac, 0x27ad, 0x27ae, 0x27af, 0x27b1, 0x27b2, 0x27b3, + 0x27b4, 0x27b5, 0x27b6, 0x27b7, 0x27b8, 0x27b9, 0x27ba, 0x27bb, 0x27bc, 0x27bd, + 0x27be, 0x3000, 0x3001, 0x3002, 0x3003, 0x3008, 0x3009, 0x300a, 0x300b, 0x300c, + 0x300d, 0x300e, 0x300f, 0x3010, 0x3011, 0x3012, 0x3014, 0x3015, 0x3016, 0x3017, + 0x3018, 0x3019, 0x301a, 0x301b, 0x3036, + 0xf6d9, 0xf6da, 0xf6db, 0xf8d7, 0xf8d8, 0xf8d9, 0xf8da, 0xf8db, 0xf8dc, + 0xf8dd, 0xf8de, 0xf8df, 0xf8e0, 0xf8e1, 0xf8e2, 0xf8e3, 0xf8e4, 0xf8e5, 0xf8e6, + 0xf8e7, 0xf8e8, 0xf8e9, 0xf8ea, 0xf8eb, 0xf8ec, 0xf8ed, 0xf8ee, 0xf8ef, 0xf8f0, + 0xf8f1, 0xf8f2, 0xf8f3, 0xf8f4, 0xf8f5, 0xf8f6, 0xf8f7, 0xf8f8, 0xf8f9, 0xf8fa, + 0xf8fb, 0xf8fc, 0xf8fd, 0xf8fe, 0xfe7c, 0xfe7d, + 0xff01, 0xff02, 0xff03, 0xff04, 0xff05, 0xff06, 0xff07, 0xff08, 0xff09, + 0xff09, 0xff0a, 0xff0b, 0xff0c, 0xff0d, 0xff0e, 0xff0f, 0xff1a, 0xff1b, 0xff1c, + 0xff1d, 0xff1e, 0xff1f, 0xff20, 0xff3b, 0xff3c, 0xff3d, 0xff3e, 0xff40, 0xff5b, + 0xff5c, 0xff5d, 0xff5e, 0xff5f, 0xff60, 0xff61, 0xff62, 0xff63, 0xff64, 0xff65, + 0xffe0, 0xffe1, 0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe8, 0xffe9, 0xffea, + 0xffeb, 0xffec, 0xffed, 0xffee, +); + +// utf8 version of above data +global $UTF8_SPECIAL_CHARS2; +$UTF8_SPECIAL_CHARS2 = + "\x1A".' !"#$%&\'()+,/;<=>?@[\]^`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•�'. + '�—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½�'. + '�¿×÷ˇ˘˙˚˛˜˝̣̀́̃̉΄΅·βφϑϒϕϖְֱֲֳִֵֶַָֹֻּֽ־ֿ�'. + '�ׁׂ׃׳״،؛؟ـًٌٍَُِّْ٪฿‌‍‎‏–—―‗‘’‚“”�'. + '��†‡•…‰′″‹›⁄₧₪₫€№℘™Ωℵ←↑→↓↔↕↵'. + '⇐⇑⇒⇓⇔∀∂∃∅∆∇∈∉∋∏∑−∕∗∙√∝∞∠∧∨�'. + '�∪∫∴∼≅≈≠≡≤≥⊂⊃⊄⊆⊇⊕⊗⊥⋅⌐⌠⌡〈〉⑩─�'. + '��┌┐└┘├┤┬┴┼═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠'. + '╡╢╣╤╥╦╧╨╩╪╫╬▀▄█▌▐░▒▓■▲▼◆◊●�'. + '�★☎☛☞♠♣♥♦✁✂✃✄✆✇✈✉✌✍✎✏✐✑✒✓✔✕�'. + '��✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✩✪✫✬✭✮✯✰✱'. + '✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋�'. + '�❏❐❑❒❖❘❙❚❛❜❝❞❡❢❣❤❥❦❧❿➉➓➔➘➙➚�'. + '��➜➝➞➟➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯➱➲➳➴➵➶'. + '➷➸➹➺➻➼➽➾'. + ' 、。〃〈〉《》「」『』【】〒〔〕〖〗〘〙〚〛〶'. + '�'. + '�ﹼﹽ'. + '!"#$%&'()*+,-./:;<=>?@[\]^`{|}~'. + '⦅⦆。「」、・¢£¬ ̄¦¥₩│←↑→↓■○'; + +/** + * Romanization lookup table + * + * This lookup tables provides a way to transform strings written in a language + * different from the ones based upon latin letters into plain ASCII. + * + * Please note: this is not a scientific transliteration table. It only works + * oneway from nonlatin to ASCII and it works by simple character replacement + * only. Specialities of each language are not supported. + * + * @author Andreas Gohr + * @author Vitaly Blokhin + * @link http://www.uconv.com/translit.htm + * @author Bisqwit + * @link http://kanjidict.stc.cx/hiragana.php?src=2 + * @link http://www.translatum.gr/converter/greek-transliteration.htm + * @link http://en.wikipedia.org/wiki/Royal_Thai_General_System_of_Transcription + * @link http://www.btranslations.com/resources/romanization/korean.asp + */ +global $UTF8_ROMANIZATION; +$UTF8_ROMANIZATION = array( + //russian cyrillic + 'а'=>'a','А'=>'A','б'=>'b','Б'=>'B','в'=>'v','В'=>'V','г'=>'g','Г'=>'G', + 'д'=>'d','Д'=>'D','е'=>'e','Е'=>'E','ё'=>'jo','Ё'=>'Jo','ж'=>'zh','Ж'=>'Zh', + 'з'=>'z','З'=>'Z','и'=>'i','И'=>'I','й'=>'j','Й'=>'J','к'=>'k','К'=>'K', + 'л'=>'l','Л'=>'L','м'=>'m','М'=>'M','н'=>'n','Н'=>'N','о'=>'o','О'=>'O', + 'п'=>'p','П'=>'P','р'=>'r','Р'=>'R','с'=>'s','С'=>'S','т'=>'t','Т'=>'T', + 'у'=>'u','У'=>'U','ф'=>'f','Ф'=>'F','х'=>'x','Х'=>'X','ц'=>'c','Ц'=>'C', + 'ч'=>'ch','Ч'=>'Ch','ш'=>'sh','Ш'=>'Sh','щ'=>'sch','Щ'=>'Sch','ъ'=>'', + 'Ъ'=>'','ы'=>'y','Ы'=>'Y','ь'=>'','Ь'=>'','э'=>'eh','Э'=>'Eh','ю'=>'ju', + 'Ю'=>'Ju','я'=>'ja','Я'=>'Ja', + // Ukrainian cyrillic + 'Ґ'=>'Gh','ґ'=>'gh','Є'=>'Je','є'=>'je','І'=>'I','і'=>'i','Ї'=>'Ji','ї'=>'ji', + // Georgian + 'ა'=>'a','ბ'=>'b','გ'=>'g','დ'=>'d','ე'=>'e','ვ'=>'v','ზ'=>'z','თ'=>'th', + 'ი'=>'i','კ'=>'p','ლ'=>'l','მ'=>'m','ნ'=>'n','ო'=>'o','პ'=>'p','ჟ'=>'zh', + 'რ'=>'r','ს'=>'s','ტ'=>'t','უ'=>'u','ფ'=>'ph','ქ'=>'kh','ღ'=>'gh','ყ'=>'q', + 'შ'=>'sh','ჩ'=>'ch','ც'=>'c','ძ'=>'dh','წ'=>'w','ჭ'=>'j','ხ'=>'x','ჯ'=>'jh', + 'ჰ'=>'xh', + //Sanskrit + 'अ'=>'a','आ'=>'ah','इ'=>'i','ई'=>'ih','उ'=>'u','ऊ'=>'uh','ऋ'=>'ry', + 'ॠ'=>'ryh','ऌ'=>'ly','ॡ'=>'lyh','ए'=>'e','ऐ'=>'ay','ओ'=>'o','औ'=>'aw', + 'अं'=>'amh','अः'=>'aq','क'=>'k','ख'=>'kh','ग'=>'g','घ'=>'gh','ङ'=>'nh', + 'च'=>'c','छ'=>'ch','ज'=>'j','झ'=>'jh','ञ'=>'ny','ट'=>'tq','ठ'=>'tqh', + 'ड'=>'dq','ढ'=>'dqh','ण'=>'nq','त'=>'t','थ'=>'th','द'=>'d','ध'=>'dh', + 'न'=>'n','प'=>'p','फ'=>'ph','ब'=>'b','भ'=>'bh','म'=>'m','य'=>'z','र'=>'r', + 'ल'=>'l','व'=>'v','श'=>'sh','ष'=>'sqh','स'=>'s','ह'=>'x', + //Hebrew + 'א'=>'a', 'ב'=>'b','ג'=>'g','ד'=>'d','ה'=>'h','ו'=>'v','ז'=>'z','ח'=>'kh','ט'=>'th', + 'י'=>'y','ך'=>'h','כ'=>'k','ל'=>'l','ם'=>'m','מ'=>'m','ן'=>'n','נ'=>'n', + 'ס'=>'s','ע'=>'ah','ף'=>'f','פ'=>'p','ץ'=>'c','צ'=>'c','ק'=>'q','ר'=>'r', + 'ש'=>'sh','ת'=>'t', + //Arabic + 'ا'=>'a','ب'=>'b','ت'=>'t','ث'=>'th','ج'=>'g','ح'=>'xh','خ'=>'x','د'=>'d', + 'ذ'=>'dh','ر'=>'r','ز'=>'z','س'=>'s','ش'=>'sh','ص'=>'s\'','ض'=>'d\'', + 'ط'=>'t\'','ظ'=>'z\'','ع'=>'y','غ'=>'gh','ف'=>'f','ق'=>'q','ك'=>'k', + 'ل'=>'l','م'=>'m','ن'=>'n','ه'=>'x\'','و'=>'u','ي'=>'i', + + // Japanese hiragana + 'あ'=>'a','え'=>'e','い'=>'i','お'=>'o','う'=>'u','ば'=>'ba','べ'=>'be', + 'び'=>'bi','ぼ'=>'bo','ぶ'=>'bu','し'=>'ci','だ'=>'da','で'=>'de','ぢ'=>'di', + 'ど'=>'do','づ'=>'du','ふぁ'=>'fa','ふぇ'=>'fe','ふぃ'=>'fi','ふぉ'=>'fo', + 'ふ'=>'fu','が'=>'ga','げ'=>'ge','ぎ'=>'gi','ご'=>'go','ぐ'=>'gu','は'=>'ha', + 'へ'=>'he','ひ'=>'hi','ほ'=>'ho','ふ'=>'hu','じゃ'=>'ja','じぇ'=>'je', + 'じ'=>'ji','じょ'=>'jo','じゅ'=>'ju','か'=>'ka','け'=>'ke','き'=>'ki', + 'こ'=>'ko','く'=>'ku','ら'=>'la','れ'=>'le','り'=>'li','ろ'=>'lo','る'=>'lu', + 'ま'=>'ma','め'=>'me','み'=>'mi','も'=>'mo','む'=>'mu','な'=>'na','ね'=>'ne', + 'に'=>'ni','の'=>'no','ぬ'=>'nu','ぱ'=>'pa','ぺ'=>'pe','ぴ'=>'pi','ぽ'=>'po', + 'ぷ'=>'pu','ら'=>'ra','れ'=>'re','り'=>'ri','ろ'=>'ro','る'=>'ru','さ'=>'sa', + 'せ'=>'se','し'=>'si','そ'=>'so','す'=>'su','た'=>'ta','て'=>'te','ち'=>'ti', + 'と'=>'to','つ'=>'tu','ヴぁ'=>'va','ヴぇ'=>'ve','ヴぃ'=>'vi','ヴぉ'=>'vo', + 'ヴ'=>'vu','わ'=>'wa','うぇ'=>'we','うぃ'=>'wi','を'=>'wo','や'=>'ya','いぇ'=>'ye', + 'い'=>'yi','よ'=>'yo','ゆ'=>'yu','ざ'=>'za','ぜ'=>'ze','じ'=>'zi','ぞ'=>'zo', + 'ず'=>'zu','びゃ'=>'bya','びぇ'=>'bye','びぃ'=>'byi','びょ'=>'byo','びゅ'=>'byu', + 'ちゃ'=>'cha','ちぇ'=>'che','ち'=>'chi','ちょ'=>'cho','ちゅ'=>'chu','ちゃ'=>'cya', + 'ちぇ'=>'cye','ちぃ'=>'cyi','ちょ'=>'cyo','ちゅ'=>'cyu','でゃ'=>'dha','でぇ'=>'dhe', + 'でぃ'=>'dhi','でょ'=>'dho','でゅ'=>'dhu','どぁ'=>'dwa','どぇ'=>'dwe','どぃ'=>'dwi', + 'どぉ'=>'dwo','どぅ'=>'dwu','ぢゃ'=>'dya','ぢぇ'=>'dye','ぢぃ'=>'dyi','ぢょ'=>'dyo', + 'ぢゅ'=>'dyu','ぢ'=>'dzi','ふぁ'=>'fwa','ふぇ'=>'fwe','ふぃ'=>'fwi','ふぉ'=>'fwo', + 'ふぅ'=>'fwu','ふゃ'=>'fya','ふぇ'=>'fye','ふぃ'=>'fyi','ふょ'=>'fyo','ふゅ'=>'fyu', + 'ぎゃ'=>'gya','ぎぇ'=>'gye','ぎぃ'=>'gyi','ぎょ'=>'gyo','ぎゅ'=>'gyu','ひゃ'=>'hya', + 'ひぇ'=>'hye','ひぃ'=>'hyi','ひょ'=>'hyo','ひゅ'=>'hyu','じゃ'=>'jya','じぇ'=>'jye', + 'じぃ'=>'jyi','じょ'=>'jyo','じゅ'=>'jyu','きゃ'=>'kya','きぇ'=>'kye','きぃ'=>'kyi', + 'きょ'=>'kyo','きゅ'=>'kyu','りゃ'=>'lya','りぇ'=>'lye','りぃ'=>'lyi','りょ'=>'lyo', + 'りゅ'=>'lyu','みゃ'=>'mya','みぇ'=>'mye','みぃ'=>'myi','みょ'=>'myo','みゅ'=>'myu', + 'ん'=>'n','にゃ'=>'nya','にぇ'=>'nye','にぃ'=>'nyi','にょ'=>'nyo','にゅ'=>'nyu', + 'ぴゃ'=>'pya','ぴぇ'=>'pye','ぴぃ'=>'pyi','ぴょ'=>'pyo','ぴゅ'=>'pyu','りゃ'=>'rya', + 'りぇ'=>'rye','りぃ'=>'ryi','りょ'=>'ryo','りゅ'=>'ryu','しゃ'=>'sha','しぇ'=>'she', + 'し'=>'shi','しょ'=>'sho','しゅ'=>'shu','すぁ'=>'swa','すぇ'=>'swe','すぃ'=>'swi', + 'すぉ'=>'swo','すぅ'=>'swu','しゃ'=>'sya','しぇ'=>'sye','しぃ'=>'syi','しょ'=>'syo', + 'しゅ'=>'syu','てゃ'=>'tha','てぇ'=>'the','てぃ'=>'thi','てょ'=>'tho','てゅ'=>'thu', + 'つゃ'=>'tsa','つぇ'=>'tse','つぃ'=>'tsi','つょ'=>'tso','つ'=>'tsu','とぁ'=>'twa', + 'とぇ'=>'twe','とぃ'=>'twi','とぉ'=>'two','とぅ'=>'twu','ちゃ'=>'tya','ちぇ'=>'tye', + 'ちぃ'=>'tyi','ちょ'=>'tyo','ちゅ'=>'tyu','ヴゃ'=>'vya','ヴぇ'=>'vye','ヴぃ'=>'vyi', + 'ヴょ'=>'vyo','ヴゅ'=>'vyu','うぁ'=>'wha','うぇ'=>'whe','うぃ'=>'whi','うぉ'=>'who', + 'うぅ'=>'whu','ゑ'=>'wye','ゐ'=>'wyi','じゃ'=>'zha','じぇ'=>'zhe','じぃ'=>'zhi', + 'じょ'=>'zho','じゅ'=>'zhu','じゃ'=>'zya','じぇ'=>'zye','じぃ'=>'zyi','じょ'=>'zyo', + 'じゅ'=>'zyu', + // Japanese katakana + 'ア'=>'a','エ'=>'e','イ'=>'i','オ'=>'o','ウ'=>'u','バ'=>'ba','ベ'=>'be','ビ'=>'bi', + 'ボ'=>'bo','ブ'=>'bu','シ'=>'ci','ダ'=>'da','デ'=>'de','ヂ'=>'di','ド'=>'do', + 'ヅ'=>'du','ファ'=>'fa','フェ'=>'fe','フィ'=>'fi','フォ'=>'fo','フ'=>'fu','ガ'=>'ga', + 'ゲ'=>'ge','ギ'=>'gi','ゴ'=>'go','グ'=>'gu','ハ'=>'ha','ヘ'=>'he','ヒ'=>'hi','ホ'=>'ho', + 'フ'=>'hu','ジャ'=>'ja','ジェ'=>'je','ジ'=>'ji','ジョ'=>'jo','ジュ'=>'ju','カ'=>'ka', + 'ケ'=>'ke','キ'=>'ki','コ'=>'ko','ク'=>'ku','ラ'=>'la','レ'=>'le','リ'=>'li','ロ'=>'lo', + 'ル'=>'lu','マ'=>'ma','メ'=>'me','ミ'=>'mi','モ'=>'mo','ム'=>'mu','ナ'=>'na','ネ'=>'ne', + 'ニ'=>'ni','ノ'=>'no','ヌ'=>'nu','パ'=>'pa','ペ'=>'pe','ピ'=>'pi','ポ'=>'po','プ'=>'pu', + 'ラ'=>'ra','レ'=>'re','リ'=>'ri','ロ'=>'ro','ル'=>'ru','サ'=>'sa','セ'=>'se','シ'=>'si', + 'ソ'=>'so','ス'=>'su','タ'=>'ta','テ'=>'te','チ'=>'ti','ト'=>'to','ツ'=>'tu','ヴァ'=>'va', + 'ヴェ'=>'ve','ヴィ'=>'vi','ヴォ'=>'vo','ヴ'=>'vu','ワ'=>'wa','ウェ'=>'we','ウィ'=>'wi', + 'ヲ'=>'wo','ヤ'=>'ya','イェ'=>'ye','イ'=>'yi','ヨ'=>'yo','ユ'=>'yu','ザ'=>'za','ゼ'=>'ze', + 'ジ'=>'zi','ゾ'=>'zo','ズ'=>'zu','ビャ'=>'bya','ビェ'=>'bye','ビィ'=>'byi','ビョ'=>'byo', + 'ビュ'=>'byu','チャ'=>'cha','チェ'=>'che','チ'=>'chi','チョ'=>'cho','チュ'=>'chu', + 'チャ'=>'cya','チェ'=>'cye','チィ'=>'cyi','チョ'=>'cyo','チュ'=>'cyu','デャ'=>'dha', + 'デェ'=>'dhe','ディ'=>'dhi','デョ'=>'dho','デュ'=>'dhu','ドァ'=>'dwa','ドェ'=>'dwe', + 'ドィ'=>'dwi','ドォ'=>'dwo','ドゥ'=>'dwu','ヂャ'=>'dya','ヂェ'=>'dye','ヂィ'=>'dyi', + 'ヂョ'=>'dyo','ヂュ'=>'dyu','ヂ'=>'dzi','ファ'=>'fwa','フェ'=>'fwe','フィ'=>'fwi', + 'フォ'=>'fwo','フゥ'=>'fwu','フャ'=>'fya','フェ'=>'fye','フィ'=>'fyi','フョ'=>'fyo', + 'フュ'=>'fyu','ギャ'=>'gya','ギェ'=>'gye','ギィ'=>'gyi','ギョ'=>'gyo','ギュ'=>'gyu', + 'ヒャ'=>'hya','ヒェ'=>'hye','ヒィ'=>'hyi','ヒョ'=>'hyo','ヒュ'=>'hyu','ジャ'=>'jya', + 'ジェ'=>'jye','ジィ'=>'jyi','ジョ'=>'jyo','ジュ'=>'jyu','キャ'=>'kya','キェ'=>'kye', + 'キィ'=>'kyi','キョ'=>'kyo','キュ'=>'kyu','リャ'=>'lya','リェ'=>'lye','リィ'=>'lyi', + 'リョ'=>'lyo','リュ'=>'lyu','ミャ'=>'mya','ミェ'=>'mye','ミィ'=>'myi','ミョ'=>'myo', + 'ミュ'=>'myu','ン'=>'n','ニャ'=>'nya','ニェ'=>'nye','ニィ'=>'nyi','ニョ'=>'nyo', + 'ニュ'=>'nyu','ピャ'=>'pya','ピェ'=>'pye','ピィ'=>'pyi','ピョ'=>'pyo','ピュ'=>'pyu', + 'リャ'=>'rya','リェ'=>'rye','リィ'=>'ryi','リョ'=>'ryo','リュ'=>'ryu','シャ'=>'sha', + 'シェ'=>'she','シ'=>'shi','ショ'=>'sho','シュ'=>'shu','スァ'=>'swa','スェ'=>'swe', + 'スィ'=>'swi','スォ'=>'swo','スゥ'=>'swu','シャ'=>'sya','シェ'=>'sye','シィ'=>'syi', + 'ショ'=>'syo','シュ'=>'syu','テャ'=>'tha','テェ'=>'the','ティ'=>'thi','テョ'=>'tho', + 'テュ'=>'thu','ツャ'=>'tsa','ツェ'=>'tse','ツィ'=>'tsi','ツョ'=>'tso','ツ'=>'tsu', + 'トァ'=>'twa','トェ'=>'twe','トィ'=>'twi','トォ'=>'two','トゥ'=>'twu','チャ'=>'tya', + 'チェ'=>'tye','チィ'=>'tyi','チョ'=>'tyo','チュ'=>'tyu','ヴャ'=>'vya','ヴェ'=>'vye', + 'ヴィ'=>'vyi','ヴョ'=>'vyo','ヴュ'=>'vyu','ウァ'=>'wha','ウェ'=>'whe','ウィ'=>'whi', + 'ウォ'=>'who','ウゥ'=>'whu','ヱ'=>'wye','ヰ'=>'wyi','ジャ'=>'zha','ジェ'=>'zhe', + 'ジィ'=>'zhi','ジョ'=>'zho','ジュ'=>'zhu','ジャ'=>'zya','ジェ'=>'zye','ジィ'=>'zyi', + 'ジョ'=>'zyo','ジュ'=>'zyu', + + // "Greeklish" + 'Γ'=>'G','Δ'=>'E','Θ'=>'Th','Λ'=>'L','Ξ'=>'X','Π'=>'P','Σ'=>'S','Φ'=>'F','Ψ'=>'Ps', + 'γ'=>'g','δ'=>'e','θ'=>'th','λ'=>'l','ξ'=>'x','π'=>'p','σ'=>'s','φ'=>'f','ψ'=>'ps', + + // Thai + 'ก'=>'k','ข'=>'kh','ฃ'=>'kh','ค'=>'kh','ฅ'=>'kh','ฆ'=>'kh','ง'=>'ng','จ'=>'ch', + 'ฉ'=>'ch','ช'=>'ch','ซ'=>'s','ฌ'=>'ch','ญ'=>'y','ฎ'=>'d','ฏ'=>'t','ฐ'=>'th', + 'ฑ'=>'d','ฒ'=>'th','ณ'=>'n','ด'=>'d','ต'=>'t','ถ'=>'th','ท'=>'th','ธ'=>'th', + 'น'=>'n','บ'=>'b','ป'=>'p','ผ'=>'ph','ฝ'=>'f','พ'=>'ph','ฟ'=>'f','ภ'=>'ph', + 'ม'=>'m','ย'=>'y','ร'=>'r','ฤ'=>'rue','ฤๅ'=>'rue','ล'=>'l','ฦ'=>'lue', + 'ฦๅ'=>'lue','ว'=>'w','ศ'=>'s','ษ'=>'s','ส'=>'s','ห'=>'h','ฬ'=>'l','ฮ'=>'h', + 'ะ'=>'a','–ั'=>'a','รร'=>'a','า'=>'a','รร'=>'an','ำ'=>'am','–ิ'=>'i','–ี'=>'i', + '–ึ'=>'ue','–ื'=>'ue','–ุ'=>'u','–ู'=>'u','เะ'=>'e','เ–็'=>'e','เ'=>'e','แะ'=>'ae', + 'แ'=>'ae','โะ'=>'o','โ'=>'o','เาะ'=>'o','อ'=>'o','เอะ'=>'oe','เ–ิ'=>'oe', + 'เอ'=>'oe','เ–ียะ'=>'ia','เ–ีย'=>'ia','เ–ือะ'=>'uea','เ–ือ'=>'uea','–ัวะ'=>'ua', + '–ัว'=>'ua','ว'=>'ua','ใ'=>'ai','ไ'=>'ai','–ัย'=>'ai','ไย'=>'ai','าย'=>'ai', + 'เา'=>'ao','าว'=>'ao','–ุย'=>'ui','โย'=>'oi','อย'=>'oi','เย'=>'oei','เ–ือย'=>'ueai', + 'วย'=>'uai','–ิว'=>'io','เ–็ว'=>'eo','เว'=>'eo','แ–็ว'=>'aeo','แว'=>'aeo', + 'เ–ียว'=>'iao', + + // Korean + 'ㄱ'=>'k','ㅋ'=>'kh','ㄲ'=>'kk','ㄷ'=>'t','ㅌ'=>'th','ㄸ'=>'tt','ㅂ'=>'p', + 'ㅍ'=>'ph','ㅃ'=>'pp','ㅈ'=>'c','ㅊ'=>'ch','ㅉ'=>'cc','ㅅ'=>'s','ㅆ'=>'ss', + 'ㅎ'=>'h','ㅇ'=>'ng','ㄴ'=>'n','ㄹ'=>'l','ㅁ'=>'m', 'ㅏ'=>'a','ㅓ'=>'e','ㅗ'=>'o', + 'ㅜ'=>'wu','ㅡ'=>'u','ㅣ'=>'i','ㅐ'=>'ay','ㅔ'=>'ey','ㅚ'=>'oy','ㅘ'=>'wa','ㅝ'=>'we', + 'ㅟ'=>'wi','ㅙ'=>'way','ㅞ'=>'wey','ㅢ'=>'uy','ㅑ'=>'ya','ㅕ'=>'ye','ㅛ'=>'oy', + 'ㅠ'=>'yu','ㅒ'=>'yay','ㅖ'=>'yey', +); + +//Setup VIM: ex: et ts=2 enc=utf-8 : + diff --git a/include/phpCache/ChangeLog b/include/phpCache/ChangeLog new file mode 100644 index 000000000..23ab95d96 --- /dev/null +++ b/include/phpCache/ChangeLog @@ -0,0 +1,30 @@ +phpCache 1.4: + - added $CACHE_HAS["function"] which tells phpCache what functions it can and cant use. This is for compatibility with php3. + - added cache_purge() and cache_purge_all() + - cache expire variables and cache variables now have the same scope. You can now access normal variables from the cache_expire_if() and vice-versa. + - Backported to PHP3, added cache_output() for outputting to the cache output (since php3 does not have output buffering). + - Storing key/object inside of the cache meta data. For future use. + - Added support for CACHE_MAX_FILENAME_LEN. If a filename length is longer than CACHE_MAX_FILENAME_LEN, an md5 of it will be used instead. This prevents fs max file length from occuring if the url is very long. + +phpCache 1.3: + - A few tweaks here and there. Hi Becca! + - Cache time is now checked for a change. If the cache time was changed, cache expires immediately (now you can get rid of those infinite caches without dynamic expressions or an rm :) ). + - Support for cache dir hashes. Cache is now distributed over multiple dirs. This will speed up sites that have thousands of cache pages. GC was also redone. If the GC probability is hit, it will be run on only ONE cache dir (not the entire structure!). You should use gc.php if you want a complete cleanup, read the header for more information. + - phpCache no longer takes control of the output buffering, it just reads the existing content. It should now be safe to let ob_start() use a different handler without any problems. + - Added KFC (Kentucky Fried Cache). The name is cool eh? It strips out various things that wont be needed once you get phpCache up and running. See KentuckyFriedCache.pl for information. + +phpCache 1.2: + - Added support for conditional expires. See expire_mtime.php and expire_every10s.php for examples. + - Added support for infinite caching (pass a cache time of 0). Thanks to Heath Boutwell for the idea. + +phpCache 1.1: + - Added support for caching headers. cache_header(). + - Added two new demos. thumbnails.php and pager.php. + - Added support for cache_variable() that allows you to + maintain a variable state between the cache and the + uncached versions. See pager.php + - Changed the default object key to use a md5sum of all the + passed data. + +phpCache 1.0: + Intial Release diff --git a/include/phpCache/LICENSE b/include/phpCache/LICENSE new file mode 100644 index 000000000..93d856164 --- /dev/null +++ b/include/phpCache/LICENSE @@ -0,0 +1,227 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + END OF TERMS AND CONDITIONS diff --git a/include/phpCache/README b/include/phpCache/README new file mode 100644 index 000000000..daba6e9d0 --- /dev/null +++ b/include/phpCache/README @@ -0,0 +1,33 @@ +phpCache v1.4 by nathan@0x00.org +http://0x00.org/phpCache/ + +NOTICE: I would like to hear success stories, please email me with any + timing results or whatever. Before and after, duh. + +This is a little caching system for PHP, it allows you to cache blocks of +code on pages. There are four functions you need to worry about, all +sorta documented in phpCache.inc. + +These are the basic functions (please see phpCache.inc for the rest): + +cache_all($cache_time); +// Caches the block of code that follows scoped to everyone +cache_session($cache_time); +// Caches the block of code that follows scoped to the session + +cache($cache_time, $object_name, $object_key); +// This is the primary function, both cache_all() and cache_session() call + this. It allows you to tweak to storage settings so you can scope it + to different things. If you don't understand this, don't use it. + +endcache($store); +// This function *MUST* be called after all the cached data has been output. + It must be called in the block. If $store is TRUE, the data will be + cached. If it is false, it will not be cached. + +Note: All these functions (except endcache()) return 0 if the block needs to be executed. + ie: the cache has expired. + +Please see the scripts in the demo directory for samples. + +-nathan@0x00.org diff --git a/include/phpCache/gc.php b/include/phpCache/gc.php new file mode 100644 index 000000000..2a9f593c9 --- /dev/null +++ b/include/phpCache/gc.php @@ -0,0 +1,27 @@ + + + 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 2 + 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* This script should be run periodically via a cron job or by hand. phpCache *WILL* try to clean up the cache on its own, but it does not do a full pass of the entire cache structure like this does. Running this once a day is recommended. */ + + set_time_limit(0); + include("phpCache.inc"); + $ret=cache_gc(); + print "cache_gc(): $ret\n"; +?> diff --git a/include/phpCache/phpCache.inc.php b/include/phpCache/phpCache.inc.php new file mode 100644 index 000000000..55127f55d --- /dev/null +++ b/include/phpCache/phpCache.inc.php @@ -0,0 +1,535 @@ + + '.1' Bug Fix By Joel Kronenberg + + 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 2 + 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +if (defined('CACHE_DIR') && (CACHE_DIR != '')) { + define('CACHE_ON', 1); /* enable caching */ +} else { + define('CACHE_ON', 0); /* disable caching */ +} +if (!defined('CACHE_DIR')) { + define('CACHE_DIR', ''); +} + + + $CACHE_DEBUG = 0; /* Default: 0 - Turn debugging on/off */ + + define('THIS_CACHE_DIR', CACHE_DIR . '/atutor_cache_' . DB_NAME); + + define('CACHE_GC', .10); /* Default: .10 - Probability of garbage collection */ + define('CACHE_USE_STORAGE_HASH', 0); /* Default: 1 - Use storage hashing. This will increase peformance if you are caching many pages. */ + define('CACHE_STORAGE_CREATED', 0); /* Default: 0 - This is a peformance tweak. If you set this to 1, phpCache will not check if storage structures have been created. Don't change this unles you are *SURE* the cache storage has been created. */ + define('CACHE_MAX_STORAGE_HASH', 23); /* Don't touch this unless you know what you're doing */ + define('CACHE_STORAGE_PERM', 0700); /* Default: 0700 - Default permissions for storage directories. */ + define('CACHE_MAX_FILENAME_LEN', 250); /* How long the cache storage filename can be before it will md5() the entire thing */ + + $CACHE_HAS=array( 'ob_start' => function_exists('ob_start'), + 'realpath' => function_exists('realpath'), + 'crc32' => function_exists('crc32') + ); + + define('CACHE_VERSION', '1.4.1'); + define('CACHE_STORAGE_CHECKFILE', THIS_CACHE_DIR + . '/.phpCache-storage-V' + . CACHE_VERSION + . '-HASH=' + . CACHE_USE_STORAGE_HASH); + + define('CACHE_INFO', 'phpCache v1.4.1 By nathan@0x00.org (.1 Bug Fix By joel.kronenberg@utoronto.ca)'); + + /* This resets the cache state */ + function cache_reset() { + global $cache_pbufferlen, $cache_absfile, $cache_data, $cache_variables, $cache_headers, $cache_expire_cond, $cache_output_buffer; + + cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile); + + $cache_pbufferlen = FALSE; + $cache_absfile = NULL; + $cache_data = array(); + $cache_fp = NULL; + $cache_expire_cond = NULL; + $cache_variables=array(); + $cache_headers=array(); + $cache_output_buffer=''; + + cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile); + } + + /* Used to output to the cache output, should only be needed if you dont have output buffering (PHP3) */ + function cache_output($str) { + global $cache_output_buffer; + if (!$GLOBALS["CACHE_HAS"]['ob_start']) { + $cache_output_buffer.=$str; + } + print $str; + } + + /* Saves a header state between caching */ + function cache_header($header) { + global $cache_headers; + Header($header); + cache_debug('Adding header '.$header); + $cache_headers[]=$header; + } + + /* This is a function used internally by phpCache to evaluate the conditional expiration. This allows the eval() to have its own simulated namespace so it doesnt conflict with any others. */ + function cache_eval_expire($cond, &$vars) { + extract($vars); + $EXPIRE=FALSE; + eval($cond); + return !!$EXPIRE; + } + + /* Call this function before a call to cache() to evaluate a dynamic expiration on cache_expire_variable()'s */ + function cache_expire_if($expr) { + global $cache_expire_cond; + $cache_expire_cond=$expr; + } + + /* Call this function to add a variable to the expire variables store */ + function cache_expire_variable($vn) { + cache_debug("Adding $vn to expire variable store"); + cache_variable($vn); + } + + /* duh ? */ + function cache_debug($s) { + global $CACHE_DEBUG; + if ($CACHE_DEBUG) { + print "Debug: $s
    \n"; + } + } + + /* Saves a variable state between caching */ + function cache_variable($vn) { + global $cache_variables; + cache_debug(__LINE__ . ": Adding $vn to the variable store"); + $cache_variables[] = $vn; + } + + + /* Returns the default key used by the helper functions */ + function cache_default_key() { + global $HTTP_POST_VARS, $HTTP_GET_VARS, $QUERY_STRING; + return md5("POST=" . serialize($HTTP_POST_VARS) . " GET=" . serialize($HTTP_GET_VARS) . "QS=" . $QUERY_STRING); + } + + /* Returns the default object used by the helper functions */ + function cache_default_object() { + global $REQUEST_URI, $SERVER_NAME, $SCRIPT_FILENAME; + if ($GLOBALS["CACHE_HAS"]["realpath"]) { + $sfn=realpath($SCRIPT_FILENAME); + } else { + $sfn=$SCRIPT_FILENAME; + } + $name="http://$SERVER_NAME/$sfn"; + return $name; + } + + /* Caches the current page based on the page name and the GET/POST + variables. All must match or else it will not be fectched + from the cache! */ + function cache_all($cachetime=120) { + $key=cache_default_key(); + $object=cache_default_object(); + return cache($cachetime, $object, $key); + } + + /* Same as cache_all() but it throws the session_id() into + the equation */ + function cache_session($cachetime=120) { + global $HTTP_POST_VARS, $HTTP_GET_VARS; + $key=cache_default_key() . 'SESSIONID=' . session_id(); + $object=cache_default_object(); + return cache($cachetime, $object, $key); + } + + /* Manually purge an item in the cache */ + function cache_purge($object, $key) { + $thefile=cache_storage($object, $key); + //cache_lock($thefile, TRUE); + if (is_file($thefile)) { + $ret=@unlink($thefile); + } + else { + $ret = false; + } + //cache_lock($thefile, FALSE); + return $ret; + } + + /* Manually purge all items in the cache */ + function cache_purge_all() { + return cache_gc(NULL, 1, TRUE); + } + + /* Caches $object based on $key for $cachetime, will return 0 if the + object has expired or the object does not exist. */ + function cache($cachetime, $object, $key=NULL) { + global $cache_pbufferlen, $cache_absfile, $cache_file, $cache_data, $cache_expire_cond; + cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile); + if (!CACHE_ON) { + cache_debug('Not caching, CACHE_ON is off'); + return 0; + } + $curtime=time(); + cache_debug(__LINE__.': Caching based on OBJECT='.$object.' KEY='.$key); + $cache_absfile=cache_storage($object, $key); + cache_debug(__LINE__.': Got cache_storage: '.$cache_absfile); + if (($buff=cache_read($cache_absfile))) { + cache_debug('Opened the cache file'); + $cdata=unserialize($buff); + if (is_array($cdata)) { + $curco = $cdata['cache_object']; + if ($curco!=$cache_absfile) { + cache_debug("Holy shit that is not my cache file! why? got=$curco wanted=$cache_absfile"); + } else { + $expireit = FALSE; + if ($cache_expire_cond) { + $expireit=cache_eval_expire($cache_expire_cond, $cdata['variables']); + } + if ($cdata['cachetime'] != $cachetime) { + cache_debug('Expiring because cachetime changed'); + $expireit=TRUE; + } + if (!$expireit && ($cdata['cachetime']=="0" || $cdata['expire']>=$curtime)) { + $expirein=$cdata['expire']-$curtime+1; + cache_debug('Cache expires in '.$expirein); + if (is_array($cdata['variables'])) { + while (list($k,$v)=each($cdata['variables'])) { + cache_debug("Restoring variable $k to value $v"); + $GLOBALS[$k]=$v; + } + } + if (is_array($cdata['headers'])) { + while(list(,$h)=each($cdata['headers'])) { + cache_debug("Restoring header $h"); + Header("$h"); + } + } + print $cdata['content']; + $ret=$expirein; + if ($cdata['cachetime']=='0') $ret='INFINITE'; + cache_reset(); + return $ret; + } + } + } + } else { + cache_debug(__LINE__.': Failed to open previous cache of '.$cache_absfile); + } + + $oldum = umask(); + umask(0077); + /* readlink() is not supported on win32, changed to is_link */ + if (is_link($cache_absfile)) { + cache_debug("$cache_absfile is a symlink! not caching!"); + $cache_absfile=NULL; + } else { + cache_debug(__LINE__.': not a symlink'); + cache_debug(__LINE__.': Got cache_storage: '.$cache_absfile); + @touch($cache_absfile); + + /* cases probs on win32 */ + //cache_lock($cache_absfile, TRUE); + /* */ + } + umask($oldum); + $cache_data['expire'] = $curtime + $cachetime; + $cache_data['cachetime']= $cachetime; + $cache_data['curtime'] = $curtime; + $cache_data['version'] = CACHE_VERSION; + $cache_data['key'] = $key; + $cache_data['object'] = $object; + + if ($GLOBALS['CACHE_HAS']['ob_start']) { + $cache_pbufferlen = ob_get_length(); + /* If ob_get_length() returns false, output buffering was not on. turn it on. */ + if (cache_iftype($cache_pbufferlen, FALSE)) { + ob_start(); + } + } else { + $cache_pbufferlen=FALSE; + } + cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile); + return 0; + } + + /* This *MUST* be at the end of a cache() block or else the cache + will not be stored! */ + function endcache($store=TRUE, $send_output = TRUE) { + global $cache_pbufferlen, $cache_absfile, $cache_data, $cache_variables, $cache_headers, $cache_ob_handler, $cache_output_buffer; + cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile); + if (!CACHE_ON) { + cache_debug('Not caching, CACHE_ON is off'); + return 0; + } /* else */ + + if ($GLOBALS[CACHE_HAS]['ob_start']) { + $content=ob_get_contents(); + if (cache_iftype($cache_pbufferlen,FALSE)) { + /* Output buffering was off before this, we just need to turn it off again */ + + /* JK's fix */ + if ($send_output) { + ob_end_flush(); + cache_debug(__LINE__.': Content sent. flush()'); + } else { + ob_end_clean(); + cache_debug(__LINE__.': Content ignored. clean()'); + } + cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile); + } else { + /* Output buffering was already on, so get our chunk of data for caching */ + $content=substr($content, $cache_pbufferlen); + } + } else { + $content=$cache_output_buffer; + } + + if (!$store) { + $cache_absfile=NULL; + } + + if ($cache_absfile != NULL) { + $cache_data['content'] = $content; + $variables = array(); + foreach ($cache_variables as $vn) { + //while(list(,$vn)=each($cache_variables)) { + cache_debug(__LINE__ . ': Found variable: '.$vn.''); + if (isset($GLOBALS[$vn])) { + $val=$GLOBALS[$vn]; + cache_debug(__LINE__ . ': Setting variable '.$vn.' to '.$val); + $variables[$vn]=$val; + } + } + $cache_data['cache_object'] = $cache_absfile; + $cache_data['variables'] = $variables; + $cache_data['headers'] = $cache_headers; + $datas = serialize($cache_data); + cache_write($cache_absfile, $datas); + } else { + cache_debug(__LINE__ .': no variables found'); + cache_debug($cache_variables[0]); + cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile); + } + /* casues probs on win32 */ + cache_lock($cache_absfile, FALSE); + /* */ + cache_reset(); + + cache_debug(__LINE__ .': $cache_absfile -> '.$cache_absfile); + cache_debug(__LINE__. ': Caching is done!
    '); + } + + /* Obtain a lock on the cache storage, this can be stripped out + and changed to a different handler like a database or + whatever */ + function cache_lock($file, $open=TRUE) { + static $fp; + + if ($open) { + cache_debug('trying to lock '.$file); + $fp = @fopen($file, 'r'); + if ($fp) { + $ret = @flock($fp, LOCK_SH); /* get a shared lock */ + } + } else { + cache_debug('trying to unlock '.$file); + $ret = @flock($fp, LOCK_UN); + @fclose($fp); + $fp = NULL; + } + return $ret; + } + + /* This is the function that writes out the cache */ + function cache_write($file, $data) { + cache_debug(__LINE__.': Writing cache data to file: '.$file); + + $fp = fopen($file, 'wb+'); + @flock($fp, LOCK_EX); /* get a shared lock */ + if (!$fp) { + cache_debug('Failed to open for write out to '.$file); + return FALSE; + } + @fwrite($fp, $data, strlen($data)); + @flock($fp, LOCK_UN); /* get a shared lock */ + fclose($fp); + + return TRUE; + } + + /* This function reads in the cache, duh */ + function cache_read($file) { + $fp = @fopen($file, 'r'); + if (!$fp) { + cache_debug(__LINE__.': Failed opening file '.realpath($file)); + return NULL; + } + flock($fp, 1); + $buff=''; + while (($tmp=fread($fp, 4096))) { + $buff.=$tmp; + } + fclose($fp); + return $buff; + } + + /* This function is called automatically by phpCache to create the cache directory structure */ + function cache_create_storage() { + $failed = 0; + $failed |= !@mkdir(THIS_CACHE_DIR, CACHE_STORAGE_PERM); + if (CACHE_USE_STORAGE_HASH) { + for ($a=0; $a=CACHE_MAX_FILENAME_LEN) $temp="HUGE." . md5($temp); + $cacheobject = 'phpCache.' . $temp; + + $thedir=THIS_CACHE_DIR . '/'; + + if (CACHE_USE_STORAGE_HASH) { + $chunksize=10; + $ustr=md5($cacheobject); + for ($i=0; $i<3; $i++) { + if ($GLOBALS['CACHE_HAS']['crc32']) { + $thenum=abs(crc32(substr($ustr,$i,4)))%CACHE_MAX_STORAGE_HASH; + } else { + $thenum=substr($ustr, $i, 4); + $thenum=(ord($thenum[0]) . ord($thenum[1]) . ord($thenum[2]) . ord($thenum[3]))%CACHE_MAX_STORAGE_HASH; + } + $thedir.= $thenum . '/'; + } + } + if (CACHE_GC>0) { + $precision=100000; + $r=(mt_rand()%$precision)/$precision; + if ($r<=(CACHE_GC/100)) { + cache_gc($thedir); + } + } + $theloc = $thedir . $cacheobject; + + return $theloc; + } + + /* Cache garbage collection */ + function cache_gc($dir=NULL, $start=1, $purgeall=FALSE) { + static $dirs=0, $files=0, $deleted=0, $ignored=0, $faileddelete=0, $empty=0; + if ($start==1) { + cache_debug("Running GC on $dir"); + if (!function_exists("getcwd")) { + $cwd=substr(`pwd`, 0, -1); + } else { + $cwd=getcwd(); + } + $dirs=$files=$deleted=$ignored=$faileddelete=$empty=0; + } + if (cache_iftype($dir, NULL)) $dir=THIS_CACHE_DIR; + $dp=opendir($dir); + if (!$dp) { + cache_debug("Error opening $dir for cleanup"); + return FALSE; + } + chdir($dir); + $dirs++; + while (!cache_iftype(($de=readdir($dp)),FALSE)) { + if (is_dir($de)) { + if ($de=='.' || $de=='..') continue; + cache_gc($de, 0, $purgeall); + chdir('..'); + continue; + } + + if (preg_match("/^phpCache./i", $de)) { + $files++; + $absfile=$de; + $cachestuff=cache_read($absfile); + $thecache=unserialize($cachestuff); + if (is_array($thecache)) { + if ($purgeall || ($cdata["cachetime"]!="0" && $thecache["expire"]<=time())) { + cache_lock($absfile, TRUE); + if (@unlink($absfile)) { + $deleted++; + cache_debug("$dir Deleted $absfile"); + } else { + $faileddelete++; + cache_debug("$dir Failed to delete $absfile"); + } + cache_lock($absfile, FALSE); + } else { + cache_debug("$dir $absfile expires in " . ($thecache["expire"]-time())); + } + } else { + cache_debug("$dir $absfile is empty, being processed in another process?"); + $empty++; + } + } else { + $ignored++; + } + } + closedir($dp); + if ($start==1) { + $str="$dir GC Processed: $dirs/dirs $files/files $deleted/deleted $ignored/ignored $faileddelete/faileddelete $empty/empty"; + cache_debug($str); + chdir($cwd); + return $str; + } + } + + function cache_iftype($a, $b) { + if (gettype($a)==gettype($b) && $a==$b) return TRUE; + return FALSE; + } + + if (CACHE_ON && !CACHE_STORAGE_CREATED && !@stat(CACHE_STORAGE_CHECKFILE)) { + cache_debug('Creating cache storage'); + cache_create_storage(); + if (!@touch(CACHE_STORAGE_CHECKFILE)) { + global $msg; + + $msg->printErrors('CACHE_DIR_BAD'); + exit; + } + } + + mt_srand(time(NULL)); + cache_reset(); + +?> \ No newline at end of file diff --git a/include/player.swf b/include/player.swf new file mode 100644 index 0000000000000000000000000000000000000000..7e166e8fdb02d3226bac2f3983e9c0965b75fb49 GIT binary patch literal 5260 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 SYnKKFIVA~79{&#g&F8%@-(v6p literal 0 HcmV?d00001 diff --git a/include/securimage/LICENSE.txt b/include/securimage/LICENSE.txt new file mode 100644 index 000000000..9a749e685 --- /dev/null +++ b/include/securimage/LICENSE.txt @@ -0,0 +1,458 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/include/securimage/README.txt b/include/securimage/README.txt new file mode 100644 index 000000000..9711d57d9 --- /dev/null +++ b/include/securimage/README.txt @@ -0,0 +1,57 @@ +NAME: + + Securimage - A PHP class for creating and managing form CAPTCHA images + +VERSION: 1.0.2 + +AUTHOR: + + Drew Phillips + +DOWNLOAD: + + The latest version can always be + found at http://www.phpcaptcha.org + +DOCUMENTATION: + + Online documentation of the class, methods, and variables can + be found at http://www.phpcaptcha.org/Securimage_Docs/ + +REQUIREMENTS: + PHP 4.3.0 + GD 2.0 + FreeType (optional, required for TTF support) + +SYNOPSIS: + + require_once 'securimage.php'; + + $image = new Securimage(); + + $image->show(); + + // Code Validation + + $image = new Securimage(); + if ($image->check($_POST['code']) == true) { + echo "Correct!"; + } else { + echo "Sorry, wrong code."; + } + +DESCRIPTION: + + What is Securimage? + + Securimage is a PHP class that is used to generate and validate CAPTCHA images. + The classes uses an existing PHP session or creates its own if none is found to store the + CAPTCHA code. Variables within the class are used to control the style and display of the image. + The class supports TTF fonts and effects for strengthening the security of the image. + If TTF support is not available, GD fonts can be used as well, but certain options such as + transparent text and angled letters cannot be used. + + +COPYRIGHT: + Copyright (c) 2007 Drew Phillips. All rights reserved. + This software is released under the GNU Lesser General Public License. diff --git a/include/securimage/audio/0.wav b/include/securimage/audio/0.wav new file mode 100644 index 0000000000000000000000000000000000000000..b01b8dd9476a3e50e1fd810c537cdedee0620783 GIT binary patch literal 22158 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 VwsXwz5#ncXtMN8EkNOcMBmQ-bu$g?cG&-*~8hL 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%0A9j*uBlY!q=Uzph3cLAzSjupXBnIirP--oP1Ts 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 ZCeNRnH>~iUWOVVA$yfOd`722t{U4Nq#25eo literal 0 HcmV?d00001 diff --git a/include/securimage/audio/2.wav b/include/securimage/audio/2.wav new file mode 100644 index 0000000000000000000000000000000000000000..66ed235afc4cd20c5c5266f861c84251b865d093 GIT binary patch literal 22158 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/3.wav b/include/securimage/audio/3.wav new file mode 100644 index 0000000000000000000000000000000000000000..45bb85fa4b10be296a2376450baddad5751739cc GIT binary patch literal 22158 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/5.wav b/include/securimage/audio/5.wav new file mode 100644 index 0000000000000000000000000000000000000000..b7654b0f10a39763e197e298f397e3e1eb6d73ce GIT binary patch literal 22158 zcmeEu1$P|D(ri`J^pIGxC0nv&W@ctsGqcysxMpT%W@ct)W=6>tGvn}d)fc`$@Fe4fg6gmHmr_`gJc^CF}qaqO{0tZ9-+LWG#**WtKIueG@n)nyxy-MU zHl!=rLJILv5q`KeEnb0x8SaF&$4P z$$T%XLYDKf+>5+mD>{jY%UakhwI2Okf>2XCWRA@h!Y5eMf3xEjyFz+DF!# zwj&pKMLg3*-kir`uUFEwbSdA;HsL-d5rtGFD|mgjk4&ev_)6Z9oFF^!OjC(SDY?xa zljUS4f5%P4K!@{w%$o+$t-LKCR}yB!Lg%Gx;~xj9FXXH6XKQ!{*-k^~D^iIc(pWE+gN_buym#(0HuPS9XexB4x;V+*vYK zZ65!_s&O5E$y<=Wt3Q$4<`>x%eEJVJ@O&O*vKUqn*U<43>@+(| zo)R;1d@8c36XL5fnaD4(8GHhsX(qkEH?RRblK9b1$f_ayH9LyfdcyXy2fQrZ!V8!m zok+(c%VzLB$jdhP{5#$W>r#?$;8U`<93iFnP%hCp(j9pmlFf;0pyHff#e|mG?c7G#NOn=>d{a?mq#1cM7};|Wch`un=QXeo+1x-@kT;|~ zDf_?pyUGs&9}M{3Jl=u_^DF!i?yoVePkivL+khj3=sU#va>T_?PPvmUz;id@O-VLz z^eA^>2Og4TGzpPoAQut;hk0S##~MLt`AZ4qzHfiMNAhOd|&>m5p7Bce8t!^o*K?BX) z-gG;%@d$G1ENa&d#OYOJS}jzR$z(S&ZUesQG3r$>Age$wA-}wlp}k3U{(%J$4_Y0S z&_+buWp&z;oFx-!H6YP-T+@eiK>Q4)I#mA7z%+^Eu|)or58$JDG&_#n5Xn%w3po=- zx+6FD0s9Kr7Iuk8@u$2P*6?4_3Du$xP_RGA19J64t{f+xBn|ANAN3?RQ5U+>gSe(^~8f$KpX|o3-mukY-8G$xR{&O zCLPE}pzt5;;&)W)uV7aWu%KzUP9A?nuF(u;AT{Yip37gb<7_ba*IUGCUE~_Ybwug{ zs_!SC_#SeHa(0JRr-KOPn-EJK`8XN}ZgK+mP>yG@56p_l*uY|$AODUl_Mzu#Nn!xo z^QX<|Ix?GIBodv=#_*S9DbFNHq$8?MIjryjaJ-6OmyeJWo>&dkcqZ~k*ykHyd=mMB z=qyD>lit9z-#i{yXvhyCMurhT(hPU_0vJ>kwZx8HpGHim>nW&(PY@3i$uD53Oot=p zlJL}ZNkiU`&jU_xA&am=xA1eTc`Vrq^r}bxV>`G=E%XL23{G;LY-Sd$+&X@qH{)H2 zk%r;E2chDg1>gOEbs3H7ItRQ=PruT5@N-!Z$V%*1PqLe~;6A)2ZAm$qh8?-X)5&?% zt0eHmp=2=)qmj73L^c<*LBVWDHo;eSVy8Bn`p;y*c43>h(H2Q%#`~t%u!ThbXK`e<50`gR_?- z-FOff>}M8*zQ9Pk&{5#j`RqPAl|Wo+FG*)E9ziz&UEgEXe&cRdlLFlT4aAQX>|qE{ zY6kGW5N$y=@Dvga45|!H{uJ>u77=?DXx@cQCk=(A{23W3bm5V>`gMK~mGvUxv^=d$ zcat1`4E41xIgD>P3+`M4DEbg}&!4>Ht+6_%`EaoCt}L0`Nl!5EANa0a;GhM(JoczH z*5fDm&~es)Pv#l8?QN|GYaAI)ZD3Ttf%p0N`9ffJDzC*H>^d@V6>o=n zx)@x|pBzW+O5ioLmE!|x0Ud?^u8n@C7imkkkP3+NVAS}o zqyhPfy0jQvqzyem%JBZ^SvsIo$R~SPPsINYv;POCasgGv101O@h=eHQa2e`Hih(INB+vN* zb`6|(D*BsM};`ys7vn z1?-mvfzT-NmJS$6~7%h$Dae0Zh-@p1-eHdH=eRpJO};xa^SEpK6@D* zP7g4}en6+(ku$o0f(KOjYbAhpnE`15d7wgc$TZUYrBP?_yQbkgd!b`NRRnW5hc0>%p5qeF1rJLB&zyl6TEqwAiGLva zcY`6X!Y*|~UwN7_z5}_rhWtcy6$bCkK{nq+1?xbfu~yU2dF|yV$zI+Ck@SeQM30}y z4wJrgIM_fV=Hzc_8!&>Kz{VI}64`%^p5-#Q(hi=`X%*0*)O@zp*f~0Svk;c?A5JkJzil zcL4{_kpW}_T}S7lJ^P4F)vyPz0+l+Pl z%&TJxvji-nFfC1OtOEG*W44S`Bg=rxE_4tT5EVo59nFzT9_VRafy30M!$@IFIUbt!cPts*c4ayOPx1gL$$1WsK$U&N$FOI}A#Ywp=z~c?IG(2lxB>$TT*At4 z#!jV!SA0b+ABM_s0zCaBa<&ONzW$g29K_^gAN~fM2~qEX`qY500RudT>6L<~7{EQy z7k@(Tc0-@sgFyw`_6&QjCy+&v9P`Z;U=vo%znjw^ftL-G!=uGmRw7^Q#M(;Ki z@%CFN0C?P>bszR=i|OM zq57UCmstf&L@u&&V5dLHTylWTLf+0~^RVupwQWGCEYvbR>QWD|-yF;VGf~kZ&|P=p zRoNhR;x7Y223gRR?m<3JC2R4FW-@@svXX2DTZ~%p3;oJa9?fUdwlswa=o_k$65xAv z!5tf7Ml%|}kHXw$3V1+&%-c4i8}0`D+KuYeA60ld_M|+S!2K{usYaK9W5tsIFoy%+ z)5S26+<_@gHPraM{0=yB6*`74Ap>b7GI0n{&%#FWXSfpwp41m8Ukf=OiGDtZuVOPX zUHXY0el}vXAA0m$RvDvK6EHp}31Kyd%xTeoe!wh0(t?Gaf&C1yz3%7)nz%k1ZCu2|Mv!I`oXk zSSw`r2;^~p@L3L4mjPB@2-AR(sG`m27_j+^!~w?2fwbPJLd!{28i=kUo#fLDn#t=S zGp5kWn6H*6b7&jh4cK>>jYa>{m0Y4%c_*;rQs7?q(P5v*PTSFyOH`!Acu!Uo-B={{ zbqMOtL2{A?Ai7%69Q0}dLM@>L>BAOi%UNSy9td&>wdxXIi|T(EakLDxD@;rgO<9P` zL#zinmLRN*j$Q#CZb3Yp!8AC4a7;g!3;Pk5PuURk&AqWoW`2P*7QX&3yFE}-CWB2B zCJpdRr$`&&IGsv*ArGT5yB>sjZmfHY_5oe~Yqp9W#FVBviDq#u185Tn{#ympDNGa4 z^WI|pfyx2M%~)EFcBFSPr-&i<=p=BxkKoV8F?~2kpR5tFh{3HCMmUu+=!nL+7!; ze9`N?&qeQ59)YHYrt^l$y0S`yqzc>ER`(+3C9A2RO>S1^(zMTs4`Q>UD@Er-YjOW2 z*;BkS$Jt&AjZF*vYX_F`^EPe~YH2fERb9*3Pa#GAVaWG-;(swPCunit(?TVD_n1c+ z!gZ(Q(MlV2zcJ3^g1M7%kG_Pml00&o>>Kk+=G4d>n?_QeC$5hViYpgeHEKiTljwgF z7G;0sr#!Y5-dS{VU~$hFVXZG4q#PgodsDPq@O5k_DT#u8wqf(Uo zpHj(C!L0Kt=Y89OS6pYMyoRyQ-IeBSfV(f&Nb}<8^ z{K;Tmc2$=#TlnYKNpPvYsg z*RgG4y<%!aw~jgMe0kB<5jMb>5L1B~U3TlD>8H#M=5b|L>n_Up|4 zY56HU_1~lm(Z~NZ{c|Vc;qObaDOsoG>p@S;lqmna=qc$|wvfc)mL&JhH?rr-D32xH zt9;#sHWc3G?b1tv(b>uMoA%K^@NHV;ZgAHkZ+x#Bla-ZHH~FGE(Adqi!BAgaEuPb= z+Gph+&5Tapn%*E|QQFU>k#V1+>i&N6^VhHXQI*oKOBaext`JnMYVl-U*IZqkN7U%} z8#&iV5B*XP;`iR)w~#Zy$7hy$gEh8GwlP|$8sfXWXkwTvD{yAO_Rp*+g zd0sJhH5?WKU3$xotfOgaX2h@z3*Z>_5%xv$CC?vMsjFav#)94p<&y4y_YX-+!R7 zlJW*qn~U-`!+FoHKCvDX^{Z(cm#tu3R#Mi$oIN>9b0%h#PmGPe8)^D8>CfQEk5LB_ z7uwJJd@jAV>ZS7M{2JQwVz&RP5&0{9sg`SK?k)Lu4Ius@zW$zVR2ymK9Bm1-w-9D} zCIk%(Z5TQysFU|q-937Pd=#tbFPJ}iFZRwhwNT!>$5~$IRLD-uvFER}ov{|r*_-k% zx=i@%pN)P^kC+%0p8T9;1l%ayq1xMWm%aL1hQxLae;ipP^B(JBH2KXcJTD;Kr-fH> zlTMk(PdR#9+dD1`Z9UXLXR**wd*CwfS^8UGT#{H$jrF+a?ezL?o+O{u)?2sa*2z~b zd#quOZPtOg8EFq=+DCYXUyJA-l@)V6enrL};Y^UZ{QathOI_99&3qd1;ODM{Q&xXn z9iKV@BmAFxsUBC%GxV1wCmU$*Z)@awBtP;V92gQ(B>0)%AVZj#%y-c_@+`v?Z^iG2 zS2JS|;g0j6^;&^eP|rTfS;@Y@5}W-jDJ3d3;#$P1-)|#RVn)Ze&+MU$FSfN(N;TgS z_rx+OnZE*lnG=s#JE`G5-Tl}3*YIj!-r$j9cqBjP6YO1W58W~1I*(_84T}vgI^6G? z>8S5IRSkJ`AvF~E%r4{0z3)L()u1e>yj=HGK z(-G-Es>heN_Eu|pJAKah+%R7?R5AQ(=&Ej^#azv7S++7nF>(LW!H%Lg0aP z`)A6uN#L(i^~z5RwaJCD!lP3E3{I9Tm*sw5kG<#ly!2RaOf@w#=#_nBo>OnT=(r_3 zFxChN3Eme}*8iJlRehM;PoAqRHcT`fHoY-9^()n*(puiq{lM|c@!3_tc50JtK6ww* z#JH=Gevu!dt0q)P5|f7|dME#5yXTQttXKITrR*M7YvV-s?|QNQaytl`xqil9maUw8l}80Qg>ZFVq9t7YYfw$Q*uZx zH+2q*n_j9$g)t=$89lQ7 zqlZL1jMe%VR*zKUj%D{Jl}sUC)4k$7I-63AF4Jax4Kdbbus*S_ za1E0C_)aZ+ukhBwyS+??D{33HK&`3H)xA-D4U3I&#^HwNIzq}iraKc{vot;4{nv9Z zv31DJPWc#jIflekNH~$KCF_#E$1hBLmJ=ph1K*WENiOuBmrgGdS22!e>~U|_`+1c2 zI^=WJQ)jXpF6vC;Ev<>8k^QWDoG!%MRH$N*DWI-rDPsq}fh~{H(W#Z@JDDXf5Up5Y`zY{Q?8$1tj?# zFmG1Z=#uph4Pl1s`X7ec#v+Dr{TE$*xfxA&m2=tL3$!S5Qv67Jxu54BO+OdkByw}) z%h>dU$mAZ$3*!Uh-zIrjmw8q#zPp^ERCA9R`Arh^e|p7T$tx_h^z7(U*vnvkZK!On zVJs&fr>~q1EPWhx=qB~K_tC;l3wQB*=~>HoUNsmt7_S&E=;!NI)vc>!dTt!8_Lb-H zv#v_+37BOU6ORj7ytcD=ZgA@F*u8&NMoIA*3Cogerqqo;6W=kz>bChUEV-miRLCT? zSazxCZINS>Yg(sC13cb(5$~JchdeqPM<|$ZxhL2bTC2Gx3Q_t#-d6&u1zhwV>T%H! zU}#}VH@Nlv4Pp8!YJk3h{<+#%w_XmF>@3F}NxBQY#r9GS-qBI4;6>)d1n0N2s24NM7j8*J8mbe zQ`t=VRSb}4h_PC<{eAwJEMscf#8wF%-sRv`|64Xdde`z}MTy&-;og zQ>|jyU?`(+q&`$9sX@AA<+|Edzgy>}v=9c8t-=`5EL5fz(oQRHe`mRr(rncp1N`BShbmSBf?0Hm#kzt<9RZFDo%6B(Z%$NaB#>4{1Hpi=~xK3rhQu*~;bR(Yna4 zkmrRG<9n%qJupA376Sm`S9h;WRKAeruy&P}$Oxs|gP zrj1DYoj5u1RN}PcA1R+x`)2&e*p)rS{lwfh@NSWt{PnMxRqY7x@H_1Zq&#@sdK*TzmBB|H{-4)?fd>}aZB3^uef_R`N%ekreYD|L## zrJq916iND;IwHMl=E1Cn zS)=khQPsO?VAsNt=3efiIa1oCv_{r+@>w};IBBpLP392OSz`@(xx0?NjBUTOFRi4; z8Xp+n7#bM|n>6!CkG1IAn3|(lGqZG|!P7yVC|O(R)> zR@ybzwyj`LcA3nTX=@W7Cf-Q=kw8;6rDmpW%+?pYW!*h?`dj=5dH)jn<;iIgskd@^ zJF=)^TI}WJ^~E#Rtm$tFBi!N6vW^dq^(kbT2&pP z*Xhpb7O26>bX~9^U45ndS2-yL3m2f1abS|Qi0@-V+@bc{xfe4%)9xjGO;i$}Bu-0N zl{!4_b>@ofh6ORgO0N$7p8^JZHD#N#AEpagRSI&nhPt7~DW*X4IMYsJJ$;NY-JNNh zW~u0SNb9QijiKg5<3fX{agDK?v8w5qf$Cl=ht($fmb!eo81iPCPBNS}`WXf&C8fH; zH_W;*QNw$b>aJFfZjOuAHdD51+y+ZuHqb3MP-Ztn=rS;d<0s5Nyc*7F& zFw@CmKZ87E>jvCge8RB)!^&SxyoesUqcxc6pj`V`S{MxxK zvlnIDOiquNWB0|5OZl2PH76v$sC7QS=Mi40T;X)DORP|KsWf|f@q!-gzLKiC^^?sd z&EbaWYBw?8HNk4Iu5`Yn%~YHIoH5=wTV11G(@!_}8{6u)=!&SX^+w}L{UyZ!40|uV z(C3L2t`6y9bMq zJLzlb6@#ijtX4C2H$68@*VoYhQnU20OwWwz@?4=T9VK>DRpTSm31dZlG2JsU1tQJ& zELz*DdElMaQ7zNa*xtx;Jx9x$k@YSuJ)w2%me?x^chj2Y4k>7832=2+zxvfLJl3a< ze7oR!TBnSTmi^io@t0CpH^4C1>%P}T;~uG%(@;>=`rfr)IIEOZx$dg+k7_cU*PD!Y z^$(R*x~}STLz>BAsH;Ae4h!eR$;v0gYEvDwXxrdoGx0oN4;QpY3gq7WSA{| z7wbwp#e7{)!%xF)BUcB?yTxy`C5w06clCB3gSzW2jpY}dGpuLxbF*t_oJcpMCnQaX zUzyZDgJqS{(qxlTMcry_Z9Hu#Z%}pjqy|!Jak*GcN!5MVk2IPM zXLMC0%!akIuDY%@S}^Yo?ad~B+nr(ew9d)*$@wR9UFMMV3rRd-Qd0HwTDcDkPFe-+ zrhco>6(2v(&C))rlwCe&xb==B-u*|Yt@Ado_BH#gGscPMoR@9QoVVNu*-=zssysE+ zHi@R5dW)PXACSMPg0YJJjoME4PPwXVlzK~Rr6kF$8?VnZ^wL*Sc8K%%WA``rG&Y5I z)T(J4F&%v8N_7^qEz0-FZJu={^J7Mvy$@gHq-`Vcxkp(8={rg%up4)cHOssw1ip; ztd9$B=PXDoi+<)->cgxCdA0I8SwdXZb!C0)`^_-_6l&Rb#1`F6XYevFmRh!>RvTS-Jsi|=Ib8Hmt>=QKp(1_6_a#Y z>?6nl`&#jrMcd&OOeM)>2ie9Sl39e>B=diF{OMW zRTsmx`Bn%-cEdb1LPw%n!EqboW$y zdL1{H*7=jO?#b?GSDrJ?dd_x}cUDFi&zh?mzvzxhm&IHvXr-Oi9ZQ`9$xS&%X{hHw z&Ee)4<2hZbvP9`4&66f7&!iQimz=BHtG}V%)EVVeF;w0qJ*H!M33i?p##<=D7qJ!E zIrkpt9Q#P?cFUiF;rWxYD`c>YU{sbJIf;2n{=kCO_TQ|iI^8_nctI&6)FRupw~m+g z6sw1w^1kwC;8A~lxLR6YQh|a*o9*&(HP(94g;HC&t8Tqf^jK<6H*D1{l~*bQ)!9mQ zF@cs8=PS?DFY01d2OiKxw^5o(vl(T(*j%;&Z<-frwY9Q*pXRVro1guHt%CJyL1Ipa z4FB{(S&2C$hh;nRo>~UmBi+TMHTr+lcqvA_C=B3ZTq7LoZF}uISdf^iRMWo&iq}-% z$Ybd))=g{e8tuNvVyIp^ulO39n+tp7nR*(V>&xouDNKGWy%t(XN0bCzj&7D(RiCB% zN4Hh(E_}lq>1}xLGEFmUBcY9$%ci&vJHI%l+VrCQ2K)u_HurYNzJpRj`UbnCSNtUDYowY=>9>O5c{YiCxTf2Y6!{MwpzKWkpr@$4Hp&GX(BEU|WXe&P9Yd1EI-Nu{9} zBN&CX+7*Y%KGJbgn?w_Yn$ltAidxGsRJ|#^$2--5d;kl@zH}wGZdpV7B^7GGGCfI&EM{}$0zOjPZMt&vU#e3)|_kBlgN1U@R zAB^r{fRw3v8J4LxtdRl9gx4y_B;(XHIsT z%;%X2IbOMbxorw+*wUQkDN|n@J%DOIw4eA1A zsG>;;csEvxPhrD>u|34g%3WO}!yDXLfH}xmS3gr{maYmN(GLv~CP-=WXPpkYIYoC) z?jr0X3rR`#!`05|=U$>saOWZWkGN{NC)$4%94NR_@H+Q(W?aU=jBgopvp?sY&#!0u zZf`}0frV%5N2&3;vAU7M8aCDCw#{*bYb&&!+C`z3vQ({Uh*lTqb}DbhOF}yRs-p+K#aJ$j|(pEnIbWYbC&ul4cPljja{A@9=QNC8t&DBL}q<^ZuQoriY zs}tlkq>OtE-U9`=e!5F&+gTap|9fQ%IPVB$lKNTcO|P-_c&q!FbMjtHk)w6T4b_pG zrHw(x&W3vGG---(UYI7_r%YHcHIS`xVR@Q#M+^})@g@n<0@)$$g=@L9y<@O*v&-c; zW*cFN%0HjCJLh@!rfgSc%dC>w|K_m#a+WA-G&`eg)jd^Osz>yG>Hx`?E^yy>Zp7V9 za|dfVyrk4YS|f|V(OJ5ea&hUC)J+(Nce0~MCM_xlDI?WShJ3>dQx#LVs;XSBD18x$ zIA5qB3=?}RuM~^)Qk*5k(`CXU!nGV0#xmT)wdd}`?wW32N3dRZ0r&p&2U5+PK%cp1MkC&@PI*#kEo& z<(+C*D*(&8O7Ebm`i6J1_ozcGsVlFxQ%~tH8iyJu>8dNubfx4@(gC3)?I}r0eW{2z zOu8r@r+LCi+M3c@?UHX%XhbR&2+YL zeROSgEVY%iJkKkecP=L@t8~u3++{h9b9)vHunxDjckaM9gp2)@Cu%jrIel5V9ctfi zZJD+~JK`RtRcDp?X0YI8B9kVe0{KYu#!5!z!aIc3eXA^lu ze~ZWDamq(|y|NT5*HrGP?2v0qi^Y@DQu&lD=v-==+DSJM$hcgYr)#Rzk(Vpp$~W@HXlu{1+$eArgch94T>zn6R<1R-NZ!kWUYN5^ z)`pAMgyCWtrL=C2?zJ*sZX`YcHV~S{rX$l^LJ{ARmwBDgkqNpOEu*+a&={cJVE*)e1abNIyA=(H+>&O~yW2z8*-JbxEtFa)#dX`2 z$MOitN1PyZfp4cW+ox@YLL?m8pkO*t>>{=nw2O-5FKhA$u><~Vthh-`5q{HWY?AxCHi}Jk-*$F(4RWrreX!D##q~1OxB9l))v{4Woco{w=TA3 z+DE&BT&-N|prDN5GlWs%An}R(P}wWT3CZL)y(>JR<1kxD<4x!%s6JFWM#!NL=^bH> zv|OGj{g7qdU8RjO19)*velI)aPx2b6r_fofDEAci(N5x7(Ms(kntr4b^sRMx9y_Z| z(e|_E?4WC-vxn=Vqncx_V+Lx~65C!|Yx_uhRlD9k$bQZd?zrP<>DuA?C055AsIAyn90&E-3E>v`NS9MR6u#@B?~J2TVfX>6CR+u&_5M}%5eo|7VF_2*v14@=jPf>*JalLXBCI9 z%L{DssH=x7-dWeV+gZ-#@7(2R(tSs3hpZlmtd4=g=o??jZfV|7jg+NbNO8EknnNF47?^%o7)Il$QCuPH6rYF_ zrJmvpafBQxB-4L|#eygf7LrgQ(uBL>blQQ>ga&*plq#cnC1_!@oZmI`}wz`5!2n%Rtw;5B{w7KobkJdEW4T)Q6I-IUG!BbQoM-;ld$kT6zeL zpmZ7oPgY;(YbxNqP63aEAEp}=f}NnjyaiPyRL}4_zJiWyE~bYopv+zm|Huw#mY+fW zRf1KA^7l9&go@h*>%EHo2YuNfJ_q_91*$J^Xp1sgbEtBELDzH?pBw=-u$wi5*0DFV zY9mQmdX0>Lx_>o^hXQf|90WDt4%0)yUK0xabyA-D5%KF5(WCtRZw#o@NM&^s64`Wy78IjyE#VY(6y9gP;yA2UkHo=n6S`%9>+E>b@KS~Tt1T&w zT6Gipz#eQ6|3UV{AyS)!!Bg}W@i7(Cpjw`_Be-zt!-HA}D!Chc2U`ixR%fV_ z2SDX_ha87e_YQnosocS8!(}=P`~D9!*!|!@>VZi44PE&~z5@Qcr}()d(6XI@LUSr{ z!S!|#YV23Ql6&~?ZSa9jf{(Bp)X_sRX=noNeG=mB26V**P|?3YRJ4KO@-}M?r(rRk z3TJZ>s9Xz?z3e&jqAf{d_Jj39^c;dde=0r;-6?eR`{AYN0k6(3UX9AM6||F2@!hAP z<7*2?j}2Pl@>u!ybSLzr8T?-=BLj>ygWtxke1g~FI~0X2kd+dgc;o2~_~ndn94?1@ z!2~5>B=dw1b|0LdS*$->4QIj_sQ)dbs&JJ|gMQWyC(8^{8`*Q3t!DYqlGa9@SdV& zci-TDS%~P1rirK(y|^1H_ycgfF2ugA2Cm3tHWc?AXlI&@r#GTjKSV|^gdb`j<{!^- zo!k5!oOJV{=Dvq$O~oo4fWrPX$%We_9Uhi=s6`!YA}vh!X#L@W427HHCGv9z+zA7P zn{*mHe8uQjpzS5p#3sn!Q^=#?|8o`EF;OCLV2;6ltcFiyCMF7r?6fukkyQ)p+Zvy0 zjhc|pJ|iBA!kJMM%(XH66$ZF3T&xScPpDsHAnyV-r=15P7ofuC&rxBTQh_evm*K5U zgtyZG_uEdm+#--`zQCW8v=TJfyMZ46!LMbbG4KqPM20%qPw200_@)!k)V|^xYlz8& zfXulHx9nfle|O;QL99c6pk^Ape@RfGk42uX#k$W0F2=IDP{sPuk~A95k-Bh8#B&*I zQWeV5wTPThe9sp6!$mkk|ARI?fJV@l#%O!;JPji{Z_vA0kyFcpHI&~iJAbVe_J@QmqF$G2-jW%-&ziGp)tIIt;ru6 zOyR(W6Rjt*uRFQLyFt0U7nOP-GH4<>!CGLYEZ_n_T2#Df)bZYY7c!|gyh`!#nSO>- z27(Im1|AR(crx>_7scU4eg(Is5uV6Li1!BI5B-24yYY#Uz^_G6$Pa?b`7E%Y0xD@$ z#QJA^`+n$hBS{(^K9vH3O@UlWIC?Y7&WUS5Tqv%YZ;%5cc3j%V~_K2xRPe0A_Ve(fNzDt zf2QC}hy=KY@1o-xj?Ah}Q{j`D4CVSDI11aq6|)7q6pE{S1jqOUXG~T20jj{mc^bZ~ z)^J^10h1^R-M&8%>Ii=B1pLu&@#!1T=Ep!g^acLhwQ$ia#_HHe5urU*z)BAz(iNN= zvL7zPS%~=Rs6%D2r#<2PN(Z70!Dl1z_b#ll2v3ZG7}!EKkq5zl@eP$TiS(fN!SE8f znYV;z^)tK|!C;Di_%WP$L6FHFc)H=J`BAusj>I1;w1x|)2g%@=@Z5qK*jROP49GSd zb^a_W#u2RlFmR3o#DY^PY+C1AD*@Qh_Y4W7yM)$5dl_m zg%-eRafJ22l~$p@yMW5s5Vd3tbo)NQ#*v8YB=#T9I!QsMq`@h56281qsES+JFnDZQ zf&mB7B-$4zXDk3e@+98$2OPlu;PZcxsV3a2H{k@%1Z#@I=_@@E)#Krhs}G-NM>v)z z@X_=sd;}{|52gWIOP~g-^aQvbysx`G_wV~Tz0#8{S@=k#IAFf?+)%iH1peeijT6|(Ts4WZQ}p2fw=wzxT}s~_nshMieg32fq}ha zUTh>Blo9+CJhUf)Xsu}j>}+vxcMCrGgHMHfzarjm7Diunh1~?Jt$^H)0FKqgE*G#R zaHV{}I)tMlXCv~)!lC7Y`|dOtYc`PMZ`~db1bs_Juw9xDPBs_{lp6tre2ac)1`uZ= z`oA(joF{B8{JDLQgICd2><3acf?s7A;^#Bl0<0Pj^qPRXiUksFW+%Y{)&mJ%;F)k{ z2wd*_fe2f$(r<40c0$fOSSXWjEPVWSG z@Ase{M5B}1zkAh6I?+Y^hDH@$4N>) zIIE*GTsAe~=#&vd;i&CD=^fG^IKLb5?oAJ%I=%$XK17^WhF5AZRxcfAT%1BL&>d$o z?Sf}&3pm_h`jXB@ZY%+!K(dT|8IQYZh-useU_)zMH(v_@6K#!py&ciA7brRzj-(st zvF3tv+=B~!BK)(BX?g7bes&h#Qv+|r?~)=o1;GnEu>u^5*WmIGfq!TMP-+$Fft)M} zJc~jf(-ZaV2kLbffzXlQ1st8%zeLabf?-d>ROco3^8uK4UpNG(q96Y232O`XGXv*Y zG=sP59DG7e;IJ!&s@fT>sT(-K5X0IpJjW+CdVxbA#b zh2_8}dlyydBr8L{Q7fu*IG9CkIP~7*oPn}9E1)r=>mB-=Z(!^DfKPRii$_Va7KD1+ z3RQA8&N!MzY9NZ%;L}5CLpmLkkMgL=$G}D4&o*bVP?Uq&A^4 z3AiICSk6X7TnLT8zZc_;=HF+VfaecJy2O_O0 z&Lvui^H$n}6W+k;xKIuIf!!qGDt&R)SX8d(Y#1wvSgTKD>~W1XD6n#z>Xu0^l z&HN?U&>6&Wdw3yNqg!f^^OXGPZ8)xmqK~+PJ?H?3UyAk%HRdQfmj~GSLFh^i>?u5U zmAQsfMY^M_xq?119XYrN&tDY2!z^1^(cjakx2JY^Arg z{_pIvn!scQXJx92a7KRTEro zDc^-R%1u$>?qUY_3tTM%sK19lgOl?ka+j% zd<5OEgO32KFN4Zk0h8qi*H_ezvZS`qQ>cilR*MKY zMazPTS}jzWz4)~y*1i_blTw68Jm()g!5KJ0C7c$Mh4}i2^HM4Z*WvYiigQn%vRate zXgI597Lf8b<}{f=!pAr@qah~TyMSEXPG!VbnFtL)6C0mb2F^@WGp zi2Dx1c~m9P%{&Jm@kTBD4=0431zyxZXZwR}rXHx5I9-5zW9xABb;2Zc6QZykoaH4d zU^raW8*zGE63#lRf!bh(W4;GGmy?l;>rvr7?Q3zW%qQ}N=7G`H#uTs)R_!SG z(q!DJKTi3G#k^=M9Ij0;^<9m=OaSt9fp0wrdtDqqJpj|FBH%7t!2OHzk?3YrdK7v7 z3Mbi|LzH3GhndG9`T-0Na|6_n{@|$srfC)V0c{MMg|3ZYuetzJrXcQ;F?H(zUU`Nr z2Zv6?yvh@?Tog~w?ordLPyK$Wa z@bPJ6HRj6M@LwLnT<`}tCR8-w9dCFWbR^qZFgt@)9*pUC8fKmYznG+X1+*4VNoJ(QxvmNr{G)RbPLdQTCF#E7PpEs~nZqU@!WjcBDJib!^D zpB^sv;d1Uh=lA>me&6ps=i5deo>BWUMmsa*mwvb??vL==Y5Imr5HW+|8K%FwWG+%L zax?naC#uIO{tX49)(WO%!x&I4B@Z*ZI3QI|WNL^I; z5Zv{GosXMjk=5t`W8=iBQ<`9F5!hQ&&N0i?H~{8EjOVlT-6tvyDq3-gm}m9-@GA_! z_3lM0=yc9V8){M>uqMBp@BUW9V8s{T-6%_{sOqV&C1Y7^QMGbJoP~Wg32JE#=f1P_ z>Fangc5||LfSz;$WZr>c4U%2QsH9%J;TA<$ z;;zUX-=Nl*a40xpgb#4Hg4pTCYpV!))ENsZYCPfBSu3{X9bO%WpyWzBcobuJTj(vL%|u%X4f5>XDXz%H@66f?)6mhjN}r5Ka#H4mjD0& literal 0 HcmV?d00001 diff --git a/include/securimage/audio/6.wav b/include/securimage/audio/6.wav new file mode 100644 index 0000000000000000000000000000000000000000..a8a23ad762b6ab30ae061aad290b94ada30318a4 GIT binary patch literal 22158 zcmdsc(YZJ|hUDN`9oxpn>0-I5WwnECXY0!x@s}g=Nfw%4<%)e^SGYUa;u0Ve%-3e8 zj+4!*)>GGLU+E&jCPu!OUnP^7tFLT7`Bs+7Hu*zSNSqeY=h{t6X%6{IZ)r#QL5u57 zJ#V|}9c^Ii+u2%JpV=C6MiR7%Hj#%?&7?GYbgme4T(9bG-UplU8fVAI4Gq+fc8|@g z;gZgzV>Ea5i5+gA*~aR&=j|)aYlcb@36)*)N<5{woMKGpbg8{#*J>&0s|WS8{H^_M zHN7NFCAZ$x`#M@im>ja0u_elSxvop~SG}P>ah2J&i>{W&I>9E>Z#iP7PBg70N_XgU zo8JVQ96D4dNoQtx#m2a6>JnWgKf0cB<}g{r^^Wp?ptP|0?Kb;^j+Jv#P0nh0U9NQ{ zUL))RyW5PH-Fik^XsA>&#U$K}l?!&I=9VK`PUEzJtA^<-i{%?#hP=<~F=-{je7xNjRZ6%$o z&{48gzmk=5-|nzabe_zztKC=if^^XDbr7rft8}s*ZD(ZLQ&rb$Gu^B2HNQsNpY3-xl-6i?tJN=*yMdgvSwBt0b+_I}> z2$Gy&mWm(O%c0TQRr_mWvr9Ihi967_B3i>tFhk_DB$LKAkMuG>%O{;=AKJCDR=3+r z>Vtk>G8eRmp3~ztjcye$R}a@>8LDrjiTt8Pq`l16fvj|0?IDBoq6{-twSqR%qPj_Q znv+oGKiIO0xEeG{G-EBi;gS@dzkU;^hEv2-Lo{|Z&Nw?`D9j8HNqRGvA&o+Lp z{nA>>$sy*}5$))rMdh{RVvNhMowBk>3TqB6%L@O^H@j?cO(UVUiLNtV@=>3gH?l=j zbI;W_t+v*Wx?XPB8ro96me;z@p0>+1udLUZcAAtlG1gb^OMSBejjw8|nzm9wd`y&D z+ea?TPAO}SNCT-WPxZ0>pvRE;TK!8;YA7;#FN37K?9=tke+TPc0$G*QG5Vvdlfh^} zG4#tDy9<*P@&f&D&nz!<@1r_c&YK&OiSgvu>RhdmnTCG8mPzPdf~_hqwW{PapLC$E zl}_e{*0r+{g2pJB<62|M_c!)poQctwBRFMIaRZ1|w@lxJ&)gXz+@4dE0ck3@$ zY!|Jf`}A)O7C&=9F6j?)OMZ}|5`ix}FF84PyZ∾t5|c>R|m%SJ@8u%I|EL8Do;; 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þzCU}IW1m(FT=LxEE{<`_xU`FqL)XV z%Go3|E3kyOuUoua=gezcZ{3kmE+eVH0mE60MIGxMy?Ju7NN)UcdWLwg0h_iW>F*GO=7 zaV)fdO@ESc(B9b*;JT<9x?S+;7tlWBSwwbJ-RMVARim0l-VOUb#3v}j@1D;(uLtf! zjZtcZid5~Kd7Qf(9**LU&GzdySJtt#*(pQ5&G}N|(~0I+n;(R zocY?v-o;f(5Aj+av@~>EcyiR@+!LZ^=4=+RKS#sB3Vxrxo4S8jkgUfn4+!N#(x;rvgbW+Z$kv(%}hsFla_3z_dz*0nCF$X&KT79iH>s)J1o7J&Pxrqht zNq&O@o(Fk`9gWD9Gc){i#K4Fgp>={E1YGm`*XO;Lzo%*0DI17?RU6j~XESFvyC0Fe z*t#e4e)`aqsN@>SeBo4B}t-);Fc`b+e;eOYas+s%*emcTrLHv$(& z5e!)IVJ?0tr=}155YOeZ@&Ca&Y(Y72}(zY-2bK35- z$=_cjRZi^vx$dU|9}C0}h>LpX_n}C_?_UNc|D1Uz+v&Vwx#(Ld;J<)^IcA3s4O<)@ zn=@~CR`C7+)3=A$Ma#eXglTcqu%5JLS^H#_ux8o|ySC}Omc1TU?^yrvz!o`Phs_EP z4G#``kRvvDcF?524}Rx-o_O{5yyEs9?WT(^;(G5ioj+x-v&CCIY%?Py5{Wa|J96DwpG0j^9JJ zh8Q{0f)@lH@hj%v!1uPtF^?(k=P7FB)C@4wICfd zG`Lvs&cNuvdj1Fel6_}*MS6z0|6&x8NvenW-gU=u(Vl4QXKQQik~uCtSGqH;V(P1J zUy^$yg?=fW*gP>YanaWTshd*L)AwXgavpMZk?-6tdrt8^7%(cZWnf0ofS^19L;U^x zqP=^#WsBOfqrU9uo}JIp$-c=}#o?8m(-z>oKv&jGUo|}4fAvi9+V0)aZ(@KophMv7 z;DN#S1A+sm1)TAl>3h(pveyf@)s`1VK8ccGmU6wzu3>LtZ)#7-dXrf+qdiZtJ*DLL zh_CIx4o)7C)cWi8?*&s|roGE-Z`aPHYQEI&4?KVOy6LkrpmI=Lz>R=)0qg1D?s(mF zpXfG6o>Yrmjhqj%?^t!#NZV_BHAf-mZr2+1Mb!{(jZnAe?oBTuklKpQpa_=Sv7lxeVwg-R==!jS*_FWr=Cuo zkTN}G+V{!do2Pe8kIpz?y>0*M=;yklvn~7F%6k0nv&DC`PYvH_|DS!oc>nFw-|L8b zj9a>~Psf>ET{#@foxeC<+boU;jy;ZduJW$Q&da7dnu`aTW?xHtw}tNK-HN)E_SoUk z$z!Cu#Y1_#aKG)b*?o$8FVEBNi`}-nop^frEx)6jrRsyB4%A6lN#jK_|;mmTY5? zrI_1EW22E^tdqx$nr?~46U$Tgv&K$2!F`fj9m`$IdFgK~HHOPg=nNmxhJHd9Tj3I} z(q?nlW=Djpywk(EmZvV_9OvlinBh!y1UOzh`&#rr} z8RiA2gtR})p5mD8oR@vq;dD)Ru6E`$kGh_k=hRZ=Qe(w*IZFClyex~1K}Jbg-?Gv2 z35RR3Tq{50+8jqujuF-6EA-eb(O+-SWu-+Hmm}mz!_8QPvU$T;WJ#3!zwo^Iqf$Hc)I^E~0wJM>i=nx&L zO6$woL?sns4{Bf$@fF?d4vy4D9nGBz7zgEG>1E6_-WgRbHshXAS*FSnWPPZmzs%B? z<)5;HST2)sgU*RGrCpoNKh$C~-ZWixUGr6WGsd;tY~^a*=R1N?$dHcMSmS-KP}aT68LigK9(kNQOqRGrmzl(6?GsULMb ziu!i-1&=TUKeMci(lPS9Fz}AridFI!caG$);Udel=s+<}KNM@_0Nn)LaR_>Ef}WxB zvHt<+r-em190!ZYqrT~DtX&h_B8hWyTKMZ)DyMia_UL?Sj+%!;I$4wv4MmKI7Q=Kr z`gbLMwyBsV;$%MgK~={g^gwSeBNm9UXuKivKVsOfQgEb#@LPh-A>gYrKo&&lFF)&Y; zM)jVHCl|tMl@yV}P1nT_{H#W3fs43IuhxHRPgbi3nrIu`6^lq#rBs-HhMSkkotvvn zou-3%>ke`u%B++8$&VXQ9>3;>$kLO{k*Nqs`K#WS3%_Hw=V=-tuAIds@sqBqX>Lirj!Js1At z-+HvJ$(sKyI*3-}%@2h;FJ8%c`9gfaH7SA9c2r!!=V~M>vp&CygW|f3k`MXnH6pV< z%4|Q@v7)HYdbidy!~#87D>+BD)?M{@k&Y%^NmUaCWKorzYlq^4sWxzG*Rj| zh_9-p{#72veJD@8d#f*q8~Ur5Dkf?dJ2nc(^gEu+D)E>%t|^MkT(Z0xF7g2 zlU2ZV`cI8R&)$a<`JA6zASTJbWE0$JU+!Z=r*ETIt9xo6UfLM6_AosB={SesxEmXV z1E1fQ)m|!G;*#7T+sl8&M!e=p-0dX)en?-yV_zlaf@+WT1Qhp<_%O9ZFS4vGQP+Z) z%mBlFB9GszSk+EP$%}Z;7WqzJAuA>-C)m{%N77r&Q|0iQdV$!vc)uXruY*L{P2Bf6 zL~W4RigVi^rM(=xQka}9rf=!VaJzW*m|bue&rt4Hi@MUKtLQVtUL2@00l(%fu3|$` z(I_pG^*M5(F@Dhwc}m<7g^Uz(pbJ-s!;Pzm*53q|?5iFix{^7evaSrm@mnRY;%Jsd z`))2CFgb!bz8(dyt~?D{WRRg0R0>2yU+9#esz~%ek>jZsKeP9M8x0x?^8lS!Gqe5VzU>#XQ4 zkE@j`LXQ8j#=^xc!*+71C%)%L5h(8ni!oW|W&$Tp9F~v8b#+_aBZph#!KZ+s`}9aW z#B*YhjFy$*7;QyKDo{Q#T)K(Yssqyl-Bg6Ii=wiX^w(2VQ{sOKSu^?g0>0uwCT;%22NitR61_;gm&Nf=wT$7J+rnz5 zfxS<4U1k(c;HO%_tv^&xqW-X6MJ^rCPw<{I)j+VLk=!Bs8Li|TVZ$#g$xOj>v5S3L zD^^fBE6YdhIQ&4LR7;eD35{m@FJ?pjghNHhhphHX5Mv$n%UhgPA5Y{m4# zeKm#Xn2FmxPQTH2K%v|E1M>!Nsf63~H{A$)N>X=CAD*hL`9#gqc3pz!+@=k6T>Y(o zRh_6lcj%L9hu_01-F8N(D7@<$ zWKy zc&&S=F_!&CKm9Ljc#Hq`XXlDhE!v1!rg=(n#Z7R(^XfDxIo|b-N;`mQfT3VzJUh7) zj#XTi60vHO9Fc~k$QKj8+pGk2zDEqX=eCJvV0^XA)nWgGV zybUv6sbn)B^8l~G^tx2xVZv(oiC|{Kwuqs|ab{Q&sW*Sf5SeD))alw`MByGj0~fQ6 zhdP`|vEH(s@tFyeyQV{>kc%ayHg|I8I>ut=gI?+du&u9J>Y>aaRaO(&(?NQ_u4?qa z$G^u^!g$!<3w2IEca0>|)6E((PaN`BEXm=olFO$s-+^zC_^>;S2ROM`GZNUlcKNdsorgLlz0 zF`gGxdMzu+ooa!;B>L+7)PRR#j0#aR0*R&oG83%ECm^h8d`bcwU322 z6;}Stz#N8ghTtc^m$}si^MbeqHz;VdH^0zbrs#@tn95e~n9BG>7jj?S(0AyHj;Ng~ zOFV^P9EPbUnM1`(BMeVHfy$B&@BGb3C8CEJiSUt7qPZqhVgHzm^lF&vRajjax`WOl zK~-Ubs~6sL2D3hy%#l3OVbrzuL`VQ!cm*BcuVRuaOND%6#=vW==6B+5EY9OmGf~zz zj++)Y{AgpIfc2GY^!Ty4wKkoTNs=1iv%k7)@X1GVkb@ z?Cl?B1M~3@&dDlFA@dH))8JS>s?bVi={ho>P=fiKV@#sFq{EvBJKHMiGmFuO*(NXf zlTIaec2Iqe(FNRwV+>{5WFK|(k(xz+@-yrsSSzNed_m8Nxbk5}F(X2EHb=1|ejtdK z{_5&0))+V7+|TJY>KN?}Z#dU!<`#wEzfmn;=v_v$t>6kyi8U^e`;)gbgf{1uP>~2Hj_d*)nMGq#= zp2HdLnWdR$2$n-+5n{fmd`%Vnh2CK#ymK^DudrrTF_%2YJm!BUlS)+M1@POeyiWn< ztm2sRsY8wJ&(qoUQdxwo-A?B=0&cdRI@%7L^h3+YWGW)w%nz$>thERB=W-H9cP#4l}_}(HY)g2V>v4)#cfsYf4p)F`+J4&odOd|)GPEF?I%mq4OG$3 z=o0qJXrghm_=Wj4G*cLl(>!B-BZ8jjw{%Q znCIQWT<{5b96VpBkE>er1AUY~^?U?eeXRVbooa)c%KTPd6##d!p#mIYrp_O3aZ6R= ziL%Ly+M*bW!3{e65bDG>s{A`x$zHho9`;n+q7D-c7f@hA(Xys%cMzl#cRWC+(^qd2 zZ)BL<3*Rk;!q5ut^AMC>FG|WTVzMsrzYgjH^MK*x(Rt#25L2s%^#gLewXDiBm`qD$ zz(Km{k<5(EW}O^I0BpVoeZ>@w|I>jvsgFKNje*KqYP?9 zcT|?2m?Hdv8hg-t;^6Xwe&~O?`H$&stwe%cBo@dIa;kc-)>GZP@$+}oM_9=rc+qEl zMXgYU=)OwBl$+~~N-^bTz>*izH$9S?C#@!W>Q^A<9z7kjU!mNo0E1vs|AF~m_}L{) zf|U{LR3OuM16Z|I^sX;a!*YXzt57;Vs3*E78GlrS>qOOusm&^4201c_>CBHzU@l;K zw=i1R6DE!O%Po2B&@T4q zSF)b$0e(zn;(IY_%5~jM9)orM3;OvH&$;zvx`g^v_HXD}Q^j3Yd!QJFQZbq?c#znn z;qGwTsW8&t=mk5<*|3h%MD8cGON^CcSfK=V>Lnd~4XXZl(VcxwQPcDs5k+LKW>4Rv zb4AO6awJ{FG4|SDcB6OQMkgYTS?DEenF`a)!qt{%byp_CM~Urfi;S10;r+>?7FuFc zS(Pc#8zAyme9Y0xKxI5ADx%R8=k1n&lQnp{cqV~wF+-iI_AvdKA~vJ@^yaCi^ON(5 z=u~>lP-;~ul`V}Yd<4V)uKyCd$@=}`rd~=%T8l~Dg4Bm-F^g&YOk(#iQ{=O#cNdwE zKLL~YN%?_9rS&6nOEHr>MRgY?jdIkIL#)pR)QMxvQnqFW&j5+ekPT_-*snZ$jgiA>M3UuEX43;6-A$Y z91UQK94aTWx-;nX%Yh>ov;nJrPm~rEJ;?ZN@)^0kSk~g}>2d`XYcd*K8S2cRvKaB_ zM&I;`4CS+@C)k%Kh2j5?d3LLJtq8Yv>()*)0y;D!<*DAxaxAIhPd7Bg3 z6CC+O?|uYL;1V7CAa=MZYw{OJv=er1A$mG6?_Ncor}KJ1)tE`NRg{}Rxd3=(4d!f@ zG6VXYtnEq_xG1Xfd__@tCK7|qd9U77^>;dlOlA7{&rrBZ#`FbQ3do;;d;8YqTQH%g(cOn_09M&HTO z)9B2DL2qAK7hgLs-R95WNq>0%P1T!O{iE>P{;W*ftT1r{}ZD^c-uB=An)-UjDMtR@-*|%)kDa#jWEw<#H7$AVVIAJ z`leLekJO4UYA8=R5#)cOlHnD_M4|u95I10!bUeM{BGB|dkxk#+nOx2SZ+@JG@D}5# zfjZz8{cRXJn|$8?q!{bPQDtIxt{!s^;@gU%P;iOoEl+j zx0~FF<~S0Ke*l?egQaXn<3FS&d9fq7`Y%6zfBunTYN^Np(kg`G9tN zk#1)goUsFU?Fmx-qO#0T-jj1VWLI9ue zb?|a8u{?}ZBtv1mWjT-0Q+(zc=fS!%=mDW}1RYI#I? zzt0(=;V9R)IlDAS#>2ndWhrLjYf@>Y% z9rueBU+{s;gN_U7vZirDtTu}B zHyBJE!=F0-!_znfvusT*`J3+H99Qc~Rmr9YiWSx5CETWyaEE-jS%1SQ{sNC*u+J9u zs4EC~UgXerrr5jEC+6YrH?@~hi#@#s(?7x4g$zzveDPsob}a2%7r|Il-1P@x-vIoElIiPZh>tVB)WK;?{q70d+1CxaZt z>C1Y+l#6jPDHQ!7n@V08RX0@jB2w4uHmKQ;L7g7t+HlTxjnlJr1)QA1L_t&ZC4b?~ z>4G?M-@FWW_;JdwKk;yhjGe=JpH^-8vn1>PoQN4sztUe-kuBv3`b``sILS7+Uq6s* zAiY5v-DwLWQVt<SsRRP(w&oBaqRa`Vz8_Uf5<7*d5Ygb_UoOl$%-|g0eQ@{yh&YygX(4uV3U3Oyc+|Wlsu~|~np3IPZ_s#eP?=wY zur=T_<*DKysGz-31-meZ9|i+>!AXcNVASti^Nn7Kl2J~KBttS`ztd!G@e{quZg$`@ zRl!NUU5iT|&kj6B1CA%#v(+JeRvp2KXdsT_TUEA&e_d7emFhLz}v3*16*fNy(!wI9qJ)?9>G8AJ~;nQGaTvn+$rZidsJd?R~$ zvo9w(O_oRGR6Af64OpF5RP76(`9nI3r<{vDss`wv=)kSo$!^9`*;?os#KdaYVnLpB zn>wNziFNGIFQ8rnPQ45zT5gD{vJ~&K3LT=EbWlO=s_keO74eX(irFd%w29~B)E7|A zj<1`B#+;y!bKd10JF=0is7~iRPF!W5I>4=7p;e6ne;3FOoK`CY+I=;r>$-*yXIt8# z4-VD4;F0Bw6`bmla;GRutr!K~6yV*asb}O#7b0c~u5%0HI_H93u*#dr&xZ7XortY@ zJoioX6;@3)lOEJx7wY3fHCjGVeRNk;&4$Fza88Rg=9KO;GH!-!MpUGdA^kXk)|Qnm zj%w(M)8~X+jnUQUo=mvPV5+H={v=qoM|Wt3K0izx=fAI$iD6*Jb9%s5=%CN2CB0-W znCL0)b5^y%FMPl?Tk0rvUNweMPKLV_#`~y`Cs$ovR=qg6x)i_(qU9jvb{xa1IId0U<+9B^s0)SRrBkR8=hY0HsHQN)z3P%`p*zAyN*VKY5%m(z zbRYjB7dSo$Rx$=ON<=%_Oz+;9>)oQut%0t*la&fXg;q{cO4hS<_N5iXQPY_qsiE>2LrP`NoBw;k89F!*x_CO(utx)ti6fYEk?zpTQAvolk1 znvQrb)pr_uyPiG#g5psQ_oSEZCC11~^lZ<`%Q9RkM304i3$pSI>=hT4n(`GM{`iN} z$W+KT6)nf%@p-^5QrJfuZdV|l@hlWDgJ&*;f!^MJde?QHRYl zbRVmEZg08)lkV;qinBx=@q^QiMj;$$j^fPeN}T^@Fsf5z-YnKKQ13vEtS<(Dk6Wm$ zrBRd(x}sn)haNpmcj8>X=HClbpIvl^qfm=O^;!109`#}zx%NZlZ-Q=A5GEDL?;}u# zVyP(GaZFpnKBDo>_v^N@EQzj( zUN2cdjt0T|flYs~G76XYH@Ke(2kj!e>t<@A>>;DklU9r0d5($1zT#x&U&L>yOrw)n z56eD_HtWW@+GSw=bE--bTAEe?sxQd90QY^WsLU1if+JJF6ts7ol(w8`UCb`jVV}d) zNcdJswI1Y~%qka05gJJ}x;3TNXN&IV6lK|pnAWA@%ZpHb_3+zK_`GEes94psW z?}JMXfFJFnm#a!vP9S$K!|T@aHa_t6Bs$**{M16Y*c#$D7RG)E@2Lqs_)J!4x(qU0 zJYy9ds8*AeubG>OQ$H&C38MB5oz`bQ?x8asY>d1Dzi7@oHzQKkGUIec6<3?dguVFk zH+e=c9P?8kdm+v%_Q#3NqK6ioJTA}M|H1TCFtz(R7}bz_yrc@8z%ehtwI9GS>QQ~a zkuiSsgMGp6*W7&>_wR|iaZE2(o3*wOdtZ6=fp&xx~c_vG`G7&Hd^}vnOpzC07 z?_u?OL7)9pwM(F3sCdhn&;!)(CB$)i(D<}EC<_=JxK|%g^CMTji`w@ub0L0I?L?S` zmuRe3P`75nJ^zHAKPT#b!^>TOgZ~>C{zPp;2`EnnE(2YcgHOB2?grGqq3DTsR3G|# zw;x)ybeGF`m-SS?ACo_8sV|n-ZXs=NRHW69xRRuKf1ht9Z{m3~t(Bve#%S8BB zJfDqFR~5zYI1RTQ02`mkXK+|m47{!(y2n8Bu{> zUdp7~9Qurp^rZ=`OX?@6A=Y?Hr<`WLR=%w2Q@L?inga0vVA^tc*qj+bHI% z!Sr6usa20ruB#CXcz5K1AsVCjT_U4<$@-vF6I7~_;F5&Jl%VVWp|IYhkN=>;K>>&A z49l|94Q;0b8zM8|N*9T?k+^D=i1xqe&<>C{b6^kW;K0pvd)B%Y+GQ=Cr4yap43Oud z`pBnTNPG;JxXJnHZ|v$N+=YE$*-hpRC*qv^M=dMPsoXrQ_iw1#ubD4-qdUMxH~;9f z$-Q5}l;63>zu?Xh9I?8h09`-_IgR{W#XeovE!AA!X99b>4sK8YbrY3V|3}|eMb9L2 zGnEToK7|~sLvQypm{3u!BladcZn^+4$c$BqOiUCj3E)WuOQ zTA+6?C-$GC*K}qoDFC(Q5*p#pV3(Eqb|MQWFlA*yA=yJDO+$yz{X^Lmvx$;&=pdo; zEEpcwP8UkB zgQ}eXYK_#@)gh|E0i3pdV8~azo#Of!$kKu-+ke=vdSqE3^;E!I?t&9JnSm^$*?V~q zhfA|gH_+ss!Ki{@)z|bgbrT=3lwK{r!rt?PaVYz^>LXCg*U8)9P#?J&x9plOhWmdG zw690^QkwNE2VMryo0Xup*5Xr5O3LHtx0hiL2Cl;!^xYV`h!a$ux2g#Iz#F%99}IjF zxGd!|oXsfqcp^&9J+6P2-ue!1Z#%kEe-=Y9`g% z%1lWmd5em+3B7r&IZgG0E$-2caSckLUT)x%hGr0xe{kJi#QzXFSC z*_&}N?do_oiy7uWiXs$4o~_|idnVwoloVIF)>d-)F`op{!?@0MOM`i}$&9>ACdK1~ zPUN#|?Cd~4{50GEI)?n9^%{*|OD?|S(>|^<_3<~ju!FgPt#q!E$wha5VkR{{7xDSC zng|}8S0$(#C+Kp2W#9kO3Fb#?@GiQBFf@z2Oedz1H^rGz`y{iOm3$?t(2*RV_Krmp zN+5=Vnc`lAPF9&U%#FI}$waCLETNay_yrA6i2MWaH3Oxr z2A>z;$<%fM)eIE8i<g^7#`vQn zoy^rsem?!cA_gbO;YNn=E!za5@g^N)a|AlFU(N$c9&E24PiKl|` zX%NiqZDn5LD=1}fy;^(*Pzp-Pd%BAjAnK2po%KJo{)i6SjP<(1XVnZvdG123*n?I$ zQ=Q~9k`n01vze_uq!z+9x8Z&_<#Qt3n67Px#yi#YLH#|zblY*Y51nT@D!>nAtu^}B z5_rdQ_QxR)$Z%bO9doL)%rC4414qEXt`Nn!c%luk!RLZc$6`9|9BZ6_uGWh_csP?I z=ZVmm|2e^>xO=_yaqSzPCHL*}YX`A`PXi!S1dRhZAOfKxJA4uUfblkec4 z&p^ifL`YM9x-D_g8fSJpYk!fqzeIic55Mbo)}km{T{5-pyZ)hQdGa|tA)KV>`#-i( z9AwA^znI8(4#E%fV-9nRS&_QF3szH^DeTia4|;AWENwKZ!9Mi*ckIR#=EX)4H~rC% z3Q;{1ad~euW%m$o;Fxkpm+FTGI0trfjUK-Fj~Nwe+){pa9NAtMOlSie=uMTcL+4>5 z?%MFQR;KkA(>qV0!>UKzZI#VseyZbR9F+W2vs$=3Km3E4u#HYI)S5UWQ{c8P?l>Pl z{1#8ImR_KWGgJIt)rT7vL9gmb9ZJ0b}>gan-1wY>SqCFSWobF>-i*_ z!Mx#o;>TV7M4g<257wQ2^#H1I9J3mG_obu|MYFMDgA$vc?!P#1P;~(_LqtS#b=6{#Z)}1!hQB91f3zB+Hir4s>NDf zrMIoj_vPgit9}6uicn|nzzRpmt;|$@rrOU2d2WGcuUWkw?4X^>kqR3y$WtdZH4^5N zPK3CKItjLT(~kmq{JR~+*uqx?-=X;l8&BlM_n7?YVg<2&8>UN?S9@_#q(=i=Xf-1qNKhw^V_h&F%?Ud5p^52{LO>!`BCRZ+y4gytr>d& literal 0 HcmV?d00001 diff --git a/include/securimage/audio/9.wav b/include/securimage/audio/9.wav new file mode 100644 index 0000000000000000000000000000000000000000..1d828f1aed4ca85ab390e687dab0bfa4910678aa GIT binary patch literal 22158 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/A.wav b/include/securimage/audio/A.wav new file mode 100644 index 0000000000000000000000000000000000000000..c3dcd8a23cd5998b064f7c627ce7738835793944 GIT binary patch literal 22158 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≧Oed5jO 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/B.wav b/include/securimage/audio/B.wav new file mode 100644 index 0000000000000000000000000000000000000000..9f380c286f427a472da1dd42f03201cad10af0ca GIT binary patch literal 22158 zcmXAx1$6X!)y5g{%shl*BiqN8?5ypiQaCw|^g?Nn8jqNb@gs)5)k3Tr;vD0NBhQa9B+ z)nBy{4z*TX6H`0;YOMZK zr$hyWo^iE(<@cq86szR-d>^5q6&~uQ;A}}Sf?r?-JYz_sd7|H^+FvK9-^W! zidMXC72YC2WeT%)L+w(x)i8AktvymRM2u=Iqh)|N!nziSTH-j8EGGJ>Z+tpbi$J0i z)nQdy%vWI|O}VKkR{ED}E?y&_gUZ2mm#a%^3wztFW~nKnmDsGhign_F8mNl0f_9>n z79@75;bN~iF2<-+mfqq%5}70wm~wc}#7(nLA2QOy_2L|b)CIptRwpe<5K zvW~W$osJfp#6|X=j&!bwhT?|2rrwE%+_$q>rs`wohg71PCccQLSj=KENIVyBgqxTr zenr1?)iZ3isq$4lL?I;Ejyuogy#O(bH5SB^5QDJJyYdRwyOs5Nhz6p7_*W!hfeTsZK6O;}7qwJDwDwk6#NYVG8MG^9 zf_jW~_h;4h#A)OmC|auou~bZ#pH+zHsj3Mt;VayVur1hKYKtB0`i`zwmRJIs`HllEqbf z@mP&k$?6C4TqX7+!$i?ST*Ko%MT*?28j9OUFH{^+dND~%6U*_l`QmR?LCwd?x{Icw zvx*dXYAo`)AwxwOREBQkGCH_@^ zi*e!(xN?R2w#U!vsq(^trbckT8u(R1d_}`MCgFAGv7#aD*a&7!6@}F{^*0g<6os(7 zZK4^woQA#(!XdImBDT~^ZIZ=7o@c72D9;_rVk^T%5LYnZk$B#An=qSoy1-CfLz=Ib0Oc z2B-q8It>|kiCna}RF+ZekY) zhJG#*Y4W4rg5smfC8(7`|HIX5_G$sQckoT_sdk9A+H3U$%g!%8AglJOq&8IO@xNWm23#{D2J2CQaJZz7MAbQ*aM?AI4Vzzcp*_@5US0vDYHP;{x zwnf&9iBDa*$8Oa|%|?6q#X`J(ED`cgn8Q2b2c3H&6Q zpOI+Qj_+L;AGw}GD7>u#vg#&=;6)8Zt_&0d#b{7zgM6n7i(*8=OeIx!bxlNQbFk~p z>b1yIT_}CvzI0G|jEoQ4_$kvSAUKYNZ72ojH3Zk#G z?0cM;C1-5MhMR3tJ6?S92iufajq%G1P{ELBRZQA!pB(|!G-!v{HB}i{_5rZU zOBhrncJUXu<|)pKZb<6`$X5f~zJpw@sdFHCNA&@_Y^V)_BfLdscktJx;*#pD?&15x zu!f85Y@#?US_+9gblC1TIAmW?b-ri}lGS4kf57|ty5b#suOUXmt;UIPkxxueH?fJT zXnus)i5_9N>eElj?uR>c6D`2|FZ>*<&WOF*P?(jwij&1)DJO^sLzKU$jRzGZ0>-m~ zrEuj1Sk5-|+krc7hQ+*4N!a>J^&3`rgw>8#E8%t@Sw|feLacv;jAGeYymA+HvBcZ* z61;7eN+K$~z-G(i3w_i(*$R|+hNi}dOm^BwbXA^6GEVjZ(XDWpf}#l#@_*`oYO>gk zmrY{lIpFsSxbtGJVS|e;QKyt2@5~12Z{dv=QA@i9dzy(ImLb+vf~A(#bfC#6^&4FD z8L`6wFF39y5XsWe*ar0yoah9qp2vsRqLU@~*aT4Rp|Z;ta<)9H3TaPNf~qU_syW;@ z2HmI1--s@!GQhZZp`}H{=$?wc})2c3M+ZhrxG~dBoyh z;ov4Rj|*rm8&01h8-q+;!RAjO@l16DNjBkAd%@J_vNCAimiVS%BR^vY_*Q}#c$_

    TkXjxHs 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

    |+nw9gZ&7Q^y;CoChG& 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/D.wav b/include/securimage/audio/D.wav new file mode 100644 index 0000000000000000000000000000000000000000..66ee3a1b5e660e4f0ae3461f702ca182d169f55c GIT binary patch literal 22158 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≻^0Zbnqpu~ildJ~66^0X|pD-lmM8+&PsrA3kL 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/F.wav b/include/securimage/audio/F.wav new file mode 100644 index 0000000000000000000000000000000000000000..4046176db4b66a40c9b4040c86684f836118b360 GIT binary patch literal 22158 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 A6951J literal 0 HcmV?d00001 diff --git a/include/securimage/audio/G.wav b/include/securimage/audio/G.wav new file mode 100644 index 0000000000000000000000000000000000000000..8700179f3d19e4130ab883edd6960a1a75ec2e33 GIT binary patch literal 22158 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 DJT0ji literal 0 HcmV?d00001 diff --git a/include/securimage/audio/J.wav b/include/securimage/audio/J.wav new file mode 100644 index 0000000000000000000000000000000000000000..5fecfaeb716360e760efe353198a678450b05071 GIT binary patch literal 22158 zcmd>GgP$bH^UW-_y9aCA#;tAJwr$(CZQFaRTieFiJ<~JY)2hn+z5V_hzuV82KArR|L;%#U;d$l z^S@Tmco@k-d|V}d9?ipvjp&^57#`#@@#1%{xPi1Ldw5H-gM8)h@Vy6oH7`MAT(g&K zAmd4Oat_}~ASwI?+f7U~gxBVAd@1Ql?~pn?2hU5!k$5tb+~ga1VX~fl;NN&au=F{=4Xg-qMC1uHYK7dQ4Dk(vVl1%R8srYUZeqR8e)xu}<_yyjWWbm>W%?n-# zPpVFKl5AuQ8AQzFB#+_>DM0Kbov$P#cvtS{i@8Y7@HZHL7QT{k7Rh^%xnwq(%B$c` zM|m~Aj2P(@GLd{Il}Rd}!Ap~Uq!)K{2Q0E4v($JT$wwwKH)}$U<5?B)+-m$P&q+#? z`XnpaL*|i1{02XazvU&r`3Z6r<5`NiS@>}Nk{#q%$Trf3Tq3Q=H$E8Sok?1dXCyya z%Rgg`(L9+SCCZ9<-=CqNVB>!OtLbC>wgw$Wp5fX5 z;M0+0E6>DJ!pKNon4c#9lEM5Hze-M#PQcFv+`B%#&qr_*X~!F36i$48fh6(|dsfalhBCq*wU^9((!N22yqTHl4FG&uQ`Me813$L2U0^S7wZAluF z8~hWiO|noQ@5L(+JDmeNGJc8wLvoS6QRgsR3+f09Sp;7T{|tIe_sM;X@hc)8Xsxv=P0)>i`8ifws%| z%)=M*TqO8Euhu6k_#$ArHQ!18q5H`-pkNzGXU|wmQiCM%BvyjwrX}e;cp#H4=52y`ZI312-SPskQT z{5@{RT^hj#TgfIqkfb68GDseJfT&~{?}$jw3#9er$N4YB=@Z@%w)FC%WItJs>n;E{ zKpeMXwi7Xm4`7%cyff)cj&n23Mdq-v{0a~o#da{pKk_`lT4i8mEuTQT({H$AT|{vN zSw!CO^@#BiB%bdF0(|Tx8wVd|Ar0tQVnGCrB@ak>+;uiaR1_Ah!B_G&gwekuUl4KMxPad-1}dwNGh{43#YEl|*659>m;fGG2&69qhVGG2 z@{pe)g-LZFYZqAh4|kKdcwQu*&o1+0qz~@}&P?UtKUk>(=>b1ACcE(c%0Se1AjS&> zePQ4DHn3I~*dmkfBOiG>e}vf{0q$Di`<21DKf&O8$wF}K3Hb97DT6p9!1`D|nxBK^ z`>_FhH0e(IWArs)|8_hN5Y++kw3KXP3Ymht^d-~aud+bdDR@)@`@aR>Hv(d6?4mrZn;b!@Ly~Z*+@nK`A1>Xqu~0F;E=iT@r{v4xj z&FApbd@M=+Uq(1X+9T8Up$m{@76WBp@RU@H?=-OXlb$8-SO=cQFJKNO`3cPNKJUy+ z&}KA+T?InRf;C!zkDCw&SRyBxg(t=E?qnf9&W<4NnsW zd@?6g=P~SG+_Mz$dysF1ZC0|d}08f_d;eH!iNE?$4C@B zHUgQD!cP0~Nm=l415^inkgE=WNva{=-^A?4!5S@)IgjziyaKOB&ZCa7BcuHbD|EzY z`ypog;+{=_GIg8jl&M%RwKz$X`3SR7wNXP+uW(Nc6q#YRYJ2Jf+O!5=C)B&VN z@Yxtg2jDCpYKR^D7g!=6(3qVUL5$7De80i2F>Dngybr313#cD1@~iAAVs{GPLcRiN z(_y!HxausjQeSxV0>6$7GMvoh#mOA7#w?(}5%B(uALhk~flfed2fz|r*>AAgA=VE5 z_mfiO7rzKr$%DB6!%wh+K*By`fDf2UZ~XQl*fNtR@H@bAJN_?eO4^{x>Ogk$SYDd{ zVQmmEb9p}=$qFGm)yG^ZP@M-eI0H;X;7&bwB0J1)lARcPH`uX1s)9RUpoc68@jz&2 z+61}p7NR^q$<9S&w57Zd?S#MI0k6yg;%kr{_x+<3 z{^)Nd#~%U%CZf>nV9^D<9~np{Ay=fp59@%N&+r@tV{G9~$vI@=sfha9u*V?Ykh~_{ zz@*iXNtctGCi4ZQyk`1r|R-r~(rJ&r_V>bw|`K8busUMdf^I7&*w z%fArkk-Rw&-WB*Sh#dGItXhI6qB6XVtZ5}B=?XrId&x^a6&C4+YN!{vhfI_MHC`lq z6$XYF%v`9Xb_4ax5Cxrqg83vCmF5^y4;VcGYfr@WP5EO~mNveIOu_w65sftjk{2Q7 z`(kEI;6)$XiFwY1O?F@ocYtxmF7V%ond7J+mgAZ(WB{p2Eo3u1(VeV?@2&8l4|QM> zq5zjMs1=FBEy*+%kDA?%nMLzH_$&oH)CiUNNYwt9fSnn*_am^mkEL=0 zeT|58!DmzP*%@$KBd~K9#NP+hA+f;UFl2Kh$wzb2li-JjETSVUvL`fm?5lBA zZ2(qF;p31AyW+aHs5XZoqSvARdBuOA1{scw*Bd@xk7w*9@5m@r7$bRWjHe0sdH!tUT6s$d_`5%n|CEY@zl1w zAc=x679o$dfd|5QIj}=I;?x0*uR>*Z6LsA@#7=j3e*h^0JMBf@io(pxQ33VnOp=}M z1h4Nw)vJ>082dg{vHeI#aAaY+mM#Rh1%UV<&&B^T{Se^(5VC3}A4CdKFHJzDvX)c< zSAGH#x+BK2;cw$%#R15)>rgv92G8#Su7;wjV1M}ybya{5LiJMwe(C@_FF}2s3pLJl zWS?@Q=u0wUHqW$Tk0ww-`lh z@*Xu%4{&{JRQau8gZp5$53B~_zBuytW^msvWS29D>f^Y!I6AKEsJo)z`yjmY0Z&~F zmY#vA7>?c~Ke~?J;QgWS$5G7fDj4Snc+3F=WC0U}(>Ca-SMz7AHS*OjUXbqoUp<{2 zjQIhPeg%A)9a&^8Y$Vfdd^7$(0{9Q2y7|EFBkODi1{%O-qcNg=$h@U!6e8I}WYj2` z@M{kuAqUn#7uy0%^@bOrBgrZ-9%LjA!{FujD1?lg3M9@xiSpb3c$?YS`?KrD-0fK3I2_R*MFcs%)<|Wn?J%Q zXSkhS1&>z)1C%5-jHW-TgtxduIFRxSp0lDi$$^e%G_DGZzP)LX8q_#@;fS{3ajKojQ3l)Zu->9t%kT*8jU+@QYAd7`Qo4kCR%= zMo;cQvuK3&fsB^A2RHC`;x5)$7)d7w6}L&qCGP^N+0NV|5afLqGB)EqU97< z(K*49f<>6Bha)123uQ@$K3_1x? zFY7Jj(BIJM;x}z6y+q4{n@4#2pEe6*@|gp`ubX-C*NIaF_@jg$K8*R_$t z71CYLNeps|o)Ijh6y!Pe+~PDAC)}j-wTC2;bY>hl-=;mFY0?BOG`LyHC+^hl@&`go zo{dcsvy$n-qU4EmneEalimi|(%j+)g5kz&t}op>6OP8!NbIEp${xXl-_pd&P~YL2{GZbiPm~aG!q_l7cl@clif< zpgk97GB@mdNq)xr`%m&vxgU)Vw4mdZ^TBZ%QGW3Lnv1j-n@9!d5TTn`ofn|LDTlqU zlXAkr;QU})=_u8LxAZDP0@)diW$)-dzDi%sc zH?ZqI8*Qi#)~@)Ai*xA?T2H8_b>>Ux48BQ<^~UwCEt9y>r@3jzHAjS#D{`RH7BvTyVS@5JoFOFET>lL10Ly%hS%9a>G2P1qow z^FIyfhIe$4{~fI&o!3rlPla2&zW)JTBRu!tC1=Ft{FZ((I93RV?X+ilPbHad3iJ}X zlPIksjY0=^PIJ-SQWO2E-a+bzE+PolyDgn!gLz414|$|NAnS$MyaPQU)eSb{Ny?t! zN&PB)%q|k2UJ+~OGr)c)P^Z+=O3_ln&|sQ2Ntnt8YKL^Az8N`oZ%{y9xx-vk;EnYz z(sEKrPZ0K?0$wZ2^etDVFG4o4zO+|-B}@>Xie90#)IyvgM9Gp^mfEHJs97sZBk6CR zMhhad%A~fQm6~}AUzm0~_&js0KgM@1LwEZ#uX~Gl)bx?w4#DsKs)3o>BCRBwENl_} z72YXZ)YFF1=5Ql7q!|a8tmaGVbT!eqSxplM$lv8I!gC=gcNT||E}~beE*2+ugpZ0S zJR+e|H(>($xdkL9IKdwiY~fYhS2CugM`i3yshOBNRZrcOI>T8$t%~=M|B&CK4J0A* zO~Vo8u-ezw*-^=sXw7enwLY?}Fm*CEQq4*Qc@GtNd$t0-oJ}tKsC!j7HLa(sX!EM0~+QL9y|4MJ4jG1ZA(mZLwlrl-Rl3F@{rXO|v zarO6g*5g@z-c_8h4mR#E=dj$jRJIol$!||KFSL}i-Y~6Jm&z8&Lx=E>dI>#R8_Y-2 zyreh{ljcbeq>kz=LkUBQVWHYj-DP+o*AvSL3SXtI2!v+(GD@fCOnH?2Z*s{vca$%x zRP2z%Vo7pRgo_4xYw>J_WHDB=+_fEbB!tWeYnWwnc#n{IcE4?bnaMkZg<=z7oW3{M zJNPDeL~F0xP>T!}M#|IFwZ<~mVYaT;>DFkAXvr{jGnP>U;z2TqH4IMld~tnC&7V{` zu2H-{zC!etKm8(d#Vkw6n#7#TGdl$vvL@0P<74w0`-;%*VL!qrho1=R6sDY%Y5n`w{zR`zHGW`z>=WW2RbLT1l6( zV}Z2H%I?tgM^0Pf$JmW=D`F-^tc{5IGb$oa!Z2q@n%8|Ocu%@+nQrCgH2bS8BeT`W z(ljg~G{L^zywEgFT}p4U=laB8u0YK|Yk$MQZA~SQg(z{Ry45_|mful4R15nRa@*d? ze$HCg6sC@r7YJqeVy%jQjoY7kB{e4bS4?s&OQ;+(@lX7pB9Von4T<|w3c6hWK4g`A z+_=v?DvyX!?uUr4PEBgZc8%NRw_wu+Je{BO9lt~_hzPN%yD^A)Wq(I#gqHSbo>(@H9Mkq z#FF^JDV@`b`{u9|@v14O?QqEH@c2+3ni@9KG12NbrK|a+6+DZ63y8eq9};*Ouxr!U zXljy%8~(HU?H?Su!qUPghHnd99K!8C&Fu}_cb0d1D{I^fT|>pq@-(b*$u zY*K7;g}9Ty5B$FSd&chz(btkAQir+E(95Px)}D^?;U}{9%z7tuRA?=m+0;ZGEZo&- zpr3!Zx1x8B|3k2qUX?q=a)#!{D2p}pSLm~_fmu$3VXz+2Eg!Zj^PyTU(HDpy*NRrrC2otYL&y_C5(&i49=hj0uyDgiwt7Wh$ ztHCL+pkuYczI+)AQ>rJtmu~b48zw`|8uB24YKjxSfa7utU~3 z!*4l6xS<#IZ^*pl&F-%fIH3(D|4PKT)biNY&c4j{&2rV`S5Hc-#ln(JS|M#xyoT!L zW!A^GMYdA5GPZu!e&!>FzRDo!KgyXe_{*D;uBGmAZcV5WyCz{*Lc^H7(Klj0#Y~Uf z9oOHxmkgvqWt%-Syh~V;)oZ$_M3ISFSD)AO#M9T?G{7`3NtXPEY_>Lz>W+o>qSpV6 z+m!~=NP*A^bglSFK4FM753&7cukLtbpKH5fsbCtb&XIqJe`r766Sd}Vf581HZJx7V zV%GRcN#B#o#pR5i9dwZ)0b6w_Yo0Q*8mtmC15qjiBfpE0}A zT^bJ0jOOFD*@50(b4G60s?=#opAzS!R8O8C-zK(m%*E(ykq={5W?bV%r2WSFjvt{r zLdQ6cnf?>H>W2bPGCO8`cB9x2Uf^jGGs?Ejj?W=2LR#5mQz_-HFo!o`2l!^1MRKdm z+`v91q-yA~km>d*mI9`LI#sSOYV;-E7=PFA2Jd*wWQXc|-H+_$%0cqeA3~%hXL$S2VV1(i`pQ(^Ik3zZmU+Yz>W|Wr`5keT;j!ha{k5aJ zqpWR&X@@da>_;T@c;o0P@siru(#rnP@h#+gNSLFGbsm`bp)^s96LO#rpsbZXE+Be# zq{XG(OI4C1lIuG|lV-)+?cyr?Ru8 zqtI7gZ=&}3A^Sp)gboN9Xq#*DEB_**@(34b2l^Us7u^H!U2nD`27ps`prpGMor#Z0|y8yy`&Na)m% zL$;}=-0E1ly(EZ3=oYLlbfiYs*k3+#u*aXVIdywVleDa^&nd$bF2(MR`4+V=;$Vy? zBRe@E?l4XYDIZqB5pQ0vG~nO-zr1}t&D;mQPqh%*Q2t_kXEiuZI0iaK+7_9P$?0?n zzpD3Pr%4%cfLh;-L&O(&%BWF1??KJja`p=2@aw~TQ-44D&BE#!fHzU78tqI_A5 z6&}(zSn;h(8ncbTRA0%=Soiq!+0GiN329ZFdlHKzSmU!qulp@VmPqrkl|lnKV5M1V zh9{Z-NO$NT{ju*zMs)fX?{)0}KPgl+?6ekkWOq<|aoZTnMKz1?l%3bVu$LsKFhY(t z3HAuby^#E&4MTd{>Y8Gek=-CUPz8f zC>k>^YH?KY=vU7CEKKYu7qRUQtL|82>ZR5ZcLZm8i+itm?qu!_79bnMEQaxxXSRv< z!giZ&u6dSPMl6H1Y^T#ehMRn$^sGE2=!=h z);V~}=k%0zmrB3q%$`~%tx*b1{*st8?q2kW$Tv|HT*>-No-Q@E=fwyHT6!8@(hAx< zf2+*B?$+K@!7BWz@LBC(Ic4i=x7!!lN?I1GmBe;rI6KGMl6SO}oMEhLt!p0=;t6Tx zsAgMfZfDpfZxn|L3u!db@V@LdE3eh^+r3KW{)}I#KT^B6<~fTbnG<)$FNsy78buCE ziPOr{U$m_GNm!fEYvx5tq>z(O4*d4CbKl9V8_dZo2$PfeXXsp)ny)J*oaQ$ zJoB*OF$#WsQ;(vU|R<&a}PS*s&wDwM{o(mg^E8tmE6@IhmR0-;5Qm z637P?tkrEtY~eP|(#`Z#UP-HS0dG3;ku^eR<&?3bWxVZ~eY1VEZHZ;M@tSfF-CUe7 z9nUW>ETSs8thWpd&kSVD%?M9FmpV4JZQ8rkP05quZ^cfGtsZkZezxZ-i4xW-G4}AV z6SfJ)d(t_Qh*2+ahk6$H53uXBm%Q9m+&0dB*j~rB-aN|iQe1!vFN{CKKCCn$U8!K+ zXbrbFcD%ATv@JJ(GR#&SQX|1ns|$04mEv=H8>_pe^(tW5(Vl>7kTcZvAgxEzkc1ma z2a?XlU5n}z`@r3dOr}}nKDM&q$@T(x$in{2;kYh|rwo^H%83$!+Wp${V=!d%)~E@qr-zF^zw(Cj&F zCoIiO-<23?jyQ+L6N7L-m?^#xKhZ??Jb1`=#*>htrOk5YNUfLZNhy;&FX2|~oS4@3RR`F=hE=!UH8xSR(IG*bzmq4Md!uiP=4Us=j>gG0-9a43ZQZO7O(E(6 zAs_DmcAAVmbql0Th7IO;YdiaCdjs1UOAl1?t)x5XxS!MELMd^Z_)8ihz9SZv5UAv> zm7Y7TRN9%;dTB>fE2MZ5rpIqc&|;@Y4vnwle=J-VHmi~L=+MbFVwflO!z$V_WY^K2 zKK{J=WL`;JX}E4lv1Yetu~{u4=J85t`T*~UAG7zYflxz!q`J);tSxMB?Tc;gtd~q@ zl>}+MpwW0*m(CL{(tNR}xSxLHO|*W#Yo4R&TT}Ivmd{QWVDA;*DO1dv<%DgXqlNu~<$?Kxu@pL__d->yRCEzj#VS&E*)GS@wQRli&R4^o zk+wATth2rIR_Yz+`^3zIF^Rq63P&4a9ce`0O`nP{%zZ+ZIi6Z>tJj2q>_y<4mwFaw zj?hl)9r$2ng=wESby}J z#35cc-#}_eovbCo2G}IBO~>pN=}d%U%hd4pP5 zoJGfBb^jQfMt+D@)OUvGrc|rPmT4VsZe=QIWO65Ij2J0=!s>`m-YPecE6RPu8FVkJ z=F_Pbs=;#*4EftM9OhKuR zULX+WGkCKEa%<}}LZj5%W@gG`Iby$PYiG`52$Oo#>)7pa3AJqvfyi|Yk4$f@lO1;Z zbn9!2)ihPTEr&=c7*A~>Q~V5ME9rkKze%m>$KC(3r>^Ml6WI_ zPi$)J+}JFMvoh<_hl1VE#!)^bXggxwqmC7)viQI*Z>;B-uR-v&Hk?jS_831}GHpZc z9_u#~I1Q*u!mhmS*!_`7DoT&k%f_;nL$>jDpDmZAg7K;vu3QmI3wMPZh>9xG6{(Gs zU;IpOk>8rvYxmqrZR(b9O@~d5~BM`vX2XHAEQs@A@N%oPubd5M)S#KI>zG|k{50;9ShQ7DYQ{6jL)9JHh0CmvL_Yvd3mpJ9TjsX5YI)tt@fH5@R!R>GBMQc>x* zh<74%ldxa7k9~xXbUo0(cii*Ho$8KC7hJW{UOV5W)JkRvEfbW4vk6Y;V{fLe6TkEZ zt9(tYxy^-*W7WxG171k~NBa{v?cWl-p%)2+WZNz|GxGCfncJWgDGh_O_;1J&z z&oQ^b?M`>1qFU&z;q)e5OlT6{D85kQQ0D{BIDIwAC5$n)0_W{9*D?(<2=X|3MXwoH z89b{^4m8pVkukz@xsj?FnwyK6BaLSbJq<6E?9v(G6x~9H(Xq6+cu=aRgsai&38Tlj z-i12@RI+Rrm= z^6$8!aS@3u*_EpL&ap-Gz7k<}T1HtbS?U>cs{N!qq_;joKdlW2ZVhhJV(?yQgfvN= zZ@gyeVLoPxGR{-K$hqY9LR}h4w~}+Tm)KVtCWotM4ONXt4e7?4rmBYWN?)uXKb0Fx zz0m{S6>ej1fE&9;-m^YhAuSneL2p>6xy7u)1D1IV5RZLY{;liGPy+bA9r^*UOP&YN&OLb+S3$^v?8J zEhkPUkJ$(9T41yPdf=G;5IcWjXi!cy#25>hE*bk9PpG@(Thd~wgj7$gApEA$!dl^$ zutXlAh8vz6vKTiRcN-TPKB@=R4ayQ}fOth*FH&J2_8wg1SJ)MOmR2J;#^1$zKJ$%d zhkIVSKlNMcW2YlIUsCV*%(#6?c~U-P%-1wMgWAB;g)Pm^4J=>HUDYB|58(#OuaV&3 zK$&2uz61W55`WA^I8MV>*wCo%-!ym?&$P}Y17h5q?(g& zB#uqonOGpnll(oSNwA7GgPGN%mh~os(Pds?d12@#Z5HB$^W>R!)_*7PLcgN<^=(3F zd71J~2~#f`ET;Pgw^GqC!B9@#BMMT2v`5@WcZ;v3`_g%Nsa#xXst!^HDM!?nhHZuo zN?U2Q)J$3?E~WLbD>{w6*2V`K`Pcc=yyHEo94tu$t*e3 zwZPXec#wUQMj8(o=NhM)f190#>B@S^BDBJrz~S1?;23SEzEOX|w+ZFNN>ZjgRxNJW zWq4wUFcdM&S2`$Jlu6QHvAfty+$7eNjM5A7g8W*!pk!4Wsa*|{!6&E5ca(tqO5BFt zGe5n;-|L;V1HtWquKvZ|qn^i}PM&+N(rGW!kS0@2Dbb0wlHaGKr@Tq88_26s_CUUE zY-MO;%x0Nwu4EXY7Lkin5B4Ud>q9g}*V#-xiER?9i}~eq$`o~xv9z(LI?PbaSXRxZ zOjMHO!&0K?6gr5_P#;_pPDr&e=HbdO<*2$!ZDu&B9+$1k2drv0rule&mW#CyM)*Sm zyM1Rf^Lu)D!ZRCZ40fGQKby8UxqfnLN~e@oDbLdW^W5|A51!yr@>F%cl5Xf?nQA_# ztdk!q@5F6!$<-V(N=1$*}t#(xU%HxH-{9o+% zeaaH`Vr(IAKz+28xJd4xJW;Hw5h$9d=EQriFYf{iuHx>VgvDCVSqGV z9wN7t%P1$521;wxU#B}R88z8Y{P!VH!N0nYt@+sB|?xc zk(SCXd5v;Itz+nAkPQvgVroE{fio$7rH`tFZhL>sNUlwrd+~1-pSG#$uGXZ?=E9!?NR-{Hc|f=9O$3wUFiwBT^U|2zX;K|I`?D~>+$Mq>~VCt^48?J+{U+FYcL0=K8)gu$c zGZIx26{osU&7eZzg-POD>4)51-Jz^iKB-02 zi^>?KiF{UmBsCGSp+tzFt%M%*BvlX@ImFYzU1{mG)Ie$}6%ahMw)B;T@}+FHo`T%~ zCD7aS@f7#e&M1_=7`5!X)U0VN?TxEg`fv9@?{r_C;D0)lS^^VCNCo7fs$U6J>H$Lr zsg&?k=q?0DVLS6$oWw%zmrCa6NOoL6A(dKQYyqiA!wjmX>kFo)8b+2 zm1LC*s`J!bK;T@prqWN2lwONDr7J?H7$(M&4#ETR4rVh<5~LWhvv@+%u{NM-O~qVbi@1H?H;f9Lx2U{s6|%detSfx zxLoXrZl|?WM*1lJ7WWG+X?|guP)cYj4559oIyYSCC9D=ZNMVvJ^^g}S&E#Wp3Hgv@ zS9VHWaa}EmN|&Wd;&h>{bXV#unWfX>VezwgUX;XzLJ?sb9fPPX%${NYNm?)lQJe1F z>dor?Cv%D?)?MDMqW&%GF6LR1IVLmIJIt>Io(3;!+4xDih-RS==qiCq=fq>eB5{iN zSeP%E;Pu}^TcHcqP_GG}g`DCOag4;oE|MzOl4r>MfeJ$UY#QiIDdG6244i<2H$Fn^@>=BOlLj$ zHSFp4Vc%^->`Qz}8i{^z>vnOlD2hK3o5iK7vO|6--3G^M(pb5wGDZ$nB;}dh6gzmb zNIRu;DIiag2T4IOO^6rv0&_p;3gW_Bm<(t@BtDw0W)t-udLR9X78A%Btgh_|9Q8-| zT)xkKmyh~m{1^RW{F<+?|7ze((4dVD7SLMg6Sa$|#lPz3wdsiDD%ktF82buOlVx~^ zxF0*38`8zt0Y6?iF1AEn6eSE6qfv!imMr2YAr74MQ+y}Ym!60#P_uuP#z=|MO1X)A z9$frOtS&aiw{2p7aWWlG_CRIm=jXB4eIDDW*JKaCGQ;#%`nTY>U|KMHu#q-bzaF#& z`)D<_Nx{hABJHMDLaPw;Vh>btjBY7(1K;$!tTq%PccEu_&&okZT!;9X3Vq0F(f~T3 zdz8Urx5#YTT^J>ZG>-lw#$cD_a8VZ$h5xW-_ODn{G>BQGX5v`H#|7b}&=|QmiY8#! z++m>+okyKQH{tKz-yS@S4PmcYebgHbST|Oco7r+bQ7fsJWk>Y{t%DY-x6s>b<@LgB z91G}!*)(1hs{32|cD)YvXP;#@D4VB1PxBAI$tFT0$9XFIt2ikIUD0x&v=%5MB_GBCRcdclEEvTueiU^ukU@3B9>1a?Zd zfu5@q)JWf<&WMB>VLAOui_wL2pb!DoQAyg6UWS4YyRAtlI)ct3(f?Bct_BZvgYNAf z$%ICv0Mv<-SO}1@o>zmmP{HcfLB#Vnz8+f7j~Lqv=sT{VuIdd1*(vBMvSVD;cy(R@ zS3Lkqi$bfjlE<@M&@B$eZtn5WWvqwJYc$6DFHa_GXe-n}9f*@9L(y0swf9u0xSK;~ zvKaYfJ`|)|p>X)Cq1g-@u7fh7BQzTGp#$oM-RF~_Cn$qD?JLQErZzwHmzS~k@ix{r zexPFct7Pm61>FZ&a1JSlz3-=?_g%-@LYr6hm9TnK7Z~Y}Cw0ZRS7LPgVCg;3Abh~?-gMd$+Mz_8U9cE>;}q=Qj^NLr7jFS; z9YUoU2|U%q%0V1Fl>`mMZR}uw0wvj6s7TVFpLowhcvNF#xK} zj@Zdun8cxedFWnoy1#coL4|8yAEC&iwdDD6gE~UYGg3$ zOLSX>v8U$~beBt5JnIWB(FbUImLLlM>KLnHbOoSoI|RCgNIH+SvrERf%@MCRbzMFANs{TP~pwS zj7-p?PJ!a&E-br=mFAa{$kO z)rd{-j9<7zIP`)pmKRFeZ{V3ZP`4C^SN{Q?7Gl(kNpV==HO7}4%9hsuQxe~T+H5J_ z-4urf3d4&NkPod;MZQBd5x{vESunqOV2wnm5}We>Fq*Ao9HMa zM!=Irz@85fnIgEf4fJE-*jr$utMP4|3&Zz=jmD86P9%5-?Oi1@jUQ%a@Mo+4DHK1zc6Ff)90ILZ z9pIxY#<3P_F{^Qcz$czUM$$0US2eIFs3j**HxqgWs?VcP14jcV<)8rS1@-k%=u2(r zurr}4(ve{g(o`~*^~T9C4CojR=B|vH{KjcBt$8-^Ye8C-G$W62mO>xg^)t9_BzR;L zl*u~u%sHSGD*~NzOHu+V$!ky)t-~1!qoG*b2W&x63?=qx*l{*6`<$1@NiTb``Z)+p zcM?%~28!La;F@IYwn~RWZxZ5k2=-0if}ZI*)H)py+1(7|F#XF#QlPTn~8l54g4>u6o9DVU!Pm zxn#U)OTs$yImBjje19dz-yN8^1NDq3Zd-_JED2LeqH#r>Y3xxcd0k4n%5YaBMVCHyTQ(8Iy%M_~0qI45W# zP8>Ob=>LYlWrGTD2Go0Xkm<7^Gq{j-ir{)T)TANMNi)p(5bjw99JB~Zu+gyc8|YNW zA>KbBM^oMQ6T&?5Ss;9ElMWhOa=!{#9p%?aDN&Y7zpey!JCKQxFUrOhq@~X@oA?2 z@k`+6{?IBuKz>*Z)!rn0-VWz5e1Yb17c`Hjh!q^v6)|Il@^;-{y)}Hl0IIl|sI(+# zX9vS7SHTR;p?q%zZCn>t2D5m9cWjfOgtQ~S_Ct(>qwbjtB;X7JoZaynYD}DFg)?Mc zB75hZ3A@+s#GzDs#$NU`Y0^AF@au!&m40Q9|!B4fp3Ip*+f!#}h87(+J zrw?MaCRpeweojM;PzHDN@xK+^5q1eDU3^2YI}?gF5r}$&Jk$Z>dxvvZ{A?q5^$N}) zSOtEmf+r9}rbbc_uZ5wG?TC}1oX9PC;QixJP`(GZ?SQiH0n3C2@F`TCDTo^x8JB4Yl2DhVz1!e48Iv!uL`uW8^Hf&Sp64iOS6EdilTx( zi+daahQB~}9fTIK5*TV7Dv0x7m}rb)EolAJ9+!Kh{joG*nfj^NYJ|i|>g8P00YYaI)$f3!ik5Ds|z{wsj zP*ttP`A##Tou7a@rXnfF3C;t_gPQLxcv{7*$C8eyDV`($T?WTZg+~){O2|_1Lq5b! zIp|AUfz5v5EH4*u^7ou06=y?iMqf|>I>xs+*P;o&^#`(FYMcf+eSk!jo0lfXta^sWVw`Nrcsj~H}m7jPPYf;d}-+|(DU-H+gp zs?e2wVj50w$_=%4BvAYZnaT?N&sCghC1EblG1}_r_YS}(EwQeD4KZC2+EOF()F_&Q z_f*@!ldVvLCLw$EhAkVyde@L$X2Itp@N*?_RaRt*rI#5ogD=fR8>v@uP!J zmLa>|M?|efRzn9z|LUuAgKY<*$GDG(>IOvqW^pVIosofTfWD>!&|eYPy+H2&!K=^( z^c`?t8_{?JOz*}S2wo_A(~+4s!gI@z6IR2HZ7~m=$%4I_#h@tu7wnHd2sJ@1oJ3&4 z40qzGG3@V|5+hJeg#fL~kSSyE^y#z~{q(;RMZz%RbEx0CLDP&gFc1wFxD)H+Q#dqj zYz@x|w5jw9qALcB*Be~F4hR~InjnomXSwNeoDU+RDxD5r?%_kR!}l(7^%!(S`7xqn zh}xRSQ+4o3D6IqLBCzJ7zr7O8j%?#cJ^B&x-xl~@f~@d~l)?VnRLm|C74aA#ei^vn zAa?2PM=mZ3ZWPFMRFZkT+4RD!P2PD`V!{D$6V9ghO@crofK=yR4} zALTD(xO$jj6EOZ))J{eCYIL$!Nh$2y@}Z8K3-mo;v-mbBi%Wo4s`BS-FO=}Tq53U| ztXu>g_jJ5%SqYBY{P+92#c8@Bnzbhm#JnA{v|HBor^Yy1yqdOv4##1YJ{k z;Eu!Bx4|Z(&_@;l`*Do?Ka6GwSm77XU<(oTWx+ouFrwVB+$h{D2DRUS|8c@u)cY%- zg>8uQCi)<5&#@pBuLk&SAl4VwVNBJ)6eo})7J-41k$>``x9SP(EQaD;z-cONP><$> zmxh8HuONc|uY-FFjpB~N06tT-M#c6;DYmBB5O0Xah&2|(Ta(%>8Z{(%1Brnsu{6fZ z>Z({#tO$xw#0Q&JMMa7i)TkIQpc5&(7Cm{@V-@7x6}g+PY*8dI~2U&Ugh!eCo;U22MY0-&QPdVmBDp(*i3ajPEW#9Nqn#q25$1hDxnRFX{ls5 z3%a|xC-j?;?qT;|AfrdsCWGoQJ3TIH@?8F);7XLs)8(Mui8q+(8Ha^qxa&8}Hi+Ua zfYlv3eI~Y5Cb5w;*FO0erQ;UK=nK&eGgr8>fhiP}VxAHU5ZcKs9%n5R#vP^H}x)*){M-8rY6+enuYBy(iUdWQEYnWsN75llUeWx!k2p;-oW-gSrQ;u=7{CJ(~=b%cr?c(HjKzB<; zEja00by7#wbW$ECW%rYCuT{L1+tntk4P4un>bw#5%;NlWlMM+o>gDpCBLT4FH z7d+uxW^KXJFy9ehQB~b`3~M+mY=Wkm^!DgYu*>ev0uh*}FIJiv>*?@bXI-m`dVKr* zA9*WG560^c@$sDSmcDh@^gf(oXf$yaW8HR)kk9$!N3+eBx?v7Yuo0Vnt>(sM96R*a z4)^=NGKJRaJ4Y~Ttw|td{^4)NYpRb|PHS`n>Q}zsNjMwr$43)+25kry#>*=v$VYt9 z{KfI2(u&dEmsVA;i6z%1YGUPe>$k_|6~&8VOE%TT3iAts;&}Oz5B*K(Cs_Utu4D=$ literal 0 HcmV?d00001 diff --git a/include/securimage/audio/K.wav b/include/securimage/audio/K.wav new file mode 100644 index 0000000000000000000000000000000000000000..27e1d37cdf6fbf441ac3ccef99f96930b5ebb8e9 GIT binary patch literal 22158 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/M.wav b/include/securimage/audio/M.wav new file mode 100644 index 0000000000000000000000000000000000000000..08d5cf53732d5ad9143fe35bd83cdc7e4ec2d486 GIT binary patch literal 22158 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/Q.wav b/include/securimage/audio/Q.wav new file mode 100644 index 0000000000000000000000000000000000000000..c757e54e1c514e26a0b2cb934fc25c9086b5cd21 GIT binary patch literal 22158 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/R.wav b/include/securimage/audio/R.wav new file mode 100644 index 0000000000000000000000000000000000000000..c2e4b24114b6c26b5d91a44761badd09f270106a GIT binary patch literal 22158 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®8O)c1H&8+Mx&sjy+JfmIvcy7Q<1@`G#I&V0)CD5?L}ZL3Aow-FwF5_ z1#U3BOUU&7@SKIT=JZ$%J;Lsa6QhZS?Sm2jdZ5_>#OxcIk32tx>}<(xz+5VzT2+L{ zIMx)5LWfF2u*XpfJ0V}$->6QLu%FR|`D2|?n{~p5)LAMZZk;n}TjYT9pLYDh#H1>$#c>_>m4IHxXlw;tH^D z`VV-)64)|{PC#|f!q{$tN4^7VZ%DdfC#D#7Q09Z>EJ6kw!TqkIS~&lk+I}d03 zfq9og^@;~)PsO*Yl9ae zg*Bm!yx&CnBhN*wN=9>j*d<(zeXZT#utoG6m}f226c=!5IjVm&D)%WGi2P_zw<)uz zgFeHoK4J$W4i)DGi{+dcZ8z*I4JB2$#r!bt2MOj+aa%|P=i#dI8@P&CXAa2`y? zr1e3O*>bNawrH6(z&6JEuWg?*-u=#VMeauP$Xjla&`?!NW7PN4H`ezvels%TCR3d0 zoAHOfk*=psQ9n>=)f>bd?9`Z8MP-3^wx^r>x+BBB$NtdjwY)D1Ec#Wn%TnBS*LK_X z#8Kb1(ACrvDNmz`+3tTKFfOBvZSb+ImN6mT3gi4vfHA!U9z`uK6eGm`HF)Uj(pdJ#SK$gRA`op&9r9KYF;iwcXL7ga6XT~He1=FG3n zb#1x!(oU1RwYMG}NEY#{q=A~Q`aXsi#_Rq@|Ji<0Kr#PnexZIBjbjZnbgQ(@w7pgJ z#HvC&(v}`mRPqM*6X(B<2DWlVk>(YJD+@;!?8_f)?o)KlTxdCDU*+&PPP$!6Tdt1K zMLMfV)zvawHLVJG6EG}bT;RvRBZ0mALrtNk-TH|dQL{$fLF&NQz>b91>-Fq#k9Xd% z*RifQ3x#zHcNF;NmoL~}7*kkKz!jafTI}P(S z8&oeSEbv9Zb$`3*xI!=l2f(u3Y)=CbmOY)phE^s&auhJUbc|&6HGg zHedZhY9M~(TCx7rAh&i!IX2r9YzxhI@^|O;&)b%3%Gr~U@MY-{51b8+%Q za){q6sdU{8R>OY34?z<`+`(T$qC;;5D?!cxo8JV}9eoGgUQG%z{vMYs50~e8Z#i1o zW?N5~cjT|mE0?!3H!`Pmwl?=c-d}mO3PLP9Z6h5A+_#lKxvRog)m?orKiRKcpe}TD z=#S-Tlb^!+OFZ6$a)$&Uuv^n)5xg zOV*U!h&)x^?7{$B3wxryySD-9$*mGg>g+~cKw3b2@R{(PVM$@TBVxi%gaiej2%P8t z%dkyXq`oJ1wN6MmqPewi4n9ck?AKPx~VvM6F`gc!anyhj8do*n!ssB~bu@t1CfW{^r2bYz86M&>;| z9R60x^2}_?pO9TCTc4xNYLL+>D=?>F&XC-ug{`b*ZT%dblw$nvqD7snzu`AK@SmWp z(0x(eA_F3(Mm3Fm61q32Wl)~~c6}qw1l3=X!kbtfdB0obl&yn{mYKEr?K3B47_;*; zDy9v|e3LUE=kM&f`L(Q)-ENPNM+*OnCsjR-#RHoJHw$?mb}VXWRGY{SQE8DU!en;dexj6SyQv(@*7z? z+9%jMd2VofrLLO(hRXq0LMnvG;hLC{v0Ti@sPd5?!ri;aD20+np@^y$z7IxG|iZ5 zOMjH+N;aqU$xg~%nKPy^z~0q)!2OynlKN|F8508JkTDUrBUeQqjBOXUKc-ex@5mbA z<%3@Mtum%+bEFge5fV=udWJaj?D>|Yg3Woga^GdNOIe;)H={y|Be_DRH+xlfhulp? z{>~+?E#CV48_gxd9Mi?1;*l$&-bB@k{T#nG{#vv%GB0v_XmCJn(=&Zd%^+b68OqLi zi#bQzV{FyTg}F;%&G#vjlY6E=N{vc-m3k&SC%a2_{lX`XC9aR|1a5-ny?%Zk#uFbyxTR`HpzOXNSANOd6#uM?T@5uDKyP5SxLH-nU?b`+nTr5 z_Qw6ivxa7=ei-KaT?&j0e-zDP=EsJ{|CTTy-V?nzB0T(6@O#r3okp`r5;&Fe#QUdP zvVXKREvlAZC#QE->$DL`+LU5xO_HxB)ySBTGa=_d-V1A_$KvhJQdISf5dmSr^TH!z zTEwNtm5Vnfc;Z*b42Y-~RxQ}%m#n?5dMa)p8)U8bq|0V|WVvJZ1fhil)4kv9%Je1KOuU#(9n`~b! z*Pxv_LF@GU9`rD@W8|RN=?P~NO2-e5_r`aMnI1MexKUttBd_i*MDj6A^#1L7=ICf! zWT{kmBWGN8W@ew{b3fIo<uPfJT( zlQ=e|YtDfDcX{25hIsa{=3GrxfiWStci5MR7BS=F_Y}LFP%LghoH}l6#Ezi9{X3Y7 zY4iCxShJ3mtGG)!Ew=X7jFkqkBXZ5AEuI!q8ZEK&nC}(n-oco=2|IPOW{XHQZ97z=+HWOAkp(ORAWrN`01m zE4^Z#vmnr1%;8Ua@LQ$9`u>6XFiS-DnDTL}<0lon7QZ>BPjsiqRUyOtM(XQmmy4^} z31ytT!*krV+p*fV$U4@tDt}b=t*ixUs}idxcTW2~r68$p#y@!}1F&Z!k!%`&zhNPp zBO_vSV&=sd;_oL^jU5*`D7lxCVeX|eN@*$I@ZOdudYgG>IBVK-Y&*?o^XlZTP3MyCr@Txbp4=;GPu3rW z#-fhaLoNdss9L4@%d|e^S)?oGN?fzpEg0|67%^gQXk_3|<4J7=DT~XX(-f!jNFil9drVhxcWa9#hY z#@pJqVomJGE&%V^rreVs$_?mZD$3EG7L|g$HV$Y8{+%LPl}xzvnXas*oqf+POu)^8a*-kaBBt;t{0~>{$4UdUk zkx&rdE4Fe>wdn2PU4z&9WodP)gTgsdMoIA$dQ+5G+Kx6v7syH$LWjuhTt}@PifZPU zN&o&UFsXf7M#{m&cPSHdzM2QyuDWvANYzN=)1bbg_ao$(-x7`|SYpDW{i7O%bqbnl zGN97l=bC~Syl|iQc2gSB*SK5n$8YC!%&DY$>f3S(kLRDv==-bHuM;WVQ(_alCU?&| zTX5fUW|(V(9iG%*>WELsM0Q?j? z6>@X=zQS1k1*uItx>VLgbA!BvsYib{ORSXQNX-6qE_GKVabDdAT_tJw6Y-eKi~xPblo*3u0!f?id$-ua%za$n^=3*h5}xl95G5mv8i^M|J~5N5nW^I#fb@X6J|xvjQA4T zE+ENxM%z=|P736A-UhP9Th3cnQIncNGikjvLbX;kTp&b}hdbI7Ey{DG&H8!shy06A zx{|m$sa$4qfxoSUCyFnrIc}^FvL(_Q-89w}S3N-=H!$*B=>5QQrnkB&s)Z!cd(2x~ z&h{Mi)Ks>z1KbsfsqU&~t4@i*qQoV;@7e~M>*VC5PWci1{Z8V6#D>38laFS_nQJ+S zB8zKvX20Cf-O;aOi^m1Wmx!MgvoGR)@Lp4G-3awTVL$!oY2Z2Lx$f!govT>MexZ%( zoW`pesYz7Ft2%Ija=5dX`EgF&bnVajU(5Ws_Vd$EYhq}6#rzSr8{UtCN_)ZR4xSzL zBGwzn#V?4f6`KA;n-m=lY1rg&mTL!+Sa-Wq#&u!O9S2<6X_XnNE2a3m}G1|xa@A_D*cLUX1_*CV*Yo%pM zZqxKliHpAkeg5=)%+JR^m!{3UUOW}AM8QDq7+oi%jOycJfl0d8vPldsO?lhj{R zKAOIU|J?QKx8zEhtl)$59_gTYY#8Hj4ecJ&Eq)}fa4)J+M8nV*fhCONRCD;ZOp*6{ zy0|7fkGQ&d6PSZb5~``6>6RP2`?c}QG!D|IsUyS(G{ZTs=v40W^xcVdzMlWM{d4m7 zl8LiYN@Ycw7rA6IPFuonPGFO;{OB3+YvS9-Ziz|=9~SKNFVdfvUX$fYSMLP(X=g2` z!8HP0awu;T`{vZiJJ(*hFx7nt0-Fm+Ad>g`|`3InraQ%nEc`+4z)C!cEn=#%75 zy_x;A$n7zRnfj0bQ*d(Fo9Mdn_2SFKG>_;Rayejw$*jF9%%e5DE8XW^Uz{_YNh@ML15Fs`Th-!OSF5W#bmjstnE#Hc=qJ^DR|N>fesls`nHyw%;E+$}u2yg70$ zno7c@7TOMm2BrgkkNmp?v=6A{KhyX_TTE4&@2_M#f46kVf1F)6voN)0((#|MKOX&f zlYB61YW}IBxy};wqZpw7;8!kiabU}!iXm-7D~8Mq4E8IgPgd7f4HnCA6=)r~gZG?Q zr_4vUS|YhB)K@pstu%OzlTAlWb^Xfv4KN(kPb znXP5f{Mq)=bDoS+?a^!ePWnCZdlB#|C@}bE;4uG}hGyD6>cy&ZVj=FS zz61N%B3Hw`i#On`y~GJ>lWvz`hw-SD8mBJYp1&z) zT~?`#iK%`mgHlGPkH|SwP}x$#c~PFjjaGft%`|Q{P4qJblnnF>92~$*4{?TWstj=y ze;@a2W++YMK1xaKc>YbA3uVQV>Ja@d!yw}a!$@NvQwh_*20{N$Q$^aum0*9!)Y-@? z7cMA>&X33q&)%QWFnxI1#k49}qx04ky|ibz9w?cdPIXv!++Z{I#lD}ve`WtRe$7pz zb@kLCs8>GD z@27vKU8i=4NnAJj**m~>-d5jo)a)pT$bXsJH~UQHUs=Pl_GC89*_=PaJlOujGm{SI zic164ceFnZCybAb*GxZ53ytdx)$|X*CL=_DVLBJW^!uxG!~8Z&NqaM(T$cLQKPr=k$|x<+VFCJ=LwGenK(MOv98s&w1A$j(*n0ma|2) zaB5-cg2cRcc|z{N>|MG31zXH3?R(rMB?T z#8-T0E{n|K-mnVF9l3#myY&1AvAycHl%=kyi`HK-^fwk6+Ukome`+RZ>ZoGHX8a7+ zUom?}d)~VmJDS^S*uGin6umIlHM{cL=S|IDk~<>jV!o^JrA2WBd-lqenVXZv5KRN! zaQ$&ZxS^e4lkuEkiGH^Bv#Oc&Md*W`ho>ZmmZbv}kv=3T{CrWRnyLP&9;sQNFJ>HX z%ribVJk&MSyiuj8x~po7X}JGBohHcjJY`))_Jg+ZmM}|QYiCQfq7?J$!ghHpa)tcw z`Ay83R*fsilPgb$ZsC?ePsQ!>X&KPQ_sJCe=s*j38__??XR|fYQd$ATY zneHNq{A{U&dbwt@wzY1QUS~LEIANHp|EN{e(^PGx^5PX<;@YvLic!w-9CJsz&N&v? zSKGc@=UT(9?Jbpyo)@+)D99U{cP9U^xs>gI^SEcL{5w5OHV7wF)wG{=^YlaY?euH) zf9u2bo3$;}7sMreOYRx&XJ+AU{1JMR-eeEBATe0AMg5nitu|a2r2nN)FdWej(rttl znoA)f3)f}nhUzEkHHI?!pRn8l(IvDKE^r#$5lf*zz}~KT+js)pHJs6oc8+wL+P>1B z3~LNAFDjUsembKh_iMQs-NR1u^Q9i@quN^fB>iRmYW*aAecfqoM@@oc z5cYAEa0hq-I?b0-Bks9|l4$!@3-xvgHL>LE538lnHP zD%2rAl$uJSyvO_2Q_eHkrFUgJ?T(%fyREo&x}{uE8b(qv|8@Q#^M0$~IPA*u&WE1k z5%-5QMU$uPi}SWK%+pD_68h%ak(%=A>B2m23VOVX&}(**9;e08TeXmDEaZwuRceh# zvs&xbR@WJHTeS}~C)I;hYU#Ofo&f6$Os`83oPEPD_}hj@#?4 zLMxFb{4P;cmq5&Z(T5xE=!5j3x^J4v>SU>gIFbL3uE%*aTY0D$5wok%1Nw!lFI*Js zs*b4NYW8Z?I;(b#Hb+xl(@I@URZdcb$NVp@DmMamLFZsRE$LC^pghDo$kW{Y&9%^3 z+Of*{#8KS##4@)?V_sO$wcuc3+oBD&L(ZS>uJTISkNe5b6?dv5wY{}BbmR0J^iwb% zS@Tup6b}oZaZmma(uqElH!4r)HQE%Kix}=Q-$y(rx>Wbo{ZP01=*DU%Y0juGsq>`X zq5$;!#*O7_aOF7k%Rnu0fZF6y@<8t}Pps#HTj$ohOS-J~Nw)5m_vTZD#|zdJOe(Bu zd1;Gt4s}2Bu0)4L3?CsKRnOHf)kW$@=&S1;x?{Sd+Rp0P(rA(J`w){`Xr}yJ9;v)m z`k^yu3s+7E7Y9ni)aTS=HLbN4&0|d&&1rS2s*I|IbW8Zjz2GYHmsklJg}$V0x?0JW zS1Hxx1kWq?71vMKYTO%K;Tq&fvK}p}ZsrSj<=-thYCdh<;QZh&C-=a;)MwmbF+>%q zKBarCKdmdRkJ2yD_0R^Xze*v}7@;qhM&ByGE0g7t$}pNp)95Ky0{03^NjIc_RLSa0 z%^OW4Z7c0nb%eUPx={SUR}}{H8Mwn=8odF(&{uOD-4!<6xwT^#<1cq__c-@)_d)jy zXG8lZTQh5pIo>?8aBJb4qQSNl`w`cA?=!`pJmfNj8B!~C7u_IzIeohR0Pb8~(u`5J zR+Yltw7OhF(wI%dU0XrT4PsFP$x>FR9bo^ z?B=)eXSjB_W4@d{pi60KwuI{GYh}JX#w&XUxy!lC&MMAjj=FZEEy4QJ{Hie8Y%p&w zdS=UZ#d~9v_2}CkiTj^P(s<2Y-8B79{X)GBci}3kt4n3Xqrw=z3hv2^w2tyoE+uD! zy-sHWH_YhMqLnG}krkgMPQJva~3wYwlkdSh%Pt*JgBHcQ=zYtOnPQZy{Dv z={09`Lktb|5xM}~dF^aXnzTSLb6>gc{1q^=Q_3J^hVnr!t^7-SaZY}#SVh%Em89CN zo}(^QuhV?e)X-c}6-af&QG!)i!EGiSfAqf|s59s%d5A8QcXT4{N^hc$kM+iSZn+w{ zG98^95A0U!*`n)(uM1n5cbVT?M%$xY<-LuSql|G1uN6gAfJU$Tq0iJ0)~j{<)lXD& zq-EH*xQ0%j>vWaUS8gS5k&7!`X(y)Q)(P{)h0+GqJk=C+HlnG6dZ~JW>Z-I+3dEi8 za(r`i95&)sp!aYZy2Fa22RR*jxL}Mdj7BKQ-a4L2ZmU!6Z0SfqWxHjqRy5OWH%pee z*87gL?$_QmES)p*dHhC!i8Ix*=B%zUV*R#ui~6cGR;RQq{VF=%h+XnpZg8qtz=+~-&?h#)X;sJC+ zI_aOv@A5?Xj@RMti;AlQTDh%dEz>LmEs>UG7Qyzy?soR`+*drvbV(=_yGy-PcJ(FA zSnYG&Io(R_eRVNu4IjbH!Hhejn{mDpMt{+#$`NHaJI{UNe(}?#p{m~MGGNODE*Wv|22#q-=%(A;HxvS`X zT*G>y!|gLVBHGg})U9;Hu4jO^BqD#Hd!=igv##Tm?Xk72^`~X2CErqD8{k~&p5_&) z3;n_tuD7^ctS!A$Cus(27HSIB87hl3SKP?U(9FC>7vCE48JN`rUBdgQhaSY9*+4#4 z+$^1tx~m4NuBf`I+DW^`q2g2Via1W}D3s!xW1r1~KCp0fs;*^FUZIa{0rap7X`XEH zeDGB8w8lNBr!LM_Vj;@^ht-_2X6wG2$MvtV*LEq<*2E zr8%a~mCj2uq$1%4Hw1l@hsodcZ|V z8)a>6TkKfrsOenc8sfIf`;{iNF&D-!Lk~r;_)7I!JyumrJwcPCnl0@S%Lp5}N}LVd zVK2~`MbHHmjXs{A(AbXSO7buG6ych5Rw}FVQ>_!%h!4e;Qa33<_>J!<{KYd;3hL2M z=o(yrUZpMQL<~U(P*3(Ztw|3k<>gJ@VyM`YJm=hhyGpy3IzHk)!3tX!`#eXebGXap z2~>*F^-%szxt-Vc@X6OR^mn3sP_(j4NTydXRUwkXR5o?M)v4{JK zpTlqCdV=X(LtklI)PpucA7o!u(Jp)yGNrXDx7b^(B3%$)BVwzda+enx@B#dLt|r%- z-^=ac)`5S8@OF6kK6ecLV8huix|42KV&$3MDxNHNj$7w?=sfED;;=f)I3;^|hu0bA zGP%}n z7xM4;Y2qWX5-{u!@uiq9d>39}?NC{$1>U}dFD*pzzw?Lr8T=H%%^%~x@%tc%=lLDn zZgl9Y*hrZQU;>YkS_!0a8{vrPX`q4OkxljzN?(RZwQ5KH!xA?KbI-!L4 zH*n)GK`jJ{&jq7U2IG0jFXw}WX~GqLDnE%6m<^SNV#qou2@TXdK4$nKV&Cf=#uXR<*tDbwh7`jZYphvQi;5;nWY z_l3<05#yZLU5pmDq6a74PsyVU&1S>?yqfu6+{E3kCcvK_^@lN{O+; zcmA2sUOdEWc`vkW{h$bsf(qz0O<|+yVp*-Iyw^O}yuqGpu3&I9jr&hmJy%CpH`iO| zB-eiMwwmtqp5pQ%?`Q7{C7L!28#JYan&)^M`@cX zLAoctk}8XRgqy-VVTl+oWr}uuUKXQT0!zjVfAG8b&wMI3fp;T+vXQGJQA?YnXR#*T zO{def%4OxV?Dib-c)e@9wY`tL2_COI-t(LLi07WWnR}mSkVo()c^i3qc?0F9@%N>0@2fWpKHRu=c@?gg~RB(D~9O0DMkwSao^wrP3o_Z*OYh5UF0LO zO71FGkxP2a?j@e>UfFB%cJ=b|NpF2{LbtcKGD1#NlIeTdp&X;PX&>}`|A{*}Cy5_= zAWv{TcmubElZ2Ak-PtL;(GL@OU9gbNbC zlv~7i;*&W8zmD^wf4>9N@mtaV?nXX*rt4`_DEYIgRjH%YMt|dWr9Z|IsWb-y=E}ch zoxIH3z`F%IuKSffa-zJKiu4?M6x-5;v@FZQ|MO^fbmnenRiNhDfDV5>*OGsZ-px?H zG+%{Z$&W{bse}f?Nxm{Si#PLbz84?Kx8{p+>0CGd3+(q4dar@pcf&`>klg4R|BD?MOkjWSUwqJ3#Roraa* zO4=J;?-RiP1lF7ypliBD@6f5}#9xWN-oxkse+OQ&j}&2_rUq2r(@}F*k|kh|spz1d z!7b(#UkHO5w)64V$lc`TP`4%0&J{9EzXfoEKcl--_ z|CgZG{v?zZ2E>17*ylHN?5@MpF=nH?ItKoIfM*D-hMJ`n^fmpVn)yl|K*^Vk&h|}g zIG#H&9vark7+W8*4NAEa=wTj()x|mV`cDO8zJ$*D_Lxa|#6%KHhZ0~7_;@XJEib_6 zDnV^f9E#tCvdQdwdl}zei=N`WnCT&Cu!bT}s^OY{L7#B|dap0g^v=Vx6S_c~ zxQ<+h%Em(LgCQ+Pf4T<9*$wm8;~5EI(A*6~m-S~H-5#0oFVr=!X&lRe66yl%dmR1L zYtT>brGK#JIJ1*pg-&}j^q@nD4%u@89DFNO7O~h9+)r+CUyz?q(eobxD{Mwjc^uAn z6RYwk@Yh*r&z{kPP!OGmhA#~Yi1~JNW+K+Dck|)8w(B5AIS1F`W{-E#Q!wOdtfsYo-C7z(ci-p zEqcSgJD|S(3mG*Ykyjt}QU$%+PQ<}l=n5~;zu5@rcv{2zH4%a7^aAaU%)gB19Mp%0 zg0X{DolxuvUV;MfFl@LSYVQb)BNWQX9IO*tKrh-4S&;po{^lS&_X<(=47%?RbRqnJ zn?nTZGtCy41@M^Ge)}tYWy`= zISnP@tSs~sjoAu3m8Co4Bo25z0=qT!p;cRlcqoV1?EsC~7}gQG>;{O_#z2*6(3Y-3 zR-c6iXD}?=8TzMDSk3l^eKVn{ng~VR8k{`~3Z(jE1#;s7vS%wid5-mme}}M-$ncrS z+f%sOAw*>a?7ay3lZ`-JD1*o-?26|B`|uPA#1n;zaTnCoy`anr=VoB{A_+0711?$- zqt$`26A&+Zka;28cVOWLc(f4DG5Cz^@nyv@>v&*LD)bxfxJAPo{+^1u+ClBW@)7mjxLQo20yMx)po4o1k50kQRIFBx zVy>sLHoF2&au(0%X^0A*hPt;OPm|#AcP13^4LKQ#pu^ArWx~c3Gvna}4YHge*8T89 zq2coV_2RFDdXJ)k-+WK(;_=EC5njB*W9OeBUpOEo#q0n3-1kl2`~Umgcf9Y9?|<@1FLc5vf;-+TY}{{O7(JGal~|2y`7e&umpHSR^3U|9oxm#}*z;krH>tMQx9 z;=cIP;}`?p_gUHnOUW3C3w|!ZUXFk)s)A>>M1jxM01ipWo?vb_RC;MZqGPCB*RWG| z0k~5Gb#yr@O%~YbcPPVt0+SnHKg17c8;oyNKzsxOiGA8O8G7j-sD;l_QG!wNN0Zsy zUeW{KC3sp~7&m|`K~`~*Tx+ntjZnD_feO4ldL~vwz5Ieja~pxuBVcV22;K`%P+ABC zOapT&gzpxyJG42l;{vd=Iqn0F2G9MDtFI)RI6QTW&W6PsfDJq&r~i8v&I{-Z-(l~H zK@A%Ow$l|)TPVxjLqEYr`W>pX#yEo)Grflj@(liv@Eo$osDe=K!7c~D-I8FzMdSu| z5NJ0A9OES{vl*+L_Lx%xU|SnJaqKWwvu5auOM`jJ^g1@=l;m3@UwF%qa#M=}T> z3)n#Ll5eQnQ=qpz4RkFH z08@a(!+jhGewhw?@wl^Lf@Xa`Fm^pSg%#Mlo7~1aRg22f0siWa893<(VE9>Bbp$NB z40j%SqJEg^CCog4{0`;bI`I8hbS6}{o$-XNi+GaBKCGOZBC2@Qk}2#inB90hX@SEo z-%YF|nnK$;jGU)xu$h`*4Hd!4MDjp{3lrde8)~z8$9={ z>wj!85Ztg7-w$4?#oa<}=mtw#!Go)SZCpg0kH`Aq5!8}*z#|{CX!G5rg!3$cjZWct5pOBM zGgJnnRdZ%|VdZiEeLs$7bTh!>71jX*StqcCbyy3o!5xAmWa%ZS zW2-=IS`%?H6#Vua6#s+4e8z#LR|LnajYz)=?|;QAp%4*!8q8rLR&wXT8AgFo-G`-I zn9Bpq?J_)h8<}_*E09ItUyEU18y!dAf$9B*NZo=se}+{phpQkCu?qSJEGP-lH2{`u z2^QELuQp)ieG0hJ6*+-D06Y_8A#lS^RoIuDj>zl^&UF`~(txoZ_RSsRISr)Tg&FLJ z+P49sa1U~7GNNw;EZq@oVF;dWbr)VMz}`Y}c%T$Xhb;@hdA?zdx(iR{@I8Ufh-~kQ zdjNgFaR%U`D*g~xqTkJx_g$oPq(uE{`0e2zc@m~IW&-w4U z|GQ^GmGn+TcF{x=oDX`SWk$ug0yekmZM>jG)8_C$t3|R@O%48O4@X>cHdv@eI?vZ* z8wa)Z@@TN!#Gis|7&H{{=A2XUWY}TF+H_mL`+u7-d%ZioRV!8zR4Vtu_Eh-^XSB7l z&2X*Wc&J{~dM4wRss!B2YZWVRES}dW@unb;4sS#gtBDhPVM|A6F z%TYO#f}8!acoCKH&2+e_XNYN3eNIEn7#%%HIX@^*L31997Gy+`R_HhT{08PL-ZEIM zPw`$B{)W7R)_c%3&z%KaF(Z7fJk2-ZZ)HJv+`jVBO!H0*l9e+@tk4&^nxNHI1u?3l zmp92H%=3&h%Svul_xPCn@lM4pT(s9IMb%I0E9U!U&#ZR9lfgPsyDFXu9(otd>W{*7 z(ewWZYQy_{BW_UYoAJp{SKJZV0`%R4np2f-it;t*!Ul{tz%gyiw>ivQEBh)mMk}7o zz;=x@|4TSv#BLI$X6m6Khb_j_&@xXX0*Ji}>G-Tx|?@^gL&e!0Oo|YAkr!yN>3h~wh$#`>1X0^{P oC)%=+u3Wx7lI}crIkK-g9*d+dc19YvM1xp7otpo@x(E*c11}c3!~g&Q literal 0 HcmV?d00001 diff --git a/include/securimage/audio/V.wav b/include/securimage/audio/V.wav new file mode 100644 index 0000000000000000000000000000000000000000..4234a9606286be3b84e6626efe297d0782eb3777 GIT binary patch literal 22158 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 K!{Lk&J^lxZd@*?d literal 0 HcmV?d00001 diff --git a/include/securimage/audio/W.wav b/include/securimage/audio/W.wav new file mode 100644 index 0000000000000000000000000000000000000000..f6fba71a13f52df7b32e25cd51af28bfeebf1eeb GIT binary patch literal 22158 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 literal 0 HcmV?d00001 diff --git a/include/securimage/audio/Y.wav b/include/securimage/audio/Y.wav new file mode 100644 index 0000000000000000000000000000000000000000..921ae5ded2fa61e01d9ac3b7ee9bb0b9bf81954f GIT binary patch literal 22158 zcmd^H1zQ}=((NAIU05UuF(ib9xVyU>arY1xBD`^TcXuPM#62PI?jgZl7M5joy1&Ey z8{f^7_Xb#Ix~oo|I@MD(Jv+8**P$;V{aW^IJ7#itU?D;X#UJm=gaqOH0`bCkLPvy; zDER9C%l|0hyub=BUYPh08&`>)C-5S~gJ_)bMDFA=vEpa%xQ=us2YF|*m;B_P@VUo) z1FuMA{LUe=l}sZI$t8Rym1OeU>;N%PZ{CzA^HroDeL$M=5mdDzW)3{eH z+_wS0!Ap`Vq$Mdz4wA)WIls*#@UH;!kDnnou%1=en>!!R-?C%;I@wLSk*lN&`Nc5T_4|_NOtjj+@&y?$jkEc^lX%wHp4Wv77gH>eV z`;jD#f8lenb|dM;hmvaa3bEp=ilhu_#pC!>-1|A!xfV9q%zyCWSpP@PLVj)2=-e*_K+}g2)HtkR-_j8REE6g2Y^iz?~A|FfFeKAl~*DWWC`!fFT$$^ zvXr;Oe>;=5bKvU%v<;2q z&47Y^K-)EZ&%&2+U*i0qS6h;`d^xb(mG2|<=@D`hDA-MM*(=tWG$QFdomJ$1v=V&; z59G5Iyg6xrwO)m9@8Yvd$q{}KSg%Fq5F42Qd&qPN8Ap2ZlY9b+ zN5~rdUL#Bn2bI|Hlu0)`pL`;b25GOTP3FXuITkLBEaJT`V*OQLi$bYh*M53X*6W`9egGqyVFCfN6 zOeB?`#U2KdYe3d$UX)a)J|rBge@A{G;(l=fKV1P-)+HB67(d5E-X7K%f~c4d9$5yY zuLg!5kwWAtzd*{8hCtSSuyhR1BOh_UIKG5k(+UhJz04+VFB<)hht@`@HAW%yfO1AOdDig1}P0$bcieqdw|RlxJT z!KM;2TO)AcOFoHwhRwe6Smc&#^fCX-mXmE{5|DoqHa!Wh|4QzWjrC*3ZUuEP6uIgsn4|&n{T=LnDy-27ne!BH%WLu$7_7ALf zoOOr)?W8jK%ddb{{1Nvt{0s{M5)LB+e8FCZ;HOW)miauDKLD1y^ZTSd=?0b6lN{hl zyb6zD-4HK}_;4P_${;(n#9k>-?T;N?048E_rGY$+MesXhAJ*Ot=ZK5729lQ} z=7(Wt?chZl+lPJ5gH85g4-bHG#v=JY#LQ_Zh&A|~zGNh6Ox?*2cwzwA1m7FsK^t^n zFj0WZL}-QbKwnu(pA2e);==UXEq{{q|H1D8xj2C_mC2hg*8C~rfa;lELQE-MDCHQ-~R)?V<% zYytF4LoyaQ`7V@Rf!5!L{r+JMh#wsdJhh-MeuRBQ^!-7Ulp$Y%^pl8N4`kUbU^WFF zu82EMMQk?)n?Fb9=*HggO~B+^{s)mU9vN>4{uC(HL*xVaWjSKKJ9&vU?}P53qyzkX zn+MbR;5UDo%D=Fgh?arak3amE28^VG`)$x>uc7DD5$Ow&>6)VUsmL3E>uZwXuylJ| zDT5!z{XZcC-vFZ>=Fw~baC@8Ar?ucw1AWO`!UhV|&mOG1HLfiJo0U;1nSmWU>jUle z9=2`(^|hG)2FCi39AtovsM{*?Jxm1mc#>K)6FRdha&}R0#Re|Y3c%|e;)L1{g^gms zJj;Ny5X4DIAf-M@0-81UoBxL$+(kSsLKg2zzVV)jjl{olk)x+De)uu8Q&V%#Dtxi}`)n_XBZoKb{Y!9}VjqLsSJ|Ppzqs;0~;=fkK*x zIF^8&so)1EKZ3oC2Ady51E zTTj3ZU@c_$-edvT+k$ILP!Lm)x2KaRcr6Xq{Q|Xm4}4voyk&vJjZ$Q*O0*eOp=i!v z4@XeHtpQ67g9S?yGuXQc{|3+hKrAQn_o&Y*LAwM}1)lDJI~-u8`4zSgpXmjU2a})h z-2yPlA~5P7V52P`N%9f%N5L5DQLO}mt$whdya_x}9&2kx*5UJCQHL*}W%0EqEr|-u z1eA=!eWvi{yc+L`dgTc>(3Y@pJ8C2%|Hoc~5p>W&Z^0uwSp#+t^%p_z><66PV8clv zD6-|e8~AP|vdA)0gZ4$%97$7vtBUYsC2(~Pa%T(p=m+0UI>e*$YUW;<3HIgrbE^C z6qVgVRIL+Xm#bU{<<=Ga6omZr8tOC*o|%J4TnTpg${T{asvv&O0?*gL(mvShRVd${ z)P)n0$D}suyH%(;+K_UzI&A-rHl#J6R(2y6%8(9VN*`q04UA$3TfkyVSvgjg|IjvQ zjZp!d;x*YK_6b@@W(Tz1>>~TA9b?_@HW#qnt$RAaqgT9eYG=uD>%Z2j7Ex{=6 z7K}mSPx@=8}l zpQHa^_+zN%*4Qo5t*ZMVH>aVS+bqLpeQCosLvuqFLp8mhvPjmX;=*!%M62LzZYyU! zWa*guD|>fVQ0AeuKgsJ7t0eS|pBEPzzbA2TnzwnFBUo@Z^z=+C?CCS2@ccq^JX^aR z)bH2jDT|bwvQD}z1Paqh3Y$va)A}NlOX*a7Ekg@8!A*60Yq+mpsei1ir+!m5%ag=J zp}p8j`Y3f$x~n~u_j0)Sjqh=V+s;{@nStui2q6JAz)Ct%ND=epE%Hn0nkWdzg_q)J#i5^ONYE$ia+EJ}StVV0 zs5aDl>l>&`l`(R8X`nnziIDx3BqdxmsuScUal~8C!AgN!NqO`lnhu??1EdcYixGi)L*G%Ok*6feAnP_>|^|@-y{3V zCgrB`Q$3|#5)YE8jA{c}A5k=zJ*s(6^Ag=lsnOzXtXY*#$}81bYCAbl*vcQ^yy1}W zRI*6#geF{chFa`7Jxqf$bJDk`mrCoE@+^61Qr*Ob>DR0y#qr+v{aXg~EH=bgjZMm~ zoIE1QKeL>5wXoH_i`Pw0y>W-`y1YR8BRZr~@@gT2Pt^{%p79A%O+&877q1OoWsOu< zMj9r(6@2A=$_KTM&Qm@hnCVWS*NvL#J@LK}PBJy4!#973`KjrCM*p-QsWB=2lNTnj zr02;;Gw<3x6=UJC{`>ta7X6|>V)IIOOX`>0Fw5wepxiO;@jCB$+MVkfDMgfH(th!} z7)oofWv<5BFFIV;)VRt!)BBQftNtgD*HySJ&Qm_>YU@XXp&=3!D*{bG-W-b1}MyT|C9Vj9tj zqvT}euKHN5q_mN$i55CnTq^x1ZIH%^y%DpFZ`EEnK3leCpHJVA5|dafWnkLQ&1pwNN3C*1DTQNoo)_QAXV< zVYgid)=W61UUBd2?ee~E?4+9`^%A&nSAM4^>9*@GDQRL;;SpUg{1Us!&*hF{YoQxm z$GWg^m(zO1Y|o~d8&acEKBt^dX_MA8b$E(r#`%0Vc~GHN{>=hT7g?p$u~?HX#)T%8 z%_+-!89I2a^q%82!+1dVNh&I?rpx$um(A(Mp0YUVpg+L8KSI7QMvLp^5Ve=?wLVC1QToWG#InL|!6aOlE-2HKS<+u| ztGJGsoi(g8&BL?irnODmnQqT)mAy7IGPP!sk~ltnp2OcztVDRwNI%i|-qA62cg(^# zpG+^;6}{EFMUgMw1B?#cYw-*eSFAQg8{*pIdd=TR_w-Jq$>Wo;vwLyfKvIhw3?uacYO<6fOcy@UFLaD}Ou8fAkpD<^gcvQ(_QTvYyBOl+R$A$d_L&)3jWTbh z_$2O6ZjpPRxIBaW8V4LLvPQXP$xov3Pg5S5W!lvkQTSgW>UrI;T6Kt5ppZUjwY8e; z56h(8l#hny9&bFR7^fQMsV(HP@-ih>8K7IJZ=n07cq=~A6k)b7Uht3}0Uei>T8b=A z<7Q`*{D~&d%%$mrGp*Sta}2p!mS0-!gi|qZ66)lxl3o`XSvtM+;zIE}*i%3Nq%VL;rgZne%HTOzxp}KN%wv;PZQvRxc z^z98UeSO`0`LN_G{S`E!i8NH2CLd5LEA?b2ZSV5Uzn;@CYjpaaw1=7I+)a72b5>?3 zNi+X#h}oDK$V+(10pClHD%M0fVR?{zE$&@PH%k_A>1%pz^6u%;-|d0UO9~ODQm!p< z`nu+DtMFE7VEQ=3WWML)TRs_4JyHtXi;H8oNxstlL7xJb+qKg(~GIjXFl zlLpb_nwve^yxv4j!*T%F|sNgdx@L=GOA|vTwlPkV! zTvWPhyDnxLs}@T2I%TY9D69sFEl8+#-x2G$;+jvE$iMYb?mLW6-17_v)T2_A_)c1( zw9%J!du`ARVY>2i1L-O9dx#h;ZIoq&E92Cz@_#~S7H_SR_a%3c>05S8_OI*`xd}O~ zG8d+HNGub#J;B{nOgQP)#xFOZav>M(oYyBgI3YLVp7o^A+0ET6)cdZ{VOXa#3*UGG zTj*-${O6iYV--I`QR5cl7QLq$uB3>w1p@9kru(g*X}GE1s-BnMOFM-v!a`A&vgBo| zNiC#ylbg~6XL(y@Uj5wrrc0*sxhKuv%t5*Fnb(tl$F+~)G3n`bnc&gi_g`RA(O2^H z{DhR>@jUgp`4M}n`{NPi^~j@)`$T;|Nk@E`&iTe3=!#}1#OFG*TQB1*_r3ah@_J#a z&_fK9Q`KZcoZEiGeEmGNwp>m05bo2B!dl5qw_LwYwaa&fVtl*ni*26eSnhSxL(`_5 z2j*9nO6JFzw^J_0-TpfzR-ZjWSWzfB;B@K#d}fMO&Giy5#1=~(m3LG)Z*1y)+UvN- zO~W%KOKe2?YGWP$>^EIa$sfsIU)VU`8tQtXCok%nY|~r6nh)dz<;LZ#%e|W$o_pBTHsfH@!4jyvIByZ&uy~vsd=u z6#w|&(M_UV$!A^byl$0xRc@HyOx-C<+oZZNPm-3Ir|?1gD6dV0o_NeRyizAhZ^>-! zgJZAbpK~rhDjrv(+?u#Q)z47(%LXw>sE+K?UHz(`s6VLxPxncVktT>k#8ZMO4w4Tl zR$Yot($!F^Q@a-8{A91;_-VUq9hX1F{N5Cp+`FiZ_=xH&PO-}hjQC-<) z~b%ZJH9Ey?lT`p`a4t1Q&h`MKS4ui)NH zS4X-jM4|pYBQ@0(H_SB5GHlalDBq+@LNr|?tQQ64iRz_`(cRRolwaV{$+&QM8tmzqJllRB{jUAaZC9jw+qPQU_Ga%MO@2ZgbK0Y~Ndv;$} zd9}MopjWtYncy^5o~{Z9V1yp_3WrV^&& znTt}h6Z^zXjg3k1b{;eO`GuA}<~L6_DgSNirnqg%B`mq*y1tvI$#ax@kUm{KA$20{ z*%{|byQga@u}dxWmEEEZlXO;Tiug@MTPFv(^;JL@I zLy)`g7TrtBwA6YDOH+>J%_VDfl{`Lsjy1Zw1?wBgv&d_g&QaLD*13-?lmF;W8`>It zbs6$RDMCCf&XKZZ4>ehRs5_^-s~%JSNzsBN3=>v}8{`zVuU^wNQKv{&JlpE5)pK=s zUUtOW583|Zm$OtepErHa>Xcb3ZAH@Ggph=;DfO(G`k}=(mU8$tGM;rLWP~SLll*cX zXkFC4Mt`qxk3cuGZkPO-6w+Ebn%HMLpYjN)jc$#8n!d13Rcz8asj@Us(xj{MAtgcm zsXL-usQ#0$i6w;k!gg_)yjzXYZPEQynoHp{n9tB6Tqj&BTz{O094l?VEnUohIa9LR zW^K>#Obtm~2dpGyU8DC4J@fAq__K(&P$#!eO3x&>%;mOULJNH-<1mj9_j88bx@fTy zpY1GRZ|?A5$wDX9tjpHb)YVa9rPES9DM)%MMaUPF%j#TRTb-m{kP}5;p@m=-ugYW8 zmAb!bQKg;ai42p-KC_LiB&(w3IsRCiSi;S}bM~6PWPi>qoYpI;dcy65ujxZw`NrNQ z68&2jGph6R_omlO$xiQN`N~ywj$3Ds7slOg;d&prDz`dn+C|4)%~z!`n`u92UM{=!*0P+TmhD1~(^f%1I0sI;1P<&9XZc3XR` z4biST7uic#m*fr3wV3SL`?5mP+a(3YkBT>@uD0JZek;)`;AwF;-4JW-%)zNGGgn*g zXdR^;I)|HPJZX$}b64;2>W*2cn>T1LgcC}b&PRO@1=mx4B%9^sKz4tzkMvwAtM@Ym z==&*4#Qn4ZjT7q2-fBJFKXskbMa~jW3U}yi62=Qk*I$tM^dLGu0>`6BbcHxF{}>|0-?O z*GefxFSiq~3bX3{RO znfw!LtgUhi_HOy%c`I_enQmtePFtDWIH`Jat@Pe`C&Yf&9C=O{mw9r9Y)ByW%hg9CB|E3}1=I!RyD zz;w-(0PzK-LcXw2x-5q(Ka?n?yAmu1h)d}sge6O zTgtqex*)kiQe0AEny2|Ko$S@J=n|g>9@~j2Z&mi+ERws|>d)?pCv^TEOT2y>Tk3BK zZJeL2f_;VS15qUnD&0r%Qva&^lt6j5c!K88!_pJ=fxd|0pRSS;hAL{fa7~C6v!&_s zIAw!+P93Hw(js~j=X|GlQ=Gz!>$%Tb0gbDZJ;n$x#FgH#uz;f8KTKm)_&UFO4~*2NJHeg za)3NuQNWD@z&rJY8p2d*km{}v){DCH@^A6G=q0&HgQc-jq-2%Ls^wL)tVo2(~nRclCL;Hm?9pP zlhnbw-|9AXyi!ZvF0~iEh3Z0ov7(%$)YCoEEmNOMgHbo8Y3sR62hi?h8ne2lxX!u^ zu6y>1)>?T1In%ONWFAj@n(``nR7#1Aq`dA@g!knl7O!f$I9H+kT6u%=a`Hm*TRJBQ zXY^yd`g^Cjy^_n3vf$vZ+Ac6+Y8xvyMRUdv0R&%$M#+Rnu3q!p*0^Kq8nT3hQJ;CNVKSSC)bkBU~Ms&2mSvHD34LH$)mN)Qi7EwQpN^_3c{Xp%{sCw>=B3ERX} z>9#ypd83??kBRMty7U|WfHT#2HiQMTNLCL>TkGuMxNkjeNz1L0JuRbcdPw^DG&y}y zR*$?5u44KD-Zed|>FWt&U488}tjjH%EsL$Ev>chZ&+ypq9-+G~rJ-ZQggC0;iqdwF ztx{{Xtgeb~i@ILvC{K`Pio1lx;&%Cx5~P+;MP&slrbS{qQ53l_O&l(z%La8e&WIv} zmb3x-D$4N`?Vu~nrPq3~=j^!_;k<4iVVjXx!NfBnQ!l6JQmdvP$nrJExsuhLMo*8L zh5+dSOLKVJ7g*a{qVgBHwhAM3zV5_rt!}!yNM0;xY!|ZY9&HL4jY__!@=IN$>!Ax* zd&=iUAIVq#C|^_t%NbI%94^apj@U#TB@UM6%5$a7;vR9B)KfOf8$=5+v1r#ATxT#d zupn0zm#6m3b<$DPmXM!lu9STu-I+EnePH_AOqRRa`iZCON4Wpd4^kcrrFm=TPTQdT z)|Pg5mBdPqbaDD+`gr|k-5GfgPNV|aDJ_|`B@4u@@@GZX-`8)}^@c((E6@oew9c$Z5hU&VyS9WWr_7oync~@)u`TPp`zwPBoe_7E* z=-U~7>c{Kzm2hD)8>De9luhRv?J0d!x*2-AP0-g+mP$U-7;&k%QEY_s+llfS#Y5dJ zWr#lVFY%U8Qk+Wt=t0q45+zAeg!MRA595*SgLcXl>Z+=>b(L_Gv0t=q%4e2Yd4F^M z%W`I%%&eOI*VNCv-Zq(((3x~2lx^}paUdPW>N_e}tLA$-LP2;i*DrfeAUHMcg8e7VF6ka60`{T7#3w zowSZPS_r0h@x)~>I`w>+h?U)OzHkn4Ep=|SyDU#E4K1@xQ?k6XzGoiH`erJV7h|d9 z^bpS|=ajl~HK~LcLaw|?d7L=i)o3YKcqmx-CUjSg5_(E)<%P-~-A|nc=BTaK zQJzb`q-^Pfpo%-CNbxozbU8lrO)M||5F^9|f*1OQo}**Ljo;Urx=K2)y4*FzImzyB zA7pp4wYR*>sbu<^lV>WF{liqqoRW9gn&>=M4hl%S2j;@El!cA$ovP)@$8a+VYRkuQkltX2^7$sH_p9sNX zy0}I9DLxe=BqrtvzXe~RH~mh>(R46dCzhc_yJDT~T@FWc`%wEtdu{8tyoKiIoad%f zrZPFja)#vYv{bXba|V&ELQ!d^yiB?%45Nd2WftP9;tbVl;<-`@Jf#^VcTpqM!SGrU z#Gegkp%%JZJS)0Mo8{$7wt7^TuTou*x<--IOenTql0O*Ph&AmMS0Dg%l2u$QKBs+Y zh+x6lz<$lH1+YafZ|4K}^NTIjy3iV$KRfqxjxF~|PK>E~PT|~=<_Jq=dqY3iR_oQhE+lDz!BlLv`ac6$E$+wxWc8^QZZ?wP*~U@9RDA$9ag$3 zZ@u2ujmp%_SJo@f-whjP%kUPl;uiu z@f5o4IFh zbWSyMA@gL@s~lHes&%sch<&r`8#+yF{1b^5MhoHSjtNDdP1z~0m&0Tx-Ib`^SdPS>5XGXjQ%)#3il5Rz8KB&gCApoHgq1xPCes7t z6rF&sjivwVeN;##_SH2-Tj9!bK7mGcT3z{D^BU)cn@u@`b3f*u&YhK8HLs8Lo$a9g zm-D376Ws?Pc)I(DMxu}S6K_JZ(Bbfrcf|R^N#UYUT`Vm7D!t`Q*`P?WMc%F~m#>5M zBjiN+HPmddazx2c`pf6#mdXp+CGV3Xq!2koZYh2icS$ekFg!sWf}WM8tTwBLX!xXc za}9TWaIUuBwhgqkwr;lc%j=W3Blmc&F7JJAx4fy=iq`JdYL26>TiP@&OPhda=;$Rz z46Xoo`_szkc6%!f6qnGmf>A0e6~`(xrG$k6p+l(pP+6s|vGs6q!O0|Dhkb0=fY-^c6hD^W!~ikLIJ*aQ<@s=Q21NIJP-*t(Wp&=FhT> zGNvh=l8a(Fm4IUl%wX;Ca!o58M;G}?*Qr;q7jT3ZMet_z~{PRtb3 zrNuad_Ea@xw5sU#s7KVkx+|)xwpZrL(Q-N1Dnf1tPZIfx+(5Y`nS~^A7oHcsrNMX# zz8eVn#7gqZTB7z-tE83JRyfDIO1ie#hS<*923QmG4f&@mk1PZ8gY#!v&RdV#{B2k5 z5zgAKGtR!QOjn5Z4wds=bQ(WIxAs)@3Du-kg>!gP@J)CjmXM&lo|%Livz&ZFY9i)}6~&!&484R-tWoG*dXHz{N3gs<`SXYIokpqABR2# z1G)%yqvz-lT`GJPbYhxlkgiGB<$Llw`Gj&_F)FVWKeZLme@q!6Z;~6STNRhwRH-Ak zlzK~VrCT_clf)k4Z9x{Mpr@f5{g1y!-^K)Xge7YxSB`5dPN?5Fra6KfE0E`H7Jq9K zM}2!c>t1WHy^!OjwXChXbD!&&v$x~Cs{#M5ZPymDVd&)Oh^LQ{=$L;^N6{^0wD6xW zfW8s3kUIv+|D=0z8Kth$LS7-QR!S)OSkYO@B#%-y%2D!u`6wb-MU?E84v1^S@!}7h z@eUG#g#fw=JzO36H|>&k&GpuK+?npuxjxu?J9|4j*jL!D+tO?|Y)Lk~y}11u{M_08 z)PBa%!P(SFU0bxr+B(-jZ4!H?oz~`|E3qAVV=3xN2f9Q8g`e~%y)9hDImT3}tGpT~ zmfhrOvQ?@fzn8lz2KBgVPzx(-lv1jP%9Ou2Bl{}OhVH#Ac#8GKFSIJUt|(8`ZnN2} zH#@+NYDBy4y6o)lIBGv*KVlngr8aN-E!!h&fNh5@z#i^cVt;R&>S*rT=i1;L;3}^f zT&b>0>=a97yV)hSgP&x}xF@}UuDX80e0q#}h$qEI(k4lfeqSxsux)nV)_0X?&kH6Erad!63Im`9J z(aLe$dBV}ie$+m}?q$>2Ydc;zIyz6=2RR-)MaM?REO7K6=Y3~m*EuanOLc{#m!T)R zHXP`O?g`ecg87nHV9@bYFPstXi2aa7I*VTgAL*$0T56a zlxSoxLGB?3O9O;`(C9DmJhuv>qZ|3ive+qJ1d}2?*gaPqtdj)Af6pO1w>gWWx=FBy zIMz8TI8&Y1oO>LniH@MxWD4Cxy9ld=6k1A{AnX%riUCq#X^P|p-8BzSMWYK)Agz=h;Q!By&7st* ziD!fbf)D+TKBxNVA{?2$eilah2h^M7B#bx+A6;Dw|i)TcC@dqMowD1I4vxx8;-70_4>BTV} z^n}gk4cTw>?6pIsSf1Bs-smJkXDy}~lC;X`P4wrhm=|xsZm@a$D%;P#X=x#ox1=noT>v9pm zqHl8TzkLC@M^^lH9E*WYEngQFiGePsVw20HI@ z&^_-!pI$F?0)|0_Z6!nK5p=4x7UFoc){~5)*U+8Y4xO^&Nhw^nE4sc^W(+bgi!hCRM88xjW+C3L*rH0vWXku5<}A0>NY~@>e%{oSsLwdQsf#Iyx{5p?|kG z{Lv0`F742Hwvz9`J^V3wI!st zRz>G(6{1Jy`gJ;p_XK7d<4$JY18NMjS3tB(IXeFS0TsW2nGp15>(L`-!u9U5LFf=( z&u*bd^B_73ucFiU0w!)^asL$bbLaBU=u10?Ubk;3Ya5X4_>~7Ud4F66P>eO=)SBE)J(#}StIl@ej|0!kJtg-cz4jjzMsFr zJd+*D`WIF=2(i_Qd`F+(DWFs!5@MzoeTtX}!30}BWRQXm_`8@pnTBrc&9IaQbVxiR zbvQm*0<&V((7$>RKmQ6IuZw=bGyD)R)&_m3z0rl|3B)hPPUoXD(}2FcOsM&3 zGxpdA@wp9M*JI!r&RW8+b|{H3MDkwDIdlS}A7#Dy3fdJrUV-Tk5%a# z4tEVbk(tB;J(f18f`eH3Hek(+UED(UZHd0xUeJ|=Fzs_3^9bj_3L{~G$(TgCg8lu) zSDzV>fF4^){;_zn4wEW3@Tqr*zpr3fboeqKD8TLDh?>A&BOrPP5y0!0&;>e$RKheG zdO^`C`IE(fMXF;;YXxQnrz3Jgz%o~1ouRn5FA1Wrv66}CH6MlE@6AB58zwjgV=}-B z{b55Uk}*5z2M_KAHu~_j=!ag3Rb0X@refMC9R2JbsC%ZN16jso(Ky8S9?W`tK+p0+ zumVTdX$E5Q9?t5!fG_`I8f7GO!vNxkJfDGyx8jHl9ndzCZ^lIHCg2s5L%>cLI*WV5 zlK$v+?u1#n4(R%Q%y+OL%)wU2rx|O^3nP;HV&x`$U5Zr3bl*(GPA7a-3>XB{;<{U4 z0gkwOA0l z7Of2wc!9G^lk2cfZ>ZQru**Ffj=94L=wki}UaJh-mxNX7z~ftaXSx`(N#np-g)r@O z0&!53e#2Y@#qah*rW!)}uwSeR|A^@&FX{m#65u%vEK&#&vLD&<3VO=xAzR%?Rx0R~ zP6j{tB8o@xrz{q8B6HEd+XwrZi8&85I(~bg8+ZqDZCBv(GV0-?sA8R9ooVpNKFmTT z;qz+K~@th^Xa1m3*pb+BFs^#4A`$weV(QzN~NthgOa<3k>z3;Qt;JrUDCbAYl% z!1`MB)lUGz>S1+{StlMw+hS(QlVq_&P!faiq@*J_HUnN>jZCORhFpRvlo`;eJ;1C_ z;KM=qx*|HK!x1I3(RW=G*7%M-`&@J<3z*L7he^1Lh`7G+qzZqXL)E+m(fAN^wUdzT zwqvGc5AZ*UGR%wBO&oF7$ABc=Z+~*=&8}aFBu%92Y z^FVH9O~5w{3hpnkwFw>h73p|fS&wS<38q)0aZMQ+eh~P&H*%I2oe#uzfn5UdJ9o(# znvTf34W81FFJHmx1YPsD!3~u$Dc%5#vz^z+{Kqj`6bkVI7$6B9>W`su2g0W-!PPx+ z-FP7DAhKH|cD@<$pMtLN0pNve;Hz;^@CB3bbCAV4@v%@gL6|2P0B&9Z9H;OT;G?IQ z)R_-uwFFFj1pVw!pr>c^+2E6P=s1tWuC5|}CK2?1P&4kjif@IQm0;y?L|S`NftJFI zMO%FT8u%>}6Ed%1$tH-Z|8N#i7e3%nB*PK^AJ{Bp{%yzv|A8OtVAehy*}e`CG6s4h z2|T!#RG~*{2T~d8w*n^z zz&n?K+cog`SJ z%*~a-8jtgf$k<)bi~fMeVXnkEDpC770FK0&?G82$Z^*$c$vV&JwuW>=nqF*YK)3d3q4G#jce7ao-1zoz6Fviwyb_ys=! zR1QaWxWqn_7}^+IR0tYx4<_Hrk-5wZ(<3dRtv=()hzkC&L2s7Fc}h5}X2iaFVA|*j za(^%|{t&nppgx0P=S@7D*)Vmw94qOKxv+_Nrm~wo#ClB7XEjM_%vd!J7Bi2dIcGh(8yuI3A~x40Gu(z^c!n z(|V)!S%&Ev#k@ZZU>p%+#+6Zs*K{s2ZlveQ6iTc}gKWdQtB7%12VOEyO2zQtta5lr=c zfPbdZm0*FPxW--R*H!qQ0tIm$@8noUuCXH6(S5{IKHHCIDqs*ltd-(S?=H{5Z)Jcn zQxLaF@LhSZcNt7&rSTZlcu%2HK4LF6=#@UOA>P%1{PP)i3;{z-FUWmB;c#H@B#;t@ zE1yS9&I3;?n3Z3N+TaB^b|dYEyY5HMyAL*Tp(I&Pu?(7EBQRVwwCVB7Ot*HW$n{4J%lM6}1IR_rnz@0+TIJ+4R6B+hU_(h zOvg!BIYeC-oVzT*{GJ;st!t=~eqde@la{EtTS2=F1MYsp5-XtEXCoiaKsI~}jfV+x zXg51DNE@)7FHVQ^VY2`*;$^VsL$K!)*kUxYd@Q_h3idyNdSwoj!XNzUhKP-X9_auc zxCaZK#AgR#{RMN%y%16H{3tR&ZP@%ie*?CwiD#q?)oMLpGyv0z&)`)L_~bq+((S00 zs(_n&5?`Ff(}K#3Zb95wu$O&MykjwKy#y%UjjR}fsm3bscVDb`0;2pecHau#yoi;S z2VY&nT$Dm*L7AR}(&$bLGIb~L_6)3lCT2CSqaN4+o_UD%7st6pFFd&&0~RZR`8*9% zY7U@d34KO7!^baidQuTc6_Jygf#<7WVoX7WUIOz}9l(QIpzR7HV@D!xIqCx=7`7pz z@EAB`74mX9%+{U7{M|xi_6{6+qBLDBES=*&^`UMzC8au!J9Mc^FSI z?ck0L&;|EUwX0zE1K4W}?%n{g{}Xx64!bp^n1#d@FQFoz$A2N-52MEJj_OViF1&!# z4F)Y?;15yDOaxCl@ckfW1YeGYw#3{#*u#NbbsSY=0Os7D;PfO5&wp}pDsTl>?g+~) z0y}g78{9+OUB`6iMd0lqvLa9qR*Obfl%UD*h5^{A8+`;uX@x7|T_~7hev2#AfX@#@ zfzHHwBa!bkwi0jJ_*pRf3p_0Wy4yh8_D8L8mEC~~bTKRMLkGaNd3fqk9o~I`Qk>xEdVhm`p8>*#3&Q zzVyT1qVO)4!SLcFc=j}uXhZ0@OTbujSf>KMe-&qI8hkh((Y+m3-Hmp-s^CiqWJ?0lNZ#)uji-|&O#> z-YJm;g#Ce)4&xl540P>!oXn+R#RYF*DvAts8i;EMeYF+u6WNPt(97WDgRoE}6yavz z@BllH`urVR0#=L%+9tunzoDC7V-4%UAe%5xdjpwc11h_wPzpLAMTcp`nLtt&YKKzD zTD4)-DagfLpbpb9aT|rR9|{%t0nBW|baf6mhT=qsz~5eo?ai=s!F!peqRL$f+x|j^ ztb=?~a1yf@D>#je(iy(Cvw}Cxqyj6$fU5!&PNxmQT;C8=OHuRnK~?GpPTPdp^9X2_ z_E6V}m^1ATP3ORgk`EM2AmXAX^2Zb&h^(Cmo+ts7P05!@{d}0UQVp1Re-3m*%gNOcL>eL$?Ulx@yCgI`NJb3aK`1Kst{|Toy1kq5S z2j@V)zT>ZOy890KT*4h2VD($@TYIs08#q1)JT)AeXAg4MTV!Y>DweU>$5&nj=f!rg z{&HYp9P-k7%;VOE2QA2ukD!R_;0&t>_V^3mJp?~rhT`o9&g=}0Vg?7TS189K2Ra?h>GD@LkB7Y-=FZVr`4EL--mqHfX+lFo`H2%Csk2v9YGzt z8$KNl_NV|J?G2xOz<+Nb-WCGynaHARfrDUhQ8m<>-Eq1ZgQ_$Kb-o9lk;s@z-^wPy z2S4%Nk~H+6R)*!wl>{g)F3zII&aVz4cIZ!*J&K37Mom^yL@kz}c!VPS)dqsz0!oJ8Fq*KpO>8 zUP3F?!_^KzS#+XSdJDW-7alX>x5H=-P6qbjUtRG;#sseph0Qbkhj135POSjkAwI;HbKEDg6%J(hUBNLfl(eLp%-H3U#p!944UV?24;Y z1Cz9dO4?NLJ~c2x6e`R}e4+_h%7~flW2mou!DR1IO^n0DRuSZ)HNbQ-H1-_Kvww$` zhJbVJ;4f5Hc)yM(=BHiY)>J&v4@3XYO|}6_`5N9n_7!JJar_z1v)`eb=m#EniIexY z%nwgr3S&im@xF}|#O+9&Up>J4j3$A1eNnxiz;|jveKdx4_rQ5{HugCKsNMxF>xbxO zI0N~KRrLj4)`CTT<1}8F6BXw;oN(Fsd+>8FsDSfOem3O6V4PK$pn>lI!OMY)Y&HXNm&RYR3c%bHAmR)> z@e4ay2$a;tE;^y&Ek^Hy$v*OOsP87?jA{;I;5syQTU?_lR9iT01eW`U)BFtZ_cLUa zJ;;M`$XYY-wu++MpWOrxP!h=kkZ(6agPCxWn~3wMBiQ3d#I7gmh5~k3gZ1NSHnh|= za9jjCTYM z0n3c$7S;oGK4JtOOoGz-h;_|IL`(z*@zx2{>f@mh&f@1opp_oLTYXT4)kPkbV99HU z#m4aBZQ!*Ve!Byn+3m*tJP_L>@nq~hczGz8^dQg|j2zJmRmx##+lI*C*O9G90-H^d zcUMBA;5`b6%LVXW3~GyJs1;*SAsC?#@J1PUBoR+IH=|mviY%j25$dNro+k#-JJ@*| z(AE^#nE@s54qN2F1{Hwv6g~rQq49^R8H_5#gs7ba-Si48mvLfU4QLTig&spBo=26J zguHZ;XTmzOfVvyVn1T4|0-Qe{M8&WIY=MbgyeaYqJTeTtl#Gg|8rJ^-3Sbic6uhA# z3CgN3-Ul)lrwIj5$S>oWOEz@Mb@1$9pm}wHp2C|k@U#i}b3Ed%BhE(7LFEj<3J2j$ zh_m3sQefDJ$SouA`PM^)d4vDH zqTLNGNJw#CQTu ztp(io4f~Gs)+SI9N013!&|(9T&4(k~MnjQqK<#2-kD+G5z#Cy$Lkx1mX}nR#54mX} zSgRP;zYF-khnoH(_Wu*-U}m<5l%fu(z&3O@>We4fvzbJtMp6YUt3-Pvw)@g*KyW5} zkc2x-WkpfnPeT@&&n#pqor|ko0iJ#WJvO!%J4!}PI~H7!!mKPG`>2PNF<1fhF};hu zKLTUv|Hlvwfvan{=Lwv=WPn#rfkz8!w4KPr3aavV@Hoyt!2s`AFQ|wii1`zUG9zp^ z3Ej>GZ;zUU43y1RqnF~=|N7M!;^iF9{kGy1*pA(H>Da4x1!#_r-P(n8Y~8iDAO78S sMCjwaU1@;P?GAFQQ>yB*#yH5`btXHiDY16S+cLkrJ_)Gf#9|B%15C8xG literal 0 HcmV?d00001 diff --git a/include/securimage/audio/Z.wav b/include/securimage/audio/Z.wav new file mode 100644 index 0000000000000000000000000000000000000000..1fa83a48dc2962a52c41fa0cc5410bf1992d4be6 GIT binary patch literal 22158 zcmeHvRd^gpvt?!$G^o2}W|^6pnHgnfw#Q6kW{>SLGmS}RW@ct)keOt$T8#yndwTwR zclTl6_VrqyEUCM?Dl6c`iO7g*+oWN`#vKUhSf@jS0mH`TPDco#_)1%hki2-Eku-Rv zU+=NKLw_ZSZp2RBi51?u0JP?h@6;hp?7W>FC z@l-6Q+i@OB<3tBBN~q)x`9W5TGvWo!L8{>VL&Pspj#R`~AF)U@7H_$YY!wZ}P%@qj zBu~k1lAY8e8Oa%OOvH+(WHT|vR&t#TC3VPhF;q+=H%T56CsOflwD^-e5si5RQIMV> zP01lLn@pg4Xe4@xUXe^9b;J)@aQkUEng~({q37@@!<5r4naxMxQCK)+V8iL4+C#8h!s zl*MWul3L`5*hLxnB0iAmyq&l}8q$H4rY8v} z8^qtDJ63R=+#;>%SK=Z~$OExdyb)PRfAW=A=e>yonoSj>vF0XX2j1TU`D7xe$O|!0 z%oBwK!TN_oepkd6qG4^>iCZis=kXh*e~HP$LTZogk3&{^loFZ0`rc@)1$vs$i z8u1$T7D2p_-EDCRTAfNpkVvvq#6wd%#SJl@TosQ*TC$R)BbCG!*sw`Xkg?>es41NI zpV8)|9e0a-q`x>oGLV}hSzO2Udf~ij$rwHdmK_v}$!u6_50Y1`gr-w?ys(iOxR>W5 z2GYup6&w^-Vd+i8QL&MfB)|DGp^>$)=V_2oDH2Aul5Mch2(p>O-ms<�lGZELK4Z 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 A6#xJL literal 0 HcmV?d00001 diff --git a/include/securimage/elephant.ttf b/include/securimage/elephant.ttf new file mode 100644 index 0000000000000000000000000000000000000000..024b076e5f0efdaa91bfc54ea520e928aee9c52d GIT binary patch literal 51652 zcmbTfcYq{inLqx%Roz`()m_y&r|O(@nx0s*ld5KRc6T-}uq?Z5VqpV$$x(7rQBV$0 zi6TlcfQp9!M34m(#5027t9Rb1XE;5T?fE@#Rrl=d-rYaH+3A_?u6L^6=XpO*e%^Nx zMhIEZm54@%Cf6x5fB5*xPayQUR}pgl_v&@!+Nq(#H^Yec;IZ-a{o4k3q-G}zc74Z7U@H(+~|9OYopY{F}p%1`I`tCD#opa#6KRrw>4xYY!-I3b)2tBwG=J4A7?S~K17g9qAJsgBT5D#qMzw76}{qg~LJ&cg)?}rYa zd){MHkLnS6dH}}%>Cid54t@OYPb~s{1sH!X!uVhD9Y~Li^tJfw@D9>`!+R%T7(r32 zj@9cankJXX_5RmHYC=R368}NbU+?}EE`A@^MEHpQbwu@^JK^u(UF?Sk@TEThle7%JdsSLGud3eP%M=z)mpvLY_$hE zgG0k3qhr$eiiydo=^0s>om)A-YW13hwd>Yz*tlu)mXl6CW$UTiPTP)loW66{?mc_Y z*mvex`wtvEboM#to_GEQ7aqRo;!7^Q?D8wFyz1&}u6^Hi*WYmCO*h|i>uu=vJJ9

    Y8CgV+bD#gh&!2hri(mTk zb6@#q^!!)9_VsVP@ZvXL`qs;@eEZcSuf6`pcfR}PTi<°9vO4*wDEwgPyk2l_8Z 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 z zR8SBo@lc-6Srn^9H7YP$BT9yhumm|{#f9ahi_e8+<1r7NnA@J~N)W3Woa3S=;-(7} zfD?2XXeb9s)Pd%L;KzGhy!7S+XXb)~8z;xtfa3BLa>h(}q$4=2&RI6xy$Kku_5@WHx6zjZcUiDcBWSlY25u=&S#9;k5JM@^JZEc5Ok40O z0g8=6VN!>00X2xQs@~{QCWa^@5;_uX4VZ&}kcv#g zLO|mJ(5ct5=XEW;d|bZ4X`mZqjJk53Iw?VT+@4ko9}??MCmRiUe7I<t zcuhXj&q_v7Y@x|%kpPG3%>Lm`uuZ*B&)pk(Ojfrbu!iWk=0)OFZwoE?Ljn+u9W7DOg&esPff;Z%i25_qI~srvFumhCvSkn}B}#TC|*#8zEJ4md6)N;7K^pVw=oDC>{ZyJE!M zi?<7+fS=!b9wgox^XuzV1I3`*>9<+Ul`V~h?HKsKliKAz~$1$L4MFR z=pOP6c?W&XYO~g?Hyh2S$z(Pp6NOaTT#hQ?0w<-Tka_ep0<7%1zT|nmr)VkoJ8n8AwD6auK~$Afzy~)HjLZNmK|C zqMH8KQTIQxW8SbihrjhJ)bL!%2*1vuF z75i&Lc(?1c_ib(@^0jiSRJ?4*4S~UGqWf3d+TF!UB2^EUM$QL*KtDPiRBe4-3S&gk zp|Dr6+F4K{tkH=N zic`dT7&Bs}`~BKY`!7Eg-_*0IV06Lt?;bSAW~MeANSAS}5{+pV1TX$ObUb^3x&)e% zvevkdNht9c!~!->KuR2OaY(B$Myo9rcO_6r7F@gx^>a1+AxcXljI4u7C)gH32KWif z;vGn$x;4xC5cV!MN=AiV+^OKn6DYqw_~!q()YW z0UF{OosRR{H{bh_Q(xZV_Bn}<2!6qTExxDwHq3koT_jC=Q<$mPGvP`)Q>_eTluC^& zN0d;I(?^J(2rHC<1bm1GE!x2xlrkBetUiL(ISDEC`Kg>Zkr`piL(OCbM z_Yn>CntmbJUs5%FcB(!pU18jlJZFmr^N04p&1T6|41#hTBe zrH*wt_l;;qjNn&uF1wLmA0EioJsd{KcwuB^V)F3Hq!{hia!JPpo!L}XbFuB~fB$YvDP)9-#y?bg~;ow&kE1ZIXtdq1o1ow8U0L@6L#97WfKrpJz!?KyHti#SAD5#+c2?mI^N?a=c^- z5YM2Oql9O`7J@vLBU}5;M5;Dx9>*X65|JuMGt^xSxB?6X!IUeX;JZFmy?rH?7K8ZB z2Jl2YITu@#E@Xb^kCkd$@>d+E>Ar*;%@|20khsg_YJXtG(2a^Gpr_y+|NK4kpaWr- zgc^mp&`DC=z(C5$TY5$AB4T!@oRPG07E`a;72 z&;`<^#bHEms5k*d0-g`(JvCt*ZDQzCCdMf0!nloqf^Q>}WhJeIbp%^&!iKAlO& z%@?I>n~kd{Dzlu9)BENdGuJ08mrXZUUKVrd7z1zfLq8#XvS6_XY`os+iw(6W9Pjz8*v{&WqI6&c(23_O=GG>YP7+fBzY?}Y0}1b z{%gS>1i^Q%qUOt+ivH|Zo=r}h{T`3Asg zrW3TK3ck+Pvo#q|omF)m`VRl7uBkTEkoD+yn!p0NU1agn&2+Eq*rM!ShcH%4=O-BJ z+_1E!);O!~#!k^0$hSvh^_x#yxWS-*m1e6Bli+iNYD3x01C~NEo1Gr5jR+iXv7eld z*#%QH8mS~Bqq7s!|4+2pL(gNg%h?GRTVbdp{vLR=1y(Tx;mjdvg@H*x8J~t}c@qj< zh6H{>^@%(I287>8o1B6Kjin}w08`E8yn(PcC%c`TJ0}Z}*9sHGK)9jRX;ox)@-{6! z4XObQiKa8)C(#dT*qVlBUM3Ez!UkLc-3PWo6+(t=#?e9|V2lb~=rW?V+12ZJdWJ2Ky1>#|II`kas5dwyYde5h1RkGZ}0t%5Wc zKl%Eprt!|}4<5R~5go}Ui|u%zX0tj0!DLqF!Rj~x!@H!RP%w~8WK%VoR}$c_Le*1A z@GM%lv832!9AQfx$QvlK-+qh*}Jj)2(x6D-SV zhYz1NT{7Vh`TRq3BmSb})ZwP*{Lm@6y;qi^ZZJ|uKcp>A&6&CG+=JJpTs^v3SPd8K zz3fO)mtAz56*kD(X~pd3S%x-=JlU`yyGbuBaq~35sYVFg6|zbIuVvG8MKwp9w2Nz9 zd<;Xe!AsX)aQD3j&b_#M#1!e|M`j!!!#JEIQNxGlHe7SwSvOyOYGQ6WKh?Oz6!wga zjf_cv71&iv(Xi{Rh_*=0ydfA?oNiWeI#@HOBn?~=hYe06XMo-;KH^9^on{&O6oV17 ziIyWAFVh*AFKIo~xSFPxLNP3)RLteqP?_;_2D>;o&uFX5)o|$chat^9{bjVu_s;q*>U6 z2SqF>=YhE0$|~CF^SEf5)PVMyO3`>u^n0TYBQJ7x z=Xt9tC0EKK*uS@S$3q8CzA+og6jzjz*>e|8UeNixuz$J~wBwtVWTBW)lz6d_c+MA2 zM7(0aFZc{Pm&?(Ktla9L>`s9(o;tH>ZK3Ut7(AV9ZJO~;73X22FJAl;;OqvF{w6vg zjU!*Orc^6jomS#W7D77KM=QLARd_R7MR6l$TrDK$LOd! z+6Mr1cfZXCKsyJ6u@H+*LB9Kn2LRdA)%cD~VQ%PWrGy8H&sOYBm0r!}5(Up9Dy2P3 zxcr@_6@D8ng1muufmc%RsQqkvB-sLj718z@ADP@`Z5Gj0*O!>C>mQ6Y*x!Vu|=vN|V@1UDfLbJ9>+|s++sM)=5+g@jw=8eDcs4c!NfC55bQEf(M~$ zaxyA#u&uJtQ3l$ak}I%EF3T#u0Gm@-BS$KP>;TFcA!ucNM%I8jRU;ebOmhQFp~)_f9`1t-+hAG-Rg#H?-TZzPT^Un`6&$j;K2y;j6pL9;dhaX{SFh z;CBGz9o-j>LH_O5ff@i3Qx*$m_>?HDd1%J2g5S!OHfNmj{{sB(81V0?iu)e`?u$|Y zP`@1MumuTl4lpM`CQ?lZRE_yD?Dki*huvj(jX{4X;I(H2ey6S2(FawS!iTEy;b*`O z%%WSQ%@8pSj7AhR%POrVN0JK=2*dLT*SCD8aUy)<%n$>ej7F!COpP&E0suBB;Q0fw zb~+Pi$K`UIzeB)sKmfBn1 zdcs>LJ6*t6S+3XU&vf66Lw?2;uxBBa9q;}!CXiP#pZe;1?w(|Ox@>b{V4YK+-#NJx zo5FUhJ6n1sn@>e6e}a55MA^O91y6TTdqcIXJ<*>FcFJY@%9X?Klu}L|gg~&f55)f# z;B6Ul4DA4vs_B09Q1L^5D73Mu?RtKi%Niq$ANGM50w!}VUq%5&b}+n*V!%3;am!UW zVN@>`#A(&&sj8Z^<`J*qcviTUlsn-az6H1ApsFp&?#q3R=INR`YiIUq>Kw9>SjnzZ zm2}qK^Bs;+xTe)t=blWd=fCBFLKBK>d%VI>#jf`@*->tXq+jr|N`-Jtz*0fT0v_)YP)0$SD zdeE`|wdkWxfqA%~ZhM9_eo^7(0(GJ3!u*8=IuVa0t30i^2Z%9+HWMhc#d(e{nKF=F zc0*fX$>AKRc1k59ErPK zPOL4$lDS;wqoh5(f(K@GNlzqVq-jQsI-$5ob$=fZ0q1QY5kt@Lk}plN#+50be^?%f zj)%sE;y&+u>OiUcc`HRbLRtL(vO!2ETZcR)&7&ULR*I445_M&K)Is1#7`zHVpDk1k zqom@5)?`2Q+w}Tu{EX9NgbgGVW;t6#bmnDGo|kc$ld%;VYoMkBUnHq=Jp6zGzy{!f z>LY;Lq&AoU@|OAGo;K6$IpE9I7bGLx;Pt>_fDw|iAxSK()+0*R zn-H!B`|5JnkCUyare^x)wcTn%Fa@ixg-wP)PowxT&6L(wCdNvsHp_94>P=%I7(UKp zSZ|jnN~Muz%m;ZnW;F2Yn!2PwPhs)*_*?jQ(1pH78mXZghHwPe{62dCXM*59a8l4M zC?!2B)v(ZHOC}#SpNBLJDGW!71=YZ?TpR#(kA_ zD+=eY%V#rb-bo3sFRV-jypeD`pKZiu7X+I%=6TsWI3xy~9)GA<7|+eGvO8TlXf(u& z-NnD)k5M0o{W1em#OX5Gye7nYO{@X7$(X&8(;EVhJFNY?CDU87L{m zo1|IN)r+aLN}?LLub7O@P87|-YlQQuR4APgR_q5~_HzNYIzH-qDb@YYdcH888Q36? z!%Y4ParrH;S??{PMHZ${6OSslbUE%9V-SLt9Z#k zx+n$==7>(ea>guJ#7B}=e{dmZp0U+d26?|*4;u_$!Frd;YJTO}xKrRjiCJ}YWahgE zqWNpSLAKNN>O}M$%y5F|!4sFpqSMMK;u8vO4s_>@6sfG`2l0K&-O_~+6#i;=|cI=$4r1k2rL^C$iLv>n)@j;J2$5IOk+ zXuKBiX9(R5wIPqS<@FSOEw!;S2@RG>Xyt&;WS=qJAcZti8)`r)4Pt5oO6v_3sHNrH z9!JFM3#aOhwA`xj1#<+tuN^+#YP96iX0uisQls|%9)4mBfNz9#{a<}>tLV@muJI1G zH}yHEWu-N&#EB0Mtb$&pW4&#p9Kw*G-2-Qxz#fB_FLZfbLA(%vo_4k-*?gXdO-#ri z+H}3!<8pOBAMlyR*V%l5ry~$DNH4w|gYGVHyRpHf=dGL8`gjMnSaf`J-_H}oMuO6{ zaqdHB`ph=fA=f!GYX}d22A+dg#n@hH*xQr+ zl!5U;ONQT3Xv(QFo6^Y+Ki@Q|p~(_(GOxP)+s(>;N#x$xvdM6nTb{|UrDi?J1g4VOX@6d3eIy-_Fd2539wEe4&$LrUCw1blkUg!H2l6&GsCm(+v6 zF~F*=fLt%exypG_zN^QjR`vWCkAKyD(|Ob6&sqE&b@k*A*zP3QJaKUtp9}pdX|y2K zl`yDSh!&w@gpf-J+e{9OTrqC|$^IBCC;gl^nbgS!fip-pogleL-2}4oV79g5l(qsO z?=O9EZ z#xtu|Q-6HV3u8H;bgN3~7OAa-L!2}1Kne@}&nV4@ZLA~VbVU6=4^)g~UzGKE5(yd# z-JEO%gKwpKRU*wvQ4^t>Fxmlf@aBs^GN`RSNyo@+H`QIC8tH^u!VbI@2>rw^Lg*k6 zKRvzYV*{1-m@4MQORKwwfI{bP=V}?YSpg6B3hRdKoxO!CBgG=naWQ(E`X>G|*y^<8 z2_Tz5OPoz$yb|pdU{jYs_NT)!51LxFzCgT2tr!4`fe1jHfIX}u2AcZj#x>z;U^S4Q zFDaW=2U?L8fuubT&AlgIZg)U8<*K3e*E(GlyM2BH!D)Ny-YEKEss%O;VGo>icPaWi z5JL=9y(PQaf1GaRa?^zfi|f(D633Y@AvFqrL<8n}l%&bSn_ReU_qD z#`>Rl;)sdfi18Cfd}HYzZ}dh?oG{|?r4hHm2#woto>rgRS~ZS(ZfVqadiQ}YvmW)@ zTz;}N;(JT?*|6+B7FZ3a)WE1|m8Q3S$FBvcnXoF&vPLshD+$fTrO!EW#KfF70z97H zOtcY%X6+tt^zM;3VFaOB8*v+q&}hyrn=8p%u2bjox;B?5$tPia?2`aUkl7?Fq>Y&9 zjqogel9u}X|may3qgxE z>N_XiNAtne*>2Wm3;Tu%ZyWtj`X3`EdLyzYj3Bes?(s%%ME-;kWVYIf+hByo+bS7D zW((RbzF5a!rv8lP;moat)^KZtY6k~0d)#nwJjkG=x=@0Z|yG$~P|DFX`1ljQm zcmOJIWDk8nvRaU2a0v*?Np{#TP0C4dVh`EK?l?B_mWR}kReM%zJv6*&#LqqG=4G*usJ=K7OZ?grT;$J+S3R9;;?y8D5N+knptwwnzoa0=)BN}EL zkkv|aY}H0vJ>EdI9JfN7CU2#AR!-AUQy_5!S%xJ~7*+wyFD#E1BdIBp{#guZs7#T_ zj#Q7+aQ2~UhDoXt_TKbLfTS_XgP$&sl!~Dyzt=XJXsvfz#-C^xH`>=vZMg5eM8iMt z_&Aj8!wIKJz%K`ea>-)M|5B8Lq z9S{q-X1+X|LaQu>W~bxId%dy<{e+ejYms>*O&J7PMg^_(1Y98@7J;XR+a7$0Gfbl4 z9@o^XS{y;Zt2IvdAWQf9TGVud`n=c5Lp(GH=^=8^xCv*vXk+><7vM~Iz`c&Qf7g~v zq5Np3ydtDCKXb*VOWLXYa6M$Hius||1Khn_D>2Xro+;y`x@8Ha(k#HvG}2G&e)XpMCZ zwWcZOn{1wzlS5pRm6ZrMYO%!ZQ*JelLRVTtwXYev|~QxS0=~amI(p10T?SlXGKAi*0H>c-Rf-D9hM95h%Nr zAef1;9+R(td`3NJqF^E}_+$?pK41(AQk=M|&~L4xlE9TnvRXkREiBNJ-rF7B!yIAo zF_i-i6y!2=E0OvVoJoLR^{yC<4Xv9T$TgDTNWx*ziP@>z!V3P&udKOf|JwV^;vn<~ zpSE}3n0I=lK3#$%&Ef&GNb9R&d3@()W&d^8&R#lZfsOH@7#w8`{RrUk>;w4v z=&og=;^=_2ZG+uvVvJR)&?W-?eT+D2s^XkMG}^3@_JB@~q&XQ%R{@>uZm@1Ra>q)` zrXgPKY9a~@EC_*>nnlxWjJE#>E)rZ3>J)hZB-Rz&xZGS-BXzTP)Upe)Z6;fw+MZ1UO04 z8NjiS0}Q{wMhan1E@cupcX_}Oi^N1{@oq6@qHMb1WO%@Yza@P5t+zgW_gkbD7wuep zjXvD7_$}yzm`x;esd~DS3|pg^?47W}VF9@?Z_Qzpv*yH{l)Ex_N6sL5D}n`Vl?&D@ zm2A}O>E4ElBCG}R7Sy#+4U&4`0Vy?Zg=Q4ej-sxJ>Nzbdn?q@8$&FZk90EyM^*S$m zhTK3O{?&sI{F?Am{O;X%fA_n0-}PqqfkU_4a>`HO|?P1l=m;%^c@i9bnw z5r2_-1;0YQO~0)Zhdmg>4tS`udAvfQR20gk@u`ZSbOt%4RN*GWN**@xhr*C+F>{X6 zu%TlpK@mkXgbaegWB|oOC5#S}Il;jHArK0WO~C;OxqO0YHpYjBr^Xu6cte=-NeQ3e zlpJunw`QYA8om!_^;!RwnjIs`Rr?}}r>}jMGOi>mON0d^;OwnrUn4L+4rd{&slgf) znuwDc*Q){n$--qtPY)!WAd5)K`P%4KyV+*InayHoZ_YVPw9WM?FBI+_S>6QyIe^=> z*9oa9+Q{)&wwu>L{SG_qAGg^l%DlqAmiocD{`v1aDQq@lm+hsw;3+o9GMY>jY)ZMu z#+oo?{=f&!n08w4w>roLOgV3^CB96;GxQ|%wLSoSt!C2KN@L=;nzg=GhguW_D*|TX z$<+BlXUG&dZH-7dlXsg0ujtRj;0$gwO{MT>sK-?wI4QX;v;zBupaI5gS9cH@6a;%t z;n;0(hRefw>+ta=arptB({R{ODiIE5ex3;g)2UD(L+$ftQo(R46NHm20`MDb3mjgh zs{`%~5O4ody19ZYRE4hSDti4GwESfC2GnGxhhkANo-36T)zx;%U9XX7>j4VszA-ZBAFp)mC$QY}7k7 zt(TjloIKrRr%|;mmCB^V<23Vn8Ray$5(M{{k;A-&jX z`JZU961G9{oS|WZf||3}>gR1o&$_86YaDmb1Z-J6w&l2qA2jIopED&DKj#Ri*zTVy znbFMOJhn8{b^UPG#?H2@f6_*BVLLgG;~(1b6Zk?rV`RHua!uS&kDB3_A+m8vryH4r zjiL^V(Fipgas&s)-Op+#Q;hF6P~A@-cUs3UKx0-Ge*rs4{|q(nC@MjXx2CtcO94el z!7;!A2$UFFfX$?gF>kUvkm!lIOL@0g$z$0BHC_=;(iF+z<)8-D0xz&fHB7)mje~@z zL;#6uTo$lEyaJA7k|vcxbq@oW7_B$fL-wCuafW0al^6cwk&E_?+D2wKcmM4@IQ?rb zIRLv*^#<(;tt0zU$Jg>}8XrCN(Kp6cn^)EEo$Nj&#?MfsZYui86rnlp>=SI z*S)ZtDFadf4!ZzH@Jd)Ga3(l|*CZj67C6I>A5im0@(>m#n^2kog-uSkRFlw$|N7U3 zzy9^FN5A&q%M0(kv+y!Bo3gX4zgZWB;+>E0~rM(bI)+}{PL&rb~c64xLB(2mMtWvA8QZ6n0f8D(acvMB! zKV0|r-ko%J_C2}jB;DyOo$hR%&f3{X8nTg%T?r&W1QJLBh$}EIC?cqgE6yyi0eed&sK0=?au3NY2)TvWdr%s(Z zCC^;WEG68@SIOH~;Z?!C8E+Lc=HvXh8fQ%6;`2ij5~D5op=pWHR$G2(W@2jeoH|hx_-;tX~`sNcJ@qPpe2xNebag>$^6OW_dxDHlW zaD1VGV+sTsM4Sq`BW+VX;bZZyXnz7OBl@v8T7DEyZQ5EVLCByLd|jr51CBa_5hnp*bLV9W zqoj#9L}_(Q$c0}H$z)86fkT=kcZotP z@&-6tR-9#dUXobE?Od)*94(ejUeq9`+pQWVwRn%a7AL!HswAT>A}6y92|k=2@O#gs%kl2WRuz>HvE zUM-jw<|E8vwn!GQrOh(evdpr^ve~lDvcqz~a?}!PsVFTdjLoiXNXe|PuFk8-mQS!` z$C_e=iN{#vbslaW;o~}iu~wxP4LH`I@qrH)Bpaix`G`W^bI`101QN*`G0-szE0ykm zF*e^a8($cob+Sd1s!5H(M|dmBXv%k_THPvBX@R>ot9Xh%3QmCG;lbi4NX!{-t68B` zKCDn@hv*DZ=BVEaHTu#0dJctqvXg5v4Jo*`s=$$vV^r#OVHG&>U{J@$MW-jkINXH= z55QM;>{SAeuw}%iSflnP&Q3&YxY%6bYrdvP!W@~w&hwV1BulCA9bqw=Go%b>hQgbg zY-FkCR8hxL1ugj}_bKAwSUfZ;g@vR>snb{pOoO26;+AZI9ssJz>o@LJIY6$!R2k4* zX#zH3w82dir6Nd(udkZZvt~`t++RJQP$$I3$0`(1f<`CCsKqxX)LC@2&!6AkK7U?& zZnW3p%xrEgw&1Rv(Ko4LnESDVqDaG-sK*U7?d)oAhs)p$afUjLMJ5%~8{+FC3Jn$6 z`lh-FUz@KqTOW~R(EIB2p+2u#-)8V?N;Qb6vt<^#1Xr}Vxz@)T9d5nD+!$R4*}x3T z!&+#ZNQMe<%N!EN&+Pf~bQ7F;xT8R52>24oBnFQ*6=5wJ^udVZP(g*%xCUk1S~GZX zLryLZ3xX;Z4t-VPetkitAt50>4Ts|$rm2&fr{RE9VRUp#WRiS7YIbh76y>w593DLy zDwSlH*2I_+R(1K739&fLQB_e{UZdkDp)yLGIM3`@mQz$;MF*#h#_*ZejEGQunj{~b ziinGdTQfJcxgpzIqM6cJ2uhp$FN@hChzdC?o1fq_M5mgRX}%P=$H-FZvUDjL0oM}6 zMJ1-eb3|ieR48lI+el^wfw)M85fYS>6-%;AC2KD+rMNZjz;aV<633LnvW&$Qn?mn( zs+82!bcL{aLz*QuO(9IKFqh@K?_Azi=Lsuuxwj3p2~`K&Fo#R1ifDT}U$%>Tr6sf} zt+(=mjrPeQK1*-KVu&qti_({38uXF|=)^AaHtX^*3*;AsN`=lsZ(&>E+`=`5+X@d9 zY72FFK0C}L!}3DCp#{dg(5g^Tr?A5}2`h|LTjL>uNrjQt`1p{=G%lFn}J(wO6o9;rVt5x7Xo}q{@pUITO!F3b2A9r?%arNy5X`fSDeRLQ4{(@B(pO`$UiSp`a)A{@>@JrjGb^42y#*&*anAGaD*$Pu}??G~i@THb|uCxp0)a2+ayO5n@ zt*>jE)Q~g7F0@wrDx37)hH7J>)yE)zTXT%ju|9olC_Gdc>lBq>=rq_B3K6boOJ!)9 zD|%9WgSW2USlQlK){3+B?a7UW-MT`JDr;ER6Bn{YBM zgVH6%2R>&J89!-gz){$9JE12HlT1jyRm9aD|ABuGVn*Rt*{VoGT?u@46of}anGHr= zL{fT2c%)X@RDW&wf9Ct+{&@zEAXX)pITK?|F)=1>ghFFUw%KrSc%gpof8-UUZT{uN z^YMkEXt7+AFyFA8xD{cbNdvRpK$Ba5Mg| z5<{9MksNCoSnHIqs`faYCL`VIXr58;X`SM&pJMd3R~fC|v~;6R?bTpgfSQ2_K4EDj z(;Ylb|AK0PF_CqwRdCV_DdfN z*|AnB%kC(fU?;1K%XQcTcCr^u&rn6{yv3c))cgo@1hrXsLRy+RN~P2)(!(?3-?x{R zS`%ZFrMT$0oWiuA0c~7)X>tONU2p^1mS&eAmL+eszPoFx&S=epX-x7C=KB+mc&_{DkF-m;y93`r70gR#1jH5hf zmWb6ADXn8P8aUULgTiwt&zX>r7iD;FFAB_wOR_~6Ui|oeeQJlhxZR?^F98Kk$@kPI z>2c$W0-n3SRosO;CTZ6=joH1i38|(ig)Y?)Wq|+nSUOAvt5&+>hF?5`$%+2X^ueAn zVRD)nZt&RXsFckkH2*x*ZFLvnZa^42p7}jydqfsPvW-$Lw>=^|6?<@95wK$-iy=y* z!QGc+pG0Z0gJ}X5LzE^zkcKRVC{3E21~hm2zgE5ic{dzBuU2>)BBK)G;ch+3Vl(9C zq-w0*)EuJ^-f_KA=w36RhS`38u(cZg^89W24y`3dc{4#ca%tS+or$^G%L#H*n^3Q8EbC}s?FfKHt5iG z^nrldOsToxg1aAHut2TkGTmuKH>Bqn+^JH7o&Ta(S9Y?+SaBAaxrmA6mDCtPz)kWrKqEWckO!y2Gmf~>HX0Xs zutONwPmCcNj2&E-9A{*v@Keo|mEO_gbdJXDUVFaPHz~WOMBUsKn-rfwBQ;|$3ttHm=yNWg z4diB@d^QM@>{}nH6(|2~YQ@PPW98H$?rVJzTM~4>Et^gDW=Gi5tk`nXr-kD74l}GC z^`U0+hLM6JQ_+&zn&wSRGn%o114S2~V*?^I4-=KSY&~(s6%(}l03|hJyEzkMg(vbe zT~z}uqLZ%BO46qme0X+*sD!0-!Q=~Yc_c8(O*`Wk`d5VTd*NxEz+;*i2Mj=WHi}%J_jS%X>Xg`GJBND3!^FS34L)I&pr%9bA z9FX0Ggc(UM%*>M6X;yfn%|@_M zv@=@K7>C393X?9bQAZX#ocxS59K=AfZev_>CATE136iX^Eji1clAM(-l;T>6Wcf#y z*qW7+Y|l(d&OSbM>b9(89?rr@&n?Nhb>3_cL(Q(oc^%n;tR`qdhE&||5Vz*8%xO}qg!O%>*uMujpWHOuKMDb?Y`FV2@`=_8_WwWp43x0TKkty3K@ zM5zs0l~Zp>_mnEDSbSr$O`q(FXN}QLtVtr2fdN%kH^^;6+Ydn}URYbw`Z8#uKubt$ z2=Y^pV<$k#^g!P98gJ3@0XTI`8A<)iEo=%&nkA@G&7;o;%?}>Gw4*JhBrCf#H#y$r z5=9KRbk6(P2sj-UZj22bea->bf#xymgT_f0I~;M@C64Sw7aaO^)u!i|0>{6RD8|M; zHWQtGEq;JAcyqjEIJKhl<>u((65#zCt824Pr}5#kf?RC|vj_?5Fs(+dEi%AIlOwNC z-&kaYMS&iyn}|6WaxErWPLd!|`ZI%!n1VBj5GLG^5Sf#y><^nJ z=o2T9;_eLzuExGM>wDa&h>gh`w+#o{WoK zot@K{f8L~m8Pn^}KQCwbP%JWF;eCr2rwpBUUcvO$t7HEsV@eu(`GxZVE0g@-ZH%XVM#)s$!1eU05rDNG4oLqzuxUWB{pAHpn+ zJ2s6fg_fpeEsn1Qrei)6qfxQu`^Bj?-h}&FxwvrQ#Hp2J0Nl9rG|!!609zU~xUv9% zl9H&TByGHwO8W&NfW!dKQT*!{aQH5b95|?bXGoulbq&yo<_#WIsvD% z%c~vBD|sw0?zx%cB(KCa^0OP~TCEW+JGM0~A*8O8RK{5!`jblV-Ndl`r zqPLt+?O0jkb(7DCP@0kejh+qUQ#n?aoQCj`(;Q`Vj)eBHi_S_jo=+|NCXl8;PJ>>_ z&nO6V-&npkutl}(3D-)bm1e~j!aX>mSr;^Mv zS5ym|#b>ZX9_=-lR8W_yjDmtG^A3^)40jkumI?*JX2ZsfhNa!2QM+J)_Hq7Zf^d)c zBwEwwbt=pu8eG$&*Z9nOojC;eIENaQUNhaYr-9466!>eXV&^jz-BCh!JVRLkfzjp6 zg9}&~+&l}-h50z-WKtLLVH^Il3i$TeZYfzCW^(75=BZ0VDG+_W*b<%OtBNnc2YB>~ z6{x{A@)}F@;&2$Q>4an&xSba}^1VWiSkcskw2N^ralQB|{Nh=?QG!ydfd@Y3Gblq1 zjT$WkCOVxDbPmFIr7X)v@Eb0JcWEy+sU{T^O;VetR(e~+SF9z)_Kt;p$alAxAykQv z{QHr0cP6ZatVxM>@q&cR%!H&&JI20EqDkn*U8JO6ad@R^VS-6#GUe&=OiCZ_bn!(g zLZceh;h}!D!1v4M5PrN}=0tGA(ZIrYf{JBNdSYTmMq*;Ry)q}KswyYDN?Zw}+@!>e z^rYNUk0Yn7j9UIiXc7DIZGnH8i?u8sS8>~6DCfaO>||jkn<*@2i-px}wXlJ05bj`i z2;13q97_8Wgpe4eIntbNE;QGfJI%f35%U%1E#`a72D6mp)5qwT9=8$d+w^mxf=?2R zdJ~JcM5PL$mKY%>!b`gUcrZnLUVK&jrg$Z(LdfnP6elSFtEM@`<$O!P{XeP%-9P63 zlA5nsC_lIIZUJh&!k;KqU~~v)iC$w&xL{I4-~`{Az?)|H+=F~Vl1M7qazF)1x;uGE z=@w#hQ)46Hu|2W^H@4I@r#YpV#B@zgp50=v;S%axF$x^{D*9A{7w0-nxOq=h8bvR5 z6BK#Wesf4A2)8NF&AElmOS_kbZrCUudi-%s-vZiM=bo_nzIibpeU8DKm^`wSjEyYZ zlL6m1r=^FQ9W%)`G$nz3a}jej<2OWwqwmf1mV}2z`)qI;gkzBr(b3G8f^AuwMyIq9 zS~hJ8yjlo2T^=5tqG$|B3vG;0=pq`U;UbBve}iqqr&?+-l9AC`s0PW$!F!@$xRj>g zMlH}#6^&gc6}T)#xO$WR%HVxbZMZw?^riaYGq02yofRm69bjIb`8r$da_?0*m?U_>7q8Ayd-}tKzl| z4BvjspfOS>ni4-tD46b@0=ija88q)&iWyHXCEY@*^CUiVK!kuf6Nq z!6jF#l!~-`l1b#1FwHmeN_ZpN8Q`3XAm;?u)O2c2UQ=&uk~yb-jB`k8Ndryw%+2O{ ztKB{WZfzmnp&R00(CSN3hZ<56QnFJNK2=z#Sp}mb8w@N|PD2PwaawI4YN}2h*60ia zX>oN9NXuwZ>`~vn-R}I`?H>uauH(ap)Aj@FJZrC0U=E;R}z@=`s}Ah73&#j-we( zVa~YZREs0F$XT47jYuISPI8iF6P6BeStu{U19pM5=ma;(6FIg&(Um|Su72Y^io3v( zx2m{JPvqJDZzc|kEG2nOx3Zhz-Nv9%X+)(~ zugxnrA1{Zn0LTp%!2Q_bKzFdYg?m5ny#K!EgZBvv>nK$E!3U-9f8fE+>rXg6bqv}V z7u)FVROje4cEN5nr-fRrc~UH#{fUWEgu<1l2(_l!bCgLoojTO4OfYD&VzNX{dQ7^Q zm7bNJ$)IUcT8+}r!edCQcQOfSoBZ69?hU}zav0j5Hs8t%K_QJPtHqY$vJ6O`eEisPSM-&L^ z7NNCOXn}*2&wlue@DbjBjdh9FiblwH*|__n>1y#7xG;qSdnJ^j2DMUY3`veiON=Uy zOM}Y{h0(4GDc9OVRf=SI9ae@IBcdRAC8(t&WjT}do4{yc(FWXBSRQ6YpD8!vKMXB- zV65GO@n=Q@OWe~2PZAm%4ZFmCw4w(O#!&Nd>yL@25X5VvqO7?#PphRWDbrz#ipsJ% zG85~pKDRB`nsuJ9ptz~2px7tmrj;jW=2&xMqOCcZQbl@WWx6XfIx;KMm0s0Q*xKaI zpWFbnsgJOD>A;lpP32!Rz2YQd*nR8oO``8V+`MR~fBNVVG`T@)e$w{7i-)DI{8@;NSQ1N2p~v!YY=a5Lkiu9CIPZM8)e^6&{B8_Bxw_ zk6C!1Srygn68L+u2yskFE50m?$PPqj-(~OQ93@ z98dONAdVnB)&G-X7t@OynNBQ4+QleiKE4?)K>iSPyvaW*Ug!Tt_|E^OA{)<={QnZZ z@qZvf5)v+9lCY2M#qK1$&rZlLLEH$x zi67uaxCK9G(==S{87@|^ctw`~Be5G=`zEy46pYLl!AaLi)`w3o&gR)cryclAQeNP{ znbVuv5qJ~L6zQPdR{sy2cJv$V#%U)pCnN%wA_sLLJT3rzYVjNGFJ9q)irS0Q?4K-4 z_$PSdBrGw(lVfy2yQ;Kk+qq~Dv=#LL;YI9|yvQQhNz5B3Sp?<9>5DdI_xq;{^~kH9 z*~DAXe(*WY>j++@wDcaQH)tLFCc1O}A|4BTc>Pc|{pMk`2k{hrIE}x8y`uc-#rI-- zcH^@Nb-5AWEAW{ctQ+__QmjPZ0Dgq0!h!M@`M&_(*NI>Ie-fTzF~k#!A#SCL-fMgm z2YHr-pihh2uzRBNcSB+zm=3rDCkRyjBapsT{=LvvtNf>!599gG?DtHE_?;Z)!E4D8 zq}L;T5#TB0p!bg;r5-6A{Hck@#N*UUIpRau0JDc)66rWQ0$%7Npd(E`tKs_u*DO`Z~6R?=0n0SPbpk9#V zF*Z`nWQ4y0H7I2Z!lzJ*8J?NC_|s`Tr5o@Bu%f4#JmpLtp2g$MfO~-_JsspvJqRCT zTX_!KIm`o$0!=914tNrjq4cFJ3{+~Nc0jpd$lcCkx&Z%(G-1Fh9^v~yy)ceL7-&+C z@LteA47HdEcnGaSPu+l9L76a6W;Ty6;qm!^r`cu>^?=mk6ypKhiuR@O4!~|ugTe)X z>p@#e8PCFjGq4B5-FUzx9PkKYVt`v1;?TMrCILnwo?tmkLV6E}@f?;0U^0&tMIWtbI^ z&%GQz%;VjF$3a!f;rDro?s~> z=wOBXX(6X%5n?>Jlb)UuEM~=kd4LCjvm0Z2I&-56+3LY*7 z{2V6)+@Rqmz(e3DHz;5RJOSQt`=19j+)N1`pqP6Bj{y_P;bFi-XjzKMV(&drgP|HUbYS6f*1YP&FYXtIp9HzQFTb^0sJGz+dA~Y z5*}j*JOs|9G%g-fz+-0c@Fc)*F;dm}zegL@`5(g=NcrRf9s}le7|**{J@B6mcp7<5 z0_+0(0eMo`4fq6Fgu=5p+y&b+YTpLL%;vC!!!E!dkfsswEr35D4TbH1PoNf!z;G9b zGkAO{;11+KXq%#eAtLOrZXQ>`T#${Taytk2ZW3a_#<$h?Efe5 zpNy93VpDj1ry$L2gsB!&z{z&jg!+~Oeu8(KKtm7UNjz-=l?nifdz+ENWSppP!@Dhr z=>q&7DO-7N1ZVPa0h@}H9@Y+QX8=+y2o?Yy0tMPR1t?}0!UvHL#gqb4?I^yS$Is?r zJKz(*gy1Y5Q^H|6>j1ZOu}-{O4oLNde2P-KPH`6;*;qpeHtKfAjPW;TQ4IRo#eBYX_` zP|P$Q-UWD?wF7<&oM-qygU1mHI{@DU9cF+^ck-AnK;n}b{tr=i%E8QL@_c6ck0O31 z;_CtF-I@LmfDb+G0;FX$PctXX9xXk9Pomz#xgBEheL_5`h(^naQ7e0FQur zvoRlK@R%aNlc3CO)F=<|6fmb!e1LxkZWPl3_%UihrL=Nb!sY;nF2En~?i_excLRQk zx8_2V6c1PqUR?&Rh-bf|Ie=~A+_ecgs}Q~qId1}|?c(8`9F_pSfwrbJzvnS_z^9NC z#proV0pJnLwvfHd!K#b{OnNORd%j82mQzsI{Q3YX+iSpGfCrHe!4e+h0o;L72+ra$1%NcFP7)|dANf^9fys8#Nh<9 zIIQRKTY1=r8r%%-rKd%JUn1vSz{CVN0vrgo01}r{d>h~iHkHF^JbpSL&AbFl;alPxVkLkHk#R$e*r0xAgRcItJprFQWzEYD25eV6n|3uO*yFCqI_2Q zj`BN|UNu>DwQ5xTh(@EiMeERR(rI;9>7LXp^hx?D`UU#$43C7^LUxCmLKlbr)41GZ zF#W;wow+?sAJ!Lc2)`}--H67B*CQ{CVo_6~_D07}9dn$9@*4jZ2QJ zihCu#D}Gx-V#4Btzb5`RDLv`>WR|=w`Q4NaDL+bH>4nsq)Q2oK%M#10Y1wIiN}rSd zenxu6uQG1S_{mykZMDv~j##g<-fG=$Jz#ysdcyjpO>K*@W!Xw?Q*5(sOKodxH`wm7 z{lWH}?Je6$+jp7zOz8MBt1{a%dooAtnf6)sSF*0o&dr{j{fC^o+|=Cr9M?J?a-7K9 z>vTA0IhQ&wb2(gl^V{-!@`v-UD40^Pr*LWEYemzGZZG;nafv(BeY8YZQd_bSf4?o+ zS@Mi$n&*V)yHa!MaOsgUMOk6ly0TxFJy^E4?5}0tmS0z)t+ZE8s$5ZdwDLsdS5@k& z_^O<$%Br@i-l~;V>#J_B+FrH4>S)!8s;|5S-Zt+R??c`tVSuf2f|v6kq-sJI%b4*i-HMC zDm#Q7=V3Z0VP^l}VKtBco`*FQFOc3DoA5jw&BHoibX>3o!npBWB-?U9Njg)b7#RpF z5N`>Dl?dkt!Yb%%Y64+3lf;>Uu$DE67X`vPmI!^1oTpxG7Ekc79{Go8$;+&uG!Q=8 zQ63JVc%6}ljXWIB!*m;fQ77X`enrA{bgofQ(!FCLaJNBWx{J%KYvr#{a_y zC6~vOFV*%8jr0vkR;hMypik;ty{u324a|2)fyX?eV{plek^aGf;T-u{+u#uY8BEmN zKYwU&cyPf8G8tOtkgAs~k%sygE*cq@hWdv4hF13VI_j77EnCzxFw)+)aK)0Iq2SNp zcX!{=FiMbIjv`kuE;o?&oH%rh^bag-?^&JtkksvRIGv8>o{^D7eX9Z$!k9nEhS*Zp z!{RSiJcZ8_ioJ^M>_H|$w)WsJ zAE8>FY6LkA;g^-i4C3uRggV)3wv2~-h?$SMO*-rC@f01vUud{^xX{*69aT79xip z&^I;Jf_ilGl4#b#iV5)}_*(>AR&gp2=5h=DyJr&eC7J|VkEkdG@{zzdMA;EYfXk3u z4^K-JqZGkb?nQ}I_l3ZZXe#GMxeS7W#2C~D_ z5u)5yA#NF-6F zo5UAXzhRCQVHA8TSSDdM$Z5P{EH%+-2ye=HQhg-;{%U+vxzs9zBjG_c?gK^7q?=6B zzA-)ww#PiYKgbrNq>0pPK<>oZ{rE<2MV|AJ=Y0GkI{#0tDS@7Y_yqYoNNd8rk6j2z z?J!Q^GkJpg1n~szzBvI`;~m06#*oGv;=ffK59&|U$E2V21(`mCc|YZ+MFm(c~S}TC?}6s z#B=o4lE5>{KiJnN_6E6E$ebl}??igZElym!0yL9*2K7mq_Ec}`O>ztQc!~AAZxR>u z@tT~wz2&iCF@ImC5aA||sWhh$e-r0P$Sv4&s4R(>Jj9<5gC}L05q|ybB8~~wP42fu zli=tZ50rgl!|&!CrYIGO>R56 zSIYRy{4CcY$WK3STbUX`yydp&LvGZYsiYCWMZEllC{5;4nq7$hs3qi1~BT;TS zu%jk-d+<#o(8T8x!vmntgx|_!B`)?;HYh6qJ!4O( z1me&MDbEViI6}1_0PfW&aR^kSalYrQbmywsnb?zD&;R)^#=7!c|2{$_ojHQ3FLW;O zj|F)jhMUmp!z&A9dDsbHB^-|3+eqw^MPp|vmc>C|lK`Dj5_T$5K=oAEj;3LCoB`>` z20e})YxZpPv|O}79>z`h?ZUdX5OM-`0kA6eU`1PoRcZyRgf*}ib*zCTQwPqOgx%yu z$V8K&8EC>R*8)va8&>Y^XoXJbUAjrLhn4V5Xe?&4IqW>x63+!)=7AfDTNYsCBd)vv zv&a(kDx%;ra2@rm5m0d@IOjsN(naiISl3^QRnFyX4O`3BLH~3GyAms%tJ!*X4ZD_Y zzY9FZ&aFlI>?tu>=gTyea8MN zi0ldMmhV&6uNWF+UR%1>f-8HHwTU<{5vzsykv3dw)*7`TFw@d%)mo)i#O;D?%8mo` z*_3(Gj?*1!dj$Q|nLAWzHNuYYDQrq-%@a7MaNx>~(L2@Z`W@Q2b+FPq`(NzN#>V>o z9o`LfJAFPm0MCr|i*`)K$Gc(f4k@i}hZ^Dbz;8=hoy>y*cuwG)$0ASQJ5r90eA~G$ z@=dia>#D}9z6ljH;&m-y?5l-QZ&JeX$ej=(=%sE%B&sGacVVzqPl-t^!ZO)tt zF^P92uSo=t(O)b0gJfoTOzi{L;m;|ZnV-^p<>LJd^I{K9`SDp{+7*}Oy}E6k`3C1Y z(d%3Xx1VOk^PAlNbz|mx<$sEQWZm}*&CbvcpnMPJQc#TjHLqd~4O(=W5^m;@$TS-TkW#FIGQYVQFl9D=xU^uB-O-Uii%Y`74IP9y)I=XL|h_DiTwDvcy6lapmHPK_t(K)nYctmRH8(B3tv^dCChM%!j zjx$SUtF-Yq`NU6*}|f)Y&bqQj@Q`#@FQQoFP@$ z*4DRm*4O1one*))w-n4uW?jyOZfB7z-{~ec1Q+7Gb$$W(kpKL@zXJb`hhK9@|6FuQ zPOf8Z;!gFh`whFpLZ`jmacsrkUMR@g_4;Yutimrp**vNXIr?7-GoLv8@#*zD?>umI z#(!Tr-8_83g)c6T`sumpr|l0_j zjL#zH{{N4Ox4YmUTkajn!44pGo~thfyQ?AMm%8i!qkb0b7Q*WNj?1^BSM z0Yk~vG%P>d^XX%cY~0+i`JKnY&g*|ifBC%4YS-Z-{`EIZddpY3<&)RdRd+se*M&1r zefQ)1`qn*$0q5s;6+fJ-d+%R^wueKf&Q%q)UVfyr_2}-Lnq!8e8~2>&|KpmYf4ljy z%Pqbd^ODzY-63?}{mjdb-`5xD?E zFP;0tZRg+9|A*HuUY0#CExAsb`unt~=SRfvd|%k&XK%l1d672z^rqwQKYsJc4fjv^ z+wfl+b$9M~d&ApNHy%>_U6;|V{<2OtvPb}v9imyefR4uS9a9*e~J9`-5sBFRWF;An{%0So%S(&lC~+}9Y8d9 zg|7d)syF(rkYRSL{>JFSi5UMc&h8#(fosC-?sOKqaOsUayRQ@G{C_TDxF$L4WTvlJ zwQ7}PC4`n?2rZ8JgG=*<`j!n2_m2z?tc(cj@3juPfP^NHL1YsPXJcIXyVUvkg8s z{KE5p+llT;nXhQL@VW<{?7r;Z%Dc9=>464 zKHhM6??|@#^(!r|q;J(TZa27R{pP#6-Bq)XKXRvcu5D)Qw8?S9B`b?R+v$;1+p=Hi zEO+hE@3Q>xm%gocpK7Q%`O@_-Ev#D7Y#DlC@|(ww-?-?R5~y)|fB8`NLOge_C|wmnAO^lzm}X_q+N3 z3j*T0pUcUqgcYtt^ebbWkxr80$K`f-eAldD=xj)$SE`+;h#K;?vUoxoxQOs8*D2Ew zowCio#p;ZS-Vd0Sm&btVQGFJb}tt6&+i%OllnR15>xaI zOW@w2z6E_leFO9Ra-^PtUa22D7%PU6Y*-o|8S0-uvShV>c*VR6`sR;FBZD~-HXOzz zBB^#PU*g-gp`Q68Bwaz=8tGfwH!vb)0u#F){V1^0;Y1-Td-|93%v(a3oSEBr50iRE zO7*`~(K5oiK6fdyK{^Q~>?G95kbyQ;yXa? z@(xgYa0f`Lt#9wF_OirLK;8sdW;9z7C$9Z<4RJy0c!w&yLP^Uu|d845_25W^#RPr_|X>ujsq$ z+kG9rhL#EGeXT80TYGhFr?0ji?;(Tc`j$?I#EbBCbabISsk*DPv9%pCus(>STqJ5B z`I_6Bd;y&6r?<7&cXUYOwE_`ZYMZ+1D3|eAJ@9U>Z?A1c)q~P?Yr8bb*V#g^PeQm_ zYO8JsQe92e?NVD;ds}NqeGVtgG+$Gb)Y96iuc_xmYO3ci)V8*C)KBdK^1kY(9K6!v z>-2R8-U*OfY6TVArMl|o>W2CbhtyGDuP0K1Rd^nC^=Oo)4p169fFt13VQFx|*}S;0 ze;BLSzFuiyaDdoqL4RLwhujgWN6<6otw0CT_g#qhIdiY%jL528WjNL0ONtXxK+F;8f~czG{d5?&39tzr_6$=b<3C zFBx1o=vdglz*$TKi9(s@%ynjOvv0Fql|k8Huoy;~{^sN5|C+q)+u0?Ks1GNZPPf-Q zdjD~@bf~9r!NIO)yPiJ!>m%5hh+WBG5FBu z|5$qY)pwq{{f_vOKRmkR!zqca?@v!W@^0AU$8H&&y6(p}PknOagPYGw-FNzfceY?fS^?($`o53_!ER(%_yomuE(XR`iWv?>lc|D?@Zvu3cl9v+dNamp$<9HLiJE zryl&*g;(60)A8lWTOQv3!gEVEH@~puwFg$`mfp7d9oIK&{^dS;aOS(t_(PH52fldj zD(hnfFDHgYyPvjHp33Y>Tw5`3mQeA|-=Ew6+U2_=-Wq=Ur#nJ^ciHpaqgUIW(rZ6@ zA?3#@)4rSYR__nGnx9-z%THH-8)ba4F64LXgrm?py);f9wQHTQ4^dB0yRY5zfBho+ zm)41A^3Dur?06LwF2mRljR4LGssVK>mx+`N&f@$+S4pAESvd1&ob#uxLis~?etBPw z|LEM?{C`vZyiJ#Mc5APhYP05kbL|_q=dAXPSSqJ~x-;S1AC!@6pW44h`OqIPo_pz? z%T7C7FMK-vsja6xwxQ8?Gt3c}Uw7>duXwIov^@1UJMwRSZ|LLwH+_EKK+B5vE>C|z zzdyVDu@|4XvO+ac#mqwv%?qP`mX=u->ttsw5{jJO^p2? D_wy^5 literal 0 HcmV?d00001 diff --git a/include/securimage/gdfonts/automatic.gdf b/include/securimage/gdfonts/automatic.gdf new file mode 100644 index 0000000000000000000000000000000000000000..3eee7068f3d178d9fcae61543edd388f2090b8a6 GIT binary patch literal 61196 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 literal 0 HcmV?d00001 diff --git a/include/securimage/gdfonts/bubblebath.gdf b/include/securimage/gdfonts/bubblebath.gdf new file mode 100644 index 0000000000000000000000000000000000000000..5dc6feab2f10aeb70130606b05a762f6a6dfb7d3 GIT binary patch literal 67516 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 a!ZR})@M-1LH#Ulyxt>$vbUwB6BKBV;AyU== literal 0 HcmV?d00001 diff --git a/include/securimage/gdfonts/caveman.gdf b/include/securimage/gdfonts/caveman.gdf new file mode 100644 index 0000000000000000000000000000000000000000..4f72cdbb2e65547549315d4152a5f6f79ed889f9 GIT binary patch literal 160540 zcmeI3Te4(1j$ONFqHj$}^Ph+C+IW-4BPvU6rlMP>69g7EKsb-z$ZL`R=fD2zzy25f z`@ij9THfnT;7#C7;7#C7;7#D)Jc0kV*H;lAgr9G-Tc4fyE4Nu2tVLBUKZ} zlPl~>ZmOISFygth@+tZ8okc_m+fWSLS?FTmTp3H&?(+@_BNxo!F= z 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 literal 0 HcmV?d00001 diff --git a/include/securimage/gdfonts/crass.gdf b/include/securimage/gdfonts/crass.gdf new file mode 100644 index 0000000000000000000000000000000000000000..73420a98d08009cb0ebb079be8e55f7304c9c208 GIT binary patch literal 39691 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 literal 0 HcmV?d00001 diff --git a/include/securimage/images/audio_icon.gif b/include/securimage/images/audio_icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..beafd518270f15bd62e15276ede1c5daab7d8892 GIT binary patch literal 621 zcmZ?wbhEHb6k`x!_^QpIqpNFdY;5n~;OXV*;u07d77`tuk`SMg4g%!``GsX=6`7eG zdAYMHE8DB9Cl}-|Dk@sq*4En8JY(Xd{;AWZcXzLtI%VaIncG&ZSh;fLjtv{u?cBL* z-n?TgR~%Wn^4iXwNA~QwzIDrk0|)jWJ$mfyncG*dUO#s1;k9c|Uc7kq`}ZFcBNGM+ zfZ|UUMg|6;KR{-H{KUZa&ta<9(x80Hu6g9xjs6OJx{*}F1irQnIh u7dv#M;XVU}-70|>#f(4=q5()DsoH-GNgp`0mIfgL4N0Ze0AvFtqn literal 0 HcmV?d00001 diff --git a/include/securimage/images/refresh.gif b/include/securimage/images/refresh.gif new file mode 100644 index 0000000000000000000000000000000000000000..a10b24717f9d5be6af61c1c3dd646f6bf0e99ab9 GIT binary patch literal 865 zcmZ?wbhEHb6k`x!_!h>%CaB6Ptji&y#V=p2w7!KzXt05lpspEM3`BTH%UXDah6V>n8Og~tI6xGK`HJXUg#tx`gY`5u<@9|a z${kJRgF-nOg9F(4R6=DG>_MWTp(f&{p)Bf_0oq%;a?r#XtQw|MwS1<*8Ce%$(jz?HI zI5;#YDAa~u-UDKWucr+83P4BI7J_Vwa^hB@P%i@|8bZRnos_J^l|21I!{Ff=78Vo= aM8QB^K+!<3$zTGg0-_A8G%&D{!5RQ?yDO0Z literal 0 HcmV?d00001 diff --git a/include/securimage/securimage.php b/include/securimage/securimage.php new file mode 100644 index 000000000..9d5f1e134 --- /dev/null +++ b/include/securimage/securimage.php @@ -0,0 +1,954 @@ + + * File: securimage.php
    + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version.

    + * + * This library 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 + * Lesser General Public License for more details.

    + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

    + * + * Any modifications to the library should be indicated clearly in the source code + * to inform users that the changes are not a part of the original software.

    + * + * If you found this script useful, please take a quick moment to rate it.
    + * http://www.hotscripts.com/rate/49400.html Thanks. + * + * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA + * @link http://www.phpcaptcha.org/latest.zip Download Latest Version + * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation + * @copyright 2007 Drew Phillips + * @author drew010 + * @version 1.0.3.1 (March 24, 2008) + * @package Securimage + * + */ + +/** + ChangeLog + + 1.0.3.1 + - Error reading from wordlist in some cases caused words to be cut off 1 letter short + + 1.0.3 + - Removed shadow_text from code which could cause an undefined property error due to removal from previous version + + 1.0.2 + - Audible CAPTCHA Code wav files + - Create codes from a word list instead of random strings + + 1.0 + - Added the ability to use a selected character set, rather than a-z0-9 only. + - Added the multi-color text option to use different colors for each letter. + - Switched to automatic session handling instead of using files for code storage + - Added GD Font support if ttf support is not available. Can use internal GD fonts or load new ones. + - Added the ability to set line thickness + - Added option for drawing arced lines over letters + - Added ability to choose image type for output + +*/ + +/** + * Output images in JPEG format + */ +define('SI_IMAGE_JPEG', 1); +/** + * Output images in PNG format + */ +define('SI_IMAGE_PNG', 2); +/** + * Output images in GIF format + * Must have GD >= 2.0.28! + */ +define('SI_IMAGE_GIF', 3); + +/** + * Securimage CAPTCHA Class. + * + * @package Securimage + * @subpackage classes + * + */ +class Securimage { + + /** + * The desired width of the CAPTCHA image. + * + * @var int + */ + var $image_width = 175; + + /** + * The desired width of the CAPTCHA image. + * + * @var int + */ + var $image_height = 45; + + /** + * The image format for output.
    + * Valid options: SI_IMAGE_PNG, SI_IMAGE_JPG, SI_IMAGE_GIF + * + * @var int + */ + var $image_type = SI_IMAGE_PNG; + + /** + * The length of the code to generate. + * + * @var int + */ + var $code_length = 4; + + /** + * The character set for individual characters in the image.
    + * Letters are converted to uppercase.
    + * The font must support the letters or there may be problematic substitutions. + * + * @var string + */ + var $charset = 'ABCDEFGHKLMNPRSTUVWYZ23456789'; + //var $charset = '0123456789'; + + /** + * Create codes using this word list + * + * @var string The path to the word list to use for creating CAPTCHA codes + */ + var $wordlist_file = '../words/words.txt'; + + /** + * True to use a word list file instead of a random code + * + * @var bool + */ + var $use_wordlist = true; + + /** + * Whether to use a GD font instead of a TTF font.
    + * TTF offers more support and options, but use this if your PHP doesn't support TTF.
    + * + * @var boolean + */ + var $use_gd_font = false; + + /** + * The GD font to use.
    + * Internal gd fonts can be loaded by their number.
    + * Alternatively, a file path can be given and the font will be loaded from file. + * + * @var mixed + */ + var $gd_font_file = 'gdfonts/bubblebath.gdf'; + + /** + * The approximate size of the font in pixels.
    + * This does not control the size of the font because that is determined by the GD font itself.
    + * This is used to aid the calculations of positioning used by this class.
    + * + * @var int + */ + var $gd_font_size = 20; + + // Note: These font options below do not apply if you set $use_gd_font to true with the exception of $text_color + + /** + * The path to the TTF font file to load. + * + * @var string + */ + var $ttf_file = "./elephant.ttf"; + + /** + * The font size.
    + * Depending on your version of GD, this should be specified as the pixel size (GD1) or point size (GD2)
    + * + * @var int + */ + var $font_size = 24; + + /** + * The minimum angle in degrees, with 0 degrees being left-to-right reading text.
    + * Higher values represent a counter-clockwise rotation.
    + * For example, a value of 90 would result in bottom-to-top reading text. + * + * @var int + */ + var $text_angle_minimum = -20; + + /** + * The minimum angle in degrees, with 0 degrees being left-to-right reading text.
    + * Higher values represent a counter-clockwise rotation.
    + * For example, a value of 90 would result in bottom-to-top reading text. + * + * @var int + */ + var $text_angle_maximum = 20; + + /** + * The X-Position on the image where letter drawing will begin.
    + * This value is in pixels from the left side of the image. + * + * @var int + */ + var $text_x_start = 8; + + /** + * Letters can be spaced apart at random distances.
    + * This is the minimum distance between two letters.
    + * This should be at least as wide as a font character.
    + * Small values can cause letters to be drawn over eachother.
    + * + * @var int + */ + var $text_minimum_distance = 30; + + /** + * Letters can be spaced apart at random distances.
    + * This is the maximum distance between two letters.
    + * This should be at least as wide as a font character.
    + * Small values can cause letters to be drawn over eachother.
    + * + * @var int + */ + var $text_maximum_distance = 33; + + /** + * The background color for the image.
    + * This should be specified in HTML hex format.
    + * Make sure to include the preceding # sign! + * + * @var string + */ + var $image_bg_color = "#e3daed"; + + /** + * The text color to use for drawing characters.
    + * This value is ignored if $use_multi_text is set to true.
    + * Make sure this contrasts well with the background color.
    + * Specify the color in HTML hex format with preceding # sign + * + * @see Securimage::$use_multi_text + * @var string + */ + var $text_color = "#ff0000"; + + /** + * Set to true to use multiple colors for each character. + * + * @see Securimage::$multi_text_color + * @var boolean + */ + var $use_multi_text = true; + + /** + * String of HTML hex colors to use.
    + * Separate each possible color with commas.
    + * Be sure to precede each value with the # sign. + * + * @var string + */ + var $multi_text_color = "#0a68dd,#f65c47,#8d32fd"; + + /** + * Set to true to make the characters appear transparent. + * + * @see Securimage::$text_transparency_percentage + * @var boolean + */ + var $use_transparent_text = true; + + /** + * The percentage of transparency, 0 to 100.
    + * A value of 0 is completely opaque, 100 is completely transparent (invisble) + * + * @see Securimage::$use_transparent_text + * @var int + */ + var $text_transparency_percentage = 15; + + + // Line options + /** + * Draw vertical and horizontal lines on the image. + * + * @see Securimage::$line_color + * @see Securimage::$line_distance + * @see Securimage::$line_thickness + * @see Securimage::$draw_lines_over_text + * @var boolean + */ + var $draw_lines = true; + + /** + * The color of the lines drawn on the image.
    + * Use HTML hex format with preceding # sign. + * + * @see Securimage::$draw_lines + * @var string + */ + var $line_color = "#80BFFF"; + + /** + * How far apart to space the lines from eachother in pixels. + * + * @see Securimage::$draw_lines + * @var int + */ + var $line_distance = 5; + + /** + * How thick to draw the lines in pixels.
    + * 1-3 is ideal depending on distance + * + * @see Securimage::$draw_lines + * @see Securimage::$line_distance + * @var int + */ + var $line_thickness = 1; + + /** + * Set to true to draw angled lines on the image in addition to the horizontal and vertical lines. + * + * @see Securimage::$draw_lines + * @var boolean + */ + var $draw_angled_lines = false; + + /** + * Draw the lines over the text.
    + * If fales lines will be drawn before putting the text on the image.
    + * This can make the image hard for humans to read depending on the line thickness and distance. + * + * @var boolean + */ + var $draw_lines_over_text = false; + + /** + * For added security, it is a good idea to draw arced lines over the letters to make it harder for bots to segment the letters.
    + * Two arced lines will be drawn over the text on each side of the image.
    + * This is currently expirimental and may be off in certain configurations. + * + * @var boolean + */ + var $arc_linethrough = true; + + /** + * The colors or color of the arced lines.
    + * Use HTML hex notation with preceding # sign, and separate each value with a comma.
    + * This should be similar to your font color for single color images. + * + * @var string + */ + var $arc_line_colors = "#8080ff"; + + /** + * Full path to the WAV files to use to make the audio files, include trailing /.
    + * Name Files [A-Z0-9].wav + * + * @since 1.0.1 + * @var string + */ + var $audio_path = './audio/'; + + + //END USER CONFIGURATION + //There should be no need to edit below unless you really know what you are doing. + + /** + * The gd image resource. + * + * @access private + * @var resource + */ + var $im; + + /** + * The background image resource + * + * @access private + * @var resource + */ + var $bgimg; + + /** + * The code generated by the script + * + * @access private + * @var string + */ + var $code; + + /** + * The code that was entered by the user + * + * @access private + * @var string + */ + var $code_entered; + + /** + * Whether or not the correct code was entered + * + * @access private + * @var boolean + */ + var $correct_code; + + /** + * Class constructor.
    + * Because the class uses sessions, this will attempt to start a session if there is no previous one.
    + * If you do not start a session before calling the class, the constructor must be called before any + * output is sent to the browser. + * + * + * $securimage = new Securimage(); + * + * + */ + function Securimage() + { + if ( session_id() == '' ) { // no session has been started yet, which is needed for validation + session_start(); + } + } + + /** + * Generate a code and output the image to the browser. + * + * + * + * include 'securimage.php'; + * $securimage = new Securimage(); + * $securimage->show('bg.jpg'); + * + * + * + * @param string $background_image The path to an image to use as the background for the CAPTCHA + */ + function show($background_image = "") + { + if($background_image != "" && is_readable($background_image)) { + $this->bgimg = $background_image; + } + + $this->doImage(); + } + + /** + * Validate the code entered by the user. + * + * + * $code = $_POST['code']; + * if ($securimage->check($code) == false) { + * die("Sorry, the code entered did not match."); + * } else { + * $valid = true; + * } + * + * @param string $code The code the user entered + * @return boolean true if the code was correct, false if not + */ + function check($code) + { + $this->code_entered = $code; + $this->validate(); + return $this->correct_code; + } + + /** + * Generate and output the image + * + * @access private + * + */ + function doImage() + { + if($this->use_transparent_text == true || $this->bgimg != "") { + $this->im = imagecreatetruecolor($this->image_width, $this->image_height); + $bgcolor = imagecolorallocate($this->im, hexdec(substr($this->image_bg_color, 1, 2)), hexdec(substr($this->image_bg_color, 3, 2)), hexdec(substr($this->image_bg_color, 5, 2))); + imagefilledrectangle($this->im, 0, 0, imagesx($this->im), imagesy($this->im), $bgcolor); + } else { //no transparency + $this->im = imagecreate($this->image_width, $this->image_height); + $bgcolor = imagecolorallocate($this->im, hexdec(substr($this->image_bg_color, 1, 2)), hexdec(substr($this->image_bg_color, 3, 2)), hexdec(substr($this->image_bg_color, 5, 2))); + } + + if($this->bgimg != "") { $this->setBackground(); } + + $this->createCode(); + + if (!$this->draw_lines_over_text && $this->draw_lines) $this->drawLines(); + + $this->drawWord(); + + if ($this->arc_linethrough == true) $this->arcLines(); + + if ($this->draw_lines_over_text && $this->draw_lines) $this->drawLines(); + + $this->output(); + + } + + /** + * Set the background of the CAPTCHA image + * + * @access private + * + */ + function setBackground() + { + $dat = @getimagesize($this->bgimg); + if($dat == false) { return; } + + switch($dat[2]) { + case 1: $newim = @imagecreatefromgif($this->bgimg); break; + case 2: $newim = @imagecreatefromjpeg($this->bgimg); break; + case 3: $newim = @imagecreatefrompng($this->bgimg); break; + case 15: $newim = @imagecreatefromwbmp($this->bgimg); break; + case 16: $newim = @imagecreatefromxbm($this->bgimg); break; + default: return; + } + + if(!$newim) return; + + imagecopy($this->im, $newim, 0, 0, 0, 0, $this->image_width, $this->image_height); + } + + /** + * Draw arced lines over the text + * + * @access private + * + */ + function arcLines() + { + $colors = explode(',', $this->randomColor()); + imagesetthickness($this->im, 3); + + $color = $colors[rand(0, sizeof($colors) - 1)]; + $linecolor = imagecolorallocate($this->im, hexdec(substr($color, 1, 2)), hexdec(substr($color, 3, 2)), hexdec(substr($color, 5, 2))); + + $xpos = $this->text_x_start + ($this->font_size * 2) + rand(-5, 5); + $width = $this->image_width / 2.66 + rand(3, 10); + $height = $this->font_size * 2.14 - rand(3, 10); + + if ( rand(0,100) % 2 == 0 ) { + $start = rand(0,66); + $ypos = $this->image_height / 2 - rand(5, 15); + $xpos += rand(5, 15); + } else { + $start = rand(180, 246); + $ypos = $this->image_height / 2 + rand(5, 15); + } + + $end = $start + rand(75, 110); + + imagearc($this->im, $xpos, $ypos, $width, $height, $start, $end, $linecolor); + + $color = $colors[rand(0, sizeof($colors) - 1)]; + $linecolor = imagecolorallocate($this->im, hexdec(substr($color, 1, 2)), hexdec(substr($color, 3, 2)), hexdec(substr($color, 5, 2))); + + if ( rand(1,75) % 2 == 0 ) { + $start = rand(45, 111); + $ypos = $this->image_height / 2 - rand(5, 15); + $xpos += rand(5, 15); + } else { + $start = rand(200, 250); + $ypos = $this->image_height / 2 + rand(5, 15); + } + + $end = $start + rand(75, 100); + + imagearc($this->im, $this->image_width * .75, $ypos, $width, $height, $start, $end, $linecolor); + } + + /** + * Draw lines on the image + * + * @access private + * + */ + function drawLines() + { + $linecolor = imagecolorallocate($this->im, hexdec(substr($this->line_color, 1, 2)), hexdec(substr($this->line_color, 3, 2)), hexdec(substr($this->line_color, 5, 2))); + imagesetthickness($this->im, $this->line_thickness); + + //vertical lines + for($x = 1; $x < $this->image_width; $x += $this->line_distance) { + imageline($this->im, $x, 0, $x, $this->image_height, $linecolor); + } + + //horizontal lines + for($y = 11; $y < $this->image_height; $y += $this->line_distance) { + imageline($this->im, 0, $y, $this->image_width, $y, $linecolor); + } + + if ($this->draw_angled_lines == true) { + for ($x = -($this->image_height); $x < $this->image_width; $x += $this->line_distance) { + imageline($this->im, $x, 0, $x + $this->image_height, $this->image_height, $linecolor); + } + + for ($x = $this->image_width + $this->image_height; $x > 0; $x -= $this->line_distance) { + imageline($this->im, $x, 0, $x - $this->image_height, $this->image_height, $linecolor); + } + } + } + + /** + * Draw the CAPTCHA code over the image + * + * @access private + * + */ + function drawWord() + { + if ($this->use_gd_font == true) { + if (!is_int($this->gd_font_file)) { //is a file name + $font = @imageloadfont($this->gd_font_file); + if ($font == false) { + trigger_error("Failed to load GD Font file {$this->gd_font_file} ", E_USER_WARNING); + return; + } + } else { //gd font identifier + $font = $this->gd_font_file; + } + + $color = imagecolorallocate($this->im, hexdec(substr($this->text_color, 1, 2)), hexdec(substr($this->text_color, 3, 2)), hexdec(substr($this->text_color, 5, 2))); + imagestring($this->im, $font, $this->text_x_start, ($this->image_height / 2) - ($this->gd_font_size / 2), $this->code, $color); + + } else { //ttf font + if($this->use_transparent_text == true) { + $alpha = intval($this->text_transparency_percentage / 100 * 127); + $font_color = imagecolorallocatealpha($this->im, hexdec(substr($this->text_color, 1, 2)), hexdec(substr($this->text_color, 3, 2)), hexdec(substr($this->text_color, 5, 2)), $alpha); + } else { //no transparency + $font_color = imagecolorallocate($this->im, hexdec(substr($this->text_color, 1, 2)), hexdec(substr($this->text_color, 3, 2)), hexdec(substr($this->text_color, 5, 2))); + } + + $x = $this->text_x_start; + $strlen = strlen($this->code); + $y_min = ($this->image_height / 2) + ($this->font_size / 2) - 2; + $y_max = ($this->image_height / 2) + ($this->font_size / 2) + 2; + $colors = explode(',', $this->multi_text_color); + + for($i = 0; $i < $strlen; ++$i) { + $angle = rand($this->text_angle_minimum, $this->text_angle_maximum); + $y = rand($y_min, $y_max); + if ($this->use_multi_text == true) { + $idx = rand(0, sizeof($colors) - 1); + $r = substr($colors[$idx], 1, 2); + $g = substr($colors[$idx], 3, 2); + $b = substr($colors[$idx], 5, 2); + if($this->use_transparent_text == true) { + $font_color = imagecolorallocatealpha($this->im, "0x$r", "0x$g", "0x$b", $alpha); + } else { + $font_color = imagecolorallocate($this->im, "0x$r", "0x$g", "0x$b"); + } + } + /* Will check if the FreeType library is loaded, if not, use the default PHP fonts. + * @customized by ATutor, Harris Wong + */ + if (function_exists('imagettftext')) { + @imagettftext($this->im, $this->font_size, $angle, $x, $y, $font_color, $this->ttf_file, $this->code{$i}); + } else { + $this->arc_linethrough = false; //no arcline then,too hard to see with the built in fonts and limited size + imagestring($this->im, 5, $x, $y-15, $this->code{$i}, $font_color); + } + + $x += rand($this->text_minimum_distance, $this->text_maximum_distance); + } //for loop + } //else ttf font + } //function + + /** + * Create a code and save to the session + * + * @since 1.0.1 + * + */ + function createCode() + { + $this->code = false; + + if ($this->use_wordlist && is_readable($this->wordlist_file)) { + $this->code = $this->readCodeFromFile(); + } + + if ($this->code == false) { + $this->code = $this->generateCode($this->code_length); + } + + $this->saveData(); + } + + /** + * Generate a code + * + * @access private + * @param int $len The code length + * @return string + */ + function generateCode($len) + { + $code = ''; + + for($i = 1, $cslen = strlen($this->charset); $i <= $len; ++$i) { + $code .= strtoupper( $this->charset{rand(0, $cslen - 1)} ); + } + return $code; + } + + /** + * Reads a word list file to get a code + * + * @access private + * @since 1.0.2 + * @return mixed false on failure, a word on success + */ + function readCodeFromFile() + { + $fp = @fopen($this->wordlist_file, 'rb'); + if (!$fp) return false; + + $fsize = filesize($this->wordlist_file); + if ($fsize < 32) return false; // too small of a list to be effective + + if ($fsize < 128) { + $max = $fsize; // still pretty small but changes the range of seeking + } else { + $max = 128; + } + + fseek($fp, rand(0, $fsize - $max), SEEK_SET); + $data = fread($fp, 128); // read a random 128 bytes from file + fclose($fp); + $data = preg_replace("/\r?\n/", "\n", $data); + + $start = strpos($data, "\n", rand(0, 100)) + 1; // random start position + $end = strpos($data, "\n", $start); // find end of word + + return strtolower(substr($data, $start, $end - $start)); // return substring in 128 bytes + } + + /** + * Output image to the browser + * + * @access private + * + */ + function output() + { + header("Expires: Sun, 1 Jan 2000 12:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT"); + header("Cache-Control: no-store, no-cache, must-revalidate"); + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); + + switch($this->image_type) + { + case SI_IMAGE_JPEG: + header("Content-Type: image/jpeg"); + imagejpeg($this->im, null, 90); + break; + + case SI_IMAGE_GIF: + header("Content-Type: image/gif"); + imagegif($this->im); + break; + + default: + header("Content-Type: image/png"); + imagepng($this->im); + break; + } + + imagedestroy($this->im); + } + + /** + * Get WAV file data of the spoken code.
    + * This is appropriate for output to the browser as audio/x-wav + * + * @since 1.0.1 + * @return string WAV data + * + */ + function getAudibleCode() + { + $letters = array(); + $code = $this->getCode(); + + if ($code == '') { + $this->createCode(); + $code = $this->getCode(); + } + + for($i = 0; $i < strlen($code); ++$i) { + $letters[] = $code{$i}; + } + + return $this->generateWAV($letters); + } + + /** + * Save the code in the session + * + * @access private + * + */ + function saveData() + { + $_SESSION['securimage_code_value'] = strtolower($this->code); + } + + /** + * Validate the code to the user code + * + * @access private + * + */ + function validate() + { + if ( isset($_SESSION['securimage_code_value']) && !empty($_SESSION['securimage_code_value']) ) { + if ( $_SESSION['securimage_code_value'] == strtolower(trim($this->code_entered)) ) { + $this->correct_code = true; + $_SESSION['securimage_code_value'] = ''; + } else { + $this->correct_code = false; + } + } else { + $this->correct_code = false; + } + } + + /** + * Get the captcha code + * + * @since 1.0.1 + * @return string + */ + function getCode() + { + if (isset($_SESSION['securimage_code_value']) && !empty($_SESSION['securimage_code_value'])) { + return $_SESSION['securimage_code_value']; + } else { + return ''; + } + } + + /** + * Check if the user entered code was correct + * + * @access private + * @return boolean + */ + function checkCode() + { + return $this->correct_code; + } + + /** + * Generate a wav file by concatenating individual files + * @since 1.0.1 + * @access private + * @param array $letters Array of letters to build a file from + * @return string WAV file data + */ + function generateWAV($letters) + { + $first = true; // use first file to write the header... + $data_len = 0; + $files = array(); + $out_data = ''; + + foreach ($letters as $letter) { + $filename = $this->audio_path . strtoupper($letter) . '.wav'; + + $fp = fopen($filename, 'rb'); + + $file = array(); + + $data = fread($fp, filesize($filename)); // read file in + + $header = substr($data, 0, 36); + $body = substr($data, 44); + + + $data = unpack('NChunkID/VChunkSize/NFormat/NSubChunk1ID/VSubChunk1Size/vAudioFormat/vNumChannels/VSampleRate/VByteRate/vBlockAlign/vBitsPerSample', $header); + + $file['sub_chunk1_id'] = $data['SubChunk1ID']; + $file['bits_per_sample'] = $data['BitsPerSample']; + $file['channels'] = $data['NumChannels']; + $file['format'] = $data['AudioFormat']; + $file['sample_rate'] = $data['SampleRate']; + $file['size'] = $data['ChunkSize'] + 8; + $file['data'] = $body; + + if ( ($p = strpos($file['data'], 'LIST')) !== false) { + // If the LIST data is not at the end of the file, this will probably break your sound file + $info = substr($file['data'], $p + 4, 8); + $data = unpack('Vlength/Vjunk', $info); + $file['data'] = substr($file['data'], 0, $p); + $file['size'] = $file['size'] - (strlen($file['data']) - $p); + } + + $files[] = $file; + $data = null; + $header = null; + $body = null; + + $data_len += strlen($file['data']); + + fclose($fp); + } + + $out_data = ''; + for($i = 0; $i < sizeof($files); ++$i) { + if ($i == 0) { // output header + $out_data .= pack('C4VC8', ord('R'), ord('I'), ord('F'), ord('F'), $data_len + 36, ord('W'), ord('A'), ord('V'), ord('E'), ord('f'), ord('m'), ord('t'), ord(' ')); + + $out_data .= pack('VvvVVvv', + 16, + $files[$i]['format'], + $files[$i]['channels'], + $files[$i]['sample_rate'], + $files[$i]['sample_rate'] * (($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8), + ($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8, + $files[$i]['bits_per_sample'] ); + + $out_data .= pack('C4', ord('d'), ord('a'), ord('t'), ord('a')); + + $out_data .= pack('V', $data_len); + } + + $out_data .= $files[$i]['data']; + } + + return $out_data; + } + + + /** + * Random color generation + * @return a 6 digits color code string + */ + function randomColor(){ + $str = '#'; + for($i=0;$i<3;$i++){ + $str .= rand(0, 255); + } + return $str; + } + +} /* class Securimage */ + +?> diff --git a/include/securimage/securimage_play.php b/include/securimage/securimage_play.php new file mode 100644 index 000000000..ab33cf896 --- /dev/null +++ b/include/securimage/securimage_play.php @@ -0,0 +1,20 @@ +getAudibleCode(); +exit; + +?> \ No newline at end of file diff --git a/include/securimage/securimage_show.php b/include/securimage/securimage_show.php new file mode 100644 index 000000000..31ca47a18 --- /dev/null +++ b/include/securimage/securimage_show.php @@ -0,0 +1,12 @@ +show(); // alternate use: $img->show('/path/to/background.jpg'); +?> diff --git a/include/side_menu.inc.php b/include/side_menu.inc.php new file mode 100644 index 000000000..fef7bdd39 --- /dev/null +++ b/include/side_menu.inc.php @@ -0,0 +1,53 @@ +assign('course_id', $_course_id); + +$side_menu = array(); +$stack_files = array(); + +if ($_course_id > 0) { + $savant->assign('my_uri', $_my_uri); + + $savant->assign('right_menu_open', TRUE); + $savant->assign('popup_help', 'MAIN_MENU'); + $savant->assign('menu_url', '
    '); + $savant->assign('close_menu_url', htmlspecialchars($_my_uri).'disable='.PREF_MAIN_MENU); + $savant->assign('close_menus', _AT('close_menus')); + + //copyright can be found in include/html/copyright.inc.php + + $side_menu = explode('|', $system_courses[$_course_id]['side_menu']); + + foreach ($side_menu as $side) { + if (isset($_stacks[$side])) { + $stack_files[] = $_stacks[$side]['file']; + } + } +} + +$savant->assign('side_menu', $stack_files); +$savant->display('include/side_menu.tmpl.php'); ?> \ No newline at end of file diff --git a/include/style_popup.css b/include/style_popup.css new file mode 100644 index 000000000..86bf32622 --- /dev/null +++ b/include/style_popup.css @@ -0,0 +1,172 @@ +body { + font-family: Verdana, Helvetica, sans-serif; + /* line-height: 15pt; */ + font-size: 1em; + background: #EFEFEF; + margin: 10px; + margin-left: 10px; + margin-bottom: 0px; + margin-top: 10px; + margin-right: 10px; +} +a:link, a:visited { + color: #006699; +} +a:hover { + color: #888888; +} + +li { + font-family: Verdana, Helvetica, sans-serif; + font-size: small; + /*line-height: 12pt; */ + padding-bottom: 5px; + padding: 2px; +} + +ul { + list-style-position: outside; + list-style-image: url('/images/bullet.gif'); + margin-top: 0px; +} +ol { + list-style-position: outside; + list-style-image: none; + margin-top: 0px; +} + +h1,h2,h3,h4,h5,h6 { + margin-bottom: 0px; + margin-top: 0px; + color: #152065; +} + +form { + margin-bottom: 0px; + margin-top: 0px; +} + + +.small { + font-family: Verdana, Helvetica, sans-serif; + color: black; + text-align: right; + vertical-align: bottom; +} + +hr { + color: #006699; + background-color: #006699; + margin-top: 0px; + margin-bottom: 0px; + height:1px; + border: 0px; +} + +.input { + font-family: Helvetica, sans-serif; + border-right: #006699 dashed 1px; + border-left: #006699 dashed 1px; + border-top: #006699 dashed 1px; + border-bottom: #006699 dashed 1px; +} +.input2 { + font-family: Helvetica, sans-serif; + border-right: 0px; + border-left: #006699 dashed 1px; + border-top: #006699 dashed 1px; + border-bottom: #006699 dashed 1px; +} + + +.submit { + background: #006699; + color: white; + border-right: white solid 1px; + border-left: white solid 1px; + border-top: white solid 1px; + border-bottom: white solid 1px; + padding: 1px; + font-size: smaller; +} + +.good { + font-family: Arial, Verdana, Helvetica, sans-serif; + font-size : 14px; + color : white; + background: green; + font-weight : bold; + padding: 4px; + } + +.selected { + font-family : Arial, Helvetica, Arial Cyr, Arial Ua, sans-serif; + font-size : 14px; + color : black; + background: #F2FF85; + font-weight : bold; + padding: 2px; + width: 45%; +} + +table.box2 { + background-color: #FFFFFF; + color: #006699; + +} + +th.box { + border-bottom: #006699 solid 1px; + color: white; + background-color: #006699; +} + +h2 { + border-bottom: 1px dashed #006699; + margin-bottom: 10px; + padding-bottom: 4px; + width: 98%; +} + +h5.heading2 { + letter-spacing: 3px; + /* text-transform: uppercase; */ + background-color: #eeeeee; + color: #006699; + font-weight: bold; + font-size: small; + padding-right: 3px; + padding-left: 3px; + text-align: center; +} + +table.box { + background-color: white; + border-right: #006699 solid 1px; + border-left: #006699 solid 1px; + border-top: #006699 solid 1px; +} + +table.box th, table.box td { + border-bottom: #006699 solid 1px; + font-size: smaller; +} + +table.box .submit { + background: #006699; + color: white; + font-size: smaller; +} + +table.box .submit:hover { + background: #0077AA; + color: white; + font-size: smaller; +} + + +.submit:hover, .submit:focus { + background: #0077AA; + color: white; + font-size: smaller; +} \ No newline at end of file diff --git a/include/unit_tests/AllTests.php b/include/unit_tests/AllTests.php new file mode 100644 index 000000000..580fe425a --- /dev/null +++ b/include/unit_tests/AllTests.php @@ -0,0 +1,30 @@ +addTestCase(new TestContentOutputUtils()); //fails, but can be modified later to not fail + } +} + +?> diff --git a/include/unit_tests/ContentOutputUtilsTest.php b/include/unit_tests/ContentOutputUtilsTest.php new file mode 100644 index 000000000..56f32e6d5 --- /dev/null +++ b/include/unit_tests/ContentOutputUtilsTest.php @@ -0,0 +1,49 @@ +This is some text.

    +

    [code]

    +

    alert('1');

    +

    alert('2');

    +

    [/code]

    "; + + private $expectedResult = "

    This is some text.

    +

    [code]alert('1');alert('2');[/code]

    "; + + /** + * Tests testStripPTags + */ + function testStripPTags1() + { + $utils = new ContentOutputUtils(); + $actualResult = $utils->stripPtags($this->testString); + $actualLength = strlen($actualResult); + $expectedLength = strlen($this->expectedResult); + $this->assertEqual($expectedLength, $actualLength); + $maxLength = max($expectedLength, $actualLength); + for ($i = 0; $i < $maxLength; $i++) { + $this->assertEqual($this->expectedResult[$i], $actualResult[$i], "i is ". $i." chars are ".$this->expectedResult[$i]." and ".$actualResult[$i]); + } + $this->assertEqual($this->expectedResult, $actualResult); + } +} +?> diff --git a/include/vitals.inc.php b/include/vitals.inc.php new file mode 100644 index 000000000..41137c7ab --- /dev/null +++ b/include/vitals.inc.php @@ -0,0 +1,1423 @@ + $v) { + if (!in_array($k, $noUnset) && isset($GLOBALS[$k])) { unset($GLOBALS[$k]); } + } +} + +//functions for properly escaping input strings +function my_add_null_slashes( $string ) { + return mysql_real_escape_string(stripslashes($string)); +} +function my_null_slashes($string) { + return $string; +} + +if ( get_magic_quotes_gpc() == 1 ) { + $addslashes = 'my_add_null_slashes'; + $stripslashes = 'stripslashes'; +} else { + $addslashes = 'mysql_real_escape_string'; + $stripslashes = 'my_null_slashes'; +} + +/* + * structure of this document (in order): + * + * 0. load config.inc.php + * 1. load constants + * 2. initilize session + * 3. load language constants + * 4. enable output compression + * 5. initilize db connection + * 6. load cache library + * 7. initilize session localization + * 8. load ContentManagement/output/Savant/Message libraries + ***/ + +/**** 0. start system configuration options block ****/ + error_reporting(0); + if (!defined('AT_REDIRECT_LOADED')){ + include_once(AT_INCLUDE_PATH.'config.inc.php'); + } + error_reporting(AT_ERROR_REPORTING); + + if (!defined('AT_INSTALL') || !AT_INSTALL) { + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Pragma: no-cache'); + + $relative_path = substr(AT_INCLUDE_PATH, 0, -strlen('include/')); + header('Location: ' . $relative_path . 'install/not_installed.php'); + exit; + } +/*** end system config block ****/ + +/*** 1. constants ***/ + if (!defined('AT_REDIRECT_LOADED')){ + require_once(AT_INCLUDE_PATH.'lib/constants.inc.php'); + } + +/***** 2. start session initilization block ****/ + if (headers_sent()) { + require_once(AT_INCLUDE_PATH . 'classes/ErrorHandler/ErrorHandler.class.php'); + $err = new ErrorHandler(); + trigger_error('VITAL#

    An error occurred. Output sent before it should have. Please correct the above error(s).' . '


    ', E_USER_ERROR); + } + + @set_time_limit(0); + @ini_set('session.gc_maxlifetime', '36000'); /* 10 hours */ + @session_cache_limiter('private, must-revalidate'); + session_name('ATutorID'); + error_reporting(AT_ERROR_REPORTING); + + if (headers_sent()) { + require_once(AT_INCLUDE_PATH . 'classes/ErrorHandler/ErrorHandler.class.php'); + $err = new ErrorHandler(); + trigger_error('VITAL#
    Headers already sent. ' . + 'Cannot initialise session.


    ', E_USER_ERROR); + exit; + } + + ob_start(); + session_set_cookie_params(0, $_base_path); + session_start(); + $str = ob_get_contents(); + ob_end_clean(); + unregister_GLOBALS(); + + if ($str) { + require_once(AT_INCLUDE_PATH . 'classes/ErrorHandler/ErrorHandler.class.php'); + $err = new ErrorHandler(); + trigger_error('VITAL#
    Error initializing session. ' . + 'Please varify that session.save_path is correctly set in your php.ini file ' . + 'and the directory exists.


    ', E_USER_ERROR); + exit; + } + + +/***** end session initilization block ****/ + +// 4. enable output compression, if it isn't already enabled: +if ((@ini_get('output_handler') == '') && (@ini_get('zlib.output_handler') == '')) { + @ini_set('zlib.output_compression', 1); +} + +/* 5. database connection */ +if (!defined('AT_REDIRECT_LOADED')){ + require_once(AT_INCLUDE_PATH.'lib/mysql_connect.inc.php'); +} + +/* get config variables. if they're not in the db then it uses the installation default value in constants.inc.php */ +$sql = "SELECT * FROM ".TABLE_PREFIX."config"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $_config[$row['name']] = $row['value']; +} + +//Check if users=valid +if (!isset($_SESSION['course_id']) && !isset($_SESSION['valid_user']) && (!isset($_user_location) || $_user_location != 'public') && !isset($_pretty_url_course_id)) { + if (isset($in_get) && $in_get && (($pos = strpos($_SERVER['PHP_SELF'], 'get.php/')) !== FALSE)) { + $redirect = substr($_SERVER['PHP_SELF'], 0, $pos) . 'login.php'; + header('Location: '.$redirect); + exit; + } + + //hack for Terms and Conditions module. Will need a better way to handle this module without adding this to the vitals. + if($_config['enable_terms_and_conditions']==1){ + header('Location: '.AT_BASE_HREF.'mods/terms_and_conditions/terms_and_conditions.php'); + exit; + } + + header('Location: '.AT_BASE_HREF.'login.php'); + exit; +} + +/* following is added as a transition period and backwards compatability: */ +define('EMAIL', $_config['contact_email']); +define('EMAIL_NOTIFY', $_config['email_notification']); +define('ALLOW_INSTRUCTOR_REQUESTS', $_config['allow_instructor_requests']); +define('AUTO_APPROVE_INSTRUCTORS', $_config['auto_approve_instructors']); +define('SITE_NAME', $_config['site_name']); +define('HOME_URL', $_config['home_url']); +define('DEFAULT_LANGUAGE', $_config['default_language']); +define('CACHE_DIR', $_config['cache_dir']); +define('AT_ENABLE_CATEGORY_THEMES', $_config['theme_categories']); +define('AT_COURSE_BACKUPS', $_config['course_backups']); +define('AT_EMAIL_CONFIRMATION', $_config['email_confirmation']); +define('AT_MASTER_LIST', $_config['master_list']); +$MaxFileSize = $_config['max_file_size']; +$MaxCourseSize = $_config['max_course_size']; +$MaxCourseFloat = $_config['max_course_float']; +$IllegalExtentions = explode('|',$_config['illegal_extentions']); +define('AT_DEFAULT_PREFS', isset($_config['prefs_default']) ? $_config['prefs_default'] : ''); +$_config['home_defaults'] .= (isset($_config['home_defaults_2']) ? $_config['home_defaults_2'] : ''); +$_config['main_defaults'] .= (isset($_config['main_defaults_2']) ? $_config['main_defaults_2'] : ''); + +require(AT_INCLUDE_PATH.'phpCache/phpCache.inc.php'); // cache library +require(AT_INCLUDE_PATH.'lib/utf8.php'); //UTF-8 multibyte library + +if ($_config['time_zone']) { + //$sql = "SET time_zone='{$_config['time_zone']}'"; + //mysql_query($sql, $db); + + if (function_exists('date_default_timezone_set')) { + foreach($utc_timezones as $zone){ + + if($zone[1] == $_config['time_zone']){ + $zone_name = $zone[2]; + break; + } + } + date_default_timezone_set($zone_name); + } else { + @putenv("TZ={$_config['time_zone']}"); + } +} +/***** 7. start language block *****/ + // set current language + require(AT_INCLUDE_PATH . '../mods/_core/languages/classes/LanguageManager.class.php'); + $languageManager = new LanguageManager(); + + $myLang =& $languageManager->getMyLanguage(); + + if ($myLang === FALSE) { + echo 'There are no languages installed!'; + exit; + } + $myLang->saveToSession(); + if (isset($_GET['lang']) && $_SESSION['valid_user']) { + if ($_SESSION['course_id'] == -1) { + $myLang->saveToPreferences($_SESSION['login'], 1); //1 for admin + } else { + $myLang->saveToPreferences($_SESSION['member_id'], 0); //0 for non-admin + } + } + $myLang->sendContentTypeHeader(); + + /* set right-to-left language */ + $rtl = ''; + if ($myLang->isRTL()) { + $rtl = 'rtl_'; /* basically the prefix to a rtl variant directory/filename. eg. rtl_tree */ + } +/***** end language block ****/ + +/* 8. load common libraries */ + require(AT_INCLUDE_PATH.'classes/ContentManager.class.php'); /* content management class */ + require_once(AT_INCLUDE_PATH.'lib/output.inc.php'); /* output functions */ + if (!(defined('AT_REDIRECT_LOADED'))){ + require_once(AT_INCLUDE_PATH . 'classes/UrlRewrite/UrlParser.class.php'); /* pretty url tool */ + } + require(AT_INCLUDE_PATH.'classes/Savant2/Savant2.php'); /* for the theme and template management */ + + // set default template paths: + $savant = new Savant2(); + $savant->addPath('template', AT_INCLUDE_PATH . '../themes/default/'); + + //if user has requested theme change, make the change here + if (($_POST['theme'] || $_POST['mobile_theme']) && $_POST['submit']) { + $_SESSION['prefs']['PREF_THEME'] = $addslashes($_POST['theme']); + $_SESSION['prefs']['PREF_MOBILE_THEME'] = $addslashes($_POST['mobile_theme']); + } else if ($_POST['set_default']) { + $_SESSION['prefs']['PREF_THEME'] = 'default'; + $_SESSION['prefs']['PREF_MOBILE_THEME'] = 'mobile'; + } + + // if the request is from a mobile device, same original session var PREF_THEME into session var PREF_THEME_ORIG + // and assign PREF_MOBILE_THEME TO PREF_THEME so that elsewhere PREF_THEME is used doesn't not need to be changed. + // At save_prefs(), switch back the theme session vars: PREF_THEME => PREF_MOBILE_THEME, PREF_THEME_ORIG => PREF_THEME + if (is_mobile_device()) { + if (!is_mobile_theme($_SESSION['prefs']['PREF_MOBILE_THEME'])) { + $_SESSION['prefs']['PREF_MOBILE_THEME'] = get_system_default_theme(); + } + + if ($_SESSION['prefs']['PREF_THEME'] <> $_SESSION['prefs']['PREF_MOBILE_THEME']) { + $_SESSION['prefs']['PREF_THEME_ORIG'] = $_SESSION['prefs']['PREF_THEME']; + $_SESSION['prefs']['PREF_THEME'] = $_SESSION['prefs']['PREF_MOBILE_THEME']; + } + } + + // use "mobile" theme for mobile devices. For now, there's only one mobile theme and it's hardcoded. + // When more mobile themes come in, this should be changed. + if (isset($_SESSION['prefs']['PREF_THEME']) && file_exists(AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME']) && isset($_SESSION['valid_user']) && $_SESSION['valid_user']) { + if ($_SESSION['course_id'] == -1) { + if ($_SESSION['prefs']['PREF_THEME'] == '' || !is_dir(AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME'])) { + $_SESSION['prefs']['PREF_THEME'] = get_system_default_theme(); + } + $savant->addPath('template', AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME'] . '/'); + } else { + //check if enabled + $sql = "SELECT status FROM ".TABLE_PREFIX."themes WHERE dir_name = '".$_SESSION['prefs']['PREF_THEME']."'"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + if ($row['status'] > 0) { + $savant->addPath('template', AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME'] . '/'); + } else { + // get default + $default_theme = get_default_theme(); + if (!is_dir(AT_INCLUDE_PATH . '../themes/' . $default_theme['dir_name'])) { + $default_theme = array('dir_name' => get_system_default_theme()); + } + $savant->addPath('template', AT_INCLUDE_PATH . '../themes/' . $default_theme['dir_name'] . '/'); + $_SESSION['prefs']['PREF_THEME'] = $default_theme['dir_name']; + } + } + } else { + // get default + $default_theme = get_default_theme(); + + if (!is_dir(AT_INCLUDE_PATH . '../themes/' . $default_theme['dir_name']) || $default_theme == '') { + $default_theme = array('dir_name' => get_system_default_theme()); + } + $savant->addPath('template', AT_INCLUDE_PATH . '../themes/' . $default_theme['dir_name'] . '/'); + $_SESSION['prefs']['PREF_THEME'] = $default_theme['dir_name']; + } + + require(AT_INCLUDE_PATH . '../themes/' . $_SESSION['prefs']['PREF_THEME'] . '/theme.cfg.php'); + + require(AT_INCLUDE_PATH.'classes/Message/Message.class.php'); + $msg = new Message($savant); + + $contentManager = new ContentManager($db, isset($_SESSION['course_id']) ? $_SESSION['course_id'] : $_GET['p_course']); + $contentManager->initContent(); +/**************************************************/ + +if (isset($_user_location) && ($_user_location == 'users') && $_SESSION['valid_user'] && ($_SESSION['course_id'] > 0)) { + $_SESSION['course_id'] = 0; +} + +if ((!isset($_SESSION['course_id']) || $_SESSION['course_id'] == 0) && ($_user_location != 'users') && ($_user_location != 'prog') && !isset($_GET['h']) && ($_user_location != 'public') && (!isset($_pretty_url_course_id) || $_pretty_url_course_id == 0)) { + header('Location:'.AT_BASE_HREF.'users/index.php'); + exit; +} +/* check if we are in the requested course, if not, bounce to it. + * @author harris, for pretty url, read AT_PRETTY_URL_HANDLER + */ +if ((isset($_SESSION['course_id']) && isset($_pretty_url_course_id) && $_SESSION['course_id'] != $_pretty_url_course_id) || + (isset($_pretty_url_course_id) && !isset($_SESSION['course_id']) && !isset($_REQUEST['ib']))) { + + if($_config['pretty_url'] == 0){ + header('Location: '.AT_BASE_HREF.'bounce.php?course='.$_pretty_url_course_id.SEP.'pu='.$_SERVER['PATH_INFO'].urlencode('?'.$_SERVER['QUERY_STRING'])); + } else { + header('Location: '.AT_BASE_HREF.'bounce.php?course='.$_pretty_url_course_id.SEP.'pu='.$_SERVER['PATH_INFO']); + } + exit; +} + + /** + * This function is used for printing variables into log file for debugging. + * @access public + * @param mixed $var The variable to output + * @param string $log The location of the log file. If not provided, use the default one. + * @author Cindy Qi Li + */ +function debug_to_log($var, $log='') { + if (!defined('AT_DEVEL') || !AT_DEVEL) { + return; + } + + if ($log == '') $log = AT_CONTENT_DIR. 'atutor.log'; + $handle = fopen($log, 'a'); + fwrite($handle, "\n\n"); + fwrite($handle, date("F j, Y, g:i a")); + fwrite($handle, "\n"); + fwrite($handle, var_export($var,1)); + + fclose($handle); +} + + /** + * This function is used for printing variables for debugging. + * @access public + * @param mixed $var The variable to output + * @param string $title The name of the variable, or some mark-up identifier. + * @author Joel Kronenberg + */ +function debug($var, $title='') { + if (!defined('AT_DEVEL') || !AT_DEVEL) { + return; + } + + echo '
    ';
    +	if ($title) {
    +		echo '

    '.$title.'

    '; + } + + ob_start(); + print_r($var); + $str = ob_get_contents(); + ob_end_clean(); + + $str = str_replace('<', '<', $str); + + $str = str_replace('[', '[', $str); + $str = str_replace(']', ']', $str); + $str = str_replace('=>', '=>', $str); + $str = str_replace('Array', 'Array', $str); + echo $str; + echo '
    '; +} + +/********************************************************************/ +/* the system course information */ +/* system_courses[course_id] = array(title, description, subject) */ +$system_courses = array(); + +// temporary set to a low number +$sql = 'SELECT * FROM '.TABLE_PREFIX.'courses ORDER BY title'; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $course = $row['course_id']; + unset($row['course_id']); + $system_courses[$course] = $row; +} +/* */ +/********************************************************************/ +// p_course is set when pretty url is on and guests access a public course. @see bounce.php +if (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0 || $_REQUEST['p_course'] > 0) { + $sql = 'SELECT * FROM '.TABLE_PREFIX.'glossary + WHERE course_id='.($_SESSION['course_id']>0 ? $_SESSION['course_id'] : $_REQUEST['p_course']).' + ORDER BY word'; + $result = mysql_query($sql, $db); + $glossary = array(); + $glossary_ids = array(); + while ($row_g = mysql_fetch_assoc($result)) { + $row_g['word'] = htmlspecialchars($row_g['word'], ENT_QUOTES, 'UTF-8'); + $glossary[$row_g['word']] = str_replace("'", "\'",$row_g['definition']); + $glossary_ids[$row_g['word_id']] = $row_g['word']; + + /* a kludge to get the related id's for when editing content */ + /* it's ugly, but beats putting this query AGAIN on the edit_content.php page */ + if (isset($get_related_glossary)) { + $glossary_ids_related[$row_g['word']] = $row_g['related_word_id']; + } + } +} + +function get_html_body($text) { + /* strip everything before */ + $start_pos = strpos(strtolower($text), '', $start_pos); + $end_pos += strlen('>'); + + $text = substr($text, $end_pos); + } + + /* strip everything after */ + $end_pos = strpos(strtolower($text), ''); + if ($end_pos !== false) { + $text = trim(substr($text, 0, $end_pos)); + } + + return $text; +} + +function get_html_head ($text) { + /* make all text lower case */ +// $text = strtolower($text); + + /* strip everything before */ + $start_pos = stripos($text, '', $start_pos); + $end_pos += strlen('>'); + + $text = substr($text, $end_pos); + } + + /* strip everything after */ + $end_pos = stripos($text, ' 0) + { + $tags = array(trim($tags)); + } + foreach ($tags as $tag) + { + $tag = strtolower($tag); + + /* strip everything before <{tag}> */ + $start_pos = stripos($head, '<'.$tag); + $temp_head = $head; + + while ($start_pos !== false) + { + $temp_text = substr($temp_head, $start_pos); + + /* strip everything after or />*/ + $end_pos = stripos($temp_text, ''); + + if ($end_pos !== false) + { + $end_pos += strlen(''); + + // add an empty line after each tag information + $rtn_text .= trim(substr($temp_text, 0, $end_pos)) . ' + +'; + } + else // match /> as ending tag if is not found + { + $end_pos = stripos($temp_text, '/>'); + + if($end_pos === false && stripos($temp_text, $tag.'>')===false){ + //if /> is not found, then this is not a valid XHTML + //text iff it's not tag> + $end_pos = stripos($temp_text, '>'); + $end_pos += strlen('>'); + } else { + $end_pos += strlen('/>'); + } + // add an empty line after each tag information + $rtn_text .= trim(substr($temp_text, 0, $end_pos)) . ' + +'; + } + + // initialize vars for next round of matching + $temp_head = substr($temp_text, $end_pos); + $start_pos = stripos($temp_head, '<'.$tag); + } + } + return $rtn_text; +} + +if (version_compare(phpversion(), '4.3.0') < 0) { + function file_get_contents($filename) { + $fd = @fopen($filename, 'rb'); + if ($fd === false) { + $content = false; + } else { + $content = @fread($fd, filesize($filename)); + @fclose($fd); + } + + return $content; + } + + function mysql_real_escape_string($input) { + return mysql_escape_string($input); + } +} + + +function add_user_online() { + if (!isset($_SESSION['member_id']) || !($_SESSION['member_id'] > 0)) { + return; + } + global $db, $addslashes; + + $expiry = time() + 900; // 15min + $sql = 'REPLACE INTO '.TABLE_PREFIX.'users_online VALUES ('.$_SESSION['member_id'].', '.$_SESSION['course_id'].', "'.$addslashes(get_display_name($_SESSION['member_id'])).'", '.$expiry.')'; + $result = mysql_query($sql, $db); + + /* garbage collect and optimize the table every so often */ + mt_srand((double) microtime() * 1000000); + $rand = mt_rand(1, 20); + if ($rand == 1) { + $sql = 'DELETE FROM '.TABLE_PREFIX.'users_online WHERE expiry<'.time(); + $result = @mysql_query($sql, $db); + } +} + +/** + * Returns the login name of a member. + * @access public + * @param int $id The ID of the member. + * @return Returns the login name of the member whose ID is $id. + * @author Joel Kronenberg + */ +function get_login($id){ + global $db, $_config_defaults; + + if (is_array($id)) { + $id = implode(',',$id); + $sql = 'SELECT login, member_id FROM '.TABLE_PREFIX.'members WHERE member_id IN ('.$id.') ORDER BY login'; + + $rows = array(); + $result = mysql_query($sql, $db); + while( $row = mysql_fetch_assoc($result)) { + $rows[$row['member_id']] = $row['login']; + } + return $rows; + } else { + $id = intval($id); + $sql = 'SELECT login FROM '.TABLE_PREFIX.'members WHERE member_id='.$id; + + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + return $row['login']; + } + +} + +function get_display_name($id) { + static $db, $_config, $display_name_formats; + if (!$id) { + return $_SESSION['login']; + } + + if (!isset($db, $_config)) { + global $db, $_config, $display_name_formats; + } + + if (substr($id, 0, 2) == 'g_' || substr($id, 0, 2) == 'G_') + { + $sql = "SELECT name FROM ".TABLE_PREFIX."guests WHERE guest_id='".$id."'"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + return _AT($display_name_formats[$_config['display_name_format']], '', $row['name'], '', ''); + } + else + { + $sql = 'SELECT login, first_name, second_name, last_name FROM '.TABLE_PREFIX.'members WHERE member_id='.$id; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + return _AT($display_name_formats[$_config['display_name_format']], $row['login'], $row['first_name'], $row['second_name'], $row['last_name']); + } +} + +function get_forum_name($fid){ + global $db; + + $fid = intval($fid); + + $sql = 'SELECT title FROM '.TABLE_PREFIX.'forums WHERE forum_id='.$fid; + $result = mysql_query($sql, $db); + if (($row = mysql_fetch_assoc($result)) && $row['title']) { + return $row['title']; + } + + $sql = "SELECT group_id FROM ".TABLE_PREFIX."forums_groups WHERE forum_id=$fid"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + return get_group_title($row['group_id']); + } + + return FALSE; +} + +/* takes the array of valid prefs and assigns them to the current session */ +function assign_session_prefs($prefs) { + if (is_array($prefs)) { + foreach($prefs as $pref_name => $value) { + $_SESSION['prefs'][$pref_name] = $value; + } + } +} + +function save_prefs( ) { + global $db, $addslashes; + + if ($_SESSION['valid_user']) { + if (is_mobile_device()) { + $_SESSION['prefs']['PREF_MOBILE_THEME'] = $_SESSION['prefs']['PREF_THEME']; + $_SESSION['prefs']['PREF_THEME'] = $_SESSION['prefs']['PREF_THEME_ORIG']; + } + unset($_SESSION['prefs']['PREF_THEME_ORIG']); + $data = $addslashes(serialize($_SESSION['prefs'])); + $sql = 'UPDATE '.TABLE_PREFIX.'members SET preferences="'.$data.'", creation_date=creation_date, last_login=last_login WHERE member_id='.$_SESSION['member_id']; + $result = mysql_query($sql, $db); + } +} + +function save_email_notification($mnot) { + global $db; + + if ($_SESSION['valid_user']) { + $sql = "UPDATE ".TABLE_PREFIX."members SET inbox_notify =". $mnot .", creation_date=creation_date, last_login=last_login WHERE member_id =".$_SESSION['member_id']; + $result = mysql_query($sql, $db); + } +} + +/** +* Saves the last viewed content page in a user's course so that on next visit, user can start reading where they left off +* @access public +* @param int $cid the content page id +* @return none +* @see $db in include/vitals.inc.php +* @author Joel Kronenberg +*/ +function save_last_cid($cid) { + if ($_SESSION['enroll'] == AT_ENROLL_NO) { + return; + } + global $db; + + $_SESSION['s_cid'] = intval($_GET['cid']); + + if (!$_SESSION['is_admin'] && + !$_SESSION['privileges'] && + !isset($in_get) && + !$_SESSION['cid_time'] && + ($_SESSION['course_id'] > 0) ) + { + $_SESSION['cid_time'] = time(); + } + + $sql = "UPDATE ".TABLE_PREFIX."course_enrollment SET last_cid=$cid WHERE course_id=$_SESSION[course_id] AND member_id=$_SESSION[member_id]"; + mysql_query($sql, $db); +} + +// there has to be a better way of expressing this if-statement! +// and, does it really have to be here? +if ((!isset($_SESSION['is_admin']) || !$_SESSION['is_admin']) && + (!isset($_SESSION['privileges']) || !$_SESSION['privileges']) && + !isset($in_get) && + isset($_SESSION['s_cid']) && $_SESSION['s_cid'] && + isset($_SESSION['cid_time']) && $_SESSION['cid_time'] && + ($_SESSION['course_id'] > 0) && + ($_SESSION['s_cid'] != $_GET['cid']) && + ($_SESSION['enroll'] != AT_ENROLL_NO) ) + { + $diff = time() - $_SESSION['cid_time']; + if ($diff > 0) { + $sql = "UPDATE ".TABLE_PREFIX."member_track SET counter=counter+1, duration=duration+$diff, last_accessed=NOW() WHERE member_id=$_SESSION[member_id] AND content_id=$_SESSION[s_cid]"; + + $result = mysql_query($sql, $db); + + if (mysql_affected_rows($db) == 0) { + $sql = "INSERT INTO ".TABLE_PREFIX."member_track VALUES ($_SESSION[member_id], $_SESSION[course_id], $_SESSION[s_cid], 1, $diff, NOW())"; + $result = mysql_query($sql, $db); + } + } + + $_SESSION['cid_time'] = 0; +} + + +/** +* Checks if the $_SESSION[member_id] is an instructor (true) or not (false) +* The result is only fetched once - it is then available via a static variable, $is_instructor +* @access public +* @param none +* @return bool true if is instructor, false otherwise. +* @see $db in include/vitals.inc.php +* @author Joel Kronenberg +*/ +function get_instructor_status() { + static $is_instructor; + + if (isset($is_instructor)) { + return $is_instructor; + } + + global $db; + + $is_instructor = false; + + $sql = 'SELECT status FROM '.TABLE_PREFIX.'members WHERE member_id='.$_SESSION['member_id']; + $result = mysql_query($sql, $db); + if (!($row = @mysql_fetch_assoc($result))) { + $is_instructor = FALSE; + return FALSE; + } + + if ($row['status'] == AT_STATUS_INSTRUCTOR) { + $is_instructor = TRUE; + return TRUE; + } + + $is_instructor = FALSE; + return FALSE; +} + +/****************************************************/ +/* update the user online list */ +if (isset($_SESSION['valid_user']) && $_SESSION['valid_user']) { + $new_minute = time()/60; + if (!isset($_SESSION['last_updated'])) { + $_SESSION['last_updated'] = $new_minute; + } + $diff = abs($_SESSION['last_updated'] - $new_minute); + if ($diff > ONLINE_UPDATE) { + $_SESSION['last_updated'] = $new_minute; + add_user_online(); + } +} + +/****************************************************/ +/* compute the $_my_uri variable */ + $bits = explode(SEP, getenv('QUERY_STRING')); + $num_bits = count($bits); + $_my_uri = ''; + + for ($i=0; $i<$num_bits; $i++) { + if ( (strpos($bits[$i], 'enable=') === 0) + || (strpos($bits[$i], 'disable=') === 0) + || (strpos($bits[$i], 'expand=') === 0) + || (strpos($bits[$i], 'collapse=') === 0) + || (strpos($bits[$i], 'lang=') === 0) + ) { + /* we don't want this variable added to $_my_uri */ + continue; + } + + if (($_my_uri == '') && ($bits[$i] != '')) { + $_my_uri .= htmlentities('?'); + } else if ($bits[$i] != ''){ + $_my_uri .= htmlentities(SEP); + } + $_my_uri .= $bits[$i]; + } + if ($_my_uri == '') { + $_my_uri .= htmlentities('?'); + } else { + $_my_uri .= htmlentities(SEP); + } + $_my_uri = $_SERVER['PHP_SELF'].$_my_uri; + +/** + * If MBString extension is loaded, 4.3.0+, then use it. + * Otherwise we will have to use include/utf8 library + * @author Harris + * @date Oct 10, 2007 + * @version 1.5.6 + */ + if (extension_loaded('mbstring')){ + $strtolower = 'mb_strtolower'; + $strtoupper = 'mb_strtoupper'; + $substr = 'mb_substr'; + $strpos = 'mb_strpos'; + $strrpos = 'mb_strrpos'; + $strlen = 'mb_strlen'; + } else { + $strtolower = 'utf8_strtolower'; + $strtoupper = 'utf8_strtoupper'; + $substr = 'utf8_substr'; + $strpos = 'utf8_strpos'; + $strrpos = 'utf8_strrpos'; + $strlen = 'utf8_strlen'; + } + + +/*~~~~~~~~~~~~~~~~~flash detection~~~~~~~~~~~~~~~~*/ +if(isset($_COOKIE["flash"])){ + $_SESSION['flash'] = $_COOKIE["flash"]; + + //delete the cookie + ATutor.setcookie("flash",'',time()-3600); +} + +if (!isset($_SESSION["flash"])) { + $_custom_head .= ' + +'; +} + + + +/*~~~~~~~~~~~~~~end flash detection~~~~~~~~~~~~~~~*/ + + + +/** +* Checks if the data exceeded the database predefined length, if so, +* truncate it. +* This is used on data that are being inserted into the database. +* If this function is used for display purposes, you may want to add the '...' +* at the end of the string by setting the $forDisplay=1 +* @param the mbstring that needed to be checked +* @param the byte length of what the input should be in the database. +* @param (OPTIONAL) +* append '...' at the end of the string. Should not use this when +* dealing with database. This should only be set for display purposes. +* @return the mbstring safe sql entry +* @author Harris Wong +*/ +function validate_length($input, $len, $forDisplay=0){ + global $strlen, $substr; + $input_bytes_len = strlen($input); + $input_len = $strlen($input); + + //If the input has exceeded the db column limit + if ($input_bytes_len > $len){ + //calculate where to chop off the string + $percentage = $input_bytes_len / $input_len; + //Get the suitable length that should be stored in the db + $suitable_len = floor($len / $percentage); + + if ($forDisplay===1){ + return $substr($input, 0, $suitable_len).'...'; + } + return $substr($input, 0, $suitable_len); + } + //if valid length + return $input; + +/* + * Instead of blindly cutting off the input from the given param + * + global $strlen, $substr; + if ($strlen($input) > $len) { + if ($forDisplay===1){ + return $substr($input, 0, $len).'...'; + } + return $substr($input, 0, $len); + } + return $input; +*/ +} + +/** + * If pretty URL within admin config is switched on. We will apply pretty URL + * to all the links in ATutor. This function will authenticate itself towards the current pages. + * In our definition, admins, login, registration pages shouldn't have pretty url applied. However, + * if one want to use url_rewrite on these pages, please force it by using the third parameter. + * Note: If system config has turned off this feature, $force will have no effect. + * @param string the Url should be a relative link, have to improve this later on, to check if + * it's a relative link, if not, truncate it. + * @param boolean Available values are AT_PRETTY_URL_IS_HEADER, AT_PRETTY_URL_NOT_HEADER(default) + * use AT_PRETTY_URL_IS_HEADER if url_rewrite is used in php header('Location:..'), absolute path is needed for this. + * @param boolean true to force the url_rewrite, false otheriwse. False is the default. + * @author Harris Wong + */ +function url_rewrite($url, $is_rewriting_header=AT_PRETTY_URL_NOT_HEADER, $force=false){ + global $_config, $db; + $url_parser = new UrlParser(); + $pathinfo = $url_parser->getPathArray(); + + /* If this is any kind of admins, don't prettify the url + * $_SESSION['is_guest'] is used to check against login/register/browse page, the links on this page will + * only be prettified when a user has logged in. + * Had used $_SESSION[valid_user] before but it created this problem: + * http://www.atutor.ca/atutor/mantis/view.php?id=3426 + */ + if ($force || (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0)) { + //if course id is defined, apply pretty url. + } + //if this is something that is displayed on the login page, don't modify the urls. + else if ( (admin_authenticate(AT_ADMIN_PRIV_ADMIN, AT_PRIV_RETURN) + || (isset($_SESSION['privileges']) && admin_authenticate($_SESSION['privileges'], AT_PRIV_RETURN))) + || (isset($_SESSION['is_guest']) && $_SESSION['is_guest']==1)){ + return $url; + } + + //if we allow pretty url in the system + if ($_config['pretty_url'] > 0){ + $course_id = 0; + //If we allow course dir name from sys perf + if ($_config['course_dir_name'] > 0){ + if (preg_match('/bounce.php\?course=([\d]+)$/', $url, $matches) == 1){ + // bounce has the highest priority, even if session is set, work on + // bounce first. + $course_id = $url_parser->getCourseDirName($matches[1]); + } elseif (isset($_REQUEST['course'])){ + //jump menu + $course_id = $url_parser->getCourseDirName($_REQUEST['course']); + } elseif (isset($_REQUEST['p_course'])){ + // is set when guests access public course. @see bounce.php + $course_id = $url_parser->getCourseDirName($_REQUEST['p_course']); + } elseif (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0){ + $course_id = $url_parser->getCourseDirName($_SESSION['course_id']); + } + } else { + $course_id = $_SESSION['course_id']; + } + $url = $pathinfo[1]->convertToPrettyUrl($course_id, $url); + } elseif ($_config['course_dir_name'] > 0) { + //enabled course directory name, disabled pretty url + if (preg_match('/bounce.php\?course=([\d]+)$/', $url, $matches) == 1){ + // bounce has the highest priority, even if session is set, work on + // bounce first. + $course_id = $url_parser->getCourseDirName($matches[1]); + } elseif (isset($_REQUEST['course'])){ + $course_id = $url_parser->getCourseDirName($_REQUEST['course']); + } elseif (isset($_REQUEST['p_course'])){ + // is set when guests access public course. @see bounce.php + $course_id = $url_parser->getCourseDirName($_REQUEST['p_course']); + } elseif (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0){ + $course_id = $url_parser->getCourseDirName($_SESSION['course_id']); + } + $url = $pathinfo[1]->convertToPrettyUrl($course_id, $url); + } + + //instead of putting AT_BASE_HREF in all the headers location, we will put it here. + //Abs paths are required for pretty url because otherwise the url location will be appeneded. + //ie. ATutor_161/blogs/CoURSe_rOAd/blogs/view.php/ot/1/oid/1/ instead of + // ATutor_161/CoURSe_rOAd/blogs/view.php/ot/1/oid/1/ + if ($is_rewriting_header==true){ + return AT_BASE_HREF.$url; + } + return $url; +} + + +/** +* Applies $addslashes or intval() recursively. +* @access public +* @param mixed $input The input to clean. +* @return A safe version of $input +* @author Joel Kronenberg +*/ +function sql_quote($input) { + global $addslashes; + + if (is_array($input)) { + foreach ($input as $key => $value) { + if (is_array($input[$key])) { + $input[$key] = sql_quote($input[$key]); + } else if (!empty($input[$key]) && is_numeric($input[$key])) { + $input[$key] = intval($input[$key]); + } else { + $input[$key] = $addslashes(trim($input[$key])); + } + } + } else { + if (!empty($input) && is_numeric($input)) { + $input = intval($input); + } else { + $input = $addslashes(trim($input)); + } + } + return $input; +} + +function query_bit( $bitfield, $bit ) { + if (!is_int($bitfield)) { + $bitfield = intval($bitfield); + } + if (!is_int($bit)) { + $bit = intval($bit); + } + return ( $bitfield & $bit ) ? true : false; +} + +/** +* Authenticates the current user against the specified privilege. +* @access public +* @param int $privilege privilege to check against. +* @param bool $check whether or not to return the result or to abort/exit. +* @return bool true if this user is authenticated, false otherwise. +* @see query_bit() in include/vitals.inc.php +* @author Joel Kronenberg +*/ +function authenticate($privilege, $check = false) { + if ($_SESSION['is_admin']) { + return true; + } + + $auth = query_bit($_SESSION['privileges'], $privilege); + + if (!$_SESSION['valid_user'] || !$auth) { + if (!$check){ + global $msg; + $msg->addInfo('NO_PERMISSION'); + require(AT_INCLUDE_PATH.'header.inc.php'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } else { + return false; + } + } + return true; +} + +function admin_authenticate($privilege = 0, $check = false) { + if (!isset($_SESSION['valid_user']) || !$_SESSION['valid_user'] || ($_SESSION['course_id'] != -1)) { + if ($check) { + return false; + } + header('Location: '.AT_BASE_HREF.'login.php'); + exit; + } + + if ($_SESSION['privileges'] == AT_ADMIN_PRIV_ADMIN) { + return true; + } + + if ($privilege) { + $auth = query_bit($_SESSION['privileges'], $privilege); + + if (!$auth) { + if ($check) { + return false; + } + global $msg; + $msg->addError('ACCESS_DENIED'); + require(AT_INCLUDE_PATH.'header.inc.php'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + } + return true; +} + +function get_default_theme() { + global $db; + + if (is_mobile_device()) { + $default_status = 3; + } else { + $default_status = 2; + } + $sql = "SELECT dir_name FROM ".TABLE_PREFIX."themes WHERE status=".$default_status; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + return $row; +} + +function get_system_default_theme() { + if (is_mobile_device()) { + return 'mobile'; + } else { + return 'default'; + } +} + +function is_mobile_theme($theme) { + global $db; + + $sql = "SELECT dir_name FROM ".TABLE_PREFIX."themes WHERE type='".MOBILE_DEVICE."'"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + if ($row['dir_name'] == $theme && is_dir(AT_INCLUDE_PATH . '../themes/' . $theme)) return true; + } + + return false; +} + +if (isset($_GET['expand'])) { + $_SESSION['menu'][intval($_GET['expand'])] = 1; +} else if (isset($_GET['collapse'])) { + unset($_SESSION['menu'][intval($_GET['collapse'])]); +} + +/** +* Writes present action to admin log db +* @access private +* @param string $operation_type The type of operation +* @param string $table_name The table affected +* @param string $num_affected The number of rows in the table affected +* @author Shozub Qureshi +*/ +function write_to_log($operation_type, $table_name, $num_affected, $details) { + global $db, $addslashes; + + if ($num_affected > 0) { + $details = $addslashes(stripslashes($details)); + $sql = "INSERT INTO ".TABLE_PREFIX."admin_log VALUES ('$_SESSION[login]', NULL, $operation_type, '$table_name', $num_affected, '$details')"; + $result = mysql_query($sql, $db); + } +} + +function get_group_title($group_id) { + global $db; + $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=$group_id"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + return $row['title']; + } + return FALSE; +} + +function get_status_name($status_id) { + switch ($status_id) { + case AT_STATUS_DISABLED: + return _AT('disabled'); + break; + case AT_STATUS_UNCONFIRMED: + return _AT('unconfirmed'); + break; + case AT_STATUS_STUDENT: + return _AT('student'); + break; + case AT_STATUS_INSTRUCTOR: + return _AT('instructor'); + break; + } +} + +function profile_image_exists($id) { + $extensions = array('gif', 'jpg', 'png'); + + foreach ($extensions as $extension) { + if (file_exists(AT_CONTENT_DIR.'profile_pictures/originals/'. $id.'.'.$extension)) { + return true; + } + } +} + +/** + * print thumbnails or profile pic + * @param int image id + * @param int 1 for thumbnail, 2 for profile + */ +function print_profile_img($id, $type=1) { + global $moduleFactory; + $mod = $moduleFactory->getModule('_standard/profile_pictures'); + if ($mod->isEnabled() === FALSE) { + return; + } + if (profile_image_exists($id)) { + if ($type==1){ + echo ''; + } elseif($type==2){ + echo ''; + } + } else { + echo ''; + } +} + +function profile_image_delete($id) { + $extensions = array('gif', 'jpg', 'png'); + + foreach ($extensions as $extension) { + if (file_exists(AT_CONTENT_DIR.'profile_pictures/originals/'. $id.'.'.$extension)) { + unlink(AT_CONTENT_DIR.'profile_pictures/originals/'. $id.'.'.$extension); + } + if (file_exists(AT_CONTENT_DIR.'profile_pictures/profile/'. $id.'.'.$extension)) { + unlink(AT_CONTENT_DIR.'profile_pictures/profile/'. $id.'.'.$extension); + } + if (file_exists(AT_CONTENT_DIR.'profile_pictures/thumbs/'. $id.'.'.$extension)) { + unlink(AT_CONTENT_DIR.'profile_pictures/thumbs/'. $id.'.'.$extension); + } + } +} + +/** + * get_group_concat + * returns a list of $field values from $table using $where_clause, separated by $separator. + * uses mysql's GROUP_CONCAT() if available and if within the limit (default is 1024), otherwise + * it does it the old school way. + * returns the list (as a string) or (int) 0, if none found. + */ +function get_group_concat($table, $field, $where_clause = 1, $separator = ',') { + global $_config, $db; + if (!isset($_config['mysql_group_concat_max_len'])) { + $sql = "SELECT @@global.group_concat_max_len AS max"; + $result = mysql_query($sql, $db); + if ($result && ($row = mysql_fetch_assoc($result))) { + $_config['mysql_group_concat_max_len'] = $row['max']; + } else { + $_config['mysql_group_concat_max_len'] = 0; + } + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES ('mysql_group_concat_max_len', '{$_config['mysql_group_concat_max_len']}')"; + mysql_query($sql, $db); + } + if ($_config['mysql_group_concat_max_len'] > 0) { + $sql = "SELECT GROUP_CONCAT($field SEPARATOR '$separator') AS list FROM ".TABLE_PREFIX."$table WHERE $where_clause"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + if (!$row['list']) { + return 0; // empty + } else if ($row['list'] && strlen($row['list']) < $_config['mysql_group_concat_max_len']) { + return $row['list']; + } // else: list is truncated, do it the old way + } else { + // doesn't actually get here. + return 0; // empty + } + } // else: + + $list = ''; + $sql = "SELECT $field AS id FROM ".TABLE_PREFIX."$table WHERE $where_clause"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $list .= $row['id'] . ','; + } + if ($list) { + return substr($list, 0, -1); } + return 0; +} + +function get_human_time($seconds) { + if ($seconds < 0) { + $out = '0'._AT('second_short'); + } else if ($seconds > 60 * 60) { // more than 60 minutes. + $hours = floor($seconds / 60 / 60); + $minutes = floor(($seconds - $hours * 60 * 60) / 60); + $out = $hours ._AT('hour_short').' '.$minutes._AT('minute_short'); + + //$out = ($seconds + } else if ($seconds > 60) { // more than a minute + $minutes = floor($seconds / 60); + $out = $minutes ._AT('minute_short').' '.($seconds - $minutes * 60)._AT('second_short'); + } else { // less than a minute + $out = $seconds . _AT('second_short'); + } + + return $out; +} + +function is_mobile_device() { + $http_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']); + return ((stripos($http_user_agent, IPOD_DEVICE) !== false && stripos($http_user_agent, IPOD_DEVICE) >= 0) || + (stripos($http_user_agent, BLACKBERRY_DEVICE) !== false && stripos($http_user_agent, BLACKBERRY_DEVICE) >= 0) || + (stripos($http_user_agent, ANDROID_DEVICE) !== false && stripos($http_user_agent, ANDROID_DEVICE) >= 0)) + ? true : false; +} + +function get_mobile_device_type() { + $http_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']); + if (stripos($http_user_agent, IPOD_DEVICE) !== false && stripos($http_user_agent, IPOD_DEVICE) >= 0) { + return IPOD_DEVICE; + } else if (stripos($http_user_agent, BLACKBERRY_DEVICE) !== false && stripos($http_user_agent, BLACKBERRY_DEVICE) >= 0) { + return BLACKBERRY_DEVICE; + } else if (stripos($http_user_agent, ANDROID_DEVICE) !== false && stripos($http_user_agent, ANDROID_DEVICE) >= 0) { + return ANDROID_DEVICE; + } else { + return UNKNOWN_DEVICE; + } +} + +/** + * Convert all input to htmlentities output, in UTF-8. + * @param string input to be convert + * @param boolean true if we wish to change all newlines(\r\n) to a
    tag, false otherwise. + * ref: http://php.net/manual/en/function.nl2br.php + * @author Harris Wong + * @date March 12, 2010 + */ +function htmlentities_utf8($str, $use_nl2br=true){ + $return = htmlentities($str, ENT_QUOTES, 'UTF-8'); + if ($use_nl2br){ + return nl2br($return); + } + return $return; +} + +/** + * Check if json_encode/json_decode exists, if not, use the json service library. + * NOTE: json_encode(), json_decode() are NOT available piror to php 5.2 + * @author Harris Wong + * @date April 21, 2010 + */ + if ( !function_exists('json_encode') ){ + function json_encode($content){ + require_once (AT_INCLUDE_PATH.'lib/json.inc.php'); + $json = new Services_JSON; + return $json->encode($content); + } +} +if ( !function_exists('json_decode') ){ + function json_decode($content, $assoc=false){ + require_once (AT_INCLUDE_PATH.'lib/json.inc.php'); + if ( $assoc ){ + $json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE); + } else { + $json = new Services_JSON; + } + return $json->decode($content); + } +} + + +require(AT_INCLUDE_PATH . '../mods/_core/modules/classes/Module.class.php'); + +$moduleFactory = new ModuleFactory(TRUE); // TRUE is for auto_loading the module.php files + +if (isset($_GET['submit_language']) && $_SESSION['valid_user']) { + if ($_SESSION['course_id'] == -1) { + $sql = "UPDATE ".TABLE_PREFIX."admins SET language = '$_SESSION[lang]' WHERE login = '$_SESSION[login]'"; + $result = mysql_query($sql, $db); + } else { + $sql = "UPDATE ".TABLE_PREFIX."members SET language = '$_SESSION[lang]', creation_date=creation_date, last_login=last_login WHERE member_id = $_SESSION[member_id]"; + $result = mysql_query($sql, $db); + } +} + +if (isset($_SESSION['course_id']) && $_SESSION['course_id'] > 0) { + $_custom_head .= ''; +} +?> diff --git a/index.php b/index.php new file mode 100644 index 000000000..9a4c044c9 --- /dev/null +++ b/index.php @@ -0,0 +1,165 @@ + + + + + + + +'; +} + +require(AT_INCLUDE_PATH . 'header.inc.php'); + +/* the "home" links: */ +$home_links = get_home_navigation(); +$savant->assign('home_links', $home_links); + + +/* the news announcements: */ +$news = array(); +$num_pages = 1; +$page = isset($_GET['p']) ? intval($_GET['p']) : 1; +if (!$page) { + $page = 1; +} + +$module =& $moduleFactory->getModule(AT_MODULE_DIR_STANDARD.'/announcements'); +if (!$module->isEnabled()) { + $result = FALSE; + $news = array(); +} else { + $sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."news WHERE course_id=$course_id"; + $result = mysql_query($sql, $db); +} + +if ($result && ($row = mysql_fetch_assoc($result))) { + $num_results = $row['cnt']; + $results_per_page = NUM_ANNOUNCEMENTS; + $num_pages = ceil($num_results / $results_per_page); + + $count = (($page-1) * $results_per_page) + 1; + + $offset = ($page-1)*$results_per_page; + + $sql = "SELECT N.*, DATE_FORMAT(N.date, '%Y-%m-%d %H:%i:%s') AS date, first_name, last_name + FROM ".TABLE_PREFIX."news N, ".TABLE_PREFIX."members M + WHERE N.course_id=$course_id + AND N.member_id = M.member_id + ORDER BY date DESC LIMIT $offset, $results_per_page"; + + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + /* this can't be cached because it called _AT */ + + $news[$row['news_id']] = array( + 'date' => AT_date( _AT('announcement_date_format'), + $row['date'], + AT_DATE_MYSQL_DATETIME), + 'author' => $row['first_name'] . ' ' . $row['last_name'], + 'title' => AT_print($row['title'], 'news.title'), + 'body' => format_content($row['body'], $row['formatting'], $glossary)); + + } +} + +$sql = "SELECT banner FROM ".TABLE_PREFIX."courses WHERE course_id=$course_id"; +$result = mysql_query($sql, $db); +if ($row = mysql_fetch_assoc($result)) { + $savant->assign('banner', AT_print($row['banner'], 'courses.banner')); +} else { + $savant->assign('banner', ''); +} + +$savant->assign('view_mode', $home_view); +$savant->assign('announcements', $news); +$savant->assign('num_pages', $num_pages); +$savant->assign('current_page', $page); +$savant->display('index.tmpl.php'); + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/install/db/atutor_convert_db_to_utf8.sql b/install/db/atutor_convert_db_to_utf8.sql new file mode 100644 index 000000000..b4509b67e --- /dev/null +++ b/install/db/atutor_convert_db_to_utf8.sql @@ -0,0 +1,196 @@ +############################################################### +# Database UTF-8 database conversion for ATutor < 1.6 +# This script only applies to those databases that have UTF-8 +# data in non-UTF-8 tables. +# ref: http://dev.mysql.com/doc/refman/5.0/en/alter-table.html +# @author harris +############################################################### + +# 1.6 UTF 8 Conversion to Binary +ALTER TABLE `admins` MODIFY COLUMN `real_name` BLOB; +ALTER TABLE `assignments` MODIFY COLUMN `title` BLOB; +ALTER TABLE `backups` MODIFY COLUMN `description` BLOB, MODIFY COLUMN `file_name` BLOB; +ALTER TABLE `blog_posts` MODIFY COLUMN `title` BLOB; +ALTER TABLE `content` MODIFY COLUMN `keywords` BLOB, MODIFY COLUMN `content_path` BLOB, + MODIFY COLUMN `title` BLOB; +ALTER TABLE `courses` MODIFY COLUMN `title` BLOB; +ALTER TABLE `course_cats` MODIFY COLUMN `cat_name` BLOB; +ALTER TABLE `external_resources` MODIFY COLUMN `title` BLOB, MODIFY COLUMN `author` BLOB, + MODIFY COLUMN `publisher` BLOB, MODIFY COLUMN `comments` BLOB; +ALTER TABLE `faq_entries` MODIFY COLUMN `question` BLOB; +ALTER TABLE `faq_topics` MODIFY COLUMN `name` BLOB; +ALTER TABLE `folders` MODIFY COLUMN `title` BLOB; +ALTER TABLE `forums` MODIFY COLUMN `title` BLOB; +ALTER TABLE `forums_threads` MODIFY COLUMN `subject` BLOB; +ALTER TABLE `glossary` MODIFY COLUMN `word` BLOB; +ALTER TABLE `groups` MODIFY COLUMN `title` BLOB; +ALTER TABLE `groups_types` MODIFY COLUMN `title` BLOB; +ALTER TABLE `links` MODIFY COLUMN `Description` BLOB, + MODIFY COLUMN `SubmitName` BLOB, MODIFY COLUMN `SubmitEmail` BLOB; +ALTER TABLE `links_categories` MODIFY COLUMN `name` BLOB; +ALTER TABLE `mail_queue` MODIFY COLUMN `subject` BLOB; +ALTER TABLE `members` MODIFY COLUMN `first_name` BLOB, MODIFY COLUMN `second_name` BLOB, + MODIFY COLUMN `last_name` BLOB, MODIFY COLUMN `address` BLOB, MODIFY COLUMN `city` BLOB, + MODIFY COLUMN `province` BLOB, MODIFY COLUMN `country` BLOB; +ALTER TABLE `messages` MODIFY COLUMN `subject` BLOB; +ALTER TABLE `messages_sent` MODIFY COLUMN `subject` BLOB; +ALTER TABLE `news` MODIFY COLUMN `title` BLOB; +ALTER TABLE `polls` MODIFY COLUMN `question` BLOB, MODIFY COLUMN `choice1` BLOB, + MODIFY COLUMN `choice2` BLOB, MODIFY COLUMN `choice3` BLOB, MODIFY COLUMN `choice4` BLOB, + MODIFY COLUMN `choice5` BLOB, MODIFY COLUMN `choice6` BLOB, MODIFY COLUMN `choice7` BLOB; +ALTER TABLE `tests` MODIFY COLUMN `title` BLOB; +ALTER TABLE `tests_questions` MODIFY COLUMN `choice_0` BLOB, + MODIFY COLUMN `choice_1` BLOB, + MODIFY COLUMN `choice_2` BLOB, + MODIFY COLUMN `choice_3` BLOB, + MODIFY COLUMN `choice_4` BLOB, + MODIFY COLUMN `choice_5` BLOB, + MODIFY COLUMN `choice_6` BLOB, + MODIFY COLUMN `choice_7` BLOB, + MODIFY COLUMN `choice_8` BLOB, + MODIFY COLUMN `choice_9` BLOB, + MODIFY COLUMN `option_0` BLOB, + MODIFY COLUMN `option_1` BLOB, + MODIFY COLUMN `option_2` BLOB, + MODIFY COLUMN `option_3` BLOB, + MODIFY COLUMN `option_4` BLOB, + MODIFY COLUMN `option_5` BLOB, + MODIFY COLUMN `option_6` BLOB, + MODIFY COLUMN `option_7` BLOB, + MODIFY COLUMN `option_8` BLOB, + MODIFY COLUMN `option_9` BLOB; + + +# 1.6 UTF 8 table VARCHAR * 4 changes +ALTER TABLE `admins` MODIFY COLUMN `real_name` VARCHAR(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `assignments` MODIFY COLUMN `title` VARCHAR(240) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `backups` MODIFY COLUMN `description` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `file_name` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `blog_posts` MODIFY COLUMN `title` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `content` MODIFY COLUMN `keywords` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `content_path` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `title` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `courses` MODIFY COLUMN `title` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `course_cats` MODIFY COLUMN `cat_name` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `external_resources` MODIFY COLUMN `title` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `author` VARCHAR(150) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `publisher` VARCHAR(150) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `comments` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `faq_entries` MODIFY COLUMN `question` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `faq_topics` MODIFY COLUMN `name` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `folders` MODIFY COLUMN `title` VARCHAR(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `forums` MODIFY COLUMN `title` VARCHAR(240) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `forums_threads` MODIFY COLUMN `subject` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `glossary` MODIFY COLUMN `word` VARCHAR(240) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `groups` MODIFY COLUMN `title` VARCHAR(80) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `groups_types` MODIFY COLUMN `title` VARCHAR(80) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `links` MODIFY COLUMN `Description` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `SubmitName` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `SubmitEmail` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `links_categories` MODIFY COLUMN `name` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `mail_queue` MODIFY COLUMN `subject` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `members` MODIFY COLUMN `first_name` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `second_name` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `last_name` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `address` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `city` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `province` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `country` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `messages` MODIFY COLUMN `subject` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `messages_sent` MODIFY COLUMN `subject` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `news` MODIFY COLUMN `title` VARCHAR(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `polls` MODIFY COLUMN `question` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice1` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice2` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice3` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice4` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice5` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice6` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice7` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `tests` MODIFY COLUMN `title` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `tests_questions` MODIFY COLUMN `choice_0` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice_1` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice_2` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice_3` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice_4` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice_5` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice_6` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice_7` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice_8` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `choice_9` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_0` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_1` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_2` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_3` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_4` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_5` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_6` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_7` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_8` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `option_9` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `tests_questions_categories` MODIFY COLUMN `title` CHAR(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; +ALTER TABLE `themes` MODIFY COLUMN `title` VARCHAR(80) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + MODIFY COLUMN `extra_info` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; + +# 1.6.1 Change all table to UTF-8 collation +ALTER TABLE `admins` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `admin_log` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `assignments` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `auto_enroll` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `auto_enroll_courses` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `backups` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `blog_posts` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `blog_posts_comments` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `config` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `content` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `courses` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `course_access` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `course_cats` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `course_enrollment` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `course_stats` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `external_resources` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `faq_entries` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `faq_topics` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `feeds` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `files` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `files_comments` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `file_storage_groups` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `folders` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `forums` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `forums_accessed` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `forums_courses` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `forums_groups` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `forums_subscriptions` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `forums_threads` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `glossary` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `groups` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `groups_members` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `groups_types` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `handbook_notes` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `instructor_approvals` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `languages` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `language_pages` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `language_text` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `links` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `links_categories` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `mail_queue` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `master_list` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `members` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `member_track` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `messages` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `messages_sent` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `modules` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `news` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `polls` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `polls_members` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `reading_list` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `related_content` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `tests` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `tests_answers` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `tests_groups` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `tests_questions` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `tests_questions_assoc` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `tests_questions_categories` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `tests_results` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `themes` CONVERT TO CHARACTER SET utf8; +ALTER TABLE `users_online` CONVERT TO CHARACTER SET utf8; diff --git a/install/db/atutor_language_text.sql b/install/db/atutor_language_text.sql new file mode 100644 index 000000000..28a106888 --- /dev/null +++ b/install/db/atutor_language_text.sql @@ -0,0 +1,2356 @@ +# Table structure for table 'language_text' +# +DROP TABLE language_text; + +CREATE TABLE `language_text` ( + `language_code` varchar(20) NOT NULL default '', + `variable` varchar(120) NOT NULL default '', + `term` varchar(180) NOT NULL default '', + `text` text NOT NULL, + `revised_date` datetime NOT NULL default '0000-00-00 00:00:00', + `context` text NOT NULL, + PRIMARY KEY (`language_code`,`variable`,`term`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_module', 'gradebook', 'Gradebook', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_ADD_FEED', 'Are you sure you want to add this syndicated feed?', '2005-11-15 13:19:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_ADD_MODULE', 'Are you sure you want to install the module in %s?', '2005-08-17 12:59:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_ADD_TEST_QUESTIONS', 'Are you sure you want to add the following questions? \r\n
      %s
    ', '2004-11-23 12:38:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_ALUMNI', 'Are you sure you want to mark the following users as course alumni:
      %s
    ', '2004-11-23 12:17:53', 'enrollment_manager, make students into alumi'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE', 'Are you sure you want to delete these items?
    \r\n\r\n
      %s
    ', '2008-05-08 12:19:32', 'generic delete confirmation msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_ADMIN', 'Are you sure you want to delete the following administrator?\r\n
    \r\n%s', '2008-01-22 09:38:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_ASSIGNMENT', 'Are you sure you want to delete assignment %s?', '2006-05-23 13:10:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_AUTO_ENROLL', 'Are you sure you want to delete auto enrollment for %s', '2008-03-10 11:52:57', 'confirm message when deleting an auto enroll definition'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_CATEGORY', 'Are you sure you want to delete category %s?', '2005-02-17 14:57:18', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_COURSE_1', 'Are you sure you want to Delete the course %s?', '2005-03-04 11:27:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_COURSE_2', 'Are you really really sure you want to Delete the course %s? Deleted courses can not be recovered.', '2005-03-04 11:31:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_FAQ_QUESTION', 'Are you sure you want to delete the question %s?', '2005-10-19 12:49:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_FAQ_TOPIC', 'Are you sure you want to delete the topic %s and its questions?', '2005-11-08 13:42:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_FEED', 'Are you sure you want to delete feed %s.?', '2005-12-07 09:36:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_FORUM', 'Are you sure you want to delete %s forum? All messages posted to this forum will be erased.', '2005-12-07 09:36:15', 'deleting a forum'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_GRADE_SCALE', 'Are you sure you want to delete grade scale %s?', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_GROUP', 'Are you sure you want to delete the group: %s?', '2005-02-21 12:07:05', 'deletep enrollment group'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_GROUP_TYPE', 'Are you sure you want to delete group type %s and all its groups?', '2006-03-23 11:27:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_LANG', 'Are you sure you want to delete the language ( %s )?', '2004-11-24 13:19:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_LINK', 'Are you sure you want to delete the link %s?', '2005-02-21 16:55:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_MSGS', 'Are you sure you want to delete the selected messages?', '2007-02-22 13:09:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_MYOWN_PATCH', 'Are you sure you want to delete patch %s', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_NEWS', 'Are you sure you want to delete %s?', '2004-11-24 13:37:55', 'news deletion in editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_POLL', 'Are you sure you want to delete this poll \'%s\'? It cannot be recovered once deleted.', '2004-11-24 14:03:36', 'editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_TEST', 'Are you sure you want to delete the test/survey %s and any results? The questions, however, will not be deleted.', '2004-12-15 10:42:13', 'Deleting Test'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_TEST_CATEGORY', 'Are you sure you want to delete test category %s?', '2005-03-07 11:58:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_TEST_FROM_GRADEBOOK', 'Are you sure you want to delete test %s from gradebook?', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_THEME', 'Are you sure you want to delete the following theme: % s ', '2004-11-23 09:30:03', 'confirm message for delete theme'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_TRACKING', 'Are you sure your want to delete the content usage data for this course?', '2005-08-11 10:08:50', 'reset tracking session'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_TRANSCRIPT', 'Are you sure you want to delete the transcript %s?', '2005-03-30 14:21:00', 'delete chat transcript'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DELETE_USER', 'Are you sure you want to delete the following users? Selected instructors who own courses will not be deleted.%s', '2007-02-19 11:44:45', 'deleting user from admin'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DIR_DELETE', 'Are you sure you want to delete the following folder(s) and all their contents?
      %s
    ', '2004-12-15 09:59:39', 'filemanager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_DIR_MOVE', 'Are you sure you want to move %1s to the folder %2s?', '2005-01-10 10:35:17', 'filemanager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_EDIT_STATUS', 'Are you sure you want to change the following users\' status to %s? Status of selected instructors who own courses will not be changed. %s', '2007-02-19 11:43:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_ENROLL_STUDENT', 'Are you sure you want to enroll the following users:
      %s
    ', '2004-11-23 12:21:00', 'enrollment manager, enroll confirm'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_FILE_DELETE', 'Are you sure you want to delete the following file(s)?
      %s
    ', '2006-07-05 11:44:15', 'filemanager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_FILE_EXISTS', 'The file %s already exists. Do you want to overwrite the file?', '2005-03-09 16:26:11', 'filemanager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_FILE_MOVE', 'Are you sure you want to move %1s to the folder %2s?', '2005-01-10 10:33:21', 'file_manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_GLOSSARY_REMAINS', 'Deleting a term will not remove the embedded glossary codes from your content.', '2005-02-24 10:34:33', 'content deletion'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_GRANT_WRITE_PERMISSION', 'Please grant write permission to folder: %s

    Note: To change permissions on Unix use chmod a+rw then the file name.

    ', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_LIST_DELETE', 'Are you sure you want to delete %s Student ID from the master student list?', '2005-04-04 15:47:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_LOGIN_INSTRUCTOR', '

    %2$s

    \r\nYou will be logged in as the instructor for this course. Are you sure you want to continue?', '2006-04-10 13:12:29', 'viewing courses from admin'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_NO_ANSWER', 'You are about to add a question with no correct choice. Continue?', '2004-12-06 14:35:10', 'adding a question without specifying answer'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_PA_DELETE_ALBUM', 'Are you sure you want to delete the album %s? Once deleted, photos can not be recovered.', '2010-03-17 16:08:25', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_PA_DELETE_COMMENT', 'Are you sure you want to delete this comment?', '2010-03-17 16:09:08', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_PA_DELETE_PHOTO', 'Are you sure you want to delete this Photo?', '2010-03-17 16:08:50', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_REMOVE_STUDENT', 'Are you sure you want to remove the following students:
      %s
    ', '2004-11-23 12:21:12', 'removing a student'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_REMOVE_TEST_QUESTION', 'Are you sure you want to remove this question from this test? Removing the question will not delete it from the question database.', '2004-11-25 10:31:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_REMOVE_WRITE_PERMISSION', 'For your security, please REMOVE write permission on the folder: %s

    Note: To remove permissions on Unix use chmod 755 then the file or folder name.

    ', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_RESET_ADMIN_LOG', 'Are you sure you want to delete the Administrator Activity Log?', '2005-03-03 10:42:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_RESET_ERROR_LOG', 'Are you sure you want to delete all the error logs?', '2005-03-10 15:49:02', 'error log reset (delete logs folder0'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_RL_DELETE_READING', 'Are you sure you want to delete this reading: %s?', '2006-10-04 10:48:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_RL_DELETE_RESOURCE', 'Are you sure you want to delete this resource: %s?
    Note: Any readings that use this resource will also be deleted.', '2006-10-04 10:48:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_STUDENT_GROUP', 'Are you sure you want to add the following users to group \'%s\'?
      %s
    ', '2005-06-02 12:07:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_STUDENT_REMOVE_GROUP', 'Are you sure you want to remove the following users from group \'%s\'?
      %s
    ', '2005-06-02 12:06:56', 'Enrollment'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_SUB_CONTENT_DELETE', 'This content page has sub content. If you delete this page all its sub pages will be deleted as well.
    ', '2004-11-24 13:31:34', 'content deletion'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_UNENROLL', 'Are you sure you want to un-enroll from %s?', '2005-03-24 12:43:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_UNENROLL_PRIV', 'Are you sure you want to un-enroll the following users (the users will have their roles and privileges removed):
      %s
    ', '2004-11-23 12:23:22', 'enrollment manager, unenrolling a TA'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_CONFIRM_UNENROLL_STUDENT', 'Are you sure you want to un-enroll the following students:
      %s
    ', '2004-11-23 12:21:36', 'enrollment manager, confirm unenroll'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ACCESS_DENIED', 'You do not have permission to access this area.', '2005-06-15 15:13:59', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ACCOUNT_DISABLED', 'That account has been disabled.', '2005-04-01 10:30:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ADMIN_EDIT_OWN_ACCOUNT', 'To edit your own account use the My Account page.', '2010-03-09 14:25:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ALREADY_ENROLED', 'You have already made a request to enroll in this course and you have not yet been approved by the instructor. You will be notified when your request has been approved.', '2009-09-17 14:47:14', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ALREADY_INSTALLED', 'Selected item appears to have been installed.', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ALREADY_OWNED', 'You own this course, and cannot enroll.', '2004-11-26 14:30:22', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ALREADY_UNINSTALLED', 'Selected item appears to have been uninstalled. Module directory does not exist.', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ALTERNATIVE_ALREADY_DECLARED', 'The file you selected has already been declared as an alternative to the selected primary resource.', '2008-09-08 15:25:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ALTERNATIVE_NOT_DEFINED', 'You must select an alternative from files available in the File Manager to the right.', '2009-12-01 11:02:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ASSIGNMENT_CUTOFF', 'The assignment cut-off date has past. Submissions are no longer accepted.', '2006-03-20 14:41:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_BACKUP_RESTORE', 'Restore failed - file is not a valid backup. Backups older than version 1.3 are not supported.', '2005-11-29 14:33:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_BACKUP_UNSUPPORTED_GREATER_VERSION', 'Backups created from versions of ATutor greater than this version are not supported.', '2005-05-11 15:56:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_BAD_DATE', 'That date is not valid.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_BAD_FILE_TYPE', 'Unsupported file type. Plain Text or HTML files only.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_BAD_FOLDER_NAME', 'The folder name contains illegal characters. You may choose from alphanumeric characters and underscores, dashes or periods.', '2005-01-10 13:13:55', 'filemanager, foilder with illegal chars'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CACHE_DIR_BAD', 'Cache dir cannot be created. Cache disabled. See the configuration options to either disable or fix the problem.', '2003-05-27 13:10:53', 'global'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CACHE_DIR_NOT_EXIST', 'Cache directory does not exist or is not writable. Create it and set write permissions to make the directory writable by the system\'s Web server user. (e.g. chmod a+rwx cache)', '2010-03-04 14:41:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_BE_EMPTY', 'Search field cannot be empty.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_CONNECT_SERVER', 'Failed to connect to server: %s', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_CREATE_DIR', 'Cannot create content directory.', '2003-10-15 12:20:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_DELETE_OWN_ACCOUNT', 'You cannot delete your own account.', '2005-03-03 10:59:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_OPEN_DIR', 'Unable to open content directory. You may try to create it now.', '2010-03-09 14:26:18', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_OPEN_FILE', 'Cannot open the file: %s ', '2004-10-18 14:38:00', 'Theme Manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_OVERWRITE_FILE', 'Cannot override file.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_READ_FILE', 'Cannot read from file: %s ', '2004-11-19 13:00:03', 'admin/error_logging.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_RENAME', 'File or directory cannot be renamed. Either a file or directory with that name already exists, the original file or directory does not exist, or the file or directory name has not changed.', '2004-08-20 12:05:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_UNINSTALL_MANUAL_MODULE', 'This module needs to be uninstalled manually instead of using the ATutor module installer. Please uninstall it manually.', '2008-10-21 15:41:53', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_UNZIP', 'Can NOT unzip the uploaded file.', '2008-04-21 15:08:55', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANNOT_WRITE_FILE', 'Cannot write to file: %s ', '2004-10-18 14:39:10', 'Theme Manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CANT_DELETE_GROUP', 'You cannot delete this group.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CHAT_TRAN_REJECTED', 'Transcript filename rejected. Please ensure that it is alphanumeric and contains no spaces.', '2003-06-02 13:47:39', 'achat'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CHOICES_EMPTY', 'Enter at least two answer choices.', '2005-03-08 15:41:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CHOOSE_UNINSTALLED_PATCH', 'Please choose an uninstalled patch.', '2008-04-21 15:08:55', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_COMBINE_TESTS', '"%1$s" cannot be combined because the following students have taken it more than once:
    \r\n%2$s.', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CONFIG_NOT_WRITEABLE', 'It is not possible to edit these settings because the configuration file is locked for security reasons. If you want to make changes, you need to unlock the config.inc.php file first by changing its permissions with the command chmod a+rw config.inc.php.', '2005-03-03 10:14:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CONFIRM_BAD', 'Your email address and account could not be confirmed.', '2005-05-31 12:55:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CONTACT_INFO_NOT_FOUND', 'Contact Support is not available, because an email address has not been specified.', '2005-03-17 11:33:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_COURSE_DIR_NAME_INVALID', 'The course directory name is invalid. It may contain only letters, numbers, and underscores, and may not contain spaces.', '2008-10-06 13:18:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_COURSE_DIR_NAME_IN_USE', 'The course directory name is already in use. Please try another.', '2008-10-06 13:19:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_COURSE_ENDED', 'This course ended on %s.', '2007-07-16 11:53:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_COURSE_NOT_RELEASED', 'This course can only be accessed on %s.', '2006-04-10 14:28:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CREATE_MASTER_USED', 'The Student ID you have entered already belongs to another user. If you wish to over-write this association with the new account, use the over-write checkbox.', '2006-06-12 15:39:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_CURL_NOT_INSTALLED', 'The curl library needs to be installed for gadgets to be added. Please contact your administrator for more information.', '2009-06-09 14:44:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_DB_NOT_UPDATED', 'Information could not be added to the database.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_DIR_NOT_DELETED', 'Cannot open directory to be deleted.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_DIR_NOT_EXIST', 'Directory %s does not exist. Please create it.', '2005-09-22 14:08:05', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_DIR_NOT_WRITEABLE', 'Directory %s is not writeable. On Unix issue the command chmod a+rw on the directory.', '2005-09-22 14:22:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_DIR_NO_PERMISSION', 'Cannot delete folder. You may not have premission, or it may not be empty.', '2003-05-22 12:20:14', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_DISABLE_CORE_MODULE', 'Core modules cannot be disabled.', '2005-08-25 14:00:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_DISABLE_MISSING_MODULE', 'You cannot disable a missing module. To continue managing this module you must first restore its directory.', '2005-10-05 16:22:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_DISABLE_PARTIALLY_UNINSTALLED_MODULE', 'You cannot disable a partially uninstalled module. To continue managing this module you must first restore it.', '2008-10-21 15:41:53', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_DOB_INVALID', 'Date of birth has invalid format.', '2004-06-24 12:53:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_EMAIL_EXISTS', 'An account with that email address already exists.', '2005-03-01 11:36:57', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_EMAIL_INVALID', 'Email address was invalid.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_EMAIL_MISMATCH', 'Email addresses do not match. Please re-type them.', '2009-11-04 14:12:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_EMAIL_NOT_FOUND', 'No account found with that email address.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_EMPTY_FIELDS', 'The following required field(s) are empty:
    \r\n%s.', '2006-10-05 15:51:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_EMPTY_ZIP_FILE', 'Downloaded zip file is empty.', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_END_DATE_INVALID', 'That end date is not valid.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ENROLLMENT_NONE_FOUND', 'There is no one to export.', '2006-06-27 11:53:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FEED_NO_CONTENT', 'The feed either cannot be found at that URL or is not valid.', '2005-11-23 10:59:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FILE_EMPTY', 'You did not select a file to import or the file was empty.', '2004-12-09 14:44:43', 'enrollment manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FILE_ILLEGAL', '%s files are not allowed.', '2003-05-22 11:52:08', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FILE_MAX_SIZE', 'The file exceeded the maximum allowable size limit of %s.', '2004-05-06 13:18:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FILE_NOT_DELETED', 'Error deleting file. Cannot delete file.', '2004-11-08 16:33:12', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FILE_NOT_EXIST', 'The selected file does not exist.', '2005-01-07 09:51:45', 'filemanager, fiule to be edited does not exist'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FILE_NOT_FOUND', 'File not found.', '2006-03-20 14:45:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FILE_NOT_SAVED', 'The file cannot be saved.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FILE_NOT_SELECTED', 'You did not select a file to upload.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FILE_TOO_BIG', 'The file size exceeds the limit of %s . Contact your ATutor Administrator to have this limit increased.', '2005-01-15 18:49:53', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FIRST_LAST_NAME_UNIQUE', 'First, second, and last names combination must be unique.', '2006-03-27 14:55:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FOLDER_NOT_CREATED', 'The folder " %s " could not be created.', '2006-06-02 13:43:53', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FOLDER_NOT_EXIST', 'Folder not found.', '2006-03-20 15:37:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FORUM_DENIED', 'You do not have permission to post in this forum.', '2005-08-10 13:48:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FORUM_NOT_FOUND', 'Forum can not be found.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FORUM_NO_DEL_SHARE', 'You do not have permission to delete a shared forum.', '2004-12-10 11:10:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FORUM_NO_EDIT_SHARE', 'You do not have permission to edit a Shared forum.', '2004-12-10 11:15:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_FORUM_NO_SUBSCRIBE', 'Cannot subscribe to the requested thread. Only subscription to the top-level post is allowed.', '2004-12-10 13:44:57', 'forum/subscribe.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GADGET_ADDED_FAILURE', 'The following error occured while adding a gadget:\r\n%s', '2009-06-22 11:19:44', 'gadget error'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GADGET_DELETED_CANNOT_BE_EMPTY', 'You need to select at least one application to delete.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GENERAL', 'This is a search and %s ATutor error.', '2003-05-22 11:52:28', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GOOGLE_KEY_INVALID', 'Invalid key.', '2005-11-30 14:35:03', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GOOGLE_KEY_MISSING', 'A Google Key must be entered in order for the enabled Web Search to be available in courses. Enter this key by going to Google Key under the Configuration tab.', '2005-11-30 14:40:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GOOGLE_QUERY_FAILED', 'Query failed.', '2005-10-20 10:52:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GROUP_CREATION_FAILED', 'Group creation failed.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GROUP_EDIT_FAILED', 'Group edit failed.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GROUP_HAS_BEEN_REMOVED', 'Group has been removed.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GROUP_NOT_FOUND', 'Group not found.', '2004-11-25 16:03:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_GROUP_NO_STUDENTS', 'There are no students to create groups for.', '2006-03-22 13:33:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_HAND_IN_FOLDER', 'Folders cannot be handed in.', '2006-03-20 15:43:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ID_ZERO', 'Content ID was zero, or was missing.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_IMPORTDIR_FAILED', 'Unable to create import directory.', '2005-07-20 14:02:57', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_IMPORTDIR_IMS_NOTVALID', 'The file does not appear to be a valid ZIP file.', '2005-06-27 11:37:47', 'IMS file is not a ZIP file.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_IMPORTFILE_EMPTY', 'The import file must not be empty.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_IMPORT_CARTRIDGE_FAILED', 'Import failed. Does not appear to be a valid content package or common cartridge:\r\n\r\n%s', '2009-12-03 09:55:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_IMPORT_ERROR_IN_ZIP', 'Zip file could not be extracted because: %s ', '2006-06-28 10:30:50', 'theme manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_IMPORT_FAILED', 'Import Failed', '2005-01-15 09:50:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_IMPORT_NOT_PROPER_FORMAT', 'The selected file does not appear to be a valid theme package.', '2004-12-09 11:34:27', 'admin/themes/import.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_IMS_AUTHORIZATION_NOT_SUPPORT', 'Import failed. ATutor does not support package authentications.', '2009-12-10 12:19:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_INCOMPLETE', 'Please fill out the forms as directed below', '2004-10-18 11:14:50', 'enrollment Manager, incomplete info in create list'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_INCORRECT_FILE_FORMAT', 'File is in the incorrect format. Review line %s of your course list file and make sure it follows format guidelines.', '2006-06-28 10:43:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_INST_INFO_NOT_FOUND', 'Instructor information cannot be found.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_INVALID_LINK', 'The link is either invalid or expired.', '2006-05-10 09:08:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_INVALID_LOGIN', 'Invalid login/password combination.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_INVALID_URL', 'Localhost is not a valid URL', '2004-11-05 10:22:37', 'AChecker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ITEM_NOT_FOUND', 'Item not found.', '2006-10-04 10:52:48', 'generic something not found msg.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_JOIN_REQUEST_FAILED', 'Request to join group failed. Perhaps you have already requested to join this group.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LANG_EXISTS', 'The language pack you are trying to import already exists. Language already exists. You can edit language properties by following the translate in the sub menu above.', '2007-02-06 15:44:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LANG_IMPORT_FAILED', 'Language import failed.', '2003-06-06 11:23:51', 'admin language manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LANG_NOT_COMPLETE', 'The language pack you are trying to import has not been marked as complete and published. To use incomplete language packs you must enable translating using the instructions at the bottom of this page.', '2005-11-30 14:49:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LANG_WRONG_VERSION', 'The language pack you are trying to import is not compatible with this version of ATutor. If you still want to import this language pack you must enable translating following the instructions at the bottom of this page.', '2005-11-30 14:50:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LAST_LANGUAGE', 'Cannot delete the last language. At least one language must be available.', '2006-06-29 16:50:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LINK_CAT_EMPTY', 'Cannot add a link because there are no categories.', '2005-03-09 13:05:25', 'adding link but no cats'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LINK_CAT_NOT_EMPTY', 'Link category cannot be deleted because it contains sub-categories and/or links.', '2005-02-22 15:22:44', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LOGIN_CHARS', 'Your Login Name must only contain letters, numbers, periods, or underscores (_\'s).', '2006-07-11 10:40:57', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LOGIN_ENROL', 'You must be logged in to enroll in a course.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LOGIN_EXISTS', 'That login already exists, please choose another.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LOGIN_TO_POST', 'You must be logged in to post.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_LOG_NOT_RESET', 'The error log could not be reset.', '2005-03-10 15:54:03', 'error logging'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_MAX_LOGIN_ATTEMPT', 'Maximum login attempts has been reached. Login has been temporarily disabled for 1 hour. Please try again later.', '2009-06-17 11:59:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_MAX_LOGIN_ATTEMPT_1', 'Invalid username or password. You have one more attempt before the login page is disabled.', '2009-06-26 16:37:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_MAX_LOGIN_ATTEMPT_2', 'You have entered an unrecognized login and password combination. You have two more attempts before the login page is disabled.', '2009-06-26 16:35:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_MAX_STORAGE_EXCEEDED', 'Adding this file exceeds the maximum course storage limit.', '2003-10-03 16:00:08', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_MISSING_THEMEXML', 'Cannot import. The import package is missing a vital component: themes.xml. Check that the theme package is for version 1.4.3 or above.', '2004-12-09 11:59:21', 'admin/themese/import.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_MODULE_INSTALL', 'The following error(s) were encountered when trying to install this module:\r\n
      %s
    ', '2005-10-11 10:48:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_MODULE_UNINSTALL', 'The following error(s) were encountered when trying to install this module:\r\n
      %s
    ', '2008-10-21 15:41:53', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_MYSQL_FAILED', 'Connection to MySQL failed.', '2009-12-01 11:07:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NEED_FILENAME', 'Filename was left empty. Need a filename to create new file.', '2004-11-09 16:19:24', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NODELETE_USER', 'Cannot delete this user because they own courses. Delete the courses first.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NOT_CONFIRMED', 'Your account\'s email address has not yet been confirmed. Please check your email account for a confirmation message. Please contact us if you do not receive it.', '2006-06-21 10:59:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NOT_IN_ANY_GROUPS', 'You are not in any groups.', '2006-04-11 13:34:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NOT_OWNER', 'You do not own this course or it does not exist.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NOT_RELEASED', 'This content has not yet been released. %s', '2003-05-22 11:52:38', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_ACTION_SELECTED', 'No action selected.', '2007-02-21 14:12:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_ASSIGNMENTS_FOUND', 'No assignments are due in this workspace. You might try another workspace.', '2007-12-13 16:12:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_CHILD_AS_PARENT', 'Cannot move the content to be the child of its own children.', '2009-09-16 13:14:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_CONTENT_SPACE', 'Not enough space to import content directory. %s KB over the limit.', '2003-10-03 15:58:38', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_COURSE_FLOAT', 'Invalid Maximum Course Float value.', '2005-06-20 10:44:45', 'Admin did not specify a float for course size.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_FAQ_TOPICS', 'You must first add a topic.', '2005-10-19 12:44:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_IMSMANIFEST', 'IMS manifest file is missing. This does not appear to be a valid IMS content package or common cartridge.', '2009-11-17 12:38:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_IMS_BACKUP', 'This file appears to be a course backup rather than a content package.', '2010-03-09 14:27:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_ITEM_SELECTED', 'You must select an item before using a button.', '2005-03-07 16:36:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_LANGUAGE', 'Selected language is not supported.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_LOGS_SELECTED', 'You must select at least one profile to create a bundle.', '2004-11-25 11:18:19', 'admin/error_logging_details.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_LOG_SELECTED', 'You did not select a log file.', '2004-11-23 15:59:57', 'admin/error_logging_details.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_MEMBERS', 'No users found.', '2005-05-18 12:43:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_PROFILE_SELECTED', 'You did not select a profile.', '2004-11-23 16:01:27', 'admin/error_logging_details.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_QUESTIONS', 'No questions were found for this test.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_QUESTIONS_SELECTED', 'You did not select any questions to add to this test.', '2004-11-23 11:56:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_SELF_AS_PARENT', 'Cannot move the content to be its own child.', '2009-09-16 13:14:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_SPACE_LEFT', 'There is no more space in this course to extract this archive.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_STUDENT_SELECTED', 'You did not select any students.', '2006-06-27 12:18:13', 'enrollment manager, no students selected'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_NO_SUCH_USER', 'The user you selected does not exist.', '2005-03-17 15:34:30', 'veiwing profile but no such user'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_ONLY_UNINSTALL_EXTRA_MODULE', 'Only modules with type "Extra" can be uninstalled.', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PACKAGE_DIR_FAILED', 'Problem with Package Directory.', '2005-05-17 12:02:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PAGE_NOT_FOUND', 'Page cannot be found.', '2005-09-19 12:18:06', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PASSWORD_CHARS', 'The password must contain letters, numbers, and symbols.', '2006-03-28 15:09:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PASSWORD_LENGTH', 'Password must be at least 8 characters long.', '2006-04-10 11:43:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PASSWORD_MISMATCH', 'Passwords did not match.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PATCH_ALREADY_INSTALLED', 'The selected patch is already installed.', '2008-04-21 15:08:55', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PATCH_DEPENDENCY', 'Due to patch dependency, please install the listed patches before installing this patch: %s', '2008-04-21 15:08:55', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PATCH_XML_NOT_FOUND', 'Patch XML file is not found.', '2008-04-21 15:08:55', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_ADD_COMMENT_FAILED', 'Comment could not be added due to an internal error. Please try again.', '2010-03-17 16:05:53', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_ADD_PHOTO_FAILED', 'Photo could not be added due to an internal error. Please try again.', '2010-03-17 16:06:23', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_CREATE_ALBUM_FAILED', 'Album could not be created due to an internal error. Please try again.', '2010-03-17 16:06:36', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_EDIT_ALBUM_FAILED', 'Album could not be edited due to an internal error. Please try again.', '2010-03-17 16:06:49', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_EDIT_PHOTO_FAILED', 'Photo could not be edited due to an internal error. Please try again.', '2010-03-17 16:07:01', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_EMPTY_COMMENT', 'Comment can not be empty.', '2010-03-17 16:06:10', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_EMTPY_ALBUM_NAME', 'Album name can not be empty.', '2010-03-17 16:07:15', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_EXCEEDED_MAX_USAGE', 'You have exceeded the maximum allowable memory usage for the photo album.', '2010-03-17 16:08:06', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_MEMORY_INPUT_ERROR', 'Invalid input. Please enter a valid Integer.', '2010-03-17 16:07:40', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_MEMORY_SQL_ERROR', 'Preferences were not updated due to an internal error. Please try again.', '2010-03-17 16:07:52', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_PA_PHOTO_NOT_FOUND', 'Photo can not be found.', '2010-03-17 16:07:27', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_POLL_QUESTION_MINIMUM', 'The poll must have at least two questions.', '2005-07-20 10:12:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_QTI_WRONG_PACKAGE', 'Import failed. Please note that ATutor only supports QTI 1.2.1 import.', '2008-12-15 10:04:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_RAND_TEST_Q_WEIGHT', 'The non-required questions must all have the same weight.', '2007-07-09 15:03:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_REGISTER_MASTER_USED', 'The student ID and PIN combination you provided is either being used or is incorrect.', '2005-04-04 13:01:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_REMOVE_WRITE_PERMISSION', 'Please remove write permission from the listed files.', '2008-04-21 15:08:55', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_RESOURCE_NOT_DEFINED', 'You did not select a resource to add.', '2008-09-08 14:44:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_RESTORE_TOO_BIG', 'The course is too small to restore this backup into.', '2004-10-15 16:42:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_RESULTS_NOT_RELEASED', 'You are not permitted to view these test results at this time.', '2004-12-15 10:13:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SAME_LOCATION', 'The content is already at the moved location.', '2009-09-16 13:14:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SCORM_SETTINGS_SAVE_FAILED', 'Save SCORM settings failed. This could mean the SCORM directory has not been setup properly. See the SCORM module readme for details.', '2009-11-04 14:13:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SCO_DIR_NOT_EXIST', 'The SCORM RTE directory does not exist. Please create a directory called sco in your installations top directory (example: server.com/ATutor/sco).', '2005-05-10 10:58:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SCO_DIR_NOT_WRITEABLE', 'The SCORM RTE directory is not writeable. Please execute the command chmod a+rwx sco while in ATutor\'s top directory.', '2005-05-10 11:00:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SCO_DISABLED', 'The SCORM 1.2 RTE has not been enabled by the Administrator.', '2005-05-10 11:06:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SEARCH_TERM_REQUIRED', 'You must specify at least one search term.', '2003-08-22 12:03:29', 'search form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SECRET_ERROR', 'Letters or numbers entered from the CAPTCHA image are incorrect. Try again.', '2009-07-16 09:01:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SELECT_ONE_ITEM', 'Only one item must be selected.', '2007-02-21 14:07:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SENDING_ERROR', 'There was an error sending the email message.', '2005-01-18 13:34:23', 'mail failed to be sent'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SEND_ENROL', 'You can only send a message to other members after you enroll in a course.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SEND_MEMBERS', 'You can only send a message to others who are enrolled in the same courses as you or your contacts.', '2009-06-12 11:37:40', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SESSION_COOKIES', 'Session cookies must be enabled in your browser to login.', '2005-07-22 13:04:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_SOCIAL_SETTINGS_NOT_SAVED', 'Social networking settings were not saved.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_START_DATE_INVALID', 'That start date is not valid.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_STUD_INFO_NOT_FOUND', 'Student information cannot be found.', '2003-05-16 13:42:17', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_TERM_EXISTS', 'The term %s already exists.', '2004-07-22 16:55:03', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_THEME_NOT_DELETED', 'The theme could not be deleted because it is either the current default theme or the original default theme.', '2005-05-10 09:27:02', 'theme manager deleting theme'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_THEME_NOT_DISABLED', 'Theme cannot be disabled because it is currently the default theme.', '2005-05-09 14:27:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_TILE_AUTHENTICATION_FAIL', 'The Transformable authentication fails at:
    %s.', '2010-02-23 10:01:47', 'tile search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_TILE_IMPORT_FAIL', 'Transformable course import failed at:
    %s.', '2010-02-23 10:02:09', 'tile search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_TRANSCRIPT_ACTIVE', 'You may not delete an active transcript.', '2005-05-16 12:46:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_UNKNOWN', 'An undetermined error has occurred.', '2003-05-16 14:24:59', 'error msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_USER_NOT_FOUND', 'User not found.', '2005-03-09 15:56:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_ERROR_WRONG_PASSWORD', 'Incorrect password.', '2006-05-11 11:34:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ACCEPT_GROUP_INVITATION', 'Invitation to join group was accepted.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ACCEPT_GROUP_REQUEST', 'Request to join group was accepted.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ACCOUNT_APPROVED', 'You have successfully upgraded to an instructor account.', '2004-11-05 10:25:41', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ACCOUNT_CONFIRMED', 'Account has been confirmed.', '2005-03-30 11:56:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ACTION_COMPLETED_SUCCESSFULLY', 'Action completed successfully.', '2006-09-28 11:26:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ADD_TEST_INTO_GRADEBOOK', '"%1$s" cannot be added into gradebook because the following students have taken it more than once:
    \r\n%2$s.', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ADMIN_CREATED', 'Administrator account created successfully.', '2005-03-03 16:12:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ADMIN_DELETED', 'Administrator account deleted successfully.', '2005-03-03 10:49:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ADMIN_LOG_RESET', 'Administrator Activity Log has been reset successfully.', '2005-03-03 10:43:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ALREADY_ENROLLED', 'The following students are already enrolled:
      %s
    ', '2005-06-23 12:50:58', 'enrollment manager, students added were alread enrolled'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ALREADY_REQUESTED', 'You have already made a request to enroll in this course and you have not yet been approved by the instructor. You will be notifed when your request has been approved.', '2005-03-17 12:55:24', 'private enroll, already requested approval once'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ALTERNATIVE_ADDED', 'Alternate added. Be sure to define the resource type for the alternative.', '2008-09-08 14:43:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ALTERNATIVE_DELETED', 'Alternative was successfully removed.', '2008-09-08 14:56:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_APPROVAL_PENDING', 'Your request has been made. You will be notified when your request has been approved.', '2009-09-17 14:47:54', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ARCHIVE_EXTRACTED', 'Archive has been extracted successfully.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ASSIGNMENT_ADDED', 'Assignment was successfully added.', '2006-04-20 14:42:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ASSIGNMENT_HANDED_IN', 'Assignment files submitted successfully.', '2006-03-20 14:35:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ATUTOR_SOCIAL_LMS', 'ATutor is now configured as a learning management system (LMS) with social networking as a module.', '2009-07-17 14:20:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ATUTOR_SOCIAL_ONLY', 'ATutor is now configured as a social networking environment. Learning management tools are turned off.', '2009-07-17 14:22:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ATUTOR_UPDATE_AVAILABLE', 'An updated version of ATutor is available! View the Change Log for details.', '2005-11-21 12:36:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_AUTO_DISABLED', 'Auto-Login has been disabled', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_AUTO_ENABLED', 'Auto-Login has been enabled. Next time you enter ATutor using this computer, you will bypass the login screen. ', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_BLOG_SUBSCRIBED', 'Subscribed to blog feed.', '2009-06-24 11:57:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_BLOG_UNSUBSCRIBED', 'Unsubscribed to blog feed.', '2009-06-24 11:57:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_CANCELLED', 'Successfully cancelled without any changes.', '2003-12-10 10:40:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_CLOSED', 'Successfully closed', '2004-05-03 11:26:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_CONFIG_SAVED', 'System preferences have been saved successfully. A backup of the old configuration file was created and saved as %s (it\'s in your content directory).', '2005-03-29 15:22:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_CONFIRMATION_SENT', 'An email confirmation message has been sent.', '2005-03-22 12:07:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_CONFIRM_EMAIL', 'An email message has been sent to the new email address you specified. You must follow the instructions in that email for the changes to take effect.', '2006-05-11 15:55:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_CONFIRM_EMAIL2', 'An email with instructions on retrieving your password has been sent.', '2006-05-10 10:23:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_CONFIRM_GOOD', 'Your email address has been confirmed successfully.', '2005-04-11 10:29:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_CONTENT_DELETED', 'Content was successfully deleted', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_CONTENT_DIR_CREATED', 'Content directory created successfully.', '2003-10-15 12:20:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_COURSE_DEFAULT_FSIZE', 'The \'Max File Size\' was set to \'Default\' as the entered file size was either empty or negative.', '2003-10-14 16:13:40', 'course properites: file size was set to default'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_COURSE_PREFS_SAVED', 'Course preferences were saved', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_COURSE_PROPERTIES', 'Course properties were successfully updated.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_COURSE_REMOVED', 'Course was removed successfully.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_DIRS_MOVED', 'Directories successfully moved.', '2004-11-10 14:12:11', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_DIR_DELETED', 'Folder was successfully deleted.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ENROLLED', 'The following unique accounts were successfully added to the course list:
      %s
    ', '2005-06-23 12:51:35', 'enrollment manager, confirm enrollmnet'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_ERROR_LOG_RESET', 'The error log was reset successfully.', '2005-03-10 15:53:27', 'error logging'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_EXPORT_CANCELLED', 'Export cancelled successfully.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILES_MOVED', 'Files moved successfully.', '2006-03-20 16:10:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILEUPLOAD_DONE', 'File(s) uploaded.', '2008-07-22 16:45:58', 'Files uploaded'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILE_EDITED', 'File successfully edited.', '2006-08-29 11:07:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILE_EDITED_SUCCESSFULLY', 'File edited successfully.', '2006-03-20 15:05:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILE_EXISTS', 'The file %1$s already exists. The newly uploaded file was saved as %2$s. Overwrite the old existing file with the newer one.', '2010-03-09 14:27:41', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILE_OVERWRITE', 'File overwrite successful.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILE_PASTED', 'The file was successfully pasted into the textarea below. Save to apply changes, or Cancel to return to the previously saved content.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILE_SAVED', 'The file was successfully saved as %s', '2005-01-10 09:35:48', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILE_UPLOADED', 'File was successfully uploaded.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FILE_UPLOADED_ZIP', 'File was successfully uploaded. You may now extract the file.', '2010-03-09 14:28:13', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FOLDER_EDITED_SUCCESSFULLY', 'Folder edited successfully.', '2006-03-20 15:42:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FORUM_POSTING', 'To post messages to the new forum, log into ATutor as regular users or instructor.', '2004-11-21 10:08:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FORUM_SUBSCRIBED', 'You have successfully subscribed to the %s forum. Messages posted to this forum will be delivered to your registered email address. View your profile to modify your email address.', '2004-12-02 15:04:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_FORUM_UNSUBSCRIBED', 'You have successfully unsubscribed from the %s forum. Messages posted to this forum will no longer be delivered to your email address.', '2004-12-03 17:25:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GADGET_ADDED_SUCCESSFULLY', 'Gadget was successsfully added.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GADGET_REMOVED_SUCCESSFULLY', 'Gadget was successsfully removed.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GADGET_SETTINGS_SAVED', 'Gadget settings have been saved.', '2009-05-28 17:21:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GOOGLE_KEY_SAVED', 'Google key saved successfully.', '2005-10-20 11:07:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GRADEBOOK_UPDATED', 'The following grades have been successfully updated into gradebook:
      %s
    ', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GROUP_CREATED', 'Group was successfully created.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GROUP_DELETED', 'Group deleted.', '2004-11-25 15:48:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GROUP_EDITED_SUCCESSFULLY', 'Group edited successfully.', '2006-03-23 11:09:18', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GROUP_JOINED', 'Group successfully joined.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GROUP_MEMBERS_SAVED', 'Group members saved successfully.', '2006-04-17 11:43:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GROUP_TYPE_DELETED', 'Group was successsfully deleted.', '2006-05-23 15:33:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GROUP_TYPE_EDITED_SUCCESSFULLY', 'Group type edited successfully.', '2006-03-23 11:18:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_GROUP_UPDATED', 'Group updated successfully.', '2004-11-25 15:49:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_IMPORT_CANCELLED', 'Import cancelled successfully.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_IMPORT_LANG_SUCCESS', 'New language was successfully imported. You may view and modify the new language by choosing it from the selection menu below.', '2003-06-05 19:38:41', 'admin language manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_IMPORT_SUCCEEDED', 'Import was successful.', '2008-10-02 13:48:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_INVITATION_SENT', 'Invitation successfully sent. Person will be added when the invitation has been accepted.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_JOIN_REQUEST_SENT', 'Request to join group has been sent. You will be added to the group when your request has been approved.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_LANG_ADDED', 'New language was successfully created. Use the language selector to choose the new language, then beginning adding translated terms. Translation tools are enabled in include/vitals.inc.php', '2004-11-20 20:14:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_LANG_DELETED', 'Language was successfully removed.', '2003-06-05 19:38:01', 'admin language manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_LANG_UPDATED', 'Language successfully updated.', '2005-04-26 16:04:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_LEFT_GROUP_SUCCESSFULLY', 'Successfully removed from group.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_LINK_ADDED', 'Link added successfully. Link will become visible if approved.', '2005-02-23 12:07:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_LOGIN_SUCCESS', 'You have logged in successfully. Welcome back!', '2005-02-15 17:07:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_LOGOUT', 'You have successfully been logged out.', '2003-10-29 10:00:12', 'after logging out'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_MASTER_LIST_NO_CHANGES', 'No changes were done to the Master Student List.', '2005-06-16 12:32:19', 'When action to master list yields no changes'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_MASTER_LIST_UPLOADED', 'Master Student List has been updated successfully.', '2005-04-04 16:12:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_MEMBERS_ALUMNI', 'Student has been assigned alumni status.', '2004-12-02 11:42:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_MEMBERS_ENROLLED', 'The selected students were successfully enrolled in the course.', '2004-12-02 11:42:37', 'enrollment manager, confirm enrollment'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_MEMBERS_REMOVED', 'The selected users were successfully removed from the course', '2004-10-18 10:58:17', 'enrollment manager, confirm removal'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_MEMBERS_UNENROLLED', 'The selected users were successfully un-enrolled from the course', '2004-10-18 10:59:09', 'enrollemnt manager, confrm unenrollment'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_MOD_INSTALLED', 'Module successfully installed. Select the installed module from the list below, and press Enable to turn it on.', '2008-10-24 14:05:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_MOVED_FILES', 'Files successfully moved.', '2004-11-10 14:11:19', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_NOT_ENROLLED', 'The following students were not enrolled:
      %s
    ', '2005-06-23 12:50:31', 'When a student was not enrolled when imported/created.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_NOW_ENROLLED', 'You are now enrolled in the %s course. You may enter the course at any time. To un-enroll, select "Un-Enroll" next to the course listing in My Courses..', '2005-06-06 10:04:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PACKAGES_IMPORT_SUCCESS', 'The packages
      %s
    were imported successfully.', '2005-05-17 12:03:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PACKAGE_IMPORT_SUCCESS', 'The package "%s" was imported successfully.', '2005-05-17 12:03:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PASSWORD_CHANGED', 'Password changed successfully. You may now login using the new password.', '2006-05-09 14:22:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PASSWORD_SUCCESS', 'Your login and password have been successfully emailed.', '2005-01-18 14:29:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PATCH_CREATED_SUCCESSFULLY', 'The patch has been created successfully.', '2008-04-21 15:11:31', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PATCH_INSTALLED_SUCCESSFULLY', 'The patch has been installed successfully.', '2008-04-21 15:08:55', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_POST_ADDED_SUCCESSFULLY', 'Blog message was successfully posted.', '2006-05-22 12:35:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PREFS_LOGIN', 'You cannot save these preferences because you are not logged in.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PRIVS_CHANGED', 'Privileges updated successfully.', '2005-08-24 11:21:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PROFILE_CREATED_ADMIN', 'User account created successfully.', '2005-03-29 15:19:07', 'admin create user'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PROFILE_UPDATED', 'Your Profile was successfully updated.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_PROFILE_UPDATED_ADMIN', 'The user profile was successfully updated', '2004-12-17 11:47:52', 'change user profile from admin area'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_QUESTION_DELETED', 'Question was successfully deleted.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_QUESTION_REMOVED', 'Question was successfully removed from test/survey.', '2004-11-24 10:03:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_QUESTION_UPDATED', 'Question was successfully updated.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_REG_THANKS', 'Thank you for registering, please login.', '2003-09-25 14:39:50', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_REG_THANKS_CONFIRM', 'Thank-you for registering. Please follow the instructions in the email we sent you on how to confirm your account. You will need to confirm your account before you can login.', '2005-03-22 11:29:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_REJECT_GROUP_INVITATION', 'Invitation to join group was rejected.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_REJECT_GROUP_REQUEST', 'Request to join group was rejected.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_REQUEST_FRIEND_ADDED', 'Your request has been sent. Your new contact will be added when the person has accepted your request.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_RESOURCE_PROPERTIES_UPDATED', 'Resource properties have been successfully updated.', '2008-10-19 14:20:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_RESULTS_UPDATED', 'Result was successfully updated.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_RESULT_DELETED', 'Successfully deleted selected results.', '2008-08-20 15:02:07', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_SOCIAL_GROUP_UPDATED', 'Group successfully updated.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_SOCIAL_SETTINGS_SAVED', 'Social networking settings have been saved.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_STUDENT_TOOLS_SAVED', 'Student tools successfully updated', '2008-11-07 09:56:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_THEME_DEFAULT', 'The theme, %s, is now the default theme.', '2004-10-18 15:17:47', 'Theme Manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_THEME_DISABLED', 'The theme, %s, has been disabled successfully (users currently using the theme will still be able to use the theme until it is deleted)', '2004-10-19 16:55:20', 'theme manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_THREAD_LOCKED', 'Thread has been successfully locked.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_THREAD_SUBSCRIBED', 'You have successfully subscribed to the %s thread. Messages posted to this thread will be forwarded to your registered email address. View your profile to modify your email address.', '2004-12-02 16:21:03', 'forum threads'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_THREAD_UNLOCKED', 'Thread has been successfully unlocked.', '2003-05-16 13:47:01', 'feedback msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_THREAD_UNSUBSCRIBED', 'You have successfully unsubscribed from the %s thread. Messages will no longer be sent to your email address.', '2004-12-02 16:34:13', 'forum threads'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_TILE_IMPORT_SUCCESS', 'The course is imported into Transformable successfully.
    View imported course at Transformable in a new window', '2010-02-23 10:02:44', 'tile search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_FEEDBACK_UPDATE_GRADEBOOK', '"%1$s" cannot be updated into gradebook because the following students have taken it more than once:
    \r\n%2$s.', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_404_BLURB', 'The page you are trying to access %s does not exist, or has been moved. Try using the SiteMap to find it.', '2010-03-09 14:29:53', '/404.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_ACCESS_PUBLIC', 'This feature is only available for protected or private courses.', '2007-02-13 15:55:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_ACCOUNT_APPROVED', 'You have successfully upgraded to an instructor account.', '2004-02-12 12:52:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_ALREADY_ENROLLED', 'You have already made a request to enroll in this course and you have not yet been approved by the instructor. You will be notifed when your request has been approved.', '2004-11-05 10:29:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_APPROVAL_PENDING', 'Your request has been made. You will be notified when your request has been approved.

    Return to My Courses.', '2009-09-17 14:48:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_ASSIGNMENT_FS_SUBMISSIONS', 'Assignments are submitted using the group File Storage tool. Assign to all students, or create a group and enable File Storage for that group, to add an assignment drop-box.', '2006-06-29 14:50:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_CANNOT_CONNECT_MOD_SERVER', 'Unable to connect to module server. Be sure your Internet connection is functioning, and your firewall is not blocking access.', '2009-12-16 18:45:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_CANNOT_CONNECT_PATCH_SERVER', 'Failed to connect to patch server: %s so patches provided by the patch server can not be listed. Please proceed with installing private patch.', '2008-10-08 12:51:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_CANNOT_CONNECT_SERVER', 'Failed to connect to SVN server to determine if the files that will be modified by the patch script have previously been modified. So, to protect any customizations you\'ve made, all local scripts manipulated by patch are considered locally customized.', '2008-10-08 12:55:18', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_COURSE_ENDED', 'This course has ended on %s.', '2007-07-16 11:56:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_COURSE_RELEASE', 'This course has not yet been released. It is scheduled to be released on %s.', '2006-04-11 12:36:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_DECISION_NOT_REVERSED', 'Decisions could not be reversed.', '2004-08-18 12:08:25', 'AChecker - can\'t reverse decision'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_DECISION_NOT_SAVED', 'Decisions could not be saved.', '2004-08-18 12:09:38', 'AChecker - can\'t save decision'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_DECISION_REVERSED', 'Decision reversed successfully.', '2004-08-18 12:07:27', 'AChecker - reverse decision'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_FEATURE_NOT_AVAILABLE', 'This feature is not available.', '2007-02-26 11:12:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_FIRST_PREFS', 'Your personal preferences have not been setup. You can click on the wand icon above to open the personal preferences wizard, or ignore this message and use the default settings. You adjust your preferences later under the Preferences tab on My Start Page.', '2010-04-02 11:23:15', 'pref wizard'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_GLOSSARY_REMAINS', 'Note that removing a linked glossary term from your content will not delete the term from the glossary.', '2003-05-16 13:45:50', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_INBOX_SENT_MSGS_TTL', 'Sent messages are automatically deleted every %s days.', '2007-02-20 14:19:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_INVALID_URL', 'Localhost is not a valid URL', '2004-08-18 12:03:14', 'Used when AChecker reading URL'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_INVALID_USER', 'You must login to use this section.', '2004-04-15 13:52:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_LOGIN_TO_POST', 'You must be logged in to post.', '2004-11-05 10:29:51', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_MASTER_LIST_DISABLED', 'The master list has been disabled. To enable this functionality go to System Preferences.', '2005-05-10 15:16:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_MAX_ATTEMPTS', 'The test cannot be taken. It is not available at this time, or you have reached the maximum number of attempts for this test. If you have already completed this test. Please visit the Tests & Surveys to see your results.', '2010-03-09 14:31:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_MSG_SEND_LOGIN', 'You must be logged in to send messages.', '2003-05-16 13:42:17', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NOT_ENROLLED', 'You need to be enrolled in this course to access this area.', '2004-05-12 11:35:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NOT_RELEASED', 'This content has not yet been released. It is scheduled to be released on %s.', '2005-11-22 15:21:27', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_CONTENT', 'This course has no content yet.', '2003-05-16 13:45:50', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_COURSES', 'No courses found. Browse existing courses.', '2006-11-24 15:24:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_COURSES_INST', 'No courses found. Browse existing courses or create a new one.', '2010-03-09 14:07:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_FORUMS', 'There are no forums that exist.', '2008-11-07 10:43:18', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_LINK_CATEGORIES', 'There are currently no link categories. A link may not be suggested until link categories have been created.', '2005-03-16 16:47:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_PACKAGES', 'No packages found.', '2005-05-17 12:03:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_PAGE_CONTENT', 'There is no content on this page.', '2003-05-16 13:45:50', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_PERMISSION', 'You do not have permission to access this page.', '2003-05-16 13:45:50', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_POLLS', 'No polls are available right now.', '2005-03-22 10:52:43', 'no active polls'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_POSTS_FOUND', 'There are no posts in this forum.', '2003-05-16 13:45:50', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_SEARCH_RESULTS', 'No search results.', '2003-08-22 11:31:53', 'search page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_STUDENTS', 'There are no students enrolled in this course.', '2005-03-22 11:09:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_TESTS', 'No tests found. Create tests using the test manage.', '2008-11-12 10:07:03', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_NO_TOOLS_FOUND', 'No student tools are turned on. Turn them on through Manage>Student Tools', '2008-12-11 11:02:22', 'FHA student tools screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_OVER_QUOTA', 'You have reached or passed your maximum quota for this course. You will have to delete some files before you will be able to upload again.', '2003-10-07 15:06:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_PATCH_INSTALLED_AND_REMOVE_PERMISSION', 'The patch has been installed. Please remove write permission as instructed below.', '2008-04-22 14:10:33', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_PRETEST', 'You must take the prerequisite test in order to view the content.', '2009-11-13 15:52:44', 'content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_PRETEST_EXPIRED', 'You can\'t view this content because the prerequisite test "%s" has expired. Please contact your instructor.', '2009-11-30 14:40:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_PRETEST_FAILED', 'You this cannot view this content because you have not passed the prerequisite test "%s".', '2009-11-13 15:51:50', 'edit/create folder'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_PRETEST_NO_PRIV', 'You can\'t view this content before taking the prerequisite test "%s".', '2009-11-13 15:51:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_PRIVATE_ENROL', 'The course you are trying to access is private. Enrollment in this course requires instructor approval.
    ', '2003-10-24 15:31:11', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_REG_DISABLED', 'Public registration is disabled.', '2008-09-10 14:09:17', 'registration form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_SAVE_CONTENT', 'Content must be saved before using this tool.', '2008-11-11 15:49:25', 'AChecker requirement in ATutor content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_SERVICE_UNAVAILABLE', 'Service currently unavailable.', '2004-08-18 12:00:53', 'Used for AChecker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_TRACKING_NO_INST', 'You have chosen your own ID number. Tracking information is not recorded for course instructors. Choose another user.', '2003-10-24 15:31:44', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_TRACKING_NO_INST1', 'Tracking information is not recorded for course instructors. See the Course Tracker for a record of course activity.', '2010-03-09 14:33:16', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_TRACKING_OFFIN', 'Tracking is not enabled for this course. Contact your system administrator to have it turned on.', '2003-05-16 13:45:50', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_INFOS_TRACKING_OFFST', 'Tracking is not enabled for this course.', '2003-05-16 13:45:50', 'info msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_WARNING_CHAT_TRAN_EXISTS', 'A transcript with the name %s already exits. Either delete the original transcript, then resubmit, or rename your new transcript.', '2003-06-02 14:05:26', 'achat instructor transcript tool'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_WARNING_DELETE_CATEGORY', 'Are you sure you want to delete this category with all its links?', '2003-05-16 13:46:16', 'warning msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_WARNING_LANG_EXISTS', 'The language you are uploading is already installed on this system. Delete the existing language first by selecting the "Remove" link next to its listing below.', '2003-06-06 09:40:14', 'admin language manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_WARNING_MAIL_NOT_ON', 'Mail configuration is not set properly. You may not be able to send out emails. See ATutor FAQ for details.', '2009-12-08 16:18:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_WARNING_QUESTION_WEIGHT', 'One or more of the questions on this test do not have points associated with them. Ensure that this is correct, or enter values for questions that do not have points.', '2007-03-05 15:52:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_WARNING_SAVE_YOUR_WORK', 'Save your work before opening or closing the File Manager.', '2003-05-16 13:46:16', 'warning msg'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_WARNING_THEME_VERSION_DIFF', 'The theme, %s, may not be compatible with this version of Atutor.', '2004-10-19 14:34:35', 'Theme Manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_msgs', 'AT_WARNING_TRANSLATE_ON', 'You are currently in Translation Mode. Any user can access the translation page at this time. To turn off the Translate mode please set AT_DEVEL_TRANSLATE in /include/vitals.inc.php to zero.', '2004-12-09 10:53:02', 'admin index and admin language manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', '15_max_chars', '8 characters minimum, 15 characters maximum', '2006-07-17 12:03:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', '20_max_chars', '20 character maximum', '2003-06-04 14:52:27', 'login name registration screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', '404', 'Error: Missing Page', '2004-03-10 11:24:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'a', 'A', '2007-01-10 13:47:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'a4a_export', 'Export AccessForAll adapted content.', '2008-10-19 15:12:28', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'a4a_import_package', 'Import available AccessForAll adapted content.', '2008-10-21 11:44:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'abacus', 'Abacus', '2008-09-09 09:03:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'about_atutor', 'About ATutor', '2004-01-14 10:25:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'about_atutor_help_text', 'Learn about various sources of ATutor help, and the help display options available.', '2004-05-19 10:40:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'about_content_tests', 'Link available tests to this content page.', '2008-09-23 13:46:28', 'content editor tests tab'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'about_pretest', 'Select from the available tests one or more that must be passed before the student can view content in this folder.', '2009-11-13 15:43:59', 'edit/create folder'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'about_private', 'Available only to users with an ATutor System Account, and Instructor approved enrollment. Login required. Enrollment is required.', '2004-01-22 14:54:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'about_protected', 'Available only to users with an ATutor System Account. Login required. Enrollment is optional.', '2004-02-05 09:43:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'about_public', 'Available to all users with or without an ATutor System Account. Login not required. Enrollment is optional.', '2004-02-05 09:42:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'accept_late_submissions', 'Accept Late Submissions', '2006-03-23 11:38:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'accept_request', 'Accept Request', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'access', 'Access', '2007-01-27 13:45:44', 'create a new course'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'accessibility', 'Accessibility', '2003-05-21 13:56:28', 'help page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'accessibility_features', 'Accessibility Features', '2003-05-21 14:28:03', 'help'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'accessibility_features_text', 'Using ATutor with assistive technology and other accessibility features.', '2003-05-21 14:00:59', 'help'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'access_credit', 'Accessibility report provided by AChecker.', '2009-11-04 16:20:03', 'include/html/editor_tabs/accessibility.inc.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'access_method', 'Access Method', '2003-05-18 09:07:28', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'access_stats', 'Access Method Statistics for', '2005-02-07 16:22:32', 'context (?)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'account_authorization', 'Account Creation Authorization', '2005-05-27 13:33:38', 'registration page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'account_information', 'Account Information', '2003-05-18 09:14:11', 'control centre/registration'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'account_settings', 'Account settings', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'account_status', 'Account Status', '2005-04-14 11:09:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'action', 'Action', '2004-10-14 14:10:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'active_admin', 'Active Administrator', '2005-03-03 11:00:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'activities', 'Activities', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'ac_access_groups', 'Access Groups', '2004-03-09 14:22:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add', 'Add', '2004-11-24 14:28:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'added_members', 'Current Group Members', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_announcement', 'Add Announcement', '2003-05-18 09:17:38', 'course announcments/home page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_application', 'Add Gadget', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_application_url', 'Add gadget by URL', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_assignment', 'Add Assignment', '2006-03-23 11:36:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_atutor_test', 'Add ATutor Assignments/Test/Surveys', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_atutor_test_info', 'Select from the available test and assignment titles, then optionally choose a "Grade Scale", to add a test or assignment to the gradebook. . If "Grade Scale" is set to none, the raw final score will be used in place of a grade scale. Only tests with the test property "Attempts Allowed" set to 1 can be added to the Gradebook. Create tests using the ATutor Tests & Surveys Manager', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_a_file', 'Add A File', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_a_new_language', 'Add a new language.', '2004-10-28 13:05:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_code', 'Add Code', '2004-05-06 12:22:57', 'Title for code tags in code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_content', 'Create Content', '2005-04-18 11:40:53', 'heading/submit button on Add Content screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_content_folder', 'Add Content Folder', '2009-09-16 13:11:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_dependent_patch', 'Add A Dependent Patch', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_external_test', 'Add External Assignments/Tests', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_file_folder', 'Add File or Folder', '2008-07-22 16:45:15', 'Title text used in File Manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_glossary', 'Add Glossary Term', '2005-03-07 11:42:42', 'glossary screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_grade_scale', 'Add Grade Scale', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_language', 'Add Language', '2004-09-01 15:42:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_link', 'Add Link', '2005-02-22 10:13:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_media', 'Add Media', '2009-11-04 12:48:56', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_more', 'Add More Students', '2004-10-15 14:10:59', 'create student list manually in enrollment manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_association', 'Add new association', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_award', 'Add new award', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_contact', 'Add new alternate contact', '2009-12-10 10:17:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_content', 'Add Alternate Contact', '2009-12-03 15:38:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_education', 'Add new education', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_interest', 'Add new interest', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_personal', 'Add personal information', '2009-12-07 13:47:05', 'Social personal info'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_position', 'Add new position', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_representation', 'Add new representative', '2009-12-10 10:17:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_representative', 'Add New Representative', '2009-12-03 15:41:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_new_website', 'Add new website', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_poll', 'Add Poll', '2004-06-14 14:41:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_question', 'Add Question', '2005-10-19 12:44:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_questions', 'Add Questions', '2003-05-18 10:27:45', 'test manager add questions screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_sibling_folder', 'Add Sibling Folder', '2009-09-16 13:12:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_sibling_page', 'Add Sibling Page', '2005-07-21 10:18:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_sub_folder', 'Add Sub Folder', '2009-09-16 13:13:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_sub_page', 'Add Sub Page', '2005-03-31 14:17:33', 'content page shortcuts'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_term', 'Add Term', '2003-05-18 10:28:38', 'glossary'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_tests', 'Add Tests/Assignments', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_topic', 'Add Topic', '2005-10-19 12:43:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_top_folder', 'Add Top Folder', '2009-09-16 13:12:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_top_page', 'Add Top Page', '2005-07-21 10:18:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_to_friends', 'Add to contacts', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_to_mygadgets', 'Add to My Gadgets', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'add_to_test_survey', 'Add to Test/Survey', '2004-11-22 16:38:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'administration', 'Administration', '2003-06-04 10:51:48', 'admin home page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'administrators', 'Administrators', '2005-03-03 10:40:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'admin_bundle_instructions', 'Please select the profile(s) you wish to bundle and send via e-mail. All error logs coupled with these profiles will also be included.', '2005-03-10 15:29:13', 'admin error reporting'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'admin_email', 'Email Users', '2005-01-06 14:57:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'admin_log', 'Administrator Activity Log', '2005-03-03 10:40:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'admin_new_account', 'A user account has been created for you at %1s. To access this account, please log into the site. It is strongly suggested you change your password upon logging in.', '2005-06-21 16:38:21', 'Admin creates a new user account.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'admin_new_account_confirm', 'An account has been created for you at %1s. To access your account, you must first confirm your email by using the following link: %2s. It is strongly suggested you change your password upon logging in.', '2005-06-21 16:37:44', 'Admin creates a new user and confirm is ON'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'admin_social', 'Social Network Settings', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'affected_entries', 'Affected Entries', '2005-03-03 12:14:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'after_topic', 'After: %s', '2004-01-16 13:19:46', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'age', 'Age', '2003-05-18 10:38:32', 'registration, profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all', 'All', '2003-05-18 11:35:37', 'glossary'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'allow_browse', 'Course Browser', '2009-07-23 15:48:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'allow_editing', 'Allow Editing', '2007-02-20 10:30:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'allow_guests', 'Allow Guests', '2007-03-06 14:44:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'allow_instructor_registration', 'Allow Instructors to Enroll Users from the System Registration List', '2008-11-14 11:51:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'allow_instructor_requests', 'Allow Instructor Requests', '2005-03-03 10:51:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'allow_registration', 'Allow Self-Registration', '2008-09-10 14:14:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'allow_test_export', 'Allow students to export tests with content packages:', '2008-09-26 13:43:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'allow_unenroll', 'Allow Users to Unenroll Themselves from Courses.', '2008-09-10 14:16:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_atutor_assignments', 'All ATutor Assignments', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_atutor_tests', 'All Applicable ATutor Tests & Surveys', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_available_courses', 'All available courses', '2004-08-18 12:03:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_courses', 'All Courses', '2005-04-01 14:08:29', 'forums'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_failed_students', 'All Failed Students', '2008-03-10 11:52:03', 'Test/Survey Property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_forums', 'Share with all courses, or', '2006-06-01 14:31:37', 'admin create shared forum'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_label', 'All (%s)', '2005-05-12 10:26:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_passed_students', 'All Passed Students', '2008-03-10 11:51:36', 'Test/Survey Property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_questions_on_page', 'All questions on one page', '2007-08-20 15:46:22', 'create/edit test'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_students', 'All Students', '2006-03-23 13:39:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'all_users', 'All Users', '2005-03-10 15:24:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alter', 'Alter', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alternate_text', 'Alternate Text', '2004-12-08 12:39:27', 'filemanager, image inset alt field'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alternatives', 'Alternatives', '2010-03-09 16:16:55', 'adapted content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alternatives_to', 'Adaptation of', '2008-10-21 10:56:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alternative_content', 'Adapted Content', '2008-10-19 12:03:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alt_contact', 'Alternate Contact', '2009-12-03 15:40:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alt_to_audio', 'Alternative To Audio', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alt_to_text', 'Alternative To Text', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alt_to_visual', 'Alternative To Visual', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'alumni', 'Alumni', '2004-10-22 15:56:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'always', 'Always', '2006-03-23 11:38:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'american-asl', 'American-ASL', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'announcements', 'Announcements', '2003-05-18 11:36:24', 'course home page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'announcement_date_format', '%%l %%F %%j, %%Y - %%H:%%i', '2006-04-11 14:10:57', 'course home page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'anonymous', 'Anonymous', '2004-08-26 13:24:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'anonymous_test', 'Anonymous', '2004-08-26 12:31:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'answer', 'Answer', '2003-05-18 11:38:10', 'test question add/edit screens'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'answers', 'Answers', '2007-01-10 13:47:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'answer_size', 'Answer Size', '2003-05-18 11:38:55', 'add/edit open ended question'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'apache_mod_rewrite', 'Apache mod_rewrite', '2008-05-14 12:04:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'append', 'Append', '2008-09-09 09:02:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'append_content', 'Append selected material', '2004-10-15 14:18:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'append_or_replace', 'Append or Replace Original Content', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'applications', 'Gadgets', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'application_control_blurb', 'Choose which gadgets to display on your networking home page.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'application_settings', 'Gadget Settings', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'applies_to_all_sub_pages', 'Applies to all sub pages.', '2007-03-01 10:23:22', 'edit/create content page. release date.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'apply', 'Apply', '2005-02-10 11:25:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'apply_theme_subcategories', 'Apply theme to subcategories.', '2004-08-04 11:35:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'apply_to_all_results', 'Apply to all results', '2007-02-21 11:49:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'approve', 'Approve', '2003-05-18 11:41:29', 'enrol administration'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'approved', 'Approved', '2005-02-23 12:17:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'approve_request', 'Approve Request', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'archive_total', 'Archive Total', '2003-05-18 11:42:55', 'file manager zip tool'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'arrange_content', 'Arrange Content', '2009-09-16 13:13:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'assigned_to', 'Assigned To', '2005-06-02 12:18:18', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'assignment', 'Assignment', '2006-03-20 14:38:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'assignments', 'Assignments', '2006-03-20 14:40:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'assignment_due', 'Assignment Due: %s - Due Date: %s', '2010-03-25 15:32:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'assign_to', 'Assign To', '2006-03-23 11:37:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'assign_unassigned', 'Assign Unassigned', '2006-03-23 11:07:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'assistant', 'Assistant', '2006-05-23 15:24:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'assistants', 'Assistants', '2004-10-15 11:45:31', 'Assistants tab in enrollment manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'associations', 'Associations', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'as_individual_content', 'As individual content pages', '2004-08-18 12:04:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'at', 'at', '2003-05-18 11:44:08', 'release date tool/links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atlas', 'Atlas', '2008-09-10 12:26:11', 'tool prefs'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'attempts', 'Attempts', '2004-08-30 11:31:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_accessibility_text', '

    ATutor includes a variety of features designed to ensure that content is accessible to all potential users, including those with slow Internet connections, older Web browsers, and people with disabilities using assistive technologies to access the Web. These features are described in detail below. Depending on the theme being used, ATutor may include all or some of the features listed here. The default theme includes them all.

    \r\n\r\n
      \r\n
    1. ByPass Links: In the top left corner of ATutor is a hidden bypass link that allows assistive technology users to skip over the navigation elements and jump directly to an anchor at the top of the content being displayed. Press the Tab key once after a page has finished loading to advance to the first bypass link then press Enter to reposition the content to the top of your screen. The content anchor can also be accessed with an accesskey [Alt-c], so it is possible to jump to the top of the content area no matter where the cursor might be located on the screen.

      \r\n\r\nOther bypass links include: "jump past table" so users can skip over various data table to quickly access content that may appear after the table; "jump past codes", so users can skip over the emoticon codes near the bottom of message posting form in the Forums.

    2. \r\n\r\n
    3. Accesskeys: Keyboard accessibility has been added to many ATutor features. To activate accesskeys, press Alt plus the assigned number, letter, or character. Accesskeys are assigned dynamically to the tabs in the Main Navigation. The first tab will be assigned [Alt-1], the second tab will be assigned [Alt-2], and so on. Note that not all browsers support accesskeys.

      \r\n\r\nGeneral AccessKeys\r\n
        \r\n
      • [Alt-1 to Alt-0] Main Navigation Tabs
      • \r\n
      • [Alt-,] Previous topic (i.e. Left angle bracket <)
      • \r\n
      • [Alt-.] Next topic (i.e. Right angle bracket > )
      • \r\n
      • [Alt-.] Resume (i.e. Right angle bracket > while outside the course content)
      • \r\n
      • [Alt-j] Jump Menu
      • \r\n
      • [Alt-s] Submit (active on many form pages)
      • \r\n
      • [Alt-c] Jump to content top (top and navigation bypass link)
      • \r\n
      \r\n\r\nContent Editor AccessKeys\r\n
        \r\n
      • [Alt-n] Content Tab
      • \r\n
      • [Alt-p] Properties Tab
      • \r\n
      • [Alt-g] Glossary Tab
      • \r\n
      • [Alt-r] Preview Tab
      • \r\n
      • [Alt-a] Accessibility Tab
      • \r\n
      • [Alt-s] Save
      • \r\n
      \r\n\r\nChat AccessKeys\r\n
        \r\n
      • [Alt-c] Jump to Compose Message field
      • \r\n
      • [Alt-r] Refresh Messages
      • \r\n
      • [Alt-m] Jump to Message List
      • \r\n
      • [Alt-q] Quit Chat
      • \r\n
      \r\n\r\nInbox AccessKeys\r\n
        \r\n
      • [Alt-r] Reply to Messages
      • \r\n
      \r\n
      \r\n\r\nVisual Editor AccessKeys\r\n
        \r\n
      • [Alt-q] jump to button bar
      • \r\n
      • [Alt-z] jump to content area of editor
      • \r\n
      • [Alt-x] jump to element path (bottm)
      • \r\n
      \r\n
    4. \r\n\r\n
    5. Accessibility Verifier: While using the ATutor Content Editor, instructors can check the accessibility of their content to be sure it can accessed by all students, including those using assistive technologies, or older technologies. While using the Content Editor, select [Alt-a] to open the accessibility verifier.
    6. \r\n\r\n
    7. Alternative Text: All meaningful images in ATutor include a text alternative that describes the image or its function. Where images are not meaningful the Alt attribute is left empty so assistive technologies will ignore the image.
    8. \r\n\r\n
    9. Alternative Navigation: Global, hierarchical, and sequential navigation tools are available so users can view or structure content in a manner that suits their style of learning. The ATutor SiteMap provides a full list of all content and tools available, so all features in a course can be accessed from a single location.
    10. \r\n\r\n
    11. Resume/Continue: ATutor remembers the last page accessed when users leave the course content, to go to the Forums for instance, so they can quickly find their way back to where they left off. The content location is also remembered between sessions, so when returning to ATutor, users can simply choose the Resume link in the course listing on My Courses, or select the the resume link in the breadcrumbs at the top of the screen once in a course. Using the [Alt-.] accesskey will also return a user to the last content page they were viewing after they have entered into a course.
    12. \r\n\r\n
    13. Hide Menus: For users of older assistive technologies that do not support columnar text laid out in tables, it is possible to hide ATutor\'s content navigation menu so that content will be displayed in a linear presentation. Hiding the content menu also conserves space for users viewing ATutor on smaller screens.
    14. \r\n\r\n
    15. Search and Sort: Most data tables are accompanied by a search tool, or filter, so specific records can be displayed in the table, reducing the amount of data displayed at any given time. Most data tables can also be sorted by selecting the column header label, again allowing users to narrow down the data display.
    16. \r\n\r\n
    17. Table Header Associations: All data tables are marked up with the TH element and the scope attribute to ensure that screen reader users are able to navigate through data displays and understand what each data cell represents.
    18. \r\n\r\n
    19. Form Labels: All form fields throughout ATutor are marked up using the LABEL element to ensure that they are properly described for assistive technology users. Explicit labeling in this manner also makes it possible to click on a form field\'s label to activate the field. This provides a larger target area for those people who have difficulty positioning a mouse pointer on a small form field such as a radio button or a checkbox.
    20. \r\n\r\n
    21. Style Sheets: Wherever possible, the presentation of content in ATutor is controlled by style sheet elements. This allows users to override ATutor\'s default appearance and apply their own preferred presentation styles (i.e. increased font sizes, different font styles, colours, etc.)
    22. \r\n\r\n
    23. Form Field Focus: For pages where the primary content is a form, the cursor will automatically be placed in the first field so that after a form page loads, users can begin typing into the form. Users may choose to disable form focus in their preference settings if it interferes with their use of form screens.
    24. \r\n\r\n
    25. Relative Sizing of Images: With browsers that support resizing of images, ATutor icons can be magnified for users with low vision, making them more easily visible. (e.g. Netscape 7 press Alt +) . Wherever possible relative measures have also been used for other feature within ATutor so when the interface is magnified or reduced, all features resize relative to each other, maintaining the screen\'s symmetry.
    26. \r\n\r\n
    ', '2005-12-06 14:38:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_handbook', 'Official ATutor Handbook', '2005-07-12 10:42:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_home', 'ATutor home', '2003-05-18 11:49:37', 'mail response link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_is', 'ATutor is an Open Source Web-based Learning Content Management System (LCMS) designed with accessibility and adaptability in mind. Administrators can install or update ATutor in minutes, create custom themes to give it a new look, and extent its functionality by enabling or developing feature modules. Educators can quickly assemble, package, and redistribute instructional content, and conduct their courses online. Students learn in an adaptive learning environment.', '2007-12-14 11:22:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_links', '

    Learn more about ATutor by browsing the following links:

    \r\n\r\n
      \r\n
    • Support Forums - Post to the user forums
    • \r\n
    • Support Services - If you need our help, support is available
    • \r\n
    • Translation - Download language packs, become a translator
    • \r\n
    • Licensing - ATutor software is available for free under certain terms
    • \r\n
    • Download ATutor - All of the system software required to run ATutor can be downloaded here
    • \r\n
    ', '2007-12-14 11:22:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_patch_id', 'ATutor Patch ID', '2008-04-21 15:08:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_settings', 'ATutor Settings', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_version', 'ATutor Version', '2005-03-10 11:21:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_version_tested_with', 'Last Tested With', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_version_text', '%s. Check Latest Version.', '2007-07-26 13:52:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'atutor_version_to_apply', 'ATutor Version to Apply', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'at_tools', 'ATutor Tools', '2003-05-18 11:51:40', 'tracker column heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'audible_captcha', 'Audible Version of CAPTCHA', '2009-07-16 08:50:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'audio', 'Audio', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auditory', 'Auditory', '2008-09-08 14:18:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'australian-auslan', 'Australian-Auslan', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'austrian', 'Austrian', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'authenticated_access', 'Authenticated Access', '2007-02-13 15:37:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'author', 'Author', '2006-03-20 14:36:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auth_access_text', 'Give this link to those you wish to have limited access to your protected or private course.', '2007-02-13 15:54:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_approve_instructors', 'Auto Approve Instructor Requests', '2005-03-03 10:51:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_check_new_version', 'Check for ATutor Updates Automatically', '2005-11-21 12:37:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_enroll', 'Auto Enrollment At Registration', '2008-03-10 11:53:18', 'Auto Enroll Property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_enrolled_msg', 'You have been enrolled into these courses:', '2008-03-10 12:02:23', 'auto enroll property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_enroll_delete', 'Delete Auto Enrollment', '2008-03-10 11:54:52', 'Auto Enroll label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_enroll_edit', 'Create/Edit Auto Enrollment', '2008-03-10 12:01:18', 'Auto Enroll property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_enroll_msg', 'You will be enrolled into these courses.', '2008-03-10 12:01:55', 'auto enroll property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_install_languages', 'Automatically Install New Language Packs', '2006-07-26 12:08:03', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_install_languages_cron', 'You must set-up the cron to use this feature.', '2006-07-26 12:08:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'auto_login1', 'Auto-Login', '2003-05-18 11:53:47', 'control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'availability', 'Availability', '2007-03-06 14:24:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'available_applications', 'Available Gadgets', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'available_immediately', 'Release immediately', '2006-04-10 14:37:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'available_on_my_courses', 'Link From The My Courses Page', '2005-05-02 12:12:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'available_patches', 'Available Patches', '2008-04-21 15:08:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'available_patches_text', 'There are %s patches available to install.', '2008-04-21 15:08:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'available_to', 'Available To', '2008-04-21 15:08:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'average', 'Average', '2003-05-18 11:56:58', 'test results / tracker courses statistics'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'avg_duration', 'Avg. Duration', '2005-03-01 12:40:29', 'tracker column heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'awards', 'Awards', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'b', 'B', '2007-12-18 14:39:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'back', 'Back', '2003-05-18 12:02:02', 'file manager back up to previous directory'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'backups', 'Backups', '2004-10-15 11:40:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'backup_manager', 'Backup Manager', '2004-10-14 13:59:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'back_to', 'Back To:', '2005-03-22 16:08:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'back_to_main', 'Back to Main', '2004-12-06 12:07:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'back_to_profile', 'Back to Profile', '2004-12-06 12:09:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'back_to_summary', 'Back to Summary', '2003-05-18 12:05:32', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'banner', 'Banner', '2006-03-28 14:32:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'basic_profile', 'Basic Profile', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'before_topic', 'Before: %s', '2004-01-16 13:19:20', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'bg_colour', 'Background Colour', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'black', 'Black', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'blogcomment_notify_body', '*DO NOT REPLY TO THIS MESSAGE*

    \r\nA new blog comment has been posted in the group %s.

    Click the following link to visit the course.

    %s', '2009-06-24 11:59:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'blogcomment_notify_subject', 'New blog comment', '2009-06-24 11:57:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'blogs', 'Blogs', '2006-05-22 12:10:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'blog_notify_body', '*DO NOT REPLY TO THIS MESSAGE*

    \r\nA new blog post has been published in the group %s.

    Click the following link to visit the course.

    %s', '2009-06-24 11:58:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'blog_notify_subject', 'New blog post', '2009-06-24 11:57:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'blog_subscribe', 'Subscribe', '2009-06-24 11:53:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'blog_unsubscribe', 'Unsubscribe', '2009-06-24 11:53:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'blue', 'Blue', '2003-05-18 12:09:34', 'global code picker tool'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'blue_guests', 'Blue = Guests', '2003-05-18 12:10:38', 'instructor course statistics'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'body', 'Body', '2003-05-18 12:11:16', 'add/edit forms for body field label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'bold', 'Bold', '2003-05-18 12:11:41', 'global code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'bottom', 'Bottom', '2003-05-23 16:58:31', 'preference settings option'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'british-bsl', 'British-BSL', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'browse_all', 'Browse All', '2009-07-16 10:57:49', 'browse all'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'browse_courses', 'Browse Courses', '2003-05-18 12:13:27', 'control centre, about, login screens'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'browse_files', 'Browse Files', '2008-07-22 16:43:56', 'For "Browse Files" button in Fluid\'\'s multiple file uploader'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'bt', 'B', '2007-12-18 14:39:47', 'byte, file manager/storage'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'bug_count', 'Bug Count', '2004-12-06 12:23:46', 'admin/error_logging.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'bug_identifier', 'Bug Identifier', '2004-12-06 12:05:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'builtin_icons', 'Builtin Icons', '2008-08-21 10:38:41', 'course properties'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'business', 'Business', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'by', 'by', '2008-07-25 15:59:13', 'anouncements by a users'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'bytes', 'Bytes', '2003-05-18 12:15:45', 'admin course size quote, course properties screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'c', 'C', '2007-01-10 13:48:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cache_directory', 'Cache Directory', '2005-03-03 10:52:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'calculator', 'Calculator', '2008-09-09 09:03:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cancel', 'Cancel', '2003-05-18 12:16:07', 'global cancel button'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cannot_find_remote_languages', 'ATutor was unable to connect to the ATutor.ca website to retrieve the list of available languages.', '2004-10-18 14:28:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'categories', 'Categories', '2005-02-22 10:12:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'category', 'Category', '2003-05-18 12:16:54', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cats_all', 'All Categories', '2005-02-23 10:22:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cats_categories', 'Categories', '2003-09-24 18:21:26', 'admin course categories'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cats_none', 'None', '2003-09-24 18:13:50', 'admin course categories'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cats_no_categories', 'No Categories', '2003-09-24 18:28:32', 'admin course categories'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cats_parent_category', 'Parent Category', '2003-09-24 18:04:38', 'admin course categories'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cats_uncategorized', 'Uncategorized', '2003-09-24 18:17:50', 'admin course categories'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cats_view_links', 'Show Links by Category', '2005-03-29 14:08:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cat_theme', 'Category Theme', '2004-08-04 11:34:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'center', 'Center', '2003-05-18 12:20:08', 'global code picker option'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'change_email', 'Change Email', '2006-05-10 11:11:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'change_password', 'Change Password', '2006-05-10 11:11:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'charset', 'Character Set', '2004-11-20 19:36:01', 'admin new language'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat', 'Chat', '2003-05-18 12:20:29', 'global chat label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_altc', 'Alt + C: Jump to Compose Message', '2003-05-30 12:41:06', 'chat options frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_altm', 'Alt + M: Jump to Messages', '2004-08-18 10:58:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_altq', 'Alt + Q: Quit Chat', '2005-05-30 15:04:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_altr', 'Alt + R: Refresh Messages', '2004-08-18 10:58:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_auto180_checking', 'Automatic (180 second interval)', '2003-05-15 14:23:13', 'chat message checking preference'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_auto20_checking', 'Automatic (20 second interval)', '2003-05-15 14:22:22', 'chat message checking preference'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_auto5_checking', 'Automatic (5 second interval)', '2003-05-27 09:09:57', 'chat message checking preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_auto60_checking', 'Automatic (60 second interval)', '2003-05-15 14:22:53', 'chat message checking preference'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_chime_help', 'Set to "Yes" to recieve an audio tone when new messsages are posted. Does not work in all Web browsers.', '2003-05-15 19:33:11', 'chat message checking preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_compose_message', 'Compose Message', '2003-05-15 13:12:38', 'chat message compose frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_currently_active', 'Currently active', '2003-05-15 12:40:47', 'chat hompage transcript activated'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_current_tran', 'A transcript is already being recorded. You may view the current transcript at', '2005-03-30 14:04:45', 'achat'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_delete_transcript', 'Delete Transcript', '2003-06-30 17:53:55', 'chat home page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_edit_prefs', 'Edit Preferences', '2003-05-15 13:47:05', 'chat options frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_enter', 'Enter Chat', '2003-05-15 14:50:48', 'chat preferences pages'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_exit', 'Exit Chat', '2003-05-15 13:48:43', 'chat options frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_full_history', 'Full History', '2003-05-15 14:00:02', 'chat options frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_display_blurb', '

    Note: Jump To links and the Quick Key list only appear if you have turned on Navigation Aids in you preferences.

    \r\n \r\n
      \r\n
    • The Jump to Quick Keys link at the top takes you to a list of quick keys for the Chat.
    • \r\n
    • The Message Area displays the most recent messages (up to ten).
    • \r\n
    • The Jump to Messages links take you to the beginning of the list of messages. (Alt+M)
    • \r\n
    • The Refresh Messages link lets you check for new messages (Alt+R).
    • \r\n
    • The Compose Message Field and Send Button let you enter and send messages (Alt+C to enter the Compose Field, Enter to send a completed message).
    • \r\n
    ', '2004-08-18 11:00:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_display_frame', 'Display Frame Help (top left)', '2003-05-15 15:24:02', 'chat help screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_display_jump', 'Jump to Display Frame Help', '2003-05-27 09:14:57', 'chat help screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_history_blurb', 'The User List shows the Chat IDs of all the users in the chat. Click on a Chat ID to see the messages sent by that participant, along with your own messages. Click on All Users to see the messages sent by all participants.', '2003-05-15 15:34:10', 'chat help screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_history_frame', 'User List and History Help', '2003-05-15 15:32:14', 'chat help screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_history_jump', 'Jump to User List and History Help', '2003-05-15 15:22:23', 'chat help screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_jump_top', 'Jump to Top', '2003-05-15 15:25:48', 'chat_help screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_options_blurb', '
      \r\n
    • The Edit Preferences link opens the preferences so you can modify your control and display settings.
    • \r\n
    • The Exit Chat link ends your chat session.
    • \r\n
    • The Help link brings you to this screen. (Alt+Q)
    • \r\n
    ', '2004-08-18 11:01:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_options_frame', 'Options Help (top right)', '2003-05-15 15:24:50', 'chat help screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_options_jump', 'Jump to Options Help', '2003-05-27 09:15:04', 'chat help screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_help_screen', 'Help Screen', '2003-05-15 15:19:33', 'chat help'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_history_messages', 'History - Messages %s to %s of %s', '2003-05-15 15:59:13', 'chat full history screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_inactive', 'Inactive', '2005-03-30 14:47:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_jump_to_message', 'Jump to Message Area', '2003-05-15 13:14:01', 'chat display frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_keep_tran', 'This section allows you to keep a transcript of the chat in progress.', '2003-05-28 11:10:47', 'achat'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_layout_prefs', 'Preferences: Layout Settings', '2003-05-15 14:53:04', 'chat layout preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_list_and_history', 'User List and History', '2003-05-15 13:49:37', 'chat options frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_logged_out', 'User %s has logged out.', '2003-05-15 16:01:51', 'logout'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_manual_checking', 'Manual Refresh', '2003-05-15 14:23:42', 'chat message checking preference'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_messages', 'Messages', '2003-05-15 13:05:28', 'chat message display frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_message_checking', 'Message Checking:', '2003-05-15 14:19:45', 'chat message checking preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_message_check_help', 'Select the interval rate at which new messages are retrieved from the server. Screen reader users set this option to Manual Refresh.', '2003-05-15 14:39:17', 'chat message checking preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_message_chime', 'New Message Chime:', '2003-05-15 14:43:05', 'chat message checking preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_message_new_help', 'Screen reader users choose "Yes", along with the Manual Refresh setting.', '2003-05-15 15:05:32', 'chat layout preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_message_order_help', 'Select New to Old to display new messages at the top of the screen, and Old to New to display new messages at the bottom of the screen.', '2003-05-15 14:58:48', 'chat layout preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_newmsg_prefs', 'Show Only New Messages?', '2003-05-15 15:00:44', 'chat layout preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_newold_prefs', 'New to Old', '2003-05-15 14:55:15', 'chat layout preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_none_found', 'No transcripts found.', '2005-03-31 11:32:44', 'chat home page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_no_new_messages', 'No new messages.', '2003-05-15 13:09:01', 'chat display frames'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_oldnew_prefs', 'Old to New', '2003-05-15 14:54:48', 'chat layout preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_options', 'Options', '2003-05-15 13:45:35', 'chat options frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_order_prefs', 'Order of Messages:', '2003-05-15 14:53:59', 'chat layout preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_post', 'Enter: Post a completed message', '2003-05-15 14:07:17', 'chat options frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_prefs_checking', 'Preferences: Message Checking Settings', '2003-05-15 14:18:37', 'chat checking preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_quick_keys', 'Quick Keys', '2003-05-15 14:03:24', 'chat options frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_refresh_message', 'Refresh Messages', '2003-05-15 13:16:05', 'chat display frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_refresh_user_list', 'Refresh User List', '2003-05-15 14:01:12', 'chat options frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_return', 'Return to Chat', '2003-05-15 15:20:17', 'chat help screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_start_tran', 'Start keeping a transcript', '2003-05-28 11:20:12', 'achat'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_start_transcript', 'Start/Stop Transcript', '2005-05-16 10:55:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_stop_tran', 'Stop keeping transcript.', '2003-05-28 12:48:38', 'achat'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_system', 'system', '2003-05-15 13:07:42', 'chat display frame system message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_transcript', 'Transcript', '2003-05-30 11:27:26', 'achat'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_transcript_end', 'Transcript End:', '2003-05-30 11:30:10', 'achat'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_transcript_start', 'Transcript Start:', '2003-05-30 11:29:26', 'achat'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_tran_file_name', 'Transcript name (alphanumeric, no spaces):', '2003-05-28 18:36:35', 'achat'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_user_logged_in', 'User %s has logged in.', '2003-05-15 16:02:29', 'chat display system login message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_user_logged_out', 'User %s has been logged out due to inactivity.', '2003-05-15 16:02:16', 'chat logout system message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chat_you', 'you', '2003-05-15 13:53:10', 'chat users online list'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'checked', 'Checked', '2006-06-26 14:55:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'child_of', 'Child of: %s', '2004-01-16 13:21:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'choice', 'Choice', '2003-05-18 12:25:24', 'test manager, multiple choice question editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'chunks_not_found', '(Warning: One or more code chunks to be deleted or replaced are not found.)', '2008-10-27 11:35:01', 'patcher message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cid', 'CID', '2003-05-18 13:17:08', 'global code picker url content id indentifier'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'city', 'City', '2003-05-18 13:17:52', 'registration, edit profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'class_avg', 'Class Avg', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'click_browse_files', 'Click Browse Files to add files to the queue', '2008-07-22 16:44:21', 'Instructional text for using fluid\'\'s multiple file uploader'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'click_code', 'Click on the code or emoticon you want to use.', '2003-05-22 15:42:52', 'global code picker instructions'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'click_hide_tools', 'Click to hide tool bar', '2010-03-30 15:32:01', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'click_show_head', 'Click to customize HTML head', '2010-03-30 15:01:59', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'click_show_tools', 'Click to show tools.', '2010-03-30 14:48:25', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'close', 'Close', '2005-08-10 16:13:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'close_after_saving', 'Close after saving', '2004-05-11 11:01:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'close_file_manager', 'Close File Manager', '2004-11-25 17:34:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'close_help_window', 'Close Help Window', '2003-05-18 13:21:37', 'close help window link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'close_menus', 'Menus', '2005-03-05 18:29:54', 'Menus'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'close_related_topics', 'Related Topics', '2005-03-05 18:30:15', 'Menus'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'close_window', 'Close Window', '2004-12-08 12:04:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'codes', 'Codes', '2003-05-18 13:23:57', 'global code picker label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'code_to_replace_from', 'Code To Replace From', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'code_to_replace_to', 'Code To Replace To', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'collapse', 'Collapse', '2003-05-18 13:24:36', 'Menus'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'colors', 'Colours', '2003-05-18 13:25:02', 'global code picker label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'combination', 'Use a combination of letters, numbers and symbols', '2004-06-21 16:26:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'combine', 'Combine', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'combine_from', 'Combine From', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'combine_into', 'Combine Into', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'combine_tests', 'Combine ATutor Tests', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'combine_tests_info', 'Before combining tests, please run section above to update marks of "Combine Into Test/Survey."', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'comment', 'Comment', '2006-03-20 14:42:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'comments', 'Comments', '2006-03-20 14:36:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'comments_num', '%s Comment(s)', '2006-05-25 14:01:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'common_cartridge', 'Common Cartridge', '2008-12-02 11:57:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'common_interest', 'Common Interest', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'company', 'Company', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'completed', 'Completed', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'completed_date', 'Completed Date', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'completed_tests', 'Your Submissions', '2005-05-11 11:19:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'configuration', 'Configuration', '2005-02-07 16:04:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'confirm', 'Confirm', '2004-10-18 11:50:20', 'enrollment manager, confirm action'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'confirm_account_text', 'If you have already created an account, you will have to confirm your email address.', '2005-07-27 15:05:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'confirm_admin_create', 'Are you sure you want to create a Super Administrator account?', '2005-03-03 10:34:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'confirm_deny_instructor', 'You are about to deny the instructor request of the following user:', '2005-07-21 16:16:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'confirm_password', 'Confirm Password', '2005-03-03 10:47:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'connections', 'My Contacts', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'contact_accepted', 'ATutor Social Contact Accepted', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'contact_email', 'Contact Email', '2005-03-03 10:50:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'contact_instructor', 'Contact Course Instructor', '2003-05-18 13:29:00', 'student contact instructor form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'contact_name', 'Contact Name', '2003-05-18 13:30:04', 'student contact instructor form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'contact_request', 'ATutor Social Contact Request', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'contact_support', 'Contact Support', '2005-07-19 10:27:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'contain_only', 'May contain only letters, numbers, underscores, hyphens or periods.', '2006-07-17 11:01:46', 'registration login name format'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content', 'Content', '2003-05-18 13:32:22', 'sitemap/delete course'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'contents', 'Contents', '2003-05-18 13:35:43', 'content page table of content menu heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_editor', 'Content Editor', '2006-11-23 13:17:41', 'user prefs'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_folder_title', 'Content Folder Title', '2009-09-16 13:11:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_navigation', 'Content Navigation', '2005-03-29 12:18:45', 'menu content nav drop down'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_package', 'Content Package', '2008-12-02 11:57:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_packaging', 'Import/Export Content', '2005-04-18 11:39:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_packaging_all', 'Available on every page', '2003-10-15 10:10:17', 'option3'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_packaging_disabled', 'Content packaging has been disabled for this course.', '2007-11-27 15:29:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_packaging_none', 'Not available on any of the pages', '2003-10-15 10:09:27', 'option1'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_packaging_top', 'Available only for top level pages', '2007-11-27 15:29:26', 'option2'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_settings', 'Content Settings', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'content_usage', 'Content Usage', '2005-03-03 15:09:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'continue', 'Continue', '2006-03-21 16:22:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'control_settings', 'Control Settings', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'copyright', 'Web site engine\'s code is copyright © ATutor®', '2008-09-18 13:02:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'core', 'Core', '2005-08-30 10:17:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'correct_answer', 'Correct Answer', '2003-05-18 13:38:45', 'test manager add/edit questions'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'count', 'Count', '2003-05-18 13:40:35', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'country', 'Country', '2003-05-18 13:41:28', 'registration / profile edit'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course', 'Course', '2003-05-18 13:42:31', 'delete course warning screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'courses', 'Courses', '2003-05-18 13:43:44', 'admin users/courses manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'courses_below', 'My Courses', '2008-04-16 19:44:10', 'jump menu divider'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_available', 'Course Available', '2003-05-18 13:46:03', 'file manager, server space available'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_available_zip1', 'Course Available (before extraction)', '2003-05-18 13:46:37', 'file manager zip tool'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_available_zip2', 'Course Available (after extraction)', '2003-05-18 13:47:06', 'file manager zip tool'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_backups', 'Course Backups', '2005-03-03 10:21:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_copyright', 'Optional Copyright Notice', '2003-05-18 13:47:33', 'course copyright label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_dir_name', 'Course Directory Name', '2008-05-14 12:03:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_email', 'Course Email', '2003-05-18 13:51:48', 'taught courses table in instructor control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_enrolment', 'Enrollment Manager', '2003-10-01 18:54:09', 'instructor - course enrollment (previously ATutor Course Enrollment)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_files', 'Course Files', '2006-03-20 14:40:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_forums', 'Course Forums', '2004-11-13 19:55:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_id', 'Course ID', '2003-05-18 13:54:57', 'admin course manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_instructor', 'Course Instructor', '2003-05-18 13:57:00', 'student contact instructor form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_list', 'Course List', '2005-03-23 15:17:14', 'enrollment manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_properties', 'Course Properties', '2003-05-18 14:00:23', 'course properties, admin course manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_quota', 'Course Quota', '2003-05-18 14:01:10', 'course properties, admin course manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_stats', 'Course Statistics', '2003-05-18 14:01:59', 'delete course'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_summaries', 'Course Summaries', '2004-08-20 15:08:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_tools', 'Course Tools', '2008-11-07 09:18:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_total', 'Course Total', '2003-05-18 14:03:39', 'file manager, zip tool'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_total_zip', 'Course Total (before extraction)', '2003-05-18 14:04:10', 'file manager zip tool'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'course_to_auto_enroll', 'Courses to Enroll', '2008-03-12 11:11:57', 'Auto Enroll label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create', 'Create', '2003-05-18 14:06:29', 'links database submit button'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'created', 'Created', '2003-05-18 14:07:28', 'browse courses, control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'created_by', 'Created By', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'created_date', 'Created', '2003-05-18 14:09:05', 'admin course managers, profile editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_admin', 'Create Administrator Account', '2005-03-03 10:40:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_backup', 'Create Backup', '2004-10-15 14:19:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_backup_about', '

    Creating a backup of this course will archive all available material into a single compressed ZIP file. Once the backup file is created, it will be available through the Backup Manager for download and safe-keeping, or available to be restored back into this or any other course.

    \r\n\r\n

    Depending on the course size and available server resources, the time needed to backup this course may take some time.

    \r\n \r\n

    Note: You are currently restricted to %s backups per course.

    ', '2005-05-30 15:04:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_basic', 'Create basic announcement, content, and forum.', '2004-10-20 14:28:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_category', 'Create Category', '2005-02-18 10:28:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_course', 'Create Course', '2005-02-15 13:16:16', 'instructor control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_folder', 'Create Folder', '2003-05-18 14:12:40', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_folder_here', 'To create a folder, enter name here:', '2009-11-05 14:42:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_forum', 'Create Forum', '2005-02-16 12:23:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_group', 'Create Group', '2005-02-22 16:51:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_groups', 'Create Groups', '2006-03-21 14:57:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_group_blurb', 'Create a new group on a particular topic, then invite people to post news items or discuss the topic. ', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_module', 'Create Module', '2005-08-17 13:06:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_new_file', 'Create New File', '2005-02-22 15:33:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_new_question', 'Create New Question', '2005-03-07 16:42:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_patch', 'Create Patch', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_test', 'Create Test/Survey', '2004-11-24 10:00:42', 'instructor test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'create_user', 'Create User Account', '2005-03-10 15:12:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'creation_date', 'Creation Date', '2009-06-17 13:32:26', 'admin/users.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'credits_and_work_experience', 'Credits and Work Experience', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cron', 'Cron', '2006-03-31 15:42:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cron_config', 'Cron Set-up', '2006-04-03 14:22:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cron_url', 'The URL to your cron service is:', '2006-06-14 12:47:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cron_url_usage', 'The URL below must be used when setting-up the cron service. See the Cron Set-Up section in the ATutor Handbook for instructions.', '2006-06-14 12:49:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'current_course_size', 'Current course size', '2003-10-07 15:41:36', 'admin course info'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'current_location', '(Current location)', '2004-01-16 13:22:03', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'current_path', 'Path to Current Directory:', '2003-05-18 14:13:52', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'curren_tests_surveys', 'Ongoing Tests & Surveys', '2005-01-03 09:57:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cursive', 'Cursive', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'custom', 'Custom', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'customized_head', 'Customized Head', '2008-04-29 15:57:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'customized_head_note', 'Edit your own styles or javascript', '2008-05-01 08:57:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'custom_grade_scale', 'Custom Grade Scale', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'custom_icons', 'Custom Icons', '2008-08-21 10:38:11', 'course properties'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'custom_test_message', 'Include a message to appear along with the link to the test.', '2008-10-10 15:30:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'cyan', 'Cyan', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'd', 'D', '2007-01-10 13:48:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'danish-dsl', 'Danish-DSL', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'dark_gray', 'Dark Gray', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'database', 'Database', '2007-07-26 13:39:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'database_table', 'Database Table', '2005-03-03 12:14:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date', 'Date', '2003-05-19 17:44:42', 'global date label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_apr', 'Apr', '2003-05-20 12:32:27', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_april', 'April', '2003-05-20 12:28:03', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_aug', 'Aug', '2003-05-20 12:33:34', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_august', 'August', '2003-05-20 12:29:12', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_created', 'Date Created', '2003-05-19 17:45:18', 'view profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_dec', 'Dec', '2003-05-20 12:34:03', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_december', 'December', '2003-05-20 12:30:35', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_feb', 'Feb', '2003-05-20 12:32:12', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_february', 'February', '2003-05-20 12:27:33', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_fri', 'Fri', '2003-05-20 12:26:47', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_friday', 'Friday', '2003-05-20 12:24:40', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_jan', 'Jan', '2003-05-20 12:32:05', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_january', 'January', '2003-05-20 12:27:11', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_jul', 'Jul', '2003-05-20 12:33:27', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_july', 'July', '2003-05-20 12:28:56', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_jun', 'Jun', '2003-05-20 12:33:21', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_june', 'June', '2003-05-20 12:28:41', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_mar', 'Mar', '2003-05-20 12:32:22', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_march', 'March', '2003-05-20 12:27:48', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_may', 'May', '2003-05-20 12:28:20', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_may_short', 'May', '2003-05-20 12:33:14', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_mon', 'Mon', '2003-05-20 12:26:18', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_monday', 'Monday', '2003-05-20 12:24:00', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_nov', 'Nov', '2003-05-20 12:33:56', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_november', 'November', '2003-05-20 12:30:20', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_oct', 'Oct', '2003-05-20 12:33:49', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_october', 'October', '2003-05-20 12:30:02', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_of_birth', 'Date of birth', '2004-06-24 12:31:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_sat', 'Sat', '2003-05-20 12:26:55', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_saturday', 'Saturday', '2003-05-20 12:24:48', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_sep', 'Sep', '2003-05-20 12:33:42', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_september', 'September', '2003-05-20 12:29:44', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_sun', 'Sun', '2003-05-20 12:26:09', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_sunday', 'Sunday', '2003-05-20 12:23:52', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_taken', 'Date Taken', '2003-05-19 17:46:06', 'My Tests'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_thu', 'Thu', '2003-05-20 12:26:40', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_thursday', 'Thursday', '2003-05-20 12:24:32', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_tue', 'Tue', '2003-05-20 12:26:24', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_tuesday', 'Tuesday', '2003-05-20 12:24:12', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_wed', 'Wed', '2003-05-20 12:26:32', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'date_wednesday', 'Wednesday', '2003-05-20 12:24:21', 'date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'day', 'Day', '2004-06-24 15:45:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'days', 'days', '2007-02-21 14:04:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'default', 'Default', '2003-05-19 17:47:07', 'edit header/admin course manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'default_announcement', 'This is a welcome announcement. You can access additional help by using the Help link available throughout ATutor.', '2005-05-11 14:52:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'default_description', 'Default Description', '2006-03-22 11:47:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'default_language', 'Default Language', '2005-03-03 10:50:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'default_modules', 'Default Course Tools', '2010-03-14 09:52:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'default_preferences', 'Default Preferences', '2005-10-05 12:19:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'default_side_menu', 'Default Side Menu', '2005-10-04 11:33:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'define_pretest', 'Prerequisite Tests.', '2009-11-13 15:43:11', 'create/edit folder'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'define_resource_type', 'Define resource type', '2008-09-08 14:18:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'degree', 'Degree/Program/Courses', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'degrees', 'Degrees', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete', 'Delete', '2003-05-19 18:25:41', 'global delete label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'deleted', 'Deleted', '2003-05-19 18:26:45', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_admin', 'Delete Administrator Account', '2005-03-03 10:48:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_announcement', 'Delete Announcement', '2003-05-19 18:27:52', 'delete news screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_applications', 'Delete applications', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_category', 'Delete Category', '2003-05-19 18:28:24', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_content', 'Delete Content', '2003-05-19 18:29:01', 'delete content screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_course', 'Delete Course', '2003-05-19 18:29:37', 'delete course screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_forum', 'Delete Forum', '2003-05-19 18:30:40', 'instructor delete forum screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_from', 'Delete From', '2005-03-03 12:16:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_glossary', 'Delete Glossary Term', '2005-03-07 11:33:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_grade_scale', 'Delete Grade Scale', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_group', 'Delete Group', '2005-01-15 16:01:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_language', 'Delete Language', '2003-06-05 18:59:51', 'admin language manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_link', 'Delete Link', '2005-03-29 14:09:36', 'links manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_module_data', 'Also delete module data', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_package', 'Delete Package', '2005-05-17 12:06:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_patch', 'Delete Patch', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_poll', 'Delete Poll', '2004-06-14 16:16:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_post', 'Delete Post', '2006-06-14 11:53:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_profile', 'Delete Profile', '2004-12-06 11:53:06', 'admin/error_logging.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_results', 'Delete Test Results', '2003-05-19 18:31:51', 'test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_selected_package_s', 'Delete selected packages', '2005-07-19 09:54:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_test', 'Delete Test/Survey', '2004-08-25 14:09:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_this_file', 'Delete This File', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_this_folder', 'Delete This Folder', '2009-11-25 10:53:28', 'Content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_this_page', 'Delete This Page', '2005-03-31 14:18:22', 'content page shortcuts'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_thread', 'Delete Thread', '2003-05-19 18:35:27', 'forums'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'delete_user', 'Delete User', '2003-05-19 18:35:55', 'admin user manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'deny', 'Deny', '2005-03-30 15:26:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'deny_instructor_request', 'Deny Instructor Request', '2005-07-21 16:17:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'dependent_patches', 'Dependent Patches', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'dependent_patch_id', 'Dependent Patch ID', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'depth_reached', 'Directory depth limit has been reached.', '2003-05-19 18:36:54', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'description', 'Description', '2003-05-19 18:37:34', 'global description label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'detailed_profile', 'Detailed Profile', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'details', 'Details', '2003-05-19 18:38:03', 'instructor taught course table of the control panel'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'detail_view', 'Detail View', '2009-06-30 16:13:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'dictionary', 'Dictionary', '2008-09-09 09:03:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'direction', 'Direction', '2004-11-20 19:36:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'directory', 'Directory', '2005-03-09 15:28:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'directory_name', 'Directory Name', '2003-05-19 18:38:42', 'file manager zip utility'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'directory_total', 'Directory Total', '2003-05-19 18:39:15', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'disable', 'Disable', '2004-10-15 15:46:20', 'to disable a theme in theme manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'disabled', 'Disabled', '2005-04-01 10:39:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'disable_syndicate', 'Disable Announcements syndication', '2005-01-04 09:48:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'disband_group', 'Disband Group', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'discussions', 'Discussions', '2003-05-19 18:43:39', 'global text for discussions label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'disk_usage', 'Disk Usage', '2007-07-26 17:02:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'display', 'Display', '2004-08-18 12:03:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'display_name_format', 'Display Name Format', '2006-09-07 10:21:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'display_name_format_fl', '%2$s (%1$s)', '2006-08-21 15:29:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'display_name_format_fst', '%2$s %3$s %4$s', '2006-08-21 15:27:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'display_name_format_fstl', '%2$s %3$s %4$s (%1$s)', '2006-08-21 15:28:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'display_name_format_l', '%s', '2006-08-21 15:25:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'display_name_format_lf', '%1$s (%2$s)', '2006-08-21 15:29:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'display_name_format_lfst', '%1$s (%2$s %3$s %4$s)', '2006-08-21 15:30:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'display_settings', 'Display Settings', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'dob', 'Date of Birth', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'donate', 'Donate', '2008-09-12 10:51:45', 'admin home'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'donate_text', 'The ATRC is a not-for-profit organization that relies on community support to maintain ATutor, and keep it free. If you have found ATutor useful, please consider making a donation.', '2008-09-12 10:53:29', 'admin home'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'done', 'Done', '2003-05-19 18:44:34', 'enrolment manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'download', 'Download', '2004-04-30 15:35:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'download_file', 'Download File', '2005-01-10 15:40:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'download_test_csv', 'Download CSV of Results', '2005-05-30 14:32:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'drag', 'Drag', '2008-05-22 09:15:04', 'alt text for fluid theme drag icon'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'due', 'Due', '2006-03-20 14:38:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'due_date', 'Due Date', '2006-03-23 11:36:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'duration', 'Total Duration', '2005-03-01 12:40:00', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'duration_sec', 'Duration (sec)', '2003-05-19 18:45:37', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'e', 'E', '2007-01-10 13:48:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit', 'Edit', '2004-05-26 18:31:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'editor', 'Editor', '2003-10-22 15:01:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'editor_properties_instructions', 'Select the radio button of the content you want to move. Use the %1$s and %2$s buttons to place this topic after or before the selected location, respectively. Use the %3$s button to add this topic as a child of the selected location.', '2009-09-16 13:13:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'editor_properties_insturctions_related', 'Add Related Topics by selecting the topic from the list.', '2004-02-11 12:33:28', 'editor properties instructions'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_admin', 'Edit Administrator Account', '2005-03-03 11:06:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_announcement', 'Edit Announcement', '2003-05-19 18:47:32', 'instructor announcement editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_backup', 'Edit - %s', '2004-10-19 12:46:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_category', 'Edit Category', '2003-05-19 18:49:44', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_contact', 'Edit Alternate Contact', '2009-12-03 15:38:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_content', 'Edit Content', '2003-05-19 18:50:07', 'instructor content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_content_folder', 'Edit Content Folder', '2009-11-25 11:36:02', 'Content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_education', 'Edit Education', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_forum', 'Edit Forum', '2003-05-19 18:50:54', 'instructor forum editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_for_minutes', 'for another %s minutes', '2007-02-23 11:53:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_glossary', 'Edit Glossary Term', '2003-05-19 18:54:15', 'instructor edit glossary term screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_grade_scale', 'Edit Grade Scale', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_group', 'Edit Group', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_language', 'Edit Language', '2005-01-21 18:24:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_link', 'Edit Link', '2003-05-19 18:54:44', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_marks', 'Edit Marks', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_patch', 'Edit Patch', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_personal', 'Edit Personal Information', '2009-12-07 13:43:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_poll', 'Edit Poll', '2004-06-14 14:53:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_position', 'Edit Position', '2009-05-28 14:21:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_post', 'Edit Post', '2003-09-25 12:54:59', 'instructor edit forum post'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_profile', 'Edit Profile', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_question', 'Edit Question', '2004-12-14 17:58:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_representation', 'Edit Representative', '2009-12-03 15:42:24', 'social profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_test', 'Edit Test/Survey', '2004-08-25 14:08:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_tests', 'Edit Tests/Assignments', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_this_page', 'Edit This Page', '2005-03-31 14:16:42', 'content page shortcuts'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_user', 'Edit Member', '2004-03-29 11:49:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'edit_websites', 'Edit Websites', '2009-05-28 14:21:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'education', 'Education', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'email', 'Email', '2003-05-19 19:07:59', 'global email label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'email_address', 'Email Address', '2003-05-19 19:09:06', 'profile, password reminder'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'email_again', 'Email Address Again', '2009-06-03 15:33:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'email_approvals', 'Email me when new enrollments require approval.', '2004-01-22 14:57:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'email_confirmation_message', 'You have registered for an account on %1s. Please finish the registration process by confirming your email address by using the following link: %2s .', '2005-06-24 15:32:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'email_confirmation_message2', 'To finish changing your account\'s email address on %1s, please confirm your email address by using the following link: %2s .', '2006-05-11 16:08:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'email_confirmation_subject', 'Email Confirmation', '2005-03-22 11:35:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'email_enrollment_message', 'An enrolment request has been made in the course %s.', '2010-01-07 13:06:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'email_enrollment_subject', 'Course Enrolment', '2010-01-07 13:06:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'emoticons', 'Emoticons', '2003-05-22 15:43:20', 'code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'empty', 'empty', '2003-05-19 19:10:56', 'prefs page, menu options'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'empty_gradebook', 'Gradebook is empty.', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enable', 'Enable', '2004-10-15 15:52:29', 'enable button in theme'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enabled', 'Enabled', '2005-05-06 12:49:07', 'theme'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enable_mail_queue', 'Enable Mail Queue', '2006-04-03 14:21:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enable_sco_rte', 'Enable the SCORM 1.2 RTE', '2005-05-10 10:52:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enable_syndicate', 'Enable Announcements syndication via RSS', '2005-01-04 10:27:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enable_uploader', 'Enable multi-file uploader tool', '2008-07-24 16:19:54', 'Used for toggling between Fluid\'s Uploader and the basic uploader'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'encyclopedia', 'Encyclopedia', '2008-09-10 12:25:52', 'tool prefs'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'end_date', 'End Date', '2003-05-19 19:13:46', 'mytests'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'end_on', 'End on', '2007-07-16 14:26:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enroll', 'Enroll', '2003-05-19 19:16:50', 'link text for enroll function'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrolled', 'Enrolled', '2003-05-19 19:18:28', 'control centre, delete course, admin course manager, text label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrolled_list_includes_assistants', 'Enrolled Students (includes Assistants)', '2004-10-15 11:56:23', 'enrolled students list in list exporter in enrollement manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrolled_privileges', 'Enrolled With Privileges', '2005-06-10 10:58:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrollment', 'Enrollment', '2005-12-07 11:19:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enroll_me', 'Enroll Me', '2005-04-14 13:15:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enroll_to_post', 'You must be enrolled to post to the forums.', '2004-12-09 11:07:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrolment', 'Enrollment', '2006-10-18 11:47:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrol_login', 'You must login to your course to approve the new enrollment request using the Enrollment section.', '2005-07-05 12:15:39', 'instructor enrolment screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrol_message3', 'ATutor Course Enrollment', '2003-04-21 15:03:49', 'context'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrol_messagenew', 'An enrollment request was made for the ATutor course %s. Log into your course at %s , go to Manage and then to the unenrolled tab in the Enrollment Manager.', '2005-03-29 12:23:47', 'instructor enrolment email notice'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrol_message_approved', 'Your enrollment request for the course %1s has been approved.\r\nLog into ATutor, then select the course from My Courses or use the login link below:\r\n\r\n%2s', '2005-03-30 11:49:50', 'email message sent to enrollee once approved'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrol_message_subject', 'Course enrollment approved', '2003-05-20 11:18:15', 'enroll email approval'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enrol_msg', 'A new enrollment request has been made for your course %s.', '2005-07-05 12:15:49', 'instructor email enrollment notification message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'entertainment_arts', 'Arts and Entertainment', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enter_chat', 'Enter the Chat', '2003-05-27 14:20:24', 'achat entry page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enter_course', 'Enter Course', '2005-03-14 15:15:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'enter_edit_mode', 'Enter Edit Mode', '2009-09-16 13:12:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'entire_course', 'Entire Course', '2006-07-24 10:44:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'error', 'Error', '2003-05-19 20:20:07', 'global error label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'error_logging', 'Error Logging', '2005-02-07 16:11:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'everyone', 'Everyone', '2004-12-07 17:54:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'existing_type', 'Existing Type:', '2006-03-22 15:11:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'exit_edit_mode', 'Exit Edit Mode', '2009-09-16 13:12:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'expand', 'Expand', '2003-05-19 20:20:35', 'menu toggle alt text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'experimental', 'Experimental', '2008-12-02 11:58:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'expertise', 'Expertise', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'expired', 'Expired', '2003-05-19 20:21:08', 'mytests'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'expire_never', 'Never expire', '2007-02-13 15:38:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'expire_on', 'Expire on', '2007-02-13 15:38:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'expiry_date', 'Expiry Date', '2007-02-13 15:37:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'export', 'Export', '2003-05-19 20:21:32', 'instructor control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'export_content', 'Export Content', '2003-10-03 11:00:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'export_content_info', 'Export content as an IMS or SCORM conformant content package, or as an IMS Common Cartridge. Import the zipped package or cartridge into another ATutor system or another course, or into another conformant LMS or LCMS.', '2009-11-12 16:52:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'export_content_package_what', 'What to export', '2003-10-14 11:11:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'export_content_text', 'Download content, unzip it onto your computer, then click "index.html" to view.', '2009-07-02 14:15:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'export_entire_course_or_chap', 'Entire course, or select a chapter below', '2003-10-07 15:14:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'export_marks_info', 'To simplify the import process, you can export an empty csv file on the test you want to import, fill in the marks, import back into ATutor.', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'external_help', 'External Help', '2005-03-17 10:31:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'external_tests', 'External Tests', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'extra', 'Extra', '2005-09-21 13:35:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'extract', 'Extract', '2003-05-19 20:23:33', 'file manager zip utility'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'extract_archive', 'Extract Archive', '2003-05-19 20:23:58', 'file manager zip utility'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'extract_tip', 'Tip: Use an empty directory name to extract the contents into the current directory.', '2003-05-19 20:24:27', 'file manager zip utility'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'f', 'F', '2007-01-10 13:48:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'factory_default', 'Reset to Factory Defaults', '2008-10-01 10:53:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fail_feedback', 'Fail Feedback', '2008-03-10 11:49:51', 'Test/Survey Property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'false', 'False', '2003-05-20 08:52:19', 'textmanager, mytests t/f label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fantasy', 'Fantasy', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'faq', 'Frequently Asked Questions (FAQ)', '2005-10-19 12:41:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'farchive', 'Forum Archiver', '2008-11-07 10:41:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'farchive_export', 'Export Forum', '2008-11-07 10:42:18', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'farchive_select_forum', 'Select Forum to Export:', '2008-11-07 10:42:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'feedback', 'Feedback', '2003-05-20 09:09:40', 'global feedback label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'female', 'Female', '2003-05-20 09:18:03', 'registration, profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fg_colour', 'Foreground Colour', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'field', 'Area of Study', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file', 'File', '2003-05-20 09:18:31', 'file manager, zip tool'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'filemanager_date_format', '%%Y-%%m-%%d %%H:%%i', '2009-06-30 16:05:29', 'file manager date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'files', 'Files', '2005-08-16 15:27:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file_manager', 'File Manager', '2003-05-20 09:27:19', 'global file manager label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file_manager_frame', 'File Manager Frame', '2005-01-31 11:14:37', 'viewing files in filemanger frame'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file_manager_new', 'Create a New File', '2004-10-29 14:53:13', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file_name', 'File Name', '2004-10-15 12:08:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file_placeholder', 'File Name Placeholder', '2008-07-22 16:44:59', 'Placeholder text used in Fluid\'\'s multiple file uploader.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file_size', 'File Size', '2004-10-14 13:57:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file_storage', 'File Storage', '2006-03-20 14:36:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file_storage_version_control', 'Maintain File Storage Version Control', '2006-03-29 12:43:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'file_to_read', 'File To Read', '2006-04-13 11:03:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fill_groups', 'Fill Groups', '2006-03-22 11:52:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fill_groups_randomly', 'Fill groups randomly upon creation.', '2006-03-22 11:53:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'filter', 'Filter', '2005-03-30 14:52:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'final_score', 'Final Score', '2007-03-16 14:10:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'find_gadgets', 'Find Gadgets', '2009-07-13 14:55:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'find_results_in', 'Find results in', '2004-08-18 12:02:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'first_name', 'First Name', '2003-05-20 09:58:26', 'registration, profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fix_content_ordering', 'Fix Content Ordering', '2004-10-18 14:03:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fix_content_ordering_text', 'Occasionally, content pages can get disordered due to miscalculation. This is an ongoing issue that will be resolved in an upcoming version of ATutor but until then, we have made a patch available that can be used to fix the issue. If you are experiencing this problem, please use the button below.', '2005-03-10 11:19:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'flowplayer', 'Flowplayer', '2010-03-18 11:46:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'focus', 'Focus', '2008-10-06 13:41:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'folder', 'Folder', '2003-05-20 10:05:51', 'file manager, zip utility'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'font_face', 'Font Face', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'font_size', 'Font Size', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'formatting', 'Formatting', '2003-05-20 10:07:25', 'add/ edit content, news, export'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'form_editor', 'Atutor Form Editor', '2005-03-11 10:31:45', 'Form editor for test questions'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'form_focus', 'Form Focus On Page Load', '2005-03-29 12:38:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forum', 'Forum', '2004-04-16 10:54:23', 'Forum list header'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forums', 'Forums', '2003-05-20 10:08:05', 'global forums label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forum_date_format', '%%D %%M %%j %%H:%%i', '2004-05-26 15:18:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forum_email_links', 'All email addresses are made into links.', '2003-05-20 10:11:14', 'forums email address links instruction'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forum_general_discussion', 'General Discussion', '2004-12-16 11:07:19', 'default forum in forum list'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forum_html_disabled', 'HTML is disabled.', '2003-05-20 10:11:42', 'forum compose message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forum_links', 'All words starting with http:// are made into links.', '2003-05-20 10:12:23', 'forum compose link creation instruction'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forum_new_submsg', '*DO NOT REPLY TO THIS MESSAGE*\r\nA discussion thread in the ATutor course "%s" has recieved a reply. Login to access the "%s" forum and view the "%s" thread. Click and view the message at: %s', '2009-12-16 11:51:02', 'forum email message to thread subscribers'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forum_posts', 'Forum Posts', '2005-03-16 14:59:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forum_reply_to', 'Replying To', '2003-05-20 10:12:48', 'forum reply to label for quoted messages'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'forward', 'Forward', '2007-02-20 16:21:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'french', 'French', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'french-lsf', 'French-LSF', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'friends', 'My Contacts', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'friends_of_friends', 'Contacts of Your Contacts', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'from', 'From', '2003-05-20 10:17:19', 'global from label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'from_atutor', 'This message was sent through the ATutor system from course %s.', '2003-05-20 10:19:30', 'instructor/admin contact for email message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'from_email', 'From Email', '2003-05-20 10:21:35', 'instructor/admin contact form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'from_name', 'From Name', '2003-05-20 10:22:02', 'instrcutor/admin contact from'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fs_comment', '%s Comment', '2006-06-29 10:09:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fs_comments', '%s Comments', '2006-06-29 09:55:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fs_revision', '%s Revision', '2006-06-29 10:05:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fs_revisions', '%s Revisions', '2006-06-29 09:52:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'full_name', 'Full Name', '2006-03-27 15:55:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'fwd', 'Fwd', '2007-02-20 16:20:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'g', 'G', '2007-01-10 13:48:18', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'gender', 'Sex', '2003-05-20 10:22:19', 'registration, profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'general_help', 'For guidance on using ATutor see the official ATutor Handbook.', '2005-07-05 14:07:32', 'atutor default footer'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'geography', 'Geographic', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'german', 'German', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'german-dgs', 'German-DGS', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'give_description', 'Brief description of your proposed course(s)', '2005-03-09 12:19:48', 'control centre request instructor account form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'global_more_than_10_pages', 'Global: More than 10 pages.', '2006-09-27 12:42:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'glossary', 'Glossary', '2003-05-20 10:24:33', 'global glossary label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'glossary_definition', 'Definition', '2003-05-20 10:25:13', 'glossary add/edit term'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'glossary_related', 'Related Term', '2003-05-20 10:26:35', 'add/edit glossary term'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'glossary_term', 'Term', '2003-05-20 10:27:01', 'add/edit glossary term'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'glossary_terms', 'Glossary Terms', '2003-05-20 10:27:47', 'delete course feedback for terms deleted'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'glossary_term_limit', 'This term exceeds the 60 character limit and cannot be added.', '2005-12-12 11:39:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'go', 'Go!', '2006-04-26 13:47:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_key', 'Google Key', '2005-10-20 11:02:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_key_txt', 'To access the Google search as an embedded service, you must first create an account and obtain a license key at google.com/apis and enter it below.\r\n

    \r\nThe search can be used as an external service by clearing the key field below.', '2007-09-04 15:14:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_new_window', 'Search opens in a new window.', '2005-11-30 16:45:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search', 'Web Search', '2005-10-20 10:35:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_ajax', 'Google AJAX Search (requires license key after Dec 5th, 2006)', '2008-07-28 10:51:24', 'Google search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_attn', 'Please note that the old Google search is only applicable to those who have obtained a license key before Dec 5th, 2006; any new license key obtained after December 5th, 2006 is only applicable to the new Google AJAX search.', '2007-09-04 14:55:56', 'Google search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_books', 'Books', '2007-08-31 11:25:12', 'Google search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_images', 'Images', '2007-08-31 11:22:38', 'Google search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_local', 'Local', '2007-08-31 11:57:07', 'Google search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_news', 'News', '2007-08-31 11:23:30', 'Google search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_soap', 'Old Google Search (requires license key before Dec 5th, 2006)', '2008-07-28 10:51:46', 'Google search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_text', 'Search the Web outside this course for additional information.', '2009-07-02 14:17:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_txt', 'To search for specific words within a website, enter them below then use the \'Search\' button. If you wish to search course content, use the content search.', '2005-10-20 11:02:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_type_txt', 'Please select one of the Google search interfaces.', '2007-08-31 14:36:30', 'Google search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'google_search_videos', 'Videos', '2007-08-31 11:22:53', 'Google search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'goto_content', 'Go to content', '2004-05-11 15:18:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'goto_menu', 'Go to Menu', '2006-07-14 10:14:57', '2nd invisible link on the page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'goto_top', 'Go to Top', '2003-05-20 10:34:03', 'bypass link text in ATutor header'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'go_to_my_start_page', 'Go To My Start Page', '2008-03-10 12:02:45', 'auto enroll property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'grade', 'Grade', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'gradebook_text', 'Review marks for your tests and assignments.', '2009-07-02 14:16:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'grades_uncomparable', 'Grades are uncomparable. Choose another way to solve conflict', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'grade_already_exists', 'Conflict: Grade already exists - %s', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'grade_info', 'Note: "Grade" field can be grade defined in "Grade Scale" or percentage like 50%%.', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'grade_scale', 'Grade Scale', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'grant_write_permission', 'Please grant write permission to folders and files listed below:

    Note: To change permissions on Unix use chmod a+rw then the file name.

    ', '2008-04-21 15:08:55', 'patcher'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'graph', 'Graph', '2003-05-20 10:36:18', 'instructor course details graph label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'gray', 'Gray', '2003-05-20 10:36:32', 'global code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'green', 'Green', '2003-05-20 10:36:45', 'global code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group', 'Group', '2005-05-03 15:42:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'grouped_by_course', 'Grouped by course', '2004-08-18 12:04:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'groups', 'Groups', '2004-11-25 15:45:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'groups_create_automatic', 'Create multiple groups automatically', '2006-03-21 15:36:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'groups_create_manual', 'Create a single group manually', '2006-03-21 15:36:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'groups_text', 'Participate in group learning activities.', '2009-07-02 14:16:03', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'groups_type', 'Groups Type', '2006-03-22 11:16:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_about_private', 'Users will have to be approved by the moderator to join this group.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_about_public', 'The group is opened to all users. No approval is needed.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_admin', 'Group Moderator', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_forums', 'Group Forums', '2006-05-24 08:48:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_info', 'Group Details', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_invitation', 'ATutor Social Group Invitation', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_invitation_accepted', 'ATutor Social Group Invitation Accepted', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_joined', 'You are a member of this group.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_last_update', 'Last Update', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_last_updated', 'Last Updated ', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_logo', 'Group Logo', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_members', 'Group Members', '2005-06-10 10:34:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_name', 'Group Name', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_prefix', 'Group Prefix', '2006-03-22 11:17:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_request', 'ATutor Social Join Group Request', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_request_accepted', 'ATutor Social Group Request Accepted', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'group_type', 'Group Type', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'guest', 'Guest', '2003-05-20 10:37:10', 'name assigned to non-registered users'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'guests', 'Guests', '2003-05-20 10:38:19', 'instructor course stats/details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'guests_not_listed', 'Guests are not listed', '2003-05-20 10:38:32', 'who\'s on menu'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'guest_information', 'Guest Information', '2008-10-06 13:40:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'guest_name', 'Guest Name', '2008-10-06 13:41:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'h', 'H', '2007-01-10 13:48:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'handout_to_read', 'Handout to Read', '2006-04-13 11:58:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hand_in', 'Hand In', '2006-03-20 14:36:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'has_added_app', 'has added the %s gadget', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'has_added_group', 'has added the group %s.', '2009-07-22 14:35:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'has_invited_join', '%s has invited you to join the group %s.', '2009-07-22 14:36:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'has_joined_group', 'has joined the group %s.', '2009-07-22 14:35:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'has_requested_to', '%s has requested to join the group %s.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'has_updated_group', 'has updated the group %s.', '2009-07-22 14:35:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'have', 'Have', '2007-02-21 13:22:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'have_not', 'Have not', '2007-02-21 14:06:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'help', 'Help', '2003-05-20 10:41:08', 'global help label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hidden', 'Hidden', '2003-05-20 10:42:27', 'instructor/admin contact form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hide', 'Hide', '2005-03-15 11:37:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hide_course', 'Hide this course from the Browse Courses list', '2003-05-20 10:42:41', 'create course screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'history', 'History', '2003-05-25 08:36:00', 'context (removed after v1.1)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hits', 'Hits', '2003-05-20 10:44:14', 'links database link hit count'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hit_count', 'Hit Count', '2003-05-20 10:45:17', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hl_colour', 'Highlight Colour', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'home', 'Home', '2006-05-31 11:12:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'home_url', 'Home URL', '2005-03-03 10:50:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'horizontal', 'Horizontal', '2004-12-01 13:35:05', 'edit/create option test questions'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hot', 'Hot!', '2003-05-20 10:47:10', '(not used)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hot_thread', 'Hot very active thread!', '2003-05-20 10:47:30', 'forum message for busy thread'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hours_24', '24hr', '2003-05-20 10:48:10', 'release date picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'hour_short', 'h', '2007-07-09 12:31:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'howto_course', 'ATutor Documentation', '2005-06-01 12:32:03', 'help'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'howto_course_text', 'In addition to the ATutor Handbook bundled with ATutor, other documentation sources are available through the ATutor Documentation site.', '2005-07-06 10:04:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'how_to_solve_conflict', 'How to solve conflict', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'html', 'HTML', '2003-05-20 10:48:48', 'global HTML label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'html_only', ' Plain Text or HTML files only.', '2003-05-20 10:49:49', 'add/edit content, header'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'i', 'I', '2007-01-10 13:48:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'icon', 'Icon', '2005-03-03 11:19:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'icon_view', 'Icon View', '2009-06-30 16:13:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'id', 'ID', '2003-05-20 10:50:57', 'global ID (identification number) label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'illegal_file', 'Illegal File Type', '2003-05-20 10:52:36', 'zip utility when archive contain illegal extension'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'illegal_file_extensions', 'Illegal File Extensions', '2005-03-03 10:52:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'image', 'Image', '2003-05-20 10:53:28', 'global code picker image code link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'image_validation_text', 'In the above image there are numbers and/or letters displayed. Please type them into the following field.', '2009-07-16 09:02:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'image_validation_text2', 'This helps ensure a live person is registering on this system.', '2009-06-03 15:33:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import', 'Import', '2003-05-20 11:56:51', 'instructor import/export screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_a_new_lang', 'Import a New Language', '2003-06-04 15:34:43', 'admin language manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_content', 'Import Content', '2005-02-18 14:05:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_content_info', 'Import a conformant IMS content package or IMS common cartridge. Select a location within the existing content to import into. Or enter a URL to content package or common cartridge, to import directly from the Web.', '2009-11-12 16:56:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_content_package', 'Import Content Package', '2003-10-03 13:07:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_content_package_bottom_subcontent', 'As top level content, or as subcontent selected below', '2003-10-20 13:36:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_content_package_where', 'Import into', '2003-10-14 12:24:03', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_err_email_exists', 'Account exists.', '2004-01-30 15:38:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_err_email_invalid', 'Email invalid.', '2004-01-30 13:28:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_err_email_missing', 'Email missing.', '2004-01-30 13:28:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_err_full_name_exists', 'First and Last name exist.', '2006-03-28 10:40:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_err_username_exists', 'Username already taken.', '2004-01-30 13:28:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_err_username_invalid', 'Username contains invalid characters.', '2004-01-30 13:28:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_export_external_marks', 'External Marks', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_lang_howto', 'You may import additional languages into this installation of ATutor by downloading the language pack from the ATutor.ca website then uploading it using the form below, or if the drop down below is able to detect the available languages remotely then it may be used as well.', '2004-10-18 14:25:21', 'admin language manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_marks_info', 'A mark list may be imported into ATutor. Create the mark list in a comma separated values (CSV) format as follows: "firstname", "lastname", "email", "mark" with one student per line. Please leave the first line as title. The mark in CSV file can be grade or percentage like 50%%. To simplify the process, you can export the CSV file with export functionality, update the marks into exported file and import back into ATutor.', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_package', 'Import Package', '2005-05-17 12:06:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_question', 'Import Questions', '2008-09-23 13:59:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_remote_language', 'Or, import a language directly from the ATutor.ca website.', '2004-10-18 14:27:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_sep_txt', 'For auto-generated usernames, separate first and last names with:', '2004-06-22 12:07:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_test', 'Import Test', '2008-09-23 13:52:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'import_theme', 'Import Theme', '2004-10-15 15:47:13', 'import theme button in theme manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'ims_files_missing', 'In this package, the file, %1$s, is missing or misplaced.', '2009-12-07 14:25:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'ims_missing_references', 'Missing file references', '2009-12-07 16:27:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'inactive_admin', 'Inactive Administrator', '2005-03-03 11:00:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'inbox', 'Inbox', '2003-05-20 11:59:47', 'global Inbox label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'inbox_date_format', '%%l %%M %%j, %%Y - %%H:%%i', '2003-05-20 12:00:47', 'inbox screen (see www.php.net/date for codes)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'inbox_notification', 'Inbox Notification', '2005-03-16 14:40:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'info', 'Course Info', '2005-04-01 15:09:41', 'global info message box label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'initial_content', 'Initial Content', '2004-10-15 15:45:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'insert', 'Insert', '2003-05-20 12:02:16', 'instructor add content screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'insert_into', 'Insert Into', '2005-03-03 12:15:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'install', 'Install', '2005-08-16 15:51:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'installed', 'Installed', '2008-10-28 10:58:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'installed_date', 'Installed Date', '2008-11-17 10:41:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'install_modules', 'Install Modules', '2005-08-16 16:17:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'install_module_text', 'To install a new module it must first be extracted into the %s directory for it to appear in the list below.', '2005-08-17 13:47:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'install_themes', 'Install Themes', '2008-11-14 11:47:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'install_themes_text', 'To install new themes the %s directory must be set to writeable. Use the command chmod a+w themes on Unix machines, while on Windows the web server must have write permissions on that directory.', '2005-12-14 12:36:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'institution', 'School/Institution', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructions', 'Instructions', '2005-04-05 14:25:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor', 'Instructor', '2003-05-20 12:02:59', 'global instructor label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructors', 'Instructors', '2003-05-20 12:03:37', 'admin general statistics label for number of instructors'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_contact_form', 'Instructor Contact Form', '2003-05-20 12:12:32', 'instructor contact form heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_request', 'ATutor Instructor Request', '2003-05-25 08:37:50', 'subject line in email to admin when instructor request is made'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_requests', 'Instructor Requests', '2003-05-20 12:19:52', 'admin home page heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_requests_text', 'There are %s instructor requests pending approval.', '2005-03-30 15:30:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_request_deny', 'Your ATutor instructor request has been denied.', '2003-10-06 15:14:48', 'email message to denied instructor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_request_denymsg1', 'Registration information is incomplete.', '2003-10-06 15:55:47', 'reason why denied'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_request_denymsg2', 'Inappropriate subject matter.', '2003-10-06 16:01:26', 'reason why denied'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_request_denymsg3', 'The course description requires more detail.', '2003-10-07 15:47:33', 'deny instructor request'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_request_denymsg4', 'You have requested enrollment with the wrong form.', '2003-10-07 15:52:12', 'admin deny instructor request'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_request_email_notification', 'Instructor Request Email Notification', '2005-03-03 10:51:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_request_enterdenymsg', 'Choose a message explaining why the user\'s request was denied or enter your own.', '2003-10-06 15:19:05', 'Header text for deny message text area'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'instructor_request_reply', 'Your ATutor instructor request has been approved. Go to %s to login to My Courses, then select "Create Course".', '2005-05-27 13:53:06', 'email in reply to instructor account approval'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'interests', 'Interests', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'internet_technology', 'Internet Technology', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'invalid_session', 'Not a Valid Session', '2003-05-20 12:26:29', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'invite', 'Invite', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'invite_groups', 'Invite New Group Members', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'in_minutes', 'Minutes', '2007-02-20 10:30:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'in_reply_to', 'In reply to', '2003-05-20 12:27:21', 'replyto label for quoted forum or inbox message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'irish-isl', 'Irish-ISL', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'italian', 'Italian', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'italian-lis', 'Italian-LIS', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'italic', 'Italic', '2003-05-20 12:27:44', 'global code picker link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'item', 'Item', '2007-01-10 13:46:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'j', 'J', '2007-01-10 13:48:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'japanese-jsl', 'Japanese-JSL', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'join_group', 'Join Group', '2009-06-12 10:06:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'jump', 'Jump', '2003-05-20 12:28:03', 'jump menu submit button text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'jump_codes', 'Jump over the code picker', '2003-05-20 12:29:53', 'alt text for bypass link to jump over the code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'jump_redirect', 'Direct Jump', '2004-11-30 10:03:55', 'preferecnes'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'just_social', 'Use just ATutor Social', '2009-07-17 14:11:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'kb', 'KB', '2007-12-09 12:56:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'keep_email_private', 'Keep email hidden from others.', '2006-03-28 11:29:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'keep_it_short', 'Tip: Keep it short, no spaces.', '2003-05-20 12:31:53', 'file manager suggestion for folder creation'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'keywords', 'Keywords', '2003-09-28 10:22:10', 'content add/edit'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'language', 'Language', '2003-05-20 12:32:05', 'profile, registration'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'languages', 'Languages', '2005-03-03 11:07:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lang_code', 'Language Code', '2006-09-27 14:12:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'last_accessed', 'Last Accessed', '2005-03-01 12:52:16', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'last_comment', 'Last Comment', '2003-05-20 12:33:10', 'forums, thread view column label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'last_login', 'Last Login', '2005-03-03 11:03:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'last_modified', 'Last Modified', '2003-05-20 12:36:35', 'instructor content editor link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'last_name', 'Last Name', '2003-05-20 12:37:39', 'registration, profile, admin user manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'last_post', 'Last Post', '2004-04-16 11:24:59', 'Refers to date of last post in forum'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'last_updated', 'Last updated: %s', '2006-05-19 14:54:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'latex_server', 'MimeTex Server for the LaTex service. For production purposes, please install mimeTeX on your own server. (http://www.atutor.ca/cgi/mimetex.cgi?)', '2010-03-04 08:46:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'leave_blank', 'Leave blank', '2003-05-20 12:39:07', 'mytest/test manager question option'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'leave_group', 'Leave Group', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'leave_unchanged', 'Leave unchanged', '2005-05-10 16:04:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'left', 'Left', '2004-04-27 11:00:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'left_blank', 'Left blank', '2003-05-20 12:40:14', 'mytests/test manager question result text for unanswerd question'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'legend', 'Legend', '2003-05-20 12:41:00', 'instructor course statistics/details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'license', 'License', '2005-08-16 15:27:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'light_gray', 'Light Gray', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'limit_to_group', 'Assign To Groups', '2004-12-10 14:39:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'link', 'Link', '2003-05-20 12:41:18', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'links', 'Links', '2005-02-09 13:25:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'list_add_course_list', 'Add to Course List', '2004-10-19 14:39:08', 'Enrollment manager, add students manually button'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'list_add_enrolled_list', 'Add to Enrolled List', '2004-10-19 14:51:02', 'enrollment manager, add to enrolled students list button'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'list_add_unenrolled_list', 'Add to Un-enrolled List', '2004-10-19 14:47:31', 'enrollment manager, add to unenrolled students list button'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'list_create_course_list', 'Create Course List', '2004-10-15 12:04:06', 'create list of students function in enrollement manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'list_export_course_list', 'Export Course List', '2003-09-16 10:15:29', 'enrol admin'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'list_import_course_list', 'Import Course List', '2003-09-16 09:35:35', 'import course list'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'list_import_howto', 'A course enrollment list may be imported into ATutor. Create the course list in a comma separated values (CSV) format as follows: "firstname", "lastname", "email" with one student per line. New students will receive their access instructions by email.', '2004-02-05 10:22:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_agree', 'Agree', '2004-08-12 15:45:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_always', 'Always', '2004-08-26 12:23:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_disagree', 'Disagree', '2004-08-12 15:46:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_excellent', 'Excellent', '2004-08-12 15:42:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_fair', 'Fair', '2004-08-12 15:43:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_good', 'Good', '2004-08-12 15:42:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_important', 'Important', '2004-08-26 12:26:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_little_importance', 'Of Little Importance', '2004-08-26 12:26:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_mod_important', 'Moderately Important', '2004-08-26 12:26:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_never', 'Never', '2004-08-26 12:25:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_occasionally', 'Occasionally', '2004-08-26 12:24:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_poor', 'Poor', '2004-08-12 15:43:18', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_rarely', 'Rarely', '2004-08-26 12:24:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_strongly_agree', 'Strongly Agree', '2004-08-12 15:45:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_strongly_disagree', 'Strongly Disagree', '2004-08-12 15:46:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_undecided', 'Undecided', '2004-08-26 11:46:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_unimportant', 'Unimportant', '2004-08-26 12:27:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_very_frequently', 'Very Frequently', '2004-08-26 12:23:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_very_good', 'Very Good', '2004-08-12 15:42:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_very_important', 'Very Important', '2004-08-26 12:25:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_very_poor', 'Very Poor', '2004-08-12 15:43:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lk_very_rarely', 'Very Rarely', '2004-08-26 12:24:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'locale', 'Locale', '2004-11-20 19:35:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'local_network', 'Local Network', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'location', 'Location', '2005-03-04 14:06:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lock_no_post', 'No posting, but allow reading.', '2003-05-20 12:46:36', 'forum instructor lock text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lock_no_post1', 'This thread is locked from posting.', '2003-05-20 12:46:45', 'forum instructor lock text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lock_no_read', 'No posting and no reading.', '2003-05-20 12:46:56', 'context'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lock_no_read1', 'This thread is locked from reading and posting.', '2003-05-20 12:47:24', 'forum thread display lock message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lock_submit', 'Apply Lock', '2003-05-20 12:47:50', 'text for instructor forum lock button'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'lock_thread', 'Lock Thread', '2003-05-20 12:48:14', 'Alt text for forum instructor lock icon'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'logged_in_within', 'logged in within', '2007-02-21 13:37:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'login', 'Login', '2006-07-27 17:35:30', 'global login label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'login_into_atutor', 'Log into your ATutor Account', '2003-05-20 12:51:48', 'login after enrollment message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'login_name', 'Login Name', '2003-05-20 12:52:32', 'control centre account information label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'login_name_or_email', 'Login Name or Email', '2006-03-28 14:21:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'login_statistics', '%s login statistics for %s', '2003-05-31 14:49:29', 'course login details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'login_text', 'Enter your login name or email address, along with your password, to access your courses and networking area.', '2009-11-20 16:12:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'logout', 'Log-out', '2003-05-20 12:53:44', 'global logout link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'log_file_bundle', 'Log File Bundle', '2005-03-10 16:36:31', 'error logging - email subject'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'ltr', 'Left to Right', '2004-11-20 19:37:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'magenta', 'Magenta', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'mail_queue_cron', 'You must set-up the cron to use this feature.', '2006-04-03 14:21:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'maintainers', 'Maintainers', '2005-08-16 15:26:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'main_navigation', 'Main Navigation', '2005-03-07 12:09:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'malaysian-msl', 'Malaysian-MSL', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'male', 'Male', '2003-05-20 12:55:53', 'registration, profile, admin user manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'manage', 'Manage', '2005-02-23 12:05:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'manage_languages', 'Manage Existing Languages', '2004-09-23 16:30:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'manage_lang_howto', 'Below are all the languages currently available in this installation of ATutor. You may export the language as an ATutor language pack or delete it from the installation.', '2004-10-18 14:23:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'manage_links', 'Manage Links', '2006-05-24 11:24:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'mark', 'Mark', '2003-05-20 12:56:19', 'mytests/test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'marked_label', 'Marked (%s)', '2005-05-12 10:25:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'marks', 'marks', '2003-05-20 12:56:34', 'mytests/test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'mark_alumni', 'Mark Alumni', '2004-10-25 12:24:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'mark_statistics', 'Submission Statistics', '2004-08-26 13:27:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'master_list_authentication', 'Authenticate Against A Master Student List', '2005-03-31 13:54:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'master_not_in_list', 'If existing user not in new list', '2005-05-10 16:04:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'master_student_list', 'Master Student List', '2005-04-04 10:39:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material', 'Material', '2004-10-14 14:10:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material_announcements', 'Announcements (%s)', '2004-10-14 14:14:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material_content_pages', 'Content Pages (%s)', '2004-10-14 14:12:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material_files', 'Files (%s)', '2004-10-14 14:22:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material_glossary', 'Glossary (%s)', '2004-10-14 14:22:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material_groups', 'Groups (%s)', '2004-11-25 15:43:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material_links', 'Links (%1$s categories, %2$s links)', '2004-10-14 14:18:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material_polls', 'Polls (%s)', '2004-10-14 14:21:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material_select_all', 'Select All', '2004-10-14 14:11:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'material_stats', 'Statistics (%s days)', '2004-10-14 14:23:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'maximum', 'Maximum', '2003-05-20 12:59:01', 'instructor course statistics/details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'maximum_course_float', 'Maximum Course Float', '2005-03-03 10:52:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'maximum_course_size', 'Maximum Course Size', '2005-03-03 10:51:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'maximum_file_size', 'Maximum File Size', '2005-03-03 10:51:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'maximum_login_attempt', 'Maximum Login Attempts', '2009-06-03 15:33:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'max_backups_reached', 'You have reached the maximum number of backups allowed.', '2004-10-15 14:16:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'max_file_size', 'Max File Size', '2003-05-20 12:59:52', 'admin course manager properties'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'max_file_size_system', 'Maximum system allows', '2003-10-14 14:32:52', 'php\'s max file size'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'mb', 'MB', '2007-07-26 14:05:45', 'short for MB (megabytes)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'media', 'Media', '2007-06-15 14:41:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'members', 'Members', '2003-05-20 13:00:39', 'instructor course statistics/details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'member_id', 'Member ID', '2003-05-20 13:01:18', 'admin user manager column heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'member_stats', 'Student Specific Usage', '2005-03-03 15:25:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'menu', 'Menu', '2003-05-20 13:02:17', 'preferences option'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'menu_menu', 'Content Navigation', '2005-03-30 10:13:27', 'context (?)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'message', 'Message', '2003-05-20 13:06:56', 'inbox send message screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'message_board', 'Message Board', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'message_notification', 'Message Notification', '2005-03-14 15:31:15', 'preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'mexican-lsm', 'Mexican-LSM', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'minimum', 'Minimum', '2003-05-20 13:08:57', 'instructor course statistics/details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'minutes', '%s min.', '2006-03-31 15:41:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'minute_short', 'm', '2007-07-09 12:31:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'missing', 'Missing', '2005-10-05 14:02:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'missing_content', 'Missing Content', '2005-03-07 15:25:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'missing_info', 'Missing Info', '2005-08-17 13:23:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'modules', 'Modules', '2005-08-16 13:40:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'module_install_directory', 'The following directory must be created for this module to install and function: %s.', '2005-09-22 14:14:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'module_name', 'Module Name', '2005-08-17 13:11:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'module_uninstall', 'Uninstall Module', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'mod_rewrite', 'mod_rewrite', '2008-05-14 12:05:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'monospaced', 'Monospaced', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'month', 'Month', '2004-06-24 15:45:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'more_options', 'More options...', '2007-02-21 11:48:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'move', 'Move', '2004-01-15 13:31:18', 'editing content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'move_down', 'Move Down', '2005-03-30 15:54:20', 'modules'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'move_thread', 'Move Thread', '2006-09-06 13:14:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'move_thread_to', 'Move thread to:', '2006-09-06 13:15:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'move_to_inbox', 'Move to Inbox', '2007-02-22 13:09:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'move_up', 'Move Up', '2005-08-02 15:44:46', 'modules'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'music', 'Music', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'mutual_connections', 'Mutual Connections', '2009-06-12 15:42:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'myown_patches', 'My Own Patches', '2008-04-21 15:08:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'mysql_version', 'MySQL Version', '2007-07-26 13:40:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'my_account', 'My Account', '2005-03-28 14:04:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'my_courses', 'My Courses', '2004-04-15 12:05:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'my_enrolled_courses', 'My enrolled courses', '2004-08-18 12:03:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'my_files', 'My Files', '2006-03-20 14:40:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'my_friends_only', 'Only my contacts', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'my_groups', 'My Network Groups', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'my_start_page', 'My Start Page', '2005-02-08 10:58:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'my_tests', 'My Tests & Surveys', '2010-03-18 12:33:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'my_tracker', 'My Tracker', '2003-05-20 13:12:27', 'global my tracker heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'na', 'N/A', '2003-05-20 13:12:48', 'short form for "not applicable"'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'name', 'Name', '2003-05-20 13:31:04', 'file manager, zip utility column heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'name_in_english', 'Language name in English', '2004-11-20 19:41:16', 'admin add language'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'name_in_language', 'Language name translated', '2004-11-20 19:39:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'native-american', 'Native-American', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'navigation', 'Navigation', '2008-09-09 09:03:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'nav_path', 'Navigation Path for User', '2003-05-20 13:31:22', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'nav_tendencies', 'Navigation Tendencies for User', '2003-05-20 13:32:03', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'netherlands-ngt', 'Netherlands-NGT', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'network_home', 'My Network', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'network_updates', 'Network Activity', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'never', 'Never', '2005-03-03 11:03:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new', 'New!', '2003-05-20 13:32:52', 'forum/inbox new message indicator'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new_account_enroll', 'A user account has been created for you at %1s and you have been enrolled into the course %2s. To access this course, please log into the site. It is strongly suggested you change your password upon logging in.', '2005-06-24 15:33:45', 'Message emailed to new users as a result of enrollment.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new_account_enroll_confirm', 'You have been enrolled into the course %1s. To access this course, you must first confirm your account by using the following link: %2s. It is strongly suggested you change your password upon logging in.', '2005-06-24 15:33:17', 'If new user created during enrollment and needs confirmation.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new_file', 'New File', '2006-03-20 14:36:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new_group_invitations', 'New Group Invitations', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new_name', 'New Name', '2005-03-07 13:26:12', 'filemanager rename file'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new_thread', 'New Thread', '2003-05-20 13:37:34', 'forum'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new_type', 'New Type:', '2006-03-22 15:11:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new_user', 'New User', '2005-07-27 15:06:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'new_window', 'Links open in a new window.', '2005-10-28 12:24:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'next', 'Next', '2003-05-20 13:38:07', 'sequence arrow link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'next_topic', 'Next Topic', '2003-05-20 13:38:42', 'sequence arrow link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no', 'No', '2005-03-03 10:18:39', 'the word no, preferences, admin course manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'none', 'None', '2003-05-20 13:40:59', 'global message when no results were retrieved'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'none_available', 'None available.', '2003-05-20 13:41:46', 'instructor add/edit content, glossary'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'none_found', 'None Found.', '2005-05-18 13:12:47', 'glossary, related topis, users online'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'non_group_members', 'Non-Group Members', '2005-06-10 10:51:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'norwegian-nsl', 'Norwegian-NSL', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'notes', 'Notes', '2003-05-20 13:43:39', 'admin hime, instructor request notes column heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'note_taking', 'Note Taking', '2008-09-09 09:03:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'notification_accept_contact', '%s has been added to your ATutor Social contacts. Follow the link below to review your new contact. \r\n\r\n------\r\nSent from ATutor Social at:\r\n%s\r\n', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'notification_group_invite', '%s has invited you to join the %s group. Follow the link below to login and accept or reject the invitation. \r\n\r\n------\r\nSent from ATutor Social at:\r\n%s\r\n', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'notification_group_invite_accepted', '%s has accepted your invitation to join the %s group. Follow the link below to login to the group.\r\n\r\n------\r\nLogin to ATutor Social at:\r\n%s\r\n', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'notification_group_request', 'A request has been made to join the %s group Follow the link below to login and accept or reject the request.\r\n\r\n------\r\nLogin to ATutor Social at:\r\n%s\r\n', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'notification_group_request_accepted', 'Your request to join the %s group has been accepted. Follow the link below to login to the group.\r\n\r\n------\r\nLogin to ATutor Social at:\r\n%s\r\n', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'notification_new_contact', '%s wants to add you to as a ATutor Social contact. Follow the link below to login and accept or reject the request. \r\n\r\n------\r\nSent from ATutor Social at:\r\n%s\r\n', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'notification_new_inbox', 'You have received a new message from %s. Login to access your inbox and view the message. Login at: %s', '2005-05-17 14:20:35', 'notification message for new mail in inbox'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'notify', 'Notify', '2003-05-20 13:45:17', 'admin course properties'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'not_added_members', 'Invite Group Members', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'not_created', 'Not Created', '2005-05-10 15:23:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'not_editable', 'Not editable.', '2006-03-20 15:17:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'not_enrolled', 'Not Enrolled', '2005-03-23 14:23:18', 'user status on my courses page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'not_installed', 'Not Installed', '2008-10-28 10:58:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'not_overwrite', 'Not overwrite', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'not_specified', 'Not specified', '2003-05-12 15:26:26', 'registration gender selection'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'now_friends1', 'and %s are now contacts.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'now_friends2', 'and %s are now contacts', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_activities', 'No network activity.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_announcements', 'No Announcements Found', '2003-05-20 13:55:19', 'course homepage when no announcments exist'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_content_avail', 'No content available.', '2005-10-20 11:00:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_courses', 'There are no available courses.', '2003-05-20 13:57:26', 'browse message when no courses exist'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_courses_found', 'No courses found.', '2003-05-20 13:58:31', 'message when query resuylt in no courses found, admin course manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_course_found', 'Course not found.', '2003-10-15 14:31:39', 'course not found'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_description', 'No description available.', '2004-08-20 15:00:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_due_date', 'No due date', '2006-05-30 12:37:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_end_date', 'No end date', '2007-07-16 14:27:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_forums', 'No Forums Found', '2003-05-20 13:59:58', 'discussions message when no forums exist'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_friends', 'You have no contacts yet.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_gadgets_installed', 'You have not installed any gadgets yet.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_glossary_items', 'No glossary terms found.', '2005-03-07 10:52:11', 'edit glossary'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_groups_yet', 'You have not joined any groups yet.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_icon', 'No Icon', '2005-03-03 11:20:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_month_data', 'There is no data for this month.', '2003-05-20 14:01:42', 'instructor course statistics when nonne exist'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_pass_score', 'No pass score', '2008-03-10 11:50:35', 'Test/Survey Property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_post', 'No post found.', '2003-05-20 14:02:44', 'forums message when attempting to view a post that no longer exists'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_questions', 'No questions found.', '2003-05-20 14:04:01', 'test manager message when no questions exist for a test.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'No_resources', 'No resources found in this content page.', '2008-12-11 12:10:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_results_available', 'No Results Available', '2003-05-20 14:07:22', 'test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_results_yet', 'No Results Yet', '2003-05-20 14:07:32', 'my tests'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_settings', 'There is no available settings.', '2009-06-09 14:25:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_terms_found', 'There are no glossary terms being used in this content page.', '2004-01-16 10:52:51', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_tests', 'No Tests or Surveys Available', '2004-08-25 14:04:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_title', 'No title.', '2005-10-20 11:00:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'no_user_found', 'No user found', '2003-05-20 14:09:41', 'admin user amanager, profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'num', 'No.', '2003-05-20 14:10:36', 'short form for the word number, test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'number_of_groups', 'Number of Groups', '2006-03-22 11:48:03', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'number_of_members', 'Number of Members', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'number_of_students_per_group', 'Number of Students per Group', '2006-03-22 11:52:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'num_pages', '# pages', '2005-03-16 13:54:47', 'content manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'num_posts', 'Number of Posts', '2005-04-14 11:07:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'num_questions_per_test', 'questions per test.', '2004-08-23 16:13:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'num_students_currently_enrolled', 'There are %s students currently enrolled in this course.', '2006-03-22 13:02:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'num_takes_test', 'Attempts Allowed', '2004-08-26 10:34:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'num_threads', 'Number of Threads', '2005-04-14 11:07:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'ok', 'OK', '2004-01-30 11:54:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'on', 'on', '2003-05-20 14:11:28', 'admin course manager properties tracking option'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'one_page', 'One Page', '2003-05-20 14:12:22', 'test manager open ended question type'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'one_question_per_page', 'One question per page', '2007-08-20 15:46:36', 'edit/create test'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'one_sentence', 'One Sentence', '2003-05-20 14:12:46', 'test manager open ended question type'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'one_word', 'One Word', '2003-05-20 14:12:53', 'test manager open ended question type'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'ongoing', 'On Going!', '2003-05-20 14:13:23', 'mytest currently running test indicator'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'online_status', 'Online Status', '2005-03-11 16:18:30', 'directory'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'only_show_edited_terms', 'Only show edit terms.', '2006-09-25 13:08:06', 'language editor filter form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'open_file_manager', 'Open File Manager', '2004-11-25 17:31:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'optional', 'Optional', '2003-05-20 14:40:09', 'registration screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'optional_description', 'Optional Description', '2004-10-27 15:31:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'optional_feedback', 'Optional Feedback', '2004-11-24 14:33:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'option_alignment', 'Alignment', '2004-12-01 13:36:36', 'edit/create option in test questions'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'or', 'Or', '2003-05-20 14:40:26', 'global word or'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'orange', 'Orange', '2003-05-20 14:40:40', 'global code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'order', 'Order', '2005-03-04 14:07:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'organization', 'Organization', '2008-10-06 13:41:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'original_resource', 'Original resource', '2010-03-09 16:16:17', 'adapted content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'original_term', 'Original term', '2006-09-25 13:09:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'origin_page', 'Originating Page', '2003-05-20 14:40:57', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'or_groups', 'Or, Groups:', '2006-08-28 15:58:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'os', 'OS', '2007-07-26 14:04:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'other', 'Other', '2005-03-03 12:17:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'others', 'Others', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'out_of', 'Maximum Score', '2008-07-17 08:36:47', 'mytests, score out of ...'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'override', 'Override selections with this course\'s custom Colour & Font theme. ( If available. )', '2003-05-20 14:42:37', 'preferences page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'overwite_content', 'Overwrite existing material with those selected', '2004-10-15 14:18:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'overwrite', 'Overwrite', '2004-11-09 15:24:23', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'overwrite_master', 'If an existing account is using this Student ID, overwrite association with new account.', '2006-06-12 13:58:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'packaged_in', 'Content Package', '2003-10-07 18:21:34', 'apckage label in edit content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'packages', 'Packages', '2005-05-17 12:09:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'packages_auto_advance', 'Automatically start next Learning Object', '2005-05-17 12:10:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'package_settings', 'Package Settings', '2005-05-17 12:06:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'package_type', 'Package Type', '2005-05-17 12:09:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'package_type_info', 'Please select the type of package you wish to upload.', '2005-05-17 12:07:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'package_upload_file', 'Package File', '2005-05-17 12:07:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'package_upload_file_info', 'Select the package file from your computer', '2005-05-17 12:07:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'package_upload_url', 'Package URL', '2005-05-17 12:06:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'package_upload_url_info', 'or specify the URL to your web accessible package file', '2005-05-17 12:06:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'page', 'Page', '2003-05-20 14:43:17', 'forum thread pages menu, tracker pages menu'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pages_found', '%s pages found', '2004-08-20 15:09:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pages_stats', 'Page Tracking Statistics for %s.', '2003-06-08 09:20:38', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'page_error', 'An error occured while generating the paginator', '2003-05-20 14:44:01', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'page_info', 'Last Modified: %s. Revision: %s. Release Date: %s.', '2004-02-12 11:27:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'page_stats', 'Content Tracking Summary', '2003-05-20 14:44:48', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'page_title', 'Page Title', '2003-05-20 14:45:16', 'tracker column heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'page_viewed', 'Page Viewed', '2003-05-20 14:45:44', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'parent', 'Parent', '2005-03-29 13:04:13', 'categories'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'partially_uninstalled', 'Partially Uninstalled', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password', 'Password', '2003-05-20 14:45:56', 'global password word'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_again', 'Password Again', '2003-05-20 14:46:52', 'registration, edit profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_blurb', 'Enter your account\'s email address below and an email with instructions on retrieving your password will be sent to you. The email address must be the same as the one you used for\r\nregistration.', '2006-06-08 11:20:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_changed', 'Password Change', '2005-11-30 13:54:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_change_confirm', 'Your password on %s has been changed successfully. Go to %s to login.', '2006-05-09 15:28:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_change_msg', 'Your password has been altered. Please use the information below.', '2005-11-30 13:55:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_forgot', 'Forgot Password', '2006-05-09 15:00:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_new_blurb', 'Enter a new password for your account.', '2006-05-09 15:54:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_old', 'Old Password', '2006-05-11 11:34:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_reminder', 'Forgot your password?', '2006-05-09 14:29:49', 'password reminder screen heading, public login header'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_reminder_text', 'If you have lost your password, use the forgotten password feature to regain access to your account.', '2006-05-09 14:56:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'password_request2', 'Dear %1$s,\r\n\r\nYour login name is %2$s.\r\n\r\nTo set a new password, follow the link below. \r\n\r\n%4$s\r\n\r\n(If this link does not take you to the site, copy and paste it into the address bar of your internet browser)\r\n\r\nThe link will become invalid after %3$s days.', '2009-01-23 10:49:55', 'password reminder'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pass_feedback', 'Pass Feedback', '2008-03-10 11:49:19', 'Test/Survey Property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pass_score', 'Pass Score', '2008-03-10 08:46:46', 'Test/Survey property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'paste', 'Paste', '2004-12-03 10:30:28', 'form_editor in create question'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'paste_file', 'Paste From File', '2003-05-20 14:49:35', 'instructor content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'patcher', 'Patcher', '2008-04-21 15:08:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'patcher_alter_modified_files', 'The listed files are modified locally. If you choose to proceed, your local file will be modified. The original\r\nfile will be backed up before the modification. Please note that the modification to your customized code may break your customization.
    ', '2008-07-28 10:52:09', 'patcher'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'patcher_overwrite_modified_files', 'The listed files have been modified locally. If you choose to proceed, the patch file will be copied to your local machine. \r\nYou have to manually merge this file and your local copy.
    ', '2008-07-28 10:52:22', 'patcher'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'patcher_show_backup_files', 'Below is the list of the backup files created by the Patcher. After ensuring ATutor works properly with the patch, you may want to \r\ndelete these files. If ATutor does not work properly with the patch, you can always revert back to the old files by renaming the backup files \r\nto the original file names, removing the [patch_id].old portion of the file name.
    ', '2008-07-28 10:52:38', 'patcher'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'patcher_show_patch_files', 'Below is the list of the patch files copied to your computer. \r\nPlease manually merge the changes between the patch files and your local copy.
    ', '2008-07-28 10:52:51', 'patcher'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'patch_dependent_patch_not_installed', '
    Warning: There are patch dependencies, please install the listed patches first: ', '2008-04-22 14:56:15', 'patcher'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'patch_local_file_not_exist', 'Cannot proceed. The listed files do not exist on your local machine. If you renamed the file, in order to proceed, please rename back.
    ', '2008-07-28 10:53:03', 'patcher'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_add_more_photos', 'Add More Photos', '2010-03-17 15:53:59', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_albums', 'Albums', '2010-03-17 15:49:41', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_album_cover', 'Album Cover', '2010-03-17 15:57:45', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_album_description', 'Album Description', '2010-03-17 15:57:34', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_album_location', 'Album Location', '2010-03-17 15:57:24', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_album_name', 'Album Name', '2010-03-17 15:57:02', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_album_permission', 'Album Permission', '2010-03-17 16:04:31', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_album_type', 'Album Type', '2010-03-17 15:57:13', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_alt_text', 'Alternative Text', '2010-03-17 15:59:00', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_choose_profile_picture', 'Upload Profile Picture', '2010-03-25 16:00:58', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_click_here_to_edit', 'Click here to edit', '2010-03-17 16:00:54', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_click_item_to_edit', 'Click item to edit', '2010-03-17 16:01:06', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_close_upload_manager', 'Close Upload Manager', '2010-03-17 16:04:18', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_course_albums', 'Course Albums', '2010-03-17 15:58:28', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_create_album', 'Create Album', '2010-03-17 15:58:50', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_delete_album', 'Delete Album', '2010-03-17 15:51:21', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_delete_comment', 'Delete Comment', '2010-03-17 15:51:34', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_delete_photo', 'Delete Photo', '2010-03-17 15:51:47', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_delete_profile_pic_blub', 'You can remove this picture, but be sure to upload another or we will display a silhouette in its place.', '2010-03-25 16:04:19', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_delete_this_photo', 'Delete This Photo', '2010-03-17 15:53:32', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_edit_album', 'Edit Album', '2010-03-17 15:51:09', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_edit_photo', 'Edit Photo', '2010-03-17 15:50:55', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_edit_photos', 'Edit Photos', '2010-03-17 15:50:39', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_last_updated', 'Last Updated', '2010-03-17 15:58:00', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_max_memory', 'Maximum Memory Size allowed per member', '2010-03-17 16:01:42', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_memory_usage', 'Memory Usage', '2010-03-17 16:03:53', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_my_albums', 'My Albums', '2010-03-17 15:58:14', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_no_album', 'No Album Available', '2010-03-17 15:59:28', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_no_image', 'No image', '2010-03-17 15:59:52', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_no_photos', 'No Photos Available', '2010-03-17 15:59:39', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_of', 'of', '2010-03-17 16:00:19', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_open_upload_manager', 'Open Upload Manager', '2010-03-17 16:04:07', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_organize_photos', 'Organize Photos', '2010-03-17 15:53:45', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_organize_photo_blurb', 'Note: Drag photos using a mouse, or [CTRL]+[Left/Right/Up/Down Arrow] keys to rearrange them.', '2010-03-17 16:00:09', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_photo', 'Photo', '2010-03-17 15:49:57', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_photos', 'Photos', '2010-03-17 15:50:12', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_photo_gallery', 'Photo Gallery', '2010-03-17 15:49:28', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_preferences', 'Album Preferences', '2010-03-17 16:01:30', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_private', 'Private', '2010-03-17 16:04:46', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_processed', 'Processed', '2010-03-17 15:59:14', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_profile_album', 'Profile Album', '2010-03-17 15:50:26', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_redo', 'Redo', '2010-03-17 16:00:41', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_set_profile_pic', 'Make Profile Picture', '2010-03-25 12:02:04', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_shared', 'Shared', '2010-03-17 16:04:58', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_shared_albums', 'Shared Albums', '2010-03-17 15:58:39', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_undo', 'Undo', '2010-03-17 16:00:31', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_uploaded_by', 'Uploaded by', '2010-03-17 16:05:10', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_upload_blurb', 'Click "Browse" to look for the picture you wish to upload. These photos will be processed and display below. You also have the option to remove the pending photos anytime. When you are done, click "Upload".', '2010-03-17 15:56:41', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pa_write_a_comment', 'Write a comment...', '2010-03-17 16:01:18', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'peer_interaction', 'Peer Interaction', '2008-09-09 09:03:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pending', 'Pending', '2003-05-20 14:50:41', 'mytests, control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pending_approval', '(pending approval)', '2003-05-20 14:51:49', 'control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pending_enrollment', 'Pending Enrollment', '2005-06-10 10:14:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pending_friend_requests', 'Pending Friend Requests', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'people_you_may_know', 'People you may know', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'percentage_from', 'Percentage From', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'percentage_score', 'percentage score', '2008-03-10 11:50:57', 'Test/Survey Property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'percentage_to', 'Percentage To', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'period', 'Period', '2004-06-22 12:09:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'personal', 'Personal Information', '2009-12-07 13:42:47', 'Social personal info'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'personal_information', 'Personal Information', '2003-05-20 14:53:12', 'registration, profile edit'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'per_day', 'per day', '2003-05-20 14:54:04', 'instructor course statistics, details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'per_disabilities', 'Disabilities', '2009-12-07 13:42:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'per_ethnicity', 'Ethnicity', '2009-12-07 13:40:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'per_eyes', 'Eye Colour', '2009-12-07 13:40:23', 'Social personal info'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'per_hair', 'Hair Colour', '2009-12-07 13:39:57', 'Social personal info'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'per_height', 'Height', '2009-12-07 13:39:21', 'social personal info'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'per_languages', 'Languages Spoken', '2009-12-07 13:41:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'per_weight', 'Body Weight', '2009-12-07 14:09:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'phone', 'Telephone Number', '2006-11-23 11:33:30', 'global phone number field label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'photos', 'Photo Gallery', '2010-03-17 15:49:12', 'photo album'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'php_version', 'PHP Version', '2007-07-26 13:40:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'picture', 'Picture', '2007-02-26 17:22:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pink', 'Pink', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'placelogo', 'Place holder logo', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'plain_text', 'Plain Text', '2006-11-23 13:16:30', 'instructor content editor, news, inbox, forum message format'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'points', 'Points', '2007-03-05 15:51:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'points_score', 'points score', '2008-03-10 11:51:16', 'Test/Survey Property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'poll', 'Poll', '2004-06-25 13:17:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'polls', 'Polls', '2004-06-14 13:55:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'position', 'Position', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'post', 'Post', '2003-05-20 14:56:13', 'forum new thread post button'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'postal_code', 'Postal/Zip Code', '2003-05-20 14:56:34', 'global postal code filed labal'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'posted_by', 'By', '2003-05-20 14:57:31', 'forum message submitter'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'posted_on', 'on', '2003-05-20 14:58:10', 'forum message post date, inbox'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'posts', 'Forum Threads', '2005-03-07 13:37:56', 'Header for posts in Forum List'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'post_lock', 'Locked from posting', '2003-05-20 14:58:32', 'forum lock thread message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'post_message', 'Post Message', '2008-05-05 15:27:19', 'forum post fieldset'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'powered_by_google', 'Results by Google.', '2005-10-20 10:52:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'preferences', 'Preferences', '2003-05-20 14:59:08', 'global preferences word'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'prefer_alt', 'Preferred Alternative', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'prefer_lang', 'Preferred Language', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'presets', 'Presets', '2004-08-24 11:56:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'preset_grade_scale', 'Preset Grade Scale', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'preset_scales', 'Preset & Previously Used Scales', '2004-08-24 13:48:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'pretty_url', 'Pretty URL', '2008-05-14 12:02:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'preview', 'Preview', '2003-05-20 14:59:52', 'test manager preview test'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'preview_questions', 'Preview Questions', '2005-06-21 14:06:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'previous', 'Previous', '2003-05-20 15:01:31', 'global sequence arrow alt/link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'previous_posts', 'Previous Posts', '2006-06-14 10:47:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'previous_topic', 'Previous Topic', '2003-05-20 15:02:06', 'global sequence arrow alt/link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'prev_used', 'Previously Used', '2004-08-13 13:29:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'primary_language', 'Original Language', '2008-10-19 17:03:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'primary_resource_language', 'Original Resource Language', '2008-10-19 17:04:18', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'primary_resource_type', 'Original Resource Type', '2008-10-19 17:04:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'print_page', 'Print Page', '2008-01-04 10:16:49', 'redux theme'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'privacy_control_blurb', 'Controls who can see your profile and related information.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'privacy_settings', 'Privacy Settings', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'private', 'Private', '2003-05-20 15:03:14', 'global private course indicator'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'private_enroll', 'The course you are trying to access is private. Enrollment in this course requires instructor approval.
    ', '2005-03-17 11:51:06', 'requesting enrollment in private course'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'privileges', 'Privileges', '2004-03-05 12:29:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'priv_ac_access_all', 'ACollab: Access all Groups', '2005-08-10 15:21:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'priv_ac_create', 'ACollab: Create Group', '2005-08-10 15:20:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'priv_admin_super', 'Super Administrator', '2005-03-03 10:36:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'priv_admin_themes', 'Themes', '2005-03-03 10:38:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'priv_forums', 'Forums & Chat', '2004-03-08 10:04:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'priv_test_create', 'Test Creation', '2005-08-10 15:20:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'priv_test_mark', 'Test Marking', '2005-08-10 15:22:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'profile', 'Profile', '2003-05-20 16:24:30', 'control centre heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'profile_bundle_select', 'Profile Bundle Selection', '2004-12-06 12:13:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'profile_control', 'Profile Visability', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'profile_picture', 'Profile Picture', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'profile_pictures', 'Profile Pictures', '2007-02-27 13:43:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'profile_student_information', 'Student Information', '2003-09-16 11:49:35', 'view profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'properties', 'Properties', '2003-05-21 12:15:58', 'instructor control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'protected', 'Protected', '2003-05-21 12:16:46', 'browse, create coruse, control centre, course properties'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'province', 'Province/State', '2003-05-21 12:17:22', 'registration, profile editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'public', 'Public', '2003-05-21 12:18:37', 'browse courses, create course, course properties, control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'publish_date', 'Publish Date', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'purple', 'Purple', '2003-05-21 12:18:50', 'global code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'put_link', 'put link name here', '2004-12-08 12:38:42', 'filemanager, isert text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'quebec-lsq', 'Quebec-LSQ', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'question', 'Question', '2003-05-21 12:30:21', 'test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'questions', 'Questions', '2003-05-21 12:30:40', 'test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'questions_for', 'Questions for', '2003-05-21 12:30:50', 'test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'question_categories', 'Question Categories', '2005-02-22 14:17:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'question_database', 'Question Database', '2004-11-24 10:14:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'question_statistics', 'Question Statistics', '2004-08-26 13:26:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'quote', 'Quote', '2003-05-21 12:31:19', 'code picker alt text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'randomize_questions', 'Randomize Questions', '2004-05-18 10:44:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'raw', 'Raw', '2003-09-13 09:25:20', 'course tracker member picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'raw_data', 'Raw Data', '2003-05-21 12:31:48', 'instructor course statistics/details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'raw_final_score', 'Raw Final Score', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 're', 'Re', '2007-02-20 16:20:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'reading_list', 'Reading List', '2006-05-09 14:19:55', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'read_lock', 'Locked from posting & reading', '2003-05-21 12:32:39', 'forum lock message in thread list'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'real_name', 'Real Name', '2003-10-20 11:33:14', 'course properties'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'reapply_default', 'Reset to System Defaults', '2008-10-29 12:18:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'recent_first', 'Recent First', '2009-09-08 13:34:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'recent_last', 'Recent Last', '2009-09-08 13:34:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'recipient_address', 'Recipient Address', '2004-12-06 12:20:03', 'admin/error_logging_bundle.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'red', 'Red', '2003-05-21 12:32:57', 'global code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'red_members', 'Red = Members', '2003-05-21 12:33:55', 'instructor course statistics/details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'refresh_image', 'Refresh Image', '2009-07-16 08:51:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'regenerate', 'Regenerate', '2007-02-16 14:00:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'register', 'Register', '2003-11-26 15:09:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'registered_members', 'Registered Members', '2008-10-06 13:42:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'register_an_account', 'Register an ATutor System Account', '2003-05-21 12:36:01', 'private course enrolment screen, login screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'registration_text', 'If you do not have an account on this system, please create a new account by clicking on the Register Button below.', '2005-07-27 15:11:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'reg_exp', 'Regular Expression', '2004-11-20 19:38:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'reject_request', 'Reject Request', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'related_topics', 'Related Topics', '2003-05-21 12:38:26', 'context(?)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'relative_directory', 'Directory is relative directory to ATutor root, for example: docs/images/ or tools/tests/. Leave empty if it is ATutor root directory.', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'release_date', 'Release Date', '2003-05-21 12:39:24', 'instructor add/edit content, control centre'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'release_immediate', 'Once quiz has been submitted', '2004-12-13 13:55:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'release_marked', 'Once quiz has been submitted and all questions have been marked', '2004-12-13 13:55:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'release_never', 'Do not release results', '2004-12-13 14:02:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'release_on', 'Release on', '2006-04-10 14:37:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'remove', 'Remove', '2003-05-21 12:40:11', 'control centre, admin user manager, instructor enrolment manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'removed', 'Removed', '2004-02-03 09:36:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'remove_frame', 'Remove Frame', '2005-01-10 14:31:34', 'filemanager preview'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'remove_question', 'Remove Question', '2004-11-25 10:30:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'remove_queued_file', 'Remove file from queue', '2008-07-22 16:44:41', 'Instruction to remove file in Fluid\'\'s multiple file uploader'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'remove_write_permission', 'Please REMOVE write permission on the listed folders and files for your security:

    Note: To remove permissions on Unix use chmod 755 then the file name..

    ', '2008-04-21 15:08:55', 'patcher'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rename', 'Rename', '2004-08-11 14:00:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'replace', 'Replace', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'replace_file', 'Replace File', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'replace_into', 'Replace Into', '2005-03-03 12:16:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'replied', 'Replied', '2003-05-21 12:40:35', 'inbox message status label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'replies', 'Replies', '2003-05-21 12:40:50', 'forum'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'reply', 'Reply', '2003-05-21 12:41:20', 'forum, inbox'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'report_errors', 'Report Errors', '2004-12-06 12:08:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'representation', 'Representative', '2009-12-03 15:41:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'request_enrollment', 'Request Enrollment', '2003-10-21 13:04:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'request_instructor', 'You do not yet have permission to create courses. If you would like an instructor account, enter the required description of the course you wish to create.', '2005-03-09 12:22:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'request_instructor_account', 'Request Instructor Account', '2003-05-21 12:43:12', 'control centre non-instructor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'request_instructor_pending', 'Your instructor account request has been made. You will be notifed by email when your request has been approved.', '2005-03-09 12:35:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'request_instructor_priv', 'Request Instructor Account', '2009-11-13 15:44:24', 'my start page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'request_services', 'Request ATutor Services', '2008-11-14 13:43:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'request_services_text', 'Purchase yearly support packages, support tickets, or other ATutor services. Requires registration on atutor.ca.', '2008-11-14 14:04:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'required', 'Required', '2003-05-21 12:43:54', 'registration, export course, test manager question editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'required_field', 'Required Field', '2005-03-04 12:52:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'required_information', 'Required Information', '2005-03-04 14:08:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'require_email_confirmation', 'Require Email Confirmation Upon Registration', '2005-03-22 10:55:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'req_message9', 'ATutor Instructor Request', '2003-05-21 18:52:38', 'email message subject line from control centre instructor request'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'req_message_instructor', 'A new Instructor account request has been made by: %s.\r\n\r\nCourse Description: %s\r\n\r\nYou must login as the administrator at %s to approve or reject this request.', '2004-02-16 16:38:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'reset', 'Reset', '2003-05-21 18:53:27', 'global text for reset buttons'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'reset_filter', 'Reset Filter', '2005-03-30 14:52:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'reset_log', 'Reset Log', '2005-03-03 10:42:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'resource_categories', 'Resource Categories', '2003-05-21 18:57:43', 'instructor export course feedback'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'resource_links', 'Resource Links', '2003-05-21 18:58:34', 'instructor export course feedback'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'resource_type', 'Resource type', '2010-03-09 16:16:42', 'adapted content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'response_text', 'The following answers were given in response to:', '2004-08-25 12:20:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'restore', 'Restore', '2003-05-21 18:59:34', 'preferences page (removed 1.2)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'restore_backup_about', 'Choose the checkboxes next to the content to be restored, select course into which to restore the content, then choose to append the content to the course, or replace the content in the course.', '2004-12-10 09:53:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'restore_upload', 'To upload a backup file, enter a description of the backup, choose a file to upload, and use the "Upload" button. Only backups created using ATutor 1.3 and later are supported. Depending on the size of the course and your Internet connection speed, uploading a backup may take a long time.', '2004-12-10 18:06:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'resubmit', 'Resubmit', '2004-01-30 16:34:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'result', 'Result', '2004-08-12 15:22:08', 'single result in search engine'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'results', 'Results', '2003-05-21 19:00:51', 'test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'results_found', 'Results Found: %s', '2005-04-14 11:01:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'results_from', 'Results from %s', '2004-08-18 12:05:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'result_release', 'Release Results', '2004-12-13 13:57:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'resume', 'Resume', '2003-05-21 19:01:48', 'alt/link text for the resume arrow'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'return', 'Return', '2003-05-21 19:02:25', 'escape from delete course function'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'return_file_manager', 'Return to the File Manager', '2004-11-05 11:20:30', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'return_to_admin_area', 'Return to Admin Area', '2006-03-28 12:31:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'revision', 'Revision', '2003-05-21 19:04:01', 'instructor content editor statistics below embedded links'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'revisions', 'Revisions', '2006-03-20 14:36:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'revision_comment', 'Revisions Comment', '2006-03-20 14:37:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'right', 'Right', '2004-04-27 11:00:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_add_av_material_to_resources', 'Add AV Material To Resources', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_add_resource_av', 'Add Resource AV', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_add_resource_book', 'Add Resource Book', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_add_resource_file', 'Add Resource File', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_add_resource_handout', 'Add Resource Handout', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_add_resource_url', 'Add Resource URL', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_av', 'AV', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_av_material_to_view', 'AV Material to View', '2006-04-13 12:18:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_book', 'book', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_book_to_read', 'Book To Read', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_create_new_av', 'Create New AV', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_create_new_book', 'Create New Book', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_create_new_file', 'Create New File', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_create_new_handout', 'Create New Handout', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_create_new_url', 'Create New URL', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_date_format', '%%M %%d, %%Y', '2006-06-26 16:10:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_delete_reading', 'Delete Reading', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_delete_resource', 'Delete Resource', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_display_resource', 'Display Resource', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_display_resources', 'Resources', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_reading_av', 'Edit Reading AV', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_reading_book', 'Edit Reading Book', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_reading_file', 'Edit Reading File', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_reading_handout', 'Edit Reading Handout', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_reading_url', 'Edit Reading URL', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_resource_av', 'Edit Resource', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_resource_book', 'Edit Resource Book', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_resource_file', 'Edit Resource File', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_resource_handout', 'Edit Resource Handout', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_edit_resource_url', 'Edit Resource URL', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_end', 'End', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_file', 'file', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_goto_url', 'view page', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_handout', 'handout', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_isbn_number', 'ISBN', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_name_reading', 'Name Of Reading', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_new_reading_av', 'New Reading AV', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_new_reading_book', 'New Reading Book', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_new_reading_file', 'New Reading File', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_new_reading_handout', 'New Reading Handout', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_new_reading_url', 'New Reading URL', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_no_read_by_date', 'No Read By Date', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_optional_reading', 'Optional Reading', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_or', 'or', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_pages', 'Pages', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_publisher', 'Publisher', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_reading_date', 'Reading Date', '2006-02-23 09:18:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_read_by_date', 'Read by Date', '2006-04-13 10:58:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_select_book', 'Select Book', '2006-07-25 09:08:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_select_file', 'Select File', '2006-07-25 09:15:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_select_handout', 'Select Handout', '2006-07-25 09:13:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_select_url', 'Select URL', '2006-07-25 09:12:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_start', 'Start', '2006-06-21 08:53:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_type_of_reading', 'Type of Reading', '2006-07-31 15:23:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_type_of_resource', 'Type of Resource', '2006-07-14 10:24:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_url', 'URL', '2007-08-02 10:46:30', 'resource type picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_view_resource_details', 'View Resource Details', '2008-06-10 14:23:50', 'reading list.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rl_year_written', 'Year Written', '2006-06-21 08:55:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'role', 'Role', '2004-03-05 12:26:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rss_feeds', 'Syndicated Feeds', '2005-10-28 11:07:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'rtl', 'Right to Left', '2004-11-20 19:37:58', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'russian-rsl', 'Russian-RSL', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'same_course_users', 'Only users in the same courses as you are listed.', '2003-05-21 19:05:32', 'inbox send message form'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sans_serif', 'Sans Serif', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'save', 'Save', '2004-10-29 14:57:46', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'save_changes', 'Save Changes', '2004-05-11 10:48:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'save_changes_saved', 'All changes have been saved.', '2004-02-16 14:06:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'save_changes_unsaved', 'Unsaved changes have been made.', '2004-02-16 14:04:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'save_types_and_language', 'Update Resource Properties', '2008-10-19 13:51:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'scaffold_text', 'Modify the URLs for the learning tools below. Leave blank to disable.', '2008-09-10 12:29:26', 'tool preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'scale', 'Scale', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'scale_value', 'Scale Value', '2008-09-25 15:59:56', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'schema_error', 'Wrong schema location', '2009-12-07 13:09:06', 'common cartridge'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search', 'Search', '2003-05-21 19:19:30', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_all_words', 'All words', '2003-08-11 11:22:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_any_word', 'Any word', '2003-08-11 11:22:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_connections', 'Search Connections', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_control', 'Search Control', '2009-11-06 15:53:36', 'networking settings'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_education', 'Search Education', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_for_friends', 'Search People', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_for_groups', 'Search for Groups', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_in', 'Search in', '2004-04-30 10:51:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_match', 'Match', '2003-08-11 11:21:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_position', 'Search Position', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_profile', 'Search Profile', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_results', 'Search Results', '2003-05-21 19:21:29', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_site', 'Only search within %s.', '2005-10-20 10:53:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_visibility', 'Search Visibility', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'search_words', 'Words', '2003-08-11 11:21:36', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'secondary_resource_body', 'Body', '2008-09-23 13:50:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'secondary_resource_language', 'Adapted Resource Language', '2008-10-19 17:05:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'secondary_resource_type', 'Adapted Resource Type', '2008-10-19 17:06:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'second_name', 'Second Name', '2006-03-27 14:53:50', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'second_short', 's', '2007-07-09 12:31:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'section', 'Section', '2005-03-04 14:06:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'see', 'See', '2003-05-21 19:24:22', 'glossary - "see" related terms'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'see_attached', 'See attached.', '2005-03-10 16:37:04', 'error logging - email body'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'select', 'Select', '2007-02-21 13:22:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'select_all', 'select/unselect all', '2004-10-29 13:25:57', 'file manager and enrollment manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'select_av', 'Select Audio/Visual', '2006-07-25 09:14:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'select_cat', 'Select Category', '2005-02-23 12:23:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'select_directory', 'Please choose the folder you want to move the selected files to:', '2004-11-30 11:40:08', 'Filamanger, moving a file'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'select_member', 'Select a Member to View', '2003-05-21 19:29:27', 'instructor course tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'select_parent_topic', 'Select parent topic', '2005-03-16 13:37:00', 'content manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'select_term_to_edit', 'Select term to edit.', '2006-09-25 13:08:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'send', 'Send', '2003-05-21 19:31:57', 'global message send button text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'send_bundle', 'Send Bundle', '2004-12-06 12:16:19', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'send_confirmation', 'Enter your email address below and the account confirmation message will be resent.', '2005-03-24 09:57:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'send_delete', 'Send & Delete', '2003-05-21 19:32:55', 'send a message from the inbox and delete the original'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'send_message', 'Send Message', '2003-05-21 19:34:13', 'inbox send message button text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sent_messages', 'Sent Messages', '2007-02-20 16:21:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sent_msgs_ttl_text', 'Number of Days to Keep Copied Sent Messages for', '2007-02-20 14:00:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sent_via_atutor', 'Sent via an ATutor system at %s', '2003-05-21 20:04:50', 'global email message footer'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sequence', 'Sequence', '2003-05-21 20:05:50', 'context (removed 1.2)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'serif', 'Serif', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'server_date_format', '%%Y-%%m-%%d %%G:%%i:%%s', '2008-10-01 15:02:49', 'date format'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'session_end', 'and ended on', '2003-05-21 20:12:12', 'tracker - to be updated ( started on [date] and ended on [date])'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'session_start', 'Session above started on', '2003-05-21 20:11:35', 'tracker (to be updated)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'set', 'Set', '2003-06-30 17:02:05', 'admin language manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'settings', 'Settings', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'set_default', 'Set as Default', '2004-10-15 15:48:04', 'set as default button in theme manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'set_preset', 'Apply Preset', '2003-05-24 10:52:43', 'preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'shared_forums', 'Shared Forums', '2004-11-03 11:18:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'shindig_blurb', 'If you have your own Shindig server setup, your can enter the URL to the server here. If you do not have your own Shindig server, you can either leave the URL empty, or you can use "http://social.atutor.ca/shindig/php" to connect to the ATutor social network. Shindig allows users to link gadgets from other sites into their social networking environment, as well as communicate with those in other social networks. If you choose not to use a Shindig server, your social network will function as a self-contained network, without access to external networks.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'shindig_url', 'URL of Optional ShinDig server.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'shortcuts', 'Shortcuts', '2004-04-27 14:57:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'short_paragraph', 'Short Paragraph', '2003-05-21 20:13:03', 'test manager test type'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show', 'Show', '2005-03-15 11:37:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_all', 'show all', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_available_applications', 'Show Available Gadgets', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_bread_crumbs', 'Show Breadcrumb Link Navigation', '2008-09-24 13:05:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_contents', 'Show Table of Contents', '2008-09-09 09:03:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_guest_form', 'Show Guest Information Collection Form', '2008-10-09 14:13:38', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_guide', 'Show Context Sensitive Links to Handbook', '2008-09-24 12:09:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_next_previous_buttons', 'Show Next/Previous Navigation Buttons', '2008-09-24 13:04:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_numbers', 'Topic Numbering', '2005-02-09 10:20:54', 'preference option'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_on_home_page', 'Show on Home Page', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_raw_tracking', 'Show raw tracking', '2003-11-26 15:19:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_summary_tracking', 'Show summary tracking', '2003-11-26 15:18:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'show_your_applications', 'Show Your Gadgets', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'side_menu', 'Side Menu', '2005-03-04 14:37:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'side_menu_text', 'Choose which menu items you would like displayed at the side.', '2005-03-04 15:17:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sign_lang', 'Sign Language', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sign_language', 'Sign language', '2008-09-08 14:19:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'singapore-sls', 'Singapore-SLS', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sitemap', 'Site-map', '2003-05-21 20:17:21', 'global site-map text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sitemap_text', 'Browse through the whole course site from one location.', '2009-07-02 14:15:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'site_name', 'Site Name', '2005-03-03 10:49:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'size', 'Size', '2003-05-22 13:01:20', 'file manager, zip tool'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_angry', 'angry', '2004-02-12 12:20:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_confused', 'confused', '2004-02-12 12:21:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_crazy', 'crazy', '2004-02-12 12:20:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_evil', 'evil', '2004-02-12 12:20:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_frown', 'frown', '2004-02-12 12:20:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_lol', 'laughing out loud', '2004-02-12 12:20:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_muah', 'muah', '2004-02-12 12:21:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_oh_well', 'oh well', '2004-02-12 12:20:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_smile', 'smile', '2004-02-12 12:20:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_tired', 'tired', '2004-02-12 12:21:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_tongue', 'tongue', '2004-02-12 12:20:27', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'smile_wink', 'wink', '2004-02-12 12:20:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'social', 'Networking', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'social_and_lms', 'Use ATutor LMS with ATutor Social module', '2009-07-17 14:12:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'social_groups', 'Network Groups', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'social_profile', 'Network Profile', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'social_switch', 'Social Networking', '2009-07-17 14:11:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'social_switch_text', 'Choose to use ATutor as a social networking environment only, or as a learning management system with a social networking module.', '2009-07-17 14:19:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'spanish', 'Spanish', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'spanish-lse', 'Spanish-LSE', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'specific_groups', 'Specific Groups', '2006-03-23 13:39:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'specify_url_to_content_package', 'Or, Specify a URL to a Content Package or Common Cartridge', '2009-11-16 10:57:14', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'specify_url_to_theme_package', 'Or, Specify a URL to a Theme', '2004-10-18 15:10:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sports_recreation', 'Sports and Recreation', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sql_statement', 'SQL Statement', '2008-04-21 15:11:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'standard', 'Standard', '2005-09-21 13:35:25', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'started_by', 'Started By', '2003-05-22 13:02:38', 'forums thread display table column header'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'startend_date_format', '%%j/%%n/%%y %%G:%%i', '2007-12-09 15:58:39', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'startend_date_longs_format', '%%Y-%%m-%%d %%H:%%i', '2008-08-27 11:23:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'startend_date_long_format', '%%Y-%%m-%%d %%H:%%i', '2008-08-27 11:24:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'start_date', 'Start Date', '2003-05-22 13:03:05', 'mytests, test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'start_test', 'Begin', '2009-11-13 16:18:58', 'test intro'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'state', 'State', '2005-08-16 15:27:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'statement', 'Statement', '2003-05-22 13:07:22', 'test manager add/edit questions'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'statistics', 'Statistics', '2003-05-22 13:08:06', 'instructor course statistics/details'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'statistics_information', 'Statistics & Information', '2007-07-26 13:39:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'status', 'Status', '2003-05-22 13:09:29', 'test manager, control center, profile, admin user manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sticky_thread', 'Sticky Thread - always at the top', '2003-05-22 13:10:18', 'instructor forum option alt text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'street_address', 'Street Address', '2003-05-22 13:10:43', 'registration, profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'student', 'Student', '2003-10-27 11:44:47', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'student1', 'Student', '2003-05-22 13:11:42', 'admin user manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'students', 'Students', '2003-05-21 20:01:44', 'course email (removed 1.2)'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'student_id', 'Student ID', '2003-05-22 13:12:40', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'student_not_exists', 'Student not exists', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'student_pin', 'Student PIN', '2005-05-27 13:35:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'student_tools', 'Student Tools', '2008-11-07 09:17:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'student_tools_text', 'Access the tools used in this course.', '2009-07-02 14:16:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'style_editor', 'Stylesheet Editor', '2003-05-22 13:13:22', 'tool page/ stylesheet editor heading text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'subject', 'Subject', '2003-05-22 13:14:21', 'global message subject label'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'submission', 'Submission', '2005-05-11 11:29:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'submissions', 'Submissions', '2003-05-22 13:14:47', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'submit', 'Submit', '2003-05-22 13:15:05', 'global submit button text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'submitted_by', 'Submitted By', '2005-02-23 12:23:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'submit_no', 'No', '2004-11-22 16:23:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'submit_yes', 'Yes', '2004-11-22 16:23:00', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'subscribe', 'Subscribe to this thread to receive notification via email of new replies.', '2003-11-06 13:47:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'subscribe1', 'Subscribe', '2004-12-02 15:32:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'sub_topics', 'Subtopics', '2005-03-16 13:55:12', 'content manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'suggestions', 'Suggestions', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'suggest_link', 'Suggest Course Link', '2006-05-25 11:40:32', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'summary', 'Summary', '2003-09-13 09:24:55', 'course tracker member picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'support_tools', 'Learner Support Tools', '2008-09-09 09:03:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'swedish', 'Swedish', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'switch_text', 'Switch to text editor', '2004-05-27 10:50:47', 'Content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'switch_visual', 'Switch to visual editor', '2004-05-27 10:52:19', 'Used in content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'syndicate_announcements', 'Syndicate Announcements', '2005-01-04 09:46:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'system_preferences', 'System Preferences', '2005-03-03 10:49:44', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'table_of_contents', 'Table of Contents', '2003-05-22 13:53:36', 'preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'take_test', 'Take Test', '2003-05-22 13:54:16', 'MyTests'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tamil', 'Tamil', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tech_support_forum', 'Community Support Forum', '2008-11-14 13:38:14', 'help'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tech_support_forum_text', 'Support questions should be of a technical nature.', '2005-07-05 14:13:20', 'help'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'template', 'Template', '2006-09-25 13:07:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tests', 'Tests & Surveys', '2009-11-17 09:49:16', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_anonymous', 'This test/survey is anonymous.', '2004-09-02 13:42:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_description', 'Test Description', '2008-03-10 11:50:11', 'Test/Survey Property'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_import_package', 'Import available tests.', '2008-10-21 11:43:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_lk', 'Likert', '2004-08-12 15:55:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_ma', 'Multiple Answer', '2007-02-01 15:49:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_matching', 'Matching (Simple)', '2007-02-01 16:20:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_matchingdd', 'Matching (Graphical)', '2007-02-01 16:20:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_mc', 'Multiple Choice', '2003-05-22 14:03:29', 'test manager question type'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_open', 'Open Ended', '2003-05-22 14:03:45', 'test manager question type'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_ordering', 'Ordering', '2007-01-10 13:45:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'test_tf', 'True or False', '2003-05-22 14:09:05', 'test manager question type'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'text', 'Text', '2004-04-20 16:40:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'textual', 'Textual', '2008-09-08 14:19:22', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'theme', 'Theme', '2004-04-23 15:26:57', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'themes', 'Themes', '2003-05-22 14:10:24', 'preferences'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'themes_disabled', 'The personal theme preference has been disabled in favour of theme specific categories.', '2004-08-04 11:58:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'theme_screenshot', 'Screenshot', '2005-05-06 14:18:00', 'theme manager, screentshot alt/title'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'theme_specific_categories', 'Theme Specific Categories', '2005-03-31 13:49:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'there_are_entries', 'There are %s entries.', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'thesaurus', 'Thesaurus', '2008-09-09 09:03:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'the_follow_errors_occurred', 'The following errors occurred:', '2005-03-07 12:41:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'this_course_only', 'This course only', '2004-08-18 12:02:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'this_is_content', 'This is just a blank content page. Use the Edit Content link to edit this page. You can manage this course by accessing the Manage section.', '2005-05-11 14:52:47', 'default content insert when a new course is created'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'thread_already_subscribed', 'You are already subscribed to this thread.', '2003-05-22 14:11:25', 'forum view thread subscribe feedback'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'thread_locked', 'Thread Locked', '2003-05-22 14:11:58', 'forum thread view thread lock message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'thread_notify1', 'Thread Subscription', '2005-01-08 09:18:48', 'Thread email notify subject'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'thread_subscribe', 'Subscribe to this thread to receive notification via email of replies.', '2003-05-22 14:42:57', 'forum thread subscribe description'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_any_field', 'Any Field', '2004-05-03 13:14:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_author', 'Author', '2004-05-03 13:14:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_export', 'Export content to the Transformable repository. A valid Transformable account is required.', '2010-02-23 10:07:47', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_howto', 'Enter the keywords to search Transformable repository.', '2010-02-23 10:03:49', 'tile search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_import_content_package_about', 'Import the content directly into this course.', '2004-05-03 13:15:54', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_keyword', 'Keyword', '2004-05-03 13:14:23', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_not_accessible', 'Transformable repository is not accessible.', '2010-02-23 10:03:28', 'tile search'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_progress', 'Transformable Importing in Progress...', '2010-02-23 10:08:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_search', 'Transformable Repository Search', '2010-02-23 10:08:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_search_text', 'Search the content repository for additional learning materials.', '2009-07-02 14:15:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tile_technical_format', 'Technical Format', '2004-05-03 13:14:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'times', 'Times', '2009-10-26 15:03:31', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'timestamp', 'Timestamp', '2004-12-06 12:04:30', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'time_spent', 'Time Spent', '2007-07-09 12:32:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'time_zone', 'Time Zone (date below should match your local time)', '2010-03-03 16:32:12', 'appears on the config page'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'time_zones_not_supported', 'Your server is not set-up to support time zones. See the Handbook for more details.', '2006-11-01 13:06:40', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'title', 'Title', '2003-05-22 14:43:54', 'admin course manager column heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'to', 'To', '2003-05-22 14:45:14', 'inbox send message "to" a user'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'to1', 'to', '2003-05-22 14:48:13', 'login form header row login -to- some place'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'toggle_disabled', 'Toggle Disabled', '2003-05-22 15:10:01', 'alt text for menu toggle'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tools', 'Tools', '2006-03-22 12:52:10', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tools_details', 'Details for Tool Usage', '2003-05-22 15:11:15', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tools_manager', 'Add Activity', '2009-10-22 16:17:01', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tool_manager', 'Tool/Activity Manager', '2009-11-19 16:12:28', 'core modules'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tool_man_comment', 'Choose from the tools available in the course, to add activities to this content.', '2009-11-05 12:29:40', 'content tool manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tool_settings', 'Tool Settings', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tool_summary', 'Tools Usage Summary', '2003-05-22 15:11:26', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'top', 'Top', '2003-05-22 15:11:53', 'global alt text for jump to top link/icon'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'topic', 'Topic', '2003-05-22 15:12:45', 'forum thread list column heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'top_20', 'Only the top 20 results have been displayed.', '2005-10-20 10:50:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'top_level', 'Top Level', '2005-03-16 13:36:35', 'content manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'total', 'Total', '2003-05-22 15:14:13', 'instructor course statistic/details, test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'total_votes', 'Total Votes', '2005-03-11 15:51:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'to_2', 'to', '2003-05-22 15:15:16', 'test manager date'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'to_email', 'To Email', '2003-05-22 15:17:02', 'help admin/instructor contact forms'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'to_name', 'To Name', '2003-05-22 15:17:20', 'help admin/instructor contact forms'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tracker', 'Content Tracker', '2005-11-25 18:36:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tracker_none_viewed', 'No content pages have been viewed in this course yet.', '2003-09-15 11:53:06', 'mytracker summary view'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tracker_not_viewed', 'Jump to pages not yet viewed.', '2003-09-13 12:16:10', 'mytracker bypass to not viewed alt text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tracker_pages_total', 'Total pages in this course %s (pages visited: %s.)', '2003-09-13 09:11:43', 'tracker summary pages read'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tracker_percent_read', 'Percentage of pages viewed %s.', '2003-09-13 12:09:57', 'tracker pages read'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tracker_summary_read', 'Summary of pages viewed by user %s.', '2003-09-13 12:30:44', 'tracker summary read'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tracking', 'Tracking', '2003-05-22 15:18:11', 'admin course manager '); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'tracking_all_page_viewed', 'All pages in this course have been viewed.', '2003-09-13 12:37:23', 'mytracker page not viewed'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'training_and_education', 'Training and Education', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'translate', 'Translate', '2004-12-07 13:59:44', 'language manager translate button'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'translate_lang_howto', 'In order to translate a language you must :
    \r\n1. Set the AT_DEVEL_TRANSLATE constant in /include/vitals.inc.php to \'1\'
    \r\n2. Make sure that you have added the new language to the database
    \r\n3. Select the new language as the one you want to translate from the \'translate to:\' option at the bottom of the page
    \r\n4. Use the translate button and translate the new language in a new window
    ', '2004-12-10 16:29:48', 'Language Manager, translation message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'translate_to', 'Translate to:', '2003-05-22 15:19:14', 'footer'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'true', 'True', '2003-05-22 15:19:50', 'test manager question answer option'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'type', 'Type', '2003-05-22 15:20:30', 'test manager questions column heading'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unchecked', 'Un-checked', '2006-06-26 14:56:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unconfirmed', 'Unconfirmed', '2005-04-01 10:39:51', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'underline', 'Underline', '2003-05-22 15:21:31', 'global code picker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'underscore', 'Underscore', '2004-06-22 12:09:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unenroll', 'Un-enroll', '2004-10-15 11:51:25', 'unenroll button in enrollment manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unenrolled', 'Un-enrolled', '2004-10-15 11:50:06', 'unenrolled tab in enrollement manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unenroll_me', 'Unenroll', '2007-12-09 08:57:03', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'uninstall', 'Uninstall', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'uninstall_module_info', 'You are about to UNINSTALL module %s.', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unique_visits', 'Unique Visits', '2005-03-01 12:41:13', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'university', 'School/Institution', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unknown', 'Unknown', '2003-06-06 10:41:35', 'links database , file upload progress window'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unknown_error', 'An unknown error occured', '2003-05-22 15:22:55', 'general catch all error message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unlimited', 'Unlimited', '2005-03-01 11:22:02', 'admin course properties & file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unlock_thread', 'Unlock Thread', '2003-05-22 15:24:58', 'forum instructor lock screen'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unmarked', 'Unmarked', '2003-05-22 15:25:33', 'mytests, test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unmarked_label', 'Unmarked (%s)', '2005-05-12 10:25:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unreleased', 'Unreleased', '2005-05-04 13:59:37', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unshared_forums', 'Unshared Forums', '2004-11-05 10:50:02', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unsubscribe', 'UnSubscribe from this thread to cancel receiving notifications via email of new replies.', '2003-11-06 13:47:20', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unsubscribe1', 'Unsubscribe', '2004-12-02 15:32:28', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'until', 'Until', '2006-03-23 11:38:49', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'unvisited_pages', 'You have not viewed the following pages:', '2003-09-13 11:56:50', 'mytracker summary'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'update', 'Update', '2004-11-24 14:28:45', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'updated', 'Updated', '2003-05-22 15:33:15', 'admin course manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'update_gradebook', 'Update ATutor Marks', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'update_list', 'Update List', '2005-05-10 14:31:59', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'update_to', 'Update To', '2005-03-03 12:16:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload', 'Upload', '2003-05-22 15:56:16', 'global upload button/link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_backup', 'Upload Backup', '2008-05-20 12:44:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_content_package', 'Upload a Content Package or Common Cartridge', '2009-11-16 10:54:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_file', 'Upload File', '2006-03-20 14:37:46', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_files', 'Upload files', '2008-07-22 16:43:38', 'Used in filemanager and any place that uses fluid for multiple file upload.'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_icon', 'Upload Custom Course Icon', '2007-11-09 13:05:42', 'Course Properties Custom Icon'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_icon_text', 'This icon will be uploaded to the file manager for this course under the folder \'/custom_icons\'.', '2008-07-28 10:36:03', 'Course Properties Custom Icon Text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_module', 'Upload a module zip file to install it:', '2008-11-05 08:48:45', 'Module installation message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_new_picture', 'Upload new picture', '2007-02-26 17:23:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_patch', 'Upload a zip file to install patch:', '2008-04-21 15:08:55', 'patcher'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_progress', 'File Upload in Progress...', '2003-06-06 10:36:38', 'file upload progress window'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_question', 'Select Question Package to Upload', '2008-09-23 14:00:24', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_test', 'Select Test Package to Upload', '2008-09-23 13:58:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'upload_theme_package', 'Upload a Theme', '2004-10-18 15:09:32', 'Theme Manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'urdu', 'Urdu', '2008-09-09 09:01:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'url', 'URL', '2003-05-22 15:56:36', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'url_to_read', 'URL To Read', '2006-04-13 12:01:15', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'usage', 'Usage', '2005-03-16 13:55:31', 'content manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'usaved_changes_made', 'Unsaved changes made', '2004-01-15 13:30:06', 'editing content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'user', 'User', '2003-05-22 15:59:34', 'links database'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'username', 'Username', '2003-05-22 16:00:31', 'global username label/link text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'users', 'Users', '2003-05-22 16:02:26', 'forum, admin user manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'users_online', 'Users Online', '2003-05-22 16:03:00', 'discussions page text'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'user_contributed_notes', 'User Contributed Handbook Notes', '2005-07-27 12:01:52', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'user_offline', 'Offline', '2005-03-11 16:19:14', 'directory'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'user_online', 'Online', '2005-03-11 16:19:28', 'directory'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'user_type', 'User Type', '2008-10-06 13:41:48', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_alt_to_audio', 'Use Alternative To Audio', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_alt_to_text', 'Use Alternative To Text', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_alt_to_visual', 'Use Alternative To Visual', '2008-09-09 08:56:34', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_as_alternative', 'Use As Alternative', '2010-03-09 16:15:53', 'adapted content'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_captcha', 'Allow the use of CAPTCHA', '2009-06-03 15:33:17', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_customized_head', 'Use Customized Head', '2008-04-29 15:59:06', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_enrol_button', 'Use the button below to enroll in this course.', '2003-05-22 16:03:37', 'enrol screen instruction'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_higher_grade', 'Use higher grade', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_lower_grade', 'Use lower grade', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_parent_theme', 'Use parent category\'s theme.', '2004-08-04 11:34:41', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_system_time', 'Use the system\'s time', '2006-11-01 13:06:01', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'use_visual_editor', '(Editor)', '2004-12-08 12:15:36', 'form editor in create test question'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'version', 'Version', '2003-05-22 16:04:54', 'copyright notice in footer'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'version_history', 'Version History', '2008-10-21 15:41:53', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'version_not_match', 'This patch is for ATutor version %s. Does not match with your current ATutor version. The installation of it may break ATutor. Are you sure you want to proceed?', '2008-06-19 13:20:45', 'patcher warning message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'vertical', 'Vertical', '2004-12-01 13:34:28', 'create/edit test question option'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view', 'View', '2004-02-16 15:23:09', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'viewing_errors', 'Viewing Error(s)', '2004-12-06 12:10:05', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'viewing_profile_bugs', 'Viewing Profile Bugs', '2004-12-06 12:03:33', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_all', 'View All', '2005-08-04 09:54:04', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_details', 'View Details', '2005-03-11 11:27:43', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_entire_post', 'View Entire Post', '2003-05-22 16:07:57', 'forum message view'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_groups', 'View Group', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_log', 'View Activity Log', '2005-03-03 10:44:12', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_mark_test', 'View & Mark Test', '2003-05-22 16:08:11', 'test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_message', 'View Message', '2005-03-14 11:48:21', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_profile_bugs', 'View Profile Bugs', '2004-12-06 11:51:58', 'admin/error_logging.php'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_readme', 'View Readme', '2009-07-21 15:31:29', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_responses', 'View Responses', '2004-08-25 11:53:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_results', 'View Results', '2003-05-22 16:09:09', 'mytests, test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_selected_bugs', 'View Selected Bugs', '2004-12-06 12:06:26', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_sub_topics', 'View subtopics', '2005-03-16 13:35:47', 'content manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'view_tracking', 'View Tracking', '2003-05-22 16:09:31', 'tracker'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'visitor_counts', 'Visitor counts', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'visits', 'Visits', '2003-09-13 09:19:09', 'tracker summary read'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'visual', 'Visual', '2008-09-08 14:19:07', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'visual_editor', 'Visual Editor', '2006-11-23 13:31:56', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'vote_to_see_results', 'Vote to see results.', '2004-06-21 16:55:42', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'warning', 'Warning', '2003-05-22 16:09:47', 'text for warning message'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'weblink', 'Web Link', '2009-10-22 16:18:13', 'content editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'websites', 'Websites', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'web_site', 'Web Site', '2003-05-22 16:10:20', 'registration, profile'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'weight', 'Weight', '2003-05-22 16:10:31', 'test manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'welcome_to_atutor', 'Welcome To ATutor', '2003-05-31 15:30:38', 'announcement default heading for new course'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'white', 'White', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'window_auto_close', 'This window will close automatically.', '2003-06-06 10:37:43', 'file upload progress window'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'workspace', 'Workspace', '2006-03-20 14:38:08', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'world_network', 'World Network', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'wrong_answer', 'Wrong Answer', '2003-05-22 16:11:51', 'test manager question editor'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'wrong_request_token', 'Mismatched request token from OAuth server.', '2010-02-23 10:03:11', 'oauth client'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'year', 'Year', '2004-06-24 15:45:35', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'yellow', 'Yellow', '2008-09-09 09:01:13', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'yes', 'Yes', '2005-03-03 10:18:48', 'preferences, admin course manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'yes_delete', 'Yes / Delete', '2003-05-22 16:14:23', 'global delete response option'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'your_applications', 'My Gadgets', '2009-05-27 12:01:11', ''); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'your_mark', 'Your Mark', '2008-09-25 16:01:17', 'gradebook'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'you_are_here', 'You are here', '2003-10-02 16:46:28', 'ALT text Global/Local Navigation link to self'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'zip_archive', 'Zip Archive', '2003-05-22 16:20:01', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'zip_file_manager', 'Zip File Manager', '2003-05-22 16:20:14', 'file manager'); +INSERT INTO `language_text` (`language_code`, `variable`, `term`, `text`, `revised_date`, `context`) VALUES ('en', '_template', 'zip_illegal_contents', 'The contents of this archive are listed below. Illegal file types will not be extracted, and file names containing illegal characters will be translated.', '2004-08-10 15:10:04', ''); \ No newline at end of file diff --git a/install/db/atutor_schema.sql b/install/db/atutor_schema.sql new file mode 100644 index 000000000..e3ccbe788 --- /dev/null +++ b/install/db/atutor_schema.sql @@ -0,0 +1,1607 @@ +##################################################### +# Database setup SQL for a new install of ATutor +##################################################### +# $Id$ + +# -------------------------------------------------------- +# Table structure for table `admin_log` +# since 1.5 + +CREATE TABLE `admins` ( + `login` VARCHAR( 30 ) NOT NULL default '', + `password` VARCHAR( 40 ) NOT NULL default '', + `real_name` VARCHAR( 120 ) NOT NULL default '', + `email` VARCHAR( 50 ) NOT NULL default '', + `language` varchar(5) NOT NULL default '', + `privileges` MEDIUMINT UNSIGNED NOT NULL default 0, + `last_login` TIMESTAMP NOT NULL default 0, + PRIMARY KEY ( `login` ) +) TYPE = MYISAM; + +CREATE TABLE `admin_log` ( + `login` varchar(30) NOT NULL default '', + `time` TIMESTAMP NOT NULL, + `operation` varchar(20) NOT NULL default '', + `table` varchar(30) NOT NULL default '', + `num_affected` tinyint(3) NOT NULL default '0', + `details` TEXT, + KEY `login` (`login`) +) TYPE = MYISAM; + + +# -------------------------------------------------------- +# Table structure for table `assignments` +# since 1.5.3 + +CREATE TABLE `assignments` ( + `assignment_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `course_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `title` VARCHAR(240) NOT NULL default '', + `assign_to` MEDIUMINT UNSIGNED default 0, + `date_due` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `date_cutoff` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `multi_submit` TINYINT DEFAULT '0', + PRIMARY KEY (`assignment_id`), + INDEX (`course_id`) +) TYPE = MYISAM; + +# -------------------------------------------------------- +# Table structure for table `backups` +# since 1.4.3 + +CREATE TABLE `backups` ( + `backup_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `date` TIMESTAMP NOT NULL, + `description` TEXT , + `file_size` int(10) unsigned NOT NULL default 0, + `system_file_name` varchar(50) NOT NULL default '', + `file_name` TEXT , + `contents` TEXT , + PRIMARY KEY (`backup_id`), + KEY `course_id` (`course_id`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- +## Table for `blog_posts` + +CREATE TABLE `blog_posts` ( + `post_id` mediumint(8) unsigned NOT NULL auto_increment, + `member_id` mediumint(8) unsigned NOT NULL default '0', + `owner_type` tinyint(3) unsigned NOT NULL default '0', + `owner_id` mediumint(8) unsigned NOT NULL default '0', + `private` tinyint(3) unsigned NOT NULL default '0', + `date` TIMESTAMP NOT NULL, + `num_comments` tinyint(3) unsigned NOT NULL default '0', + `title` VARCHAR(255) NOT NULL, + `body` TEXT, + PRIMARY KEY (`post_id`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- +## Table for `blog_posts_comments` + +CREATE TABLE `blog_posts_comments` ( + `comment_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , + `post_id` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , + `member_id` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , + `date` TIMESTAMP NOT NULL, + `private` TINYINT UNSIGNED DEFAULT '0' NOT NULL , + `comment` TEXT , + PRIMARY KEY ( `comment_id` ) , + INDEX ( `post_id` ) +) TYPE = MYISAM; + +# -------------------------------------------------------- +# Table structure for table `config` +# since 1.5.2 + +CREATE TABLE `config` ( + `name` CHAR( 30 ) NOT NULL default '', + `value` TEXT, + PRIMARY KEY ( `name` ) +) TYPE = MYISAM; + +# -------------------------------------------------------- +# Table structure for table `content` + +CREATE TABLE `content` ( + `content_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `content_parent_id` mediumint(8) unsigned NOT NULL default '0', + `ordering` mediumint(8) NOT NULL default '0', + `last_modified` TIMESTAMP NOT NULL, + `revision` tinyint(3) unsigned NOT NULL default '0', + `formatting` tinyint(4) NOT NULL default '0', + `release_date` datetime NOT NULL default '0000-00-00 00:00:00', + `keywords` TEXT , + `content_path` TEXT , + `title` VARCHAR(255) NOT NULL , + `text` TEXT , + `head` TEXT, + `use_customized_head` TINYINT(4) NOT NULL, + `test_message` TEXT, + `allow_test_export` TINYINT(1) UNSIGNED NOT NULL, + `content_type` TINYINT(1) UNSIGNED NOT NULL, + PRIMARY KEY (`content_id`), + KEY `course_id` (`course_id`) +) TYPE=MyISAM ; + +# -------------------------------------------------------- +# Table structure for table `course_access` + +CREATE TABLE `course_access` ( + `password` char(8) NOT NULL , + `course_id` mediumint(8) unsigned NOT NULL , + `expiry_date` timestamp NOT NULL , + `enabled` tinyint(4) NOT NULL , + PRIMARY KEY ( `password` ) , + UNIQUE (`course_id`) +) TYPE=MyISAM ; + +# -------------------------------------------------------- +# Table structure for table `course_cats` + +CREATE TABLE `course_cats` ( + `cat_id` mediumint(8) unsigned NOT NULL auto_increment, + `cat_name` VARCHAR(255) NOT NULL , + `cat_parent` mediumint(8) unsigned NOT NULL default '0', + `theme` VARCHAR(30) NOT NULL default '', + PRIMARY KEY (`cat_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `course_tests_assoc` +# since 1.6.2 +CREATE TABLE `content_tests_assoc` ( + `content_id` INTEGER UNSIGNED NOT NULL, + `test_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`content_id`, `test_id`) +) +TYPE = MyISAM; + + +# -------------------------------------------------------- +# Table structure for table `content_forums_assoc` + +CREATE TABLE `content_forums_assoc` ( +`content_id` INTEGER UNSIGNED NOT NULL, +`forum_id` INTEGER UNSIGNED NOT NULL, +PRIMARY KEY ( `content_id` , `forum_id` ) +) +TYPE = MyISAM; + +# -------------------------------------------------------- +# Table structure for table `course_enrollment` + +CREATE TABLE `course_enrollment` ( + `member_id` mediumint(8) unsigned NOT NULL default '0', + `course_id` mediumint(8) unsigned NOT NULL default '0', + `approved` enum('y','n','a') NOT NULL default 'n', + `privileges` INT(10) unsigned NOT NULL default '0', + `role` varchar(35) NOT NULL default '', + `last_cid` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`member_id`,`course_id`) +) TYPE=MyISAM; + + + +# -------------------------------------------------------- +# Table structure for table `course_stats` + +CREATE TABLE `course_stats` ( + `course_id` mediumint(8) unsigned NOT NULL default '0', + `login_date` date NOT NULL default '0000-00-00', + `guests` mediumint(8) unsigned NOT NULL default '0', + `members` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`course_id`,`login_date`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `courses` + +CREATE TABLE `courses` ( + `course_id` mediumint(8) unsigned NOT NULL auto_increment, + `member_id` mediumint(8) unsigned NOT NULL default '0', + `cat_id` mediumint(8) unsigned NOT NULL default '0', + `content_packaging` enum('none','top','all') NOT NULL default 'top', + `access` enum('public','protected','private') NOT NULL default 'public', + `created_date` datetime NOT NULL default '0000-00-00 00:00:00', + `title` VARCHAR(255) NOT NULL , + `description` TEXT , + `course_dir_name` VARCHAR(255) NOT NULL, + `notify` tinyint(4) NOT NULL default '0', + `max_quota` varchar(30) NOT NULL default '', + `max_file_size` varchar(30) NOT NULL default '', + `hide` tinyint(4) NOT NULL default '0', + `copyright` TEXT , + `primary_language` varchar(5) NOT NULL default '', + `rss` tinyint NOT NULL default 0, + `icon` varchar(75) NOT NULL default '', + `home_links` TEXT , + `main_links` TEXT , + `side_menu` VARCHAR( 255 ) NOT NULL default '', + `release_date` datetime NOT NULL default '0000-00-00 00:00:00', + `end_date` datetime NOT NULL default '0000-00-00 00:00:00', + `banner` TEXT, + `home_view` tinyint NOT NULL DEFAULT 1, + PRIMARY KEY (`course_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `faq_topics` + +CREATE TABLE `faq_topics` ( + `topic_id` mediumint(8) NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `name` TEXT , + KEY `course_id` (`course_id`), + PRIMARY KEY (`topic_id`) +) TYPE = MYISAM; + +# -------------------------------------------------------- +# Table structure for table `faq_entries` +CREATE TABLE `faq_entries` ( + `entry_id` mediumint(8) NOT NULL auto_increment, + `topic_id` mediumint(8) NOT NULL default '0', + `revised_date` TIMESTAMP NOT NULL, + `approved` tinyint(4) NOT NULL default '0', + `question` TEXT , + `answer` TEXT , + PRIMARY KEY (`entry_id`) +) TYPE = MYISAM ; + +# -------------------------------------------------------- +# Table structure for table `feeds` +CREATE TABLE `feeds` ( + `feed_id` mediumint(8) unsigned NOT NULL auto_increment, + `url` varchar(255) NOT NULL default '', + PRIMARY KEY (`feed_id`) +) ; + +# -------------------------------------------------------- + +# +# Table structure for table `file_storage_groups` +# added 1.5.3 + +CREATE TABLE `file_storage_groups` ( + `group_id` MEDIUMINT UNSIGNED NOT NULL default 0, + PRIMARY KEY ( `group_id` ) +) TYPE = MYISAM; + +# +# Table structure for table `files` +# added 1.5.3 + +CREATE TABLE `files` ( + `file_id` mediumint(8) unsigned NOT NULL auto_increment, + `owner_type` tinyint(3) unsigned NOT NULL default '0', + `owner_id` mediumint(8) unsigned NOT NULL default '0', + `member_id` mediumint(8) unsigned NOT NULL default '0', + `folder_id` mediumint(8) unsigned NOT NULL default '0', + `parent_file_id` mediumint(8) unsigned NOT NULL default '0', + `date` TIMESTAMP NOT NULL, + `num_comments` tinyint(3) unsigned NOT NULL default '0', + `num_revisions` tinyint(3) unsigned NOT NULL default '0', + `file_name` varchar(80) NOT NULL default '', + `file_size` int(11) NOT NULL default '0', + `description` TEXT , + PRIMARY KEY (`file_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- + +# +# Table structure for table `files_comments` +# added 1.5.3 + +CREATE TABLE `files_comments` ( + `comment_id` mediumint(8) unsigned NOT NULL auto_increment, + `file_id` mediumint(8) unsigned NOT NULL default '0', + `member_id` mediumint(8) unsigned NOT NULL default '0', + `date` TIMESTAMP NOT NULL, + `comment` TEXT , + PRIMARY KEY (`comment_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- + +# +# Table structure for table `folders` +# added 1.5.3 + +CREATE TABLE `folders` ( + `folder_id` mediumint(8) unsigned NOT NULL auto_increment, + `parent_folder_id` mediumint(8) unsigned NOT NULL default '0', + `owner_type` tinyint(3) unsigned NOT NULL default '0', + `owner_id` mediumint(8) unsigned NOT NULL default '0', + `title` varchar(120) NOT NULL default '', + PRIMARY KEY (`folder_id`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- +# Table structure for table `forums` + +CREATE TABLE `forums` ( + `forum_id` mediumint(8) unsigned NOT NULL auto_increment, + `title` varchar(240) NOT NULL default '', + `description` TEXT , + `num_topics` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , + `num_posts` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , + `last_post` TIMESTAMP NOT NULL, + `mins_to_edit` SMALLINT UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`forum_id`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- +# Table structure for table `forums_accessed` + +CREATE TABLE `forums_accessed` ( + `post_id` mediumint(8) unsigned NOT NULL default '0', + `member_id` mediumint(8) unsigned NOT NULL default '0', + `last_accessed` timestamp NOT NULL, + `subscribe` tinyint(4) NOT NULL default '0', + PRIMARY KEY (`post_id`,`member_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `forums_courses` + +CREATE TABLE `forums_courses` ( + `forum_id` MEDIUMINT UNSIGNED NOT NULL default '0', + `course_id` MEDIUMINT UNSIGNED NOT NULL default '0', + PRIMARY KEY (`forum_id`,`course_id`), + KEY `course_id` (`course_id`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- +# Table structure for table `forums_groups` + +CREATE TABLE `forums_groups` ( +`forum_id` mediumint( 8 ) unsigned NOT NULL default '0', +`group_id` mediumint( 8 ) unsigned NOT NULL default '0', +PRIMARY KEY ( `forum_id` , `group_id` ) , +KEY `group_id` ( `group_id` ) +) TYPE = MYISAM ; + +# -------------------------------------------------------- +# Table structure for table `forums_subscriptions` +# + +CREATE TABLE `forums_subscriptions` ( + forum_id mediumint(8) unsigned NOT NULL default '0', + member_id mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`forum_id`,`member_id`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- +# Table structure for table `forums_threads` + +CREATE TABLE `forums_threads` ( + `post_id` mediumint(8) unsigned NOT NULL auto_increment, + `parent_id` mediumint(8) unsigned NOT NULL default '0', + `member_id` mediumint(8) unsigned NOT NULL default '0', + `forum_id` mediumint(8) unsigned NOT NULL default '0', + `last_comment` TIMESTAMP NOT NULL, + `num_comments` mediumint(8) unsigned NOT NULL default '0', + `subject` VARCHAR(255) NOT NULL , + `body` TEXT , + `date` TIMESTAMP NOT NULL, + `locked` tinyint(4) NOT NULL default '0', + `sticky` tinyint(4) NOT NULL default '0', + PRIMARY KEY (`post_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `glossary` + +CREATE TABLE `glossary` ( + `word_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `word` varchar(240) NOT NULL default '', + `definition` TEXT , + `related_word_id` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`word_id`), + KEY `course_id` (`course_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `groups` + +CREATE TABLE `groups` ( + `group_id` mediumint(8) unsigned NOT NULL auto_increment, + `type_id` mediumint(8) unsigned NOT NULL default '0', + `title` varchar(80) NOT NULL default '', + `description` TEXT , + `modules` varchar(100) NOT NULL default '', + PRIMARY KEY (`group_id`) +) TYPE = MYISAM; + + +# -------------------------------------------------------- +# Table structure for table `groups_members` + +CREATE TABLE `groups_members` ( +`group_id` MEDIUMINT UNSIGNED NOT NULL default '0', +`member_id` MEDIUMINT UNSIGNED NOT NULL default '0', + PRIMARY KEY (`group_id`,`member_id`) +) TYPE = MYISAM; + +# -------------------------------------------------------- +# Table structure for table `groups_types` (since 1.5.3) + +CREATE TABLE `groups_types` ( + `type_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `title` VARCHAR(80) NOT NULL , + PRIMARY KEY (`type_id`), + KEY `course_id` (`course_id`) +) TYPE = MYISAM; + +# -------------------------------------------------------- +# Table structure for table `guests` (since 1.6.2) +CREATE TABLE `guests` ( + `guest_id` VARCHAR(10) NOT NULL, + `name` VARCHAR(255), + `organization` VARCHAR(255), + `location` VARCHAR(255), + `role` VARCHAR(255), + `focus` VARCHAR(255), + PRIMARY KEY (`guest_id`) +) TYPE = MYISAM; + +# -------------------------------------------------------- +# Table structure for table `handbook_notes` + +CREATE TABLE `handbook_notes` ( +`note_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , +`date` TIMESTAMP NOT NULL, +`section` VARCHAR( 15 ) NOT NULL default '', +`page` VARCHAR( 50 ) NOT NULL default '', +`approved` tinyint NOT NULL default 0, +`email` VARCHAR( 50 ) NOT NULL default '', +`note` TEXT , +PRIMARY KEY ( `note_id` ) +) TYPE = MYISAM; + + +# -------------------------------------------------------- +# Table structure for table `instructor_approvals` + +CREATE TABLE `instructor_approvals` ( + `member_id` mediumint(8) unsigned NOT NULL default '0', + `request_date` TIMESTAMP NOT NULL, + `notes` TEXT , + PRIMARY KEY (`member_id`) +) TYPE=MyISAM; + + +CREATE TABLE `languages` ( + `language_code` varchar(20) NOT NULL default '', + `char_set` varchar(80) NOT NULL default '', + `direction` varchar(16) NOT NULL default '', + `reg_exp` varchar(124) NOT NULL default '', + `native_name` varchar(80) NOT NULL default '', + `english_name` varchar(80) NOT NULL default '', + `status` TINYINT UNSIGNED DEFAULT '0' NOT NULL, + PRIMARY KEY (`language_code`,`char_set`) +) TYPE=MyISAM; + +# +# Dumping data for table `languages` +# + +INSERT INTO `languages` VALUES ('en', 'utf-8', 'ltr', 'en([-_][[:alpha:]]{2})?|english', 'English', 'English', 3); + + +# Table structure for table `links_categories` + +CREATE TABLE `links_categories` ( + `cat_id` mediumint(8) unsigned NOT NULL auto_increment, + `owner_type` tinyint(4) NOT NULL default '0', + `owner_id` mediumint(8) unsigned NOT NULL default '0', + `name` VARCHAR(255) NOT NULL , + `parent_id` mediumint(8) unsigned default NULL, + PRIMARY KEY (`cat_id`), + KEY `owner_id` (`owner_id`) +) TYPE=MyISAM ; + + +# -------------------------------------------------------- +# Table structure for table `links` + +CREATE TABLE `links` ( + `link_id` mediumint(8) unsigned NOT NULL auto_increment, + `cat_id` mediumint(8) unsigned NOT NULL default '0', + `Url` varchar(255) NOT NULL default '', + `LinkName` varchar(64) NOT NULL default '', + `Description` TEXT , + `Approved` tinyint(8) default '0', + `SubmitName` varchar(64) NOT NULL default '', + `SubmitEmail` varchar(64) NOT NULL default '', + `SubmitDate` date NOT NULL default '0000-00-00', + `hits` int(11) default '0', + PRIMARY KEY (`link_id`) +) TYPE=MyISAM ; + +# -------------------------------------------------------- +# Table structure for table `language_pages` + +CREATE TABLE `language_pages` ( + `term` varchar(30) NOT NULL default '', + `page` varchar(50) NOT NULL default '', + PRIMARY KEY (`term`,`page`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `master_list` + +CREATE TABLE `master_list` ( + `public_field` CHAR( 30 ) NOT NULL default '', + `hash_field` CHAR( 40 ) NOT NULL default '', + `member_id` MEDIUMINT UNSIGNED NOT NULL default 0, + PRIMARY KEY ( `public_field` ) +) TYPE = MYISAM; + +# -------------------------------------------------------- +# Table structure for table `members` + +CREATE TABLE `members` ( + `member_id` mediumint(8) unsigned NOT NULL auto_increment, + `login` varchar(20) NOT NULL default '', + `password` varchar(40) NOT NULL default '', + `email` varchar(50) NOT NULL default '', + `website` varchar(200) NOT NULL default '', + `first_name` VARCHAR(100) NOT NULL , + `second_name` varchar(100) NOT NULL default '', + `last_name` VARCHAR(100) NOT NULL , + `dob` date NOT NULL default '0000-00-00', + `gender` enum('m','f','n') NOT NULL default 'n', + `address` TEXT , + `postal` varchar(15) NOT NULL default '', + `city` varchar(100) NOT NULL default '', + `province` varchar(100) NOT NULL default '', + `country` varchar(100) NOT NULL default '', + `phone` varchar(15) NOT NULL default '', + `status` tinyint(4) NOT NULL default '0', + `preferences` TEXT , + `creation_date` TIMESTAMP NOT NULL, + `language` varchar(5) NOT NULL default '', + `inbox_notify` tinyint(3) unsigned NOT NULL default '0', + `private_email` TINYINT DEFAULT '1' NOT NULL, + `last_login` TIMESTAMP NOT NULL, + PRIMARY KEY (`member_id`), + UNIQUE KEY `login` (`login`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `member_track` + +CREATE TABLE `member_track` ( + `member_id` mediumint(8) unsigned NOT NULL default '0', + `course_id` mediumint(8) unsigned NOT NULL default '0', + `content_id` mediumint(8) unsigned NOT NULL default '0', + `counter` mediumint(8) unsigned NOT NULL default '0', + `duration` mediumint(8) unsigned NOT NULL default '0', + `last_accessed` TIMESTAMP NULL, + KEY `member_id` (`member_id`), + KEY `content_id` (`content_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `messages` + +CREATE TABLE `messages` ( + `message_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `from_member_id` mediumint(8) unsigned NOT NULL default '0', + `to_member_id` mediumint(8) unsigned NOT NULL default '0', + `date_sent` TIMESTAMP NOT NULL, + `new` tinyint(4) NOT NULL default '0', + `replied` tinyint(4) NOT NULL default '0', + `subject` VARCHAR(255) NOT NULL , + `body` TEXT , + PRIMARY KEY (`message_id`), + KEY `to_member_id` (`to_member_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `messages_sent` (since 1.5.4) + +CREATE TABLE `messages_sent` ( + `message_id` mediumint( 8 ) unsigned NOT NULL AUTO_INCREMENT , + `course_id` mediumint( 8 ) unsigned NOT NULL default '0', + `from_member_id` mediumint( 8 ) unsigned NOT NULL default '0', + `to_member_id` mediumint( 8 ) unsigned NOT NULL default '0', + `date_sent` timestamp NOT NULL , + `subject` VARCHAR(255) NOT NULL , + `body` TEXT , + PRIMARY KEY ( `message_id` ) , + KEY `from_member_id` ( `from_member_id` ) +) TYPE = MYISAM; + +# -------------------------------------------------------- +# Table structure for table `modules` (since 1.5.2) + +CREATE TABLE `modules` ( + `dir_name` VARCHAR( 50 ) NOT NULL default '', + `status` TINYINT NOT NULL default 0, + `privilege` INT UNSIGNED NOT NULL default 0, + `admin_privilege` MEDIUMINT UNSIGNED NOT NULL default 0, + `cron_interval` SMALLINT UNSIGNED DEFAULT '0' NOT NULL , + `cron_last_run` INT UNSIGNED DEFAULT '0' NOT NULL, + PRIMARY KEY ( `dir_name` ) +) TYPE = MYISAM; + +INSERT INTO `modules` VALUES ('_core/properties', 2, 1, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/statistics', 2, 1, 0, 0, 0); +INSERT INTO `modules` VALUES ('_core/content', 2, 2, 0, 0, 0); +INSERT INTO `modules` VALUES ('_core/glossary', 2, 4, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/tests', 2, 8, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/chat', 2, 16, 0, 0, 0); +INSERT INTO `modules` VALUES ('_core/file_manager', 2, 32, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/links', 2, 64, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/forums', 2, 128, 16, 0, 0); +INSERT INTO `modules` VALUES ('_standard/course_tools', 2, 256, 0, 0, 0); +INSERT INTO `modules` VALUES ('_core/enrolment', 2, 512, 512, 0, 0); +INSERT INTO `modules` VALUES ('_standard/course_email', 2, 1024, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/announcements', 2, 2048, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/polls', 2, 16384, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/faq', 2, 32768, 0, 0, 0); +INSERT INTO `modules` VALUES ('_core/groups', 2, 65536, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/reading_list', 2, 131072, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/file_storage', 2, 262144, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/assignments', 2, 524288, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/gradebook', 2, 1048576, 4096, 0, 0); +INSERT INTO `modules` VALUES ('_standard/student_tools', 2, 2097152, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/farchive', 2, 4194304, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/social', 2, 8388608, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/photos', 2, 16777216, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/flowplayer', 2, 33554432, 0, 0, 0); +INSERT INTO `modules` VALUES ('_core/users', 2, 0, 2, 0, 0); +INSERT INTO `modules` VALUES ('_core/courses', 2, 0, 4, 0, 0); +INSERT INTO `modules` VALUES ('_core/backups', 2, 1, 8, 0, 0); +INSERT INTO `modules` VALUES ('_core/cats_categories', 2, 0, 32, 0, 0); +INSERT INTO `modules` VALUES ('_core/languages', 2, 0, 64, 1440, 0); +INSERT INTO `modules` VALUES ('_core/themes', 2, 0, 128, 0, 0); +INSERT INTO `modules` VALUES ('_standard/rss_feeds', 2, 0, 256, 0, 0); +INSERT INTO `modules` VALUES ('_standard/directory', 2, 0, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/tile_search', 2, 0, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/sitemap', 2, 0, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/tracker', 2, 0, 0, 0, 0); +INSERT INTO `modules` VALUES ('_core/content_packaging', 2, 0, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/google_search', 2, 0, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/blogs', 2, 0, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/profile_pictures', 2, 0, 0, 0, 0); +INSERT INTO `modules` VALUES ('_standard/patcher', 2, 0, 1024, 0, 0); +INSERT INTO `modules` VALUES ('_standard/support_tools', 2, 0, 2048, 0, 0); +# added by Bologna CC. Please check if it is the right position to insert it! +INSERT INTO `modules` VALUES ('_core/tool_manager', 2, 0, 0, 0, 0); +INSERT INTO `modules` VALUES ('_core/modules', 2, 0, 8192, 0, 0); + + + + +# -------------------------------------------------------- +# Table structure for table `news` + +CREATE TABLE `news` ( + `news_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `member_id` mediumint(8) unsigned NOT NULL default '0', + `date` TIMESTAMP NOT NULL, + `formatting` tinyint(4) NOT NULL default '0', + `title` VARCHAR(200) NOT NULL , + `body` TEXT , + PRIMARY KEY (`news_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- + +# Table structure for table `polls` +CREATE TABLE `polls` ( + `poll_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , + `course_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `question` VARCHAR(255) NOT NULL , + `created_date` TIMESTAMP NOT NULL, + `total` SMALLINT UNSIGNED NOT NULL default '0', + `choice1` VARCHAR(255) NOT NULL , + `count1` SMALLINT UNSIGNED NOT NULL default '0', + `choice2` VARCHAR(255) NOT NULL , + `count2` SMALLINT UNSIGNED NOT NULL default '0', + `choice3` VARCHAR(255) NOT NULL , + `count3` SMALLINT UNSIGNED NOT NULL default '0', + `choice4` VARCHAR(255) NOT NULL , + `count4` SMALLINT UNSIGNED NOT NULL default '0', + `choice5` VARCHAR(255) NOT NULL , + `count5` SMALLINT UNSIGNED NOT NULL default '0', + `choice6` VARCHAR(255) NOT NULL , + `count6` SMALLINT UNSIGNED NOT NULL default '0', + `choice7` VARCHAR(255) NOT NULL , + `count7` SMALLINT UNSIGNED NOT NULL default '0', + PRIMARY KEY ( `poll_id` ) , + INDEX ( `course_id` ) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `mail_queue` +# since 1.5.3 + +CREATE TABLE `mail_queue` ( + `mail_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , + `to_email` VARCHAR( 50 ) NOT NULL default '', + `to_name` VARCHAR( 50 ) NOT NULL default '', + `from_email` VARCHAR( 50 ) NOT NULL default '', + `from_name` VARCHAR( 50 ) NOT NULL default '', + `char_set` VARCHAR( 20 ) NOT NULL default '', + `subject` VARCHAR(255) NOT NULL , + `body` TEXT , + PRIMARY KEY ( `mail_id` ) +) TYPE = MYISAM; + +# -------------------------------------------------------- + +# Table structure for table `polls_members` + +CREATE TABLE `polls_members` ( + `poll_id` MEDIUMINT UNSIGNED NOT NULL default '0', + `member_id` MEDIUMINT UNSIGNED NOT NULL default '0', + PRIMARY KEY ( `poll_id` , `member_id` ) +) TYPE=MyISAM; + +# -------------------------------------------------------- + +# Table structure for table `related_content` +CREATE TABLE `related_content` ( + `content_id` mediumint(8) unsigned NOT NULL default '0', + `related_content_id` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`content_id`,`related_content_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Since 1.5.3 +# Table structure for table `reading_list` + +CREATE TABLE `reading_list` ( + `reading_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `course_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `resource_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `required` enum('required','optional') NOT NULL DEFAULT 'required', + `date_start` DATE NOT NULL DEFAULT '0000-00-00', + `date_end` DATE NOT NULL DEFAULT '0000-00-00', + `comment` TEXT , + PRIMARY KEY (`reading_id`), + INDEX (`course_id`) +) TYPE = MYISAM; + +# Since 1.5.3 +# Table structure for table `external_resources` + +CREATE TABLE `external_resources` ( + `resource_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `course_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `type` TINYINT UNSIGNED NOT NULL DEFAULT 0, + `title` VARCHAR(255) NOT NULL , + `author` VARCHAR(150) NOT NULL , + `publisher` VARCHAR(150) NOT NULL , + `date` varchar(20) NOT NULL DEFAULT '', + `comments` TEXT , + `id` varchar(50) NOT NULL DEFAULT '', + `url` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`resource_id`), + INDEX (`course_id`) +) TYPE = MYISAM; + + +# -------------------------------------------------------- +# Table structure for table `tests` + +CREATE TABLE `tests` ( + `test_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `title` VARCHAR(255) NOT NULL , + `format` tinyint(4) NOT NULL default '0', + `start_date` datetime NOT NULL default '0000-00-00 00:00:00', + `end_date` datetime NOT NULL default '0000-00-00 00:00:00', + `randomize_order` tinyint(4) NOT NULL default '0', + `num_questions` tinyint(3) unsigned NOT NULL default '0', + `instructions` TEXT , + `content_id` mediumint(8) NOT NULL default '0', + `result_release` tinyint(4) unsigned NOT NULL default '0', + `random` tinyint(4) unsigned NOT NULL default '0', + `difficulty` tinyint(4) unsigned NOT NULL default '0', + `num_takes` tinyint(4) unsigned NOT NULL default '0', + `anonymous` tinyint(4) NOT NULL default '0', + `out_of` varchar(4) NOT NULL default '', + `guests` TINYINT NOT NULL DEFAULT '0', + `display` TINYINT NOT NULL DEFAULT '0', + `description` TEXT, + `passscore` MEDIUMINT NOT NULL default '0', + `passpercent` MEDIUMINT NOT NULL default '0', + `passfeedback` TEXT, + `failfeedback` TEXT, + `show_guest_form` TINYINT(1) UNSIGNED NOT NULL default '0', + PRIMARY KEY (`test_id`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- +# Table structure for table `tests_answers` + +CREATE TABLE `tests_answers` ( + `result_id` mediumint(8) unsigned NOT NULL default '0', + `question_id` mediumint(8) unsigned NOT NULL default '0', + `member_id` mediumint(8) unsigned NOT NULL default '0', + `answer` TEXT , + `score` varchar(5) NOT NULL default '', + `notes` TEXT , + PRIMARY KEY (`result_id`,`question_id`,`member_id`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- +# Table structure for table `tests_groups` + +CREATE TABLE `tests_groups` ( + `test_id` MEDIUMINT UNSIGNED NOT NULL default '0', + `group_id` MEDIUMINT UNSIGNED NOT NULL default '0', + PRIMARY KEY (`test_id`,`group_id`), + KEY `test_id` (`test_id`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- +# Table structure for table `tests_questions` + +CREATE TABLE `tests_questions` ( + `question_id` mediumint(8) unsigned NOT NULL auto_increment, + `category_id` mediumint(8) unsigned NOT NULL default '0', + `course_id` mediumint(8) unsigned NOT NULL default '0', + `type` tinyint(3) unsigned NOT NULL default '0', + `feedback` TEXT , + `question` TEXT , + `choice_0` TEXT , + `choice_1` TEXT , + `choice_2` TEXT , + `choice_3` TEXT , + `choice_4` TEXT , + `choice_5` TEXT , + `choice_6` TEXT , + `choice_7` TEXT , + `choice_8` TEXT , + `choice_9` TEXT , + `answer_0` tinyint(4) NOT NULL default '0', + `answer_1` tinyint(4) NOT NULL default '0', + `answer_2` tinyint(4) NOT NULL default '0', + `answer_3` tinyint(4) NOT NULL default '0', + `answer_4` tinyint(4) NOT NULL default '0', + `answer_5` tinyint(4) NOT NULL default '0', + `answer_6` tinyint(4) NOT NULL default '0', + `answer_7` tinyint(4) NOT NULL default '0', + `answer_8` tinyint(4) NOT NULL default '0', + `answer_9` tinyint(4) NOT NULL default '0', + `option_0` TEXT , + `option_1` TEXT , + `option_2` TEXT , + `option_3` TEXT , + `option_4` TEXT , + `option_5` TEXT , + `option_6` TEXT , + `option_7` TEXT , + `option_8` TEXT , + `option_9` TEXT , + `properties` tinyint(4) NOT NULL default '0', + `content_id` mediumint(8) NOT NULL, + PRIMARY KEY (`question_id`), + KEY `category_id` (category_id) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `tests_questions_assoc` + +CREATE TABLE `tests_questions_assoc` ( + `test_id` mediumint(8) unsigned NOT NULL default '0', + `question_id` mediumint(8) unsigned NOT NULL default '0', + `weight` varchar(4) NOT NULL default '', + `ordering` mediumint(8) unsigned NOT NULL default '0', + `required` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`test_id`,`question_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `tests_questions_categories` + +CREATE TABLE `tests_questions_categories` ( + `category_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `title` char(200) NOT NULL default '', + PRIMARY KEY (`category_id`), + KEY `course_id` (`course_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `tests_results` + +CREATE TABLE `tests_results` ( + `result_id` mediumint(8) unsigned NOT NULL auto_increment, + `test_id` mediumint(8) unsigned NOT NULL default '0', + `member_id` VARCHAR(10) NOT NULL default '', + `date_taken` TIMESTAMP NOT NULL, + `final_score` char(5) NOT NULL default '', + `status` TINYINT NOT NULL DEFAULT '0', + `end_time` TIMESTAMP NOT NULL , + `max_pos` TINYINT UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`result_id`), + KEY `test_id` (`test_id`) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `themes` +# since 1.4.3 + +CREATE TABLE `themes` ( + `title` varchar(80) NOT NULL default '', + `version` varchar(10) NOT NULL default '', + `dir_name` varchar(20) NOT NULL default '', + `type` varchar(20) NOT NULL default 'Desktop', + `last_updated` date NOT NULL default '0000-00-00', + `extra_info` TEXT , + `status` tinyint(3) unsigned NOT NULL default '1', + PRIMARY KEY (`title`) +) TYPE = MYISAM; + + +# -------------------------------------------------------- +# Table structure for table `patches` +# since 1.6.1 + +CREATE TABLE `patches` ( + `patches_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `atutor_patch_id` VARCHAR(20) NOT NULL default '', + `applied_version` VARCHAR(10) NOT NULL default '', + `patch_folder` VARCHAR(250) NOT NULL default '', + `description` TEXT, + `available_to` VARCHAR(250) NOT NULL default '', + `sql_statement` TEXT, + `status` varchar(20) NOT NULL default '', + `remove_permission_files` TEXT, + `backup_files` TEXT, + `patch_files` TEXT, + `author` VARCHAR(255) NOT NULL, + `installed_date` datetime NOT NULL, + PRIMARY KEY (`patches_id`) +); + + +# -------------------------------------------------------- +# Table structure for table `patches_files` +# since 1.6.1 + +CREATE TABLE `patches_files` ( + `patches_files_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `patches_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `action` VARCHAR(20) NOT NULL default '', + `name` TEXT, + `location` VARCHAR(250) NOT NULL default '', + PRIMARY KEY (`patches_files_id`) +); + +# -------------------------------------------------------- +# Table structure for table `patches_files_actions` +# since 1.6.1 + +CREATE TABLE `patches_files_actions` ( + `patches_files_actions_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `patches_files_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `action` VARCHAR(20) NOT NULL default '', + `code_from` TEXT, + `code_to` TEXT, + PRIMARY KEY (`patches_files_actions_id`) +); + + + +# -------------------------------------------------------- +# New tables for patch creator +# since 1.6.1 +CREATE TABLE `myown_patches` ( + `myown_patch_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `atutor_patch_id` VARCHAR(20) NOT NULL default '', + `applied_version` VARCHAR(10) NOT NULL default '', + `description` TEXT, + `sql_statement` TEXT, + `status` varchar(20) NOT NULL default '', + `last_modified` datetime NOT NULL, + PRIMARY KEY (`myown_patch_id`) +); + +CREATE TABLE `myown_patches_dependent` ( + `myown_patches_dependent_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `myown_patch_id` MEDIUMINT UNSIGNED NOT NULL, + `dependent_patch_id` VARCHAR(50) NOT NULL default '', + PRIMARY KEY (`myown_patches_dependent_id`) +); + +CREATE TABLE `myown_patches_files` ( + `myown_patches_files_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `myown_patch_id` MEDIUMINT UNSIGNED NOT NULL, + `action` VARCHAR(20) NOT NULL default '', + `name` VARCHAR(250) NOT NULL, + `location` VARCHAR(250) NOT NULL default '', + `code_from` TEXT, + `code_to` TEXT, + `uploaded_file` TEXT, + PRIMARY KEY (`myown_patches_files_id`) +); + + + + + +# insert the default theme +INSERT INTO `themes` VALUES ('ATutor', '2.0', 'default', 'Desktop', NOW(), 'This is the default ATutor theme and cannot be deleted as other themes inherit from it. Please do not alter this theme directly as it would complicate upgrading. Instead, create a new theme derived from this one.', 2); +INSERT INTO `themes` VALUES ('Fluid', '2.0', 'fluid', 'Desktop', NOW(), 'Theme that implements the Fluid reorderer used to drag-and-drop the menu from side-to-side.', 1); +INSERT INTO `themes` VALUES ('ATutor Classic', '2.0', 'default_classic', 'Desktop', NOW(), 'This is the ATutor Classic theme which makes use of the custom Header and logo images. To customize those images you must edit the theme.cfg.php in this themes directory.', 1); +INSERT INTO `themes` VALUES ('Blumin', '2.0', 'blumin', 'Desktop', NOW(), 'This is the plone look-alike theme.', 1); +INSERT INTO `themes` VALUES ('Greenmin', '2.0', 'greenmin', 'Desktop', NOW(), 'This is the plone look-alike theme in green.', 1); +INSERT INTO `themes` VALUES ('ATutor 1.5', '2.0', 'default15', 'Desktop', NOW(), 'This is the 1.5 series default theme.', 1); +INSERT INTO `themes` VALUES ('ATutor 1.6', '2.0', 'default16', 'Desktop', NOW(), 'This is the 1.6 series default theme.', 1); +INSERT INTO `themes` VALUES ('Mobile', '2.0', 'mobile', 'Mobile', NOW(), 'This is the default theme for mobile devices.', 1); + +# -------------------------------------------------------- +# Table structure for table `users_online` + +CREATE TABLE `users_online` ( + `member_id` mediumint(8) unsigned NOT NULL default '0', + `course_id` mediumint(8) unsigned NOT NULL default '0', + `login` varchar(255) NOT NULL default '', + `expiry` int(10) unsigned NOT NULL default '0', + PRIMARY KEY (`member_id`) +) TYPE=HEAP MAX_ROWS=500; + +# -------------------------------------------------------- +# Table structure for table `auto_enroll` + +CREATE TABLE `auto_enroll` ( + `auto_enroll_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `associate_string` VARCHAR(10) NOT NULL, + `name` VARCHAR( 50 ) NOT NULL default '', + PRIMARY KEY ( `auto_enroll_id` ) +); + +# -------------------------------------------------------- +# Table structure for table `auto_enroll_courses` + +CREATE TABLE `auto_enroll_courses` ( + `auto_enroll_courses_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `auto_enroll_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `course_id` MEDIUMINT UNSIGNED NOT NULL default 0, + PRIMARY KEY ( `auto_enroll_courses_id` ) +); + + +#Setup Table for Access4All +CREATE TABLE `primary_resources` ( + `primary_resource_id` mediumint(8) unsigned NOT NULL auto_increment, + `content_id` mediumint(8) unsigned NOT NULL default '0', + `resource` TEXT, + `language_code` varchar(20) default NULL, + PRIMARY KEY (`primary_resource_id`) +) TYPE = MYISAM; + +CREATE TABLE `primary_resources_types` ( + `primary_resource_id` mediumint(8) unsigned NOT NULL, + `type_id` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`primary_resource_id`,`type_id`) +) TYPE = MYISAM; + +CREATE TABLE `resource_types` ( + `type_id` mediumint(8) unsigned NOT NULL auto_increment, + `type` TEXT, + PRIMARY KEY (`type_id`) +) TYPE = MYISAM; + +CREATE TABLE `secondary_resources` ( + `secondary_resource_id` mediumint(8) unsigned NOT NULL auto_increment, + `primary_resource_id` mediumint(8) unsigned NOT NULL, + `secondary_resource` TEXT, + `language_code` varchar(20) default NULL, + PRIMARY KEY (`secondary_resource_id`) +) TYPE = MYISAM; + +CREATE TABLE `secondary_resources_types` ( + `secondary_resource_id` mediumint(8) unsigned NOT NULL, + `type_id` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`secondary_resource_id`,`type_id`) +) TYPE = MYISAM; + +INSERT INTO `resource_types` VALUES +(1, 'auditory'), +(2, 'sign_language'), +(3, 'textual'), +(4, 'visual'); + +INSERT INTO `config` (`name`, `value`) VALUES('encyclopedia', 'http://www.wikipedia.org'); +INSERT INTO `config` (`name`, `value`) VALUES('dictionary', 'http://dictionary.reference.com/'); +INSERT INTO `config` (`name`, `value`) VALUES('thesaurus', 'http://thesaurus.reference.com/'); +INSERT INTO `config` (`name`, `value`) VALUES('atlas', 'http://maps.google.ca/'); +INSERT INTO `config` (`name`, `value`) VALUES('calculator', 'http://www.calculateforfree.com/'); +INSERT INTO `config` (`name`, `value`) VALUES('note_taking', 'http://www.aypwip.org/webnote/'); +INSERT INTO `config` (`name`, `value`) VALUES('abacas', 'http://www.mandarintools.com/abacus.html'); +INSERT INTO `config` (`name`, `value`) VALUES('transformable_uri', 'http://localhost/transformable/'); +INSERT INTO `config` (`name`, `value`) VALUES('transformable_web_service_id', '90c3cd6f656739969847f3a99ac0f3c7'); +INSERT INTO `config` (`name`, `value`) VALUES('transformable_oauth_expire', '93600'); + +#End Access4All setup + +# sql file for gradebook module + +CREATE TABLE `grade_scales` ( + `grade_scale_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `member_id` mediumint(8) unsigned NOT NULL default '0', + `scale_name` VARCHAR(255) NOT NULL default '', + `created_date` datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY ( `grade_scale_id` ) +); + +CREATE TABLE `grade_scales_detail` ( + `grade_scale_id` mediumint(8) unsigned NOT NULL, + `scale_value` VARCHAR(50) NOT NULL default '', + `percentage_from` MEDIUMINT NOT NULL default '0', + `percentage_to` MEDIUMINT NOT NULL default '0', + PRIMARY KEY (`grade_scale_id`, `scale_value`) +); + +CREATE TABLE `gradebook_tests` ( + `gradebook_test_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `id` mediumint(8) unsigned NOT NULL default '0' COMMENT 'Values: 0, tests.test_id or assignments.assignment_id. 0 for external tests/assignments. tests.test_id for ATutor tests, assignments.assignment_id for ATutor assignments.', + `type` VARCHAR(50) NOT NULL default '' COMMENT 'Values: ATutor Test, ATutor Assignment, External', + `course_id` mediumint(8) unsigned NOT NULL default '0' COMMENT 'Values: 0 or courses.course_id. Only has value for external tests/assignments. When ATutor internal assignments/tests/surveys, always 0.', + `title` VARCHAR(255) NOT NULL default '' COMMENT 'Values: Null or test name. Always null if ATutor internal assignments/tests/surveys.', + `due_date` datetime NOT NULL default '0000-00-00 00:00:00', + `grade_scale_id` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY ( `gradebook_test_id` ) +); + +CREATE TABLE `gradebook_detail` ( + `gradebook_test_id` mediumint(8) unsigned NOT NULL, + `member_id` mediumint(8) unsigned NOT NULL default '0', + `grade` VARCHAR(255) NOT NULL default '', + PRIMARY KEY (`gradebook_test_id`, `member_id`) +); + +INSERT INTO `grade_scales` (grade_scale_id, member_id, scale_name, created_date) values (1, 0, 'Letter Grade', now()); +INSERT INTO `grade_scales` (grade_scale_id, member_id, scale_name, created_date) values (2, 0, 'Competency 1', now()); +INSERT INTO `grade_scales` (grade_scale_id, member_id, scale_name, created_date) values (3, 0, 'Competency 2', now()); + +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'A+', 90, 100); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'A', 80, 89); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'B', 70, 79); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'C', 60, 69); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'D', 50, 59); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'E', 0, 49); + +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (2, 'Pass', 75, 100); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (2, 'Fail', 0, 74); + +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (3, 'Excellent', 80, 100); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (3, 'Good', 70, 79); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (3, 'Adequate', 60, 69); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (3, 'Inadequate', 0, 59); + +# END gradebook SQL + +# Create table for standalone student tools page + +CREATE TABLE `fha_student_tools` ( + `course_id` mediumint(8) unsigned NOT NULL, + `links` TEXT , + `home_view` tinyint NOT NULL DEFAULT 1, + PRIMARY KEY ( `course_id` ) +); + + +# Setup tables for Social Networking module +# Activities +CREATE TABLE `social_activities` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `application_id` INTEGER UNSIGNED NOT NULL, + `title` TEXT, + `created_date` TIMESTAMP NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Applications/ Gagdets table +CREATE TABLE `social_applications` ( + `id` INTEGER UNSIGNED, + `url` VARCHAR(255) NOT NULL DEFAULT '', + `title` VARCHAR(255) NOT NULL, + `height` INTEGER UNSIGNED, + `scrolling` INTEGER UNSIGNED, + `screenshot` VARCHAR(255) NOT NULL, + `thumbnail` VARCHAR(255) NOT NULL, + `author` VARCHAR(255) NOT NULL, + `author_email` VARCHAR(128) NOT NULL, + `description` TEXT, + `settings` TEXT, + `views` TEXT, + `last_updated` TIMESTAMP NOT NULL, + PRIMARY KEY (`url`) +) +ENGINE = MyISAM; + +# Application Settings, like storing the perference string. +CREATE TABLE `social_application_settings` ( + `application_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `name` VARCHAR(255) NOT NULL, + `value` TEXT, + PRIMARY KEY (`application_id`, `member_id`, `name`) +) +ENGINE = MyISAM; + +# Application members mapping +CREATE TABLE `social_members_applications` ( + `member_id` INTEGER UNSIGNED NOT NULL, + `application_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`member_id`, `application_id`) +) +ENGINE = MyISAM; + +# Friends table +CREATE TABLE `social_friends` ( + `member_id` INTEGER UNSIGNED NOT NULL, + `friend_id` INTEGER UNSIGNED NOT NULL, + `relationship` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`member_id`, `friend_id`) +) +ENGINE = MyISAM; + +# Friend requests table +CREATE TABLE `social_friend_requests` ( + `member_id` INTEGER UNSIGNED NOT NULL, + `friend_id` INTEGER UNSIGNED NOT NULL, + `relationship` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`member_id`, `friend_id`) +) +ENGINE = MyISAM; + +# Person Positions (jobs) +CREATE TABLE `social_member_position` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `title` VARCHAR(255) NOT NULL, + `company` VARCHAR(255) NOT NULL, + `from` VARCHAR(10) NOT NULL DEFAULT 0, + `to` VARCHAR(10) NOT NULL DEFAULT 0, + `description` TEXT, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Person education +CREATE TABLE `social_member_education` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `university` VARCHAR(255) NOT NULL, + `country` VARCHAR(128), + `province` VARCHAR(128), + `degree` VARCHAR(64), + `field` VARCHAR(64), + `from` VARCHAR(10) NOT NULL DEFAULT 0, + `to` VARCHAR(10) NOT NULL DEFAULT 0, + `description` TEXT, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Person related web sites +CREATE TABLE `social_member_websites` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `url` VARCHAR(255) NOT NULL, + `site_name` VARCHAR(255), + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Tracks visitor counts +CREATE TABLE `social_member_track` ( + `member_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `visitor_id` INTEGER UNSIGNED NOT NULL, + `timestamp` TIMESTAMP NOT NULL, + PRIMARY KEY (`member_id`, `visitor_id`, `timestamp`) +) +ENGINE = MyISAM; + +# Person additional information cojoint with the members table +CREATE TABLE `social_member_additional_information` ( + `member_id` INTEGER UNSIGNED NOT NULL, + `expertise` VARCHAR(255) NOT NULL, + `interests` TEXT, + `associations` TEXT, + `awards` TEXT, + `others` TEXT, + PRIMARY KEY (`member_id`) +) +ENGINE = MyISAM; + +# New Social Tables +CREATE TABLE `social_member_contact` ( + `contact_id` int(10) unsigned NOT NULL auto_increment, + `member_id` int(10) unsigned NOT NULL, + `con_name` varchar(200) NOT NULL, + `con_phone` varchar(15) NOT NULL, + `con_email` varchar(50) NOT NULL, + `con_address` text NOT NULL, + PRIMARY KEY (`contact_id`) +) ENGINE=MyISAM ; + + +CREATE TABLE `social_member_representation` ( + `rep_id` int(10) unsigned NOT NULL auto_increment, + `member_id` int(10) unsigned NOT NULL, + `rep_name` varchar(200) NOT NULL, + `rep_title` varchar(50) NOT NULL, + `rep_phone` varchar(15) NOT NULL, + `rep_email` varchar(50) NOT NULL, + `rep_address` text NOT NULL, + PRIMARY KEY (`rep_id`) +) ENGINE=MyISAM ; + + + +CREATE TABLE `social_member_personal` ( + `per_id` int(10) unsigned NOT NULL auto_increment, + `member_id` int(10) unsigned NOT NULL, + `per_weight` varchar(200) NOT NULL, + `per_height` varchar(50) NOT NULL, + `per_hair` varchar(15) NOT NULL, + `per_eyes` varchar(50) NOT NULL, + `per_ethnicity` varchar(50) NOT NULL, + `per_languages` varchar(255) NOT NULL, + `per_disabilities` varchar(255) NOT NULL, + PRIMARY KEY (`per_id`) +) ENGINE=MyISAM; + +# Privacy Control Preferences +CREATE TABLE `social_privacy_preferences` ( + `member_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `preferences` TEXT, + PRIMARY KEY (`member_id`) +) +ENGINE = MyISAM; + +# Social Group tables +CREATE TABLE `social_groups` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `type_id` INTEGER UNSIGNED NOT NULL, + `privacy` INTEGER UNSIGNED NOT NULL, + `name` VARCHAR(255) NOT NULL, + `logo` VARCHAR(255) NOT NULL, + `description` TEXT, + `created_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', + `last_updated` TIMESTAMP NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_activities` ( + `activity_id` INTEGER UNSIGNED NOT NULL, + `group_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`activity_id`, `group_id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_members` ( + `group_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`group_id`, `member_id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_invitations` ( + `sender_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `group_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`sender_id`, `member_id`, `group_id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_requests` ( + `sender_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `group_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`sender_id`, `member_id`, `group_id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_types` ( + `type_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `title` VARCHAR(127) NOT NULL, + PRIMARY KEY (`type_id`) +) +ENGINE = MyISAM; + +# CREATE TABLE `social_groups_forums` ( +# `group_id` INTEGER UNSIGNED NOT NULL, +# `forum_id` INTEGER UNSIGNED NOT NULL, +# PRIMARY KEY (`group_id`, `forum_id`) +# ) +# ENGINE = MyISAM; + +# Groups message board +CREATE TABLE `social_groups_board` ( + `id` int(10) unsigned NOT NULL auto_increment, + `member_id` int(10) unsigned NOT NULL, + `group_id` int(10) unsigned NOT NULL, + `body` TEXT, + `created_date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=MyISAM; + + +# Settings +CREATE TABLE `social_user_settings` ( + `member_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `app_settings` TEXT, + PRIMARY KEY (`member_id`) +) +ENGINE = MyISAM; + + +#====== Initial Data ======== +INSERT INTO social_groups_types SET title='business', type_id=1; +INSERT INTO social_groups_types SET title='common_interest', type_id=2; +INSERT INTO social_groups_types SET title='entertainment_arts', type_id=3; +INSERT INTO social_groups_types SET title='geography', type_id=4; +INSERT INTO social_groups_types SET title='internet_technology', type_id=5; +INSERT INTO social_groups_types SET title='organization', type_id=6; +INSERT INTO social_groups_types SET title='music', type_id=7; +INSERT INTO social_groups_types SET title='sports_recreation', type_id=8; + +# END Social Networking setup + +# Login attempt control table +CREATE TABLE `member_login_attempt` ( + `login` varchar(20) NOT NULL, + `attempt` tinyint(3) unsigned default NULL, + `expiry` int(10) unsigned default NULL, + PRIMARY KEY (`login`) +) ENGINE=MyISAM; + +# -------------------------------------------------------- +# Adding feature of blog subsription +# Table structure for table `blog_subscription` +# since 1.6.3 +CREATE TABLE `blog_subscription` ( + `group_id` MEDIUMINT NOT NULL , + `member_id` MEDIUMINT NOT NULL , + PRIMARY KEY (group_id,member_id) +) TYPE=MyISAM; + +# END Adding feature of blog subsription + +# -------------------------------------------------------- +# Adding feature of content pre-requisites +# Table structure for table `content_prerequisites` +# since 1.6.4 +CREATE TABLE `content_prerequisites` ( + `content_id` MEDIUMINT NOT NULL, + `type` varchar(50) NOT NULL DEFAULT '', + `item_id` MEDIUMINT NOT NULL, + PRIMARY KEY (content_id,type, item_id) +) TYPE=MyISAM; + +# END Adding feature of content pre-requisites + +# -------------------------------------------------------- +# Adding feature of oauth client +# Table structure for table `oauth_client_servers` +# since 1.6.5 + +CREATE TABLE `oauth_client_servers` ( + `oauth_server_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `oauth_server` VARCHAR(255) NOT NULL default '', + `consumer_key` TEXT NOT NULL , + `consumer_secret` TEXT NOT NULL , + `expire_threshold` INT NOT NULL default 0, + `create_date` datetime NOT NULL, + PRIMARY KEY ( `oauth_server_id` ), + UNIQUE INDEX idx_consumer ( `oauth_server` ) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `oauth_client_tokens` +# since 1.6.5 + +CREATE TABLE `oauth_client_tokens` ( + `oauth_server_id` MEDIUMINT UNSIGNED NOT NULL, + `token` VARCHAR(50) NOT NULL default '', + `token_type` VARCHAR(50) NOT NULL NOT NULL default '', + `token_secret` TEXT NOT NULL, + `member_id` mediumint(8) unsigned NOT NULL , + `assign_date` datetime NOT NULL, + PRIMARY KEY ( `oauth_server_id`, `token` ) +) TYPE=MyISAM; + +# END Adding feature of oauth client + + +# -------------- Photo Album Module Setup ---------------- + +# Photo Album Table +CREATE TABLE `pa_albums` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `location` VARCHAR(255) NOT NULL, + `description` TEXT NOT NULL, + `permission` TINYINT(1) UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `photo_id` INTEGER UNSIGNED NOT NULL, + `type_id` TINYINT(1) UNSIGNED NOT NULL, + `created_date` DATETIME NOT NULL, + `last_updated` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Photos Table +CREATE TABLE `pa_photos` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `description` TEXT, + `alt_text` TEXT, + `member_id` INTEGER UNSIGNED NOT NULL, + `album_id` INTEGER UNSIGNED NOT NULL, + `ordering` SMALLINT UNSIGNED NOT NULL, + `created_date` DATETIME NOT NULL, + `last_updated` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Course Album Table +CREATE TABLE `pa_course_album` ( + `course_id` INTEGER UNSIGNED, + `album_id` INTEGER UNSIGNED, + PRIMARY KEY (`course_id`, `album_id`) +) +ENGINE = MyISAM; + +# Photo Album Comments +CREATE TABLE `pa_album_comments` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `album_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `comment` TEXT NOT NULL, + `created_date` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Photo Comments +CREATE TABLE `pa_photo_comments` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `photo_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `comment` TEXT NOT NULL, + `created_date` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Initiali Config +INSERT INTO `config` VALUES ('pa_max_memory_per_member', '50'); + +# -------------- Photo Album Module Ends ----------------- \ No newline at end of file diff --git a/install/db/atutor_upgrade_1.0_to_1.1.sql b/install/db/atutor_upgrade_1.0_to_1.1.sql new file mode 100644 index 000000000..1b68c9469 --- /dev/null +++ b/install/db/atutor_upgrade_1.0_to_1.1.sql @@ -0,0 +1,182 @@ +############################################################### +# Database upgrade SQL from ATutor 1.0-Stable to ATutor 1.1 +############################################################### + +################################## +# `news` table + +ALTER TABLE `news` ADD `formatting` TINYINT DEFAULT '0' NOT NULL AFTER `date`; +UPDATE `news` SET formatting=1; + + +################################## +# `courses` table +ALTER TABLE `courses` ADD `preferences` TEXT NOT NULL , +ADD `header` TEXT NOT NULL , +ADD `footer` TEXT NOT NULL , +ADD `copyright` TEXT NOT NULL , +ADD `tracking` ENUM( 'on', 'off' ) DEFAULT 'off' NOT NULL ; + +################################## +# `g_click_data` +ALTER TABLE `g_click_data` ADD `course_id` MEDIUMINT UNSIGNED NOT NULL AFTER `member_id` ; +ALTER TABLE `g_click_data` CHANGE `timestamp` `timestamp` INT UNSIGNED NOT NULL ; +ALTER TABLE `g_click_data` ADD `duration` DOUBLE UNSIGNED NOT NULL ; + +################################## +# `g_refs` + +CREATE TABLE g_refs ( + g_id tinyint(4) default NULL, + reference varchar(65) default NULL, + KEY g_id (g_id) +) TYPE=MyISAM; + + +# data for `g_refs` + +INSERT INTO g_refs VALUES (1, 'Users Online'); +INSERT INTO g_refs VALUES (2, 'Local Menu'); +INSERT INTO g_refs VALUES (3, 'Global Menu'); +INSERT INTO g_refs VALUES (4, 'Related topic'); +INSERT INTO g_refs VALUES (5, 'Jump'); +INSERT INTO g_refs VALUES (6, 'Top/#bypass anchor'); +INSERT INTO g_refs VALUES (7, 'Sequence'); +INSERT INTO g_refs VALUES (8, 'Within sitemap'); +INSERT INTO g_refs VALUES (9, 'Global Home link'); +INSERT INTO g_refs VALUES (10, 'Breadcrumb'); +INSERT INTO g_refs VALUES (11, 'Headings'); +INSERT INTO g_refs VALUES (12, 'Embedded links'); +INSERT INTO g_refs VALUES (13, 'Table of contents'); +INSERT INTO g_refs VALUES (14, 'Home'); +INSERT INTO g_refs VALUES (15, 'Tools'); +INSERT INTO g_refs VALUES (16, 'Resources'); +INSERT INTO g_refs VALUES (17, 'Discussions'); +INSERT INTO g_refs VALUES (18, 'Help'); +INSERT INTO g_refs VALUES (19, 'Logout'); +INSERT INTO g_refs VALUES (20, 'Preferences'); +INSERT INTO g_refs VALUES (21, 'Inbox'); +INSERT INTO g_refs VALUES (22, 'Local major topic'); +INSERT INTO g_refs VALUES (23, 'To sitemap'); +INSERT INTO g_refs VALUES (24, 'Embedded glossary'); +INSERT INTO g_refs VALUES (25, 'Menu glossary'); +INSERT INTO g_refs VALUES (26, 'Local Home link'); +INSERT INTO g_refs VALUES (27, 'Print Compiler'); +INSERT INTO g_refs VALUES (28, 'My Tracker'); +INSERT INTO g_refs VALUES (29, 'Links DB'); +INSERT INTO g_refs VALUES (30, 'Session Start'); + +################################## +# `instructor_approvals` +ALTER TABLE `instructor_approvals` ADD `request_date` DATETIME NOT NULL , +ADD `notes` TEXT NOT NULL ; + + +################################## +# `messages` +ALTER TABLE `messages` ADD INDEX ( `to_member_id` ); + +################################## +# `preferences` +CREATE TABLE preferences ( + member_id mediumint(8) unsigned NOT NULL default '0', + course_id mediumint(8) unsigned NOT NULL default '0', + preferences text NOT NULL, + PRIMARY KEY (member_id,course_id) +) TYPE=MyISAM; + +################################## +# `tests` +CREATE TABLE tests ( + test_id mediumint(8) unsigned NOT NULL auto_increment, + course_id mediumint(8) unsigned NOT NULL default '0', + title varchar(100) NOT NULL default '', + format tinyint(4) NOT NULL default '0', + start_date datetime NOT NULL default '0000-00-00 00:00:00', + end_date datetime NOT NULL default '0000-00-00 00:00:00', + randomize_order tinyint(4) NOT NULL default '0', + num_questions tinyint(3) unsigned NOT NULL default '0', + instructions text NOT NULL, + PRIMARY KEY (test_id) +) TYPE=MyISAM; + + +################################## +# `tests_answers` +CREATE TABLE tests_answers ( + result_id mediumint(8) unsigned NOT NULL default '0', + question_id mediumint(8) unsigned NOT NULL default '0', + member_id mediumint(8) unsigned NOT NULL default '0', + answer text NOT NULL, + score varchar(5) NOT NULL default '', + notes text NOT NULL, + PRIMARY KEY (result_id,question_id,member_id) +) TYPE=MyISAM; + + +################################## +# `tests_questions` +CREATE TABLE tests_questions ( + question_id mediumint(8) unsigned NOT NULL auto_increment, + test_id mediumint(8) unsigned NOT NULL default '0', + course_id mediumint(8) unsigned NOT NULL default '0', + ordering tinyint(3) unsigned NOT NULL default '0', + type tinyint(3) unsigned NOT NULL default '0', + weight tinyint(3) unsigned NOT NULL default '0', + required tinyint(4) NOT NULL default '0', + feedback text NOT NULL, + question text NOT NULL, + choice_0 varchar(255) NOT NULL default '', + choice_1 varchar(255) NOT NULL default '', + choice_2 varchar(255) NOT NULL default '', + choice_3 varchar(255) NOT NULL default '', + choice_4 varchar(255) NOT NULL default '', + choice_5 varchar(255) NOT NULL default '', + choice_6 varchar(255) NOT NULL default '', + choice_7 varchar(255) NOT NULL default '', + choice_8 varchar(255) NOT NULL default '', + choice_9 varchar(255) NOT NULL default '', + answer_0 tinyint(4) NOT NULL default '0', + answer_1 tinyint(4) NOT NULL default '0', + answer_2 tinyint(4) NOT NULL default '0', + answer_3 tinyint(4) NOT NULL default '0', + answer_4 tinyint(4) NOT NULL default '0', + answer_5 tinyint(4) NOT NULL default '0', + answer_6 tinyint(4) NOT NULL default '0', + answer_7 tinyint(4) NOT NULL default '0', + answer_8 tinyint(4) NOT NULL default '0', + answer_9 tinyint(4) NOT NULL default '0', + answer_size tinyint(4) NOT NULL default '0', + PRIMARY KEY (question_id), + KEY test_id (test_id) +) TYPE=MyISAM; + +################################## +# table `tests_results` +CREATE TABLE tests_results ( + result_id mediumint(8) unsigned NOT NULL auto_increment, + test_id mediumint(8) unsigned NOT NULL default '0', + member_id mediumint(8) unsigned NOT NULL default '0', + date_taken datetime NOT NULL default '0000-00-00 00:00:00', + final_score char(5) NOT NULL default '', + PRIMARY KEY (result_id), + KEY test_id (test_id) +) TYPE=MyISAM; + +################################## +# `theme_settings` +CREATE TABLE theme_settings ( + theme_id tinyint(4) unsigned NOT NULL auto_increment, + name varchar(50) NOT NULL default '', + preferences text NOT NULL, + PRIMARY KEY (theme_id) +) TYPE=MyISAM; + +# data for `theme_settings` +INSERT INTO theme_settings VALUES (1, 'Accessbility', + +'a:16:{s:19:"PREF_MAIN_MENU_SIDE";i:2;s:14:"PREF_MAIN_MENU";i:0;s:10:"PREF_THEME";i:0;s:12:"PREF_DISPLAY";i:0;s:9:"PREF_TIPS";i:0;s:8:"PREF_SEQ";i:1;s:8:"PREF_TOC";i:2;s:14:"PREF_NUMBERING";i:0;s:11:"PREF_ONLINE";i:0;s:14:"PREF_SEQ_ICONS";i:2;s:14:"PREF_NAV_ICONS";i:2;s:16:"PREF_LOGIN_ICONS";i:2;s:13:"PREF_HEADINGS";i:0;s:16:"PREF_BREADCRUMBS";i:0;s:9:"PREF_FONT";i:0;s:15:"PREF_STYLESHEET";i:0;}'); +INSERT INTO theme_settings VALUES (2, 'Icons only', 'a:4:{s:14:"PREF_SEQ_ICONS";i:1;s:14:"PREF_NAV_ICONS";i:1;s:16:"PREF_LOGIN_ICONS";i:1;s:16:"PREF_BREADCRUMBS";i:1;}'); +INSERT INTO theme_settings VALUES (3, 'Both icons and text', 'a:5:{s:14:"PREF_MAIN_MENU";i:1;s:14:"PREF_SEQ_ICONS";i:0;s:14:"PREF_NAV_ICONS";i:0;s:16:"PREF_LOGIN_ICONS";i:0;s:16:"PREF_BREADCRUMBS";i:1;}'); +INSERT INTO theme_settings VALUES (4, 'ATutor Defaults', 'a:17:{s:10:\"PREF_STACK\";a:5:{i:0;s:1:\"0\";i:1;s:1:\"1\";i:2;s:1:\"2\";i:3;s:1:\"3\";i:4;s:1:\"4\";}s:14:\"PREF_MAIN_MENU\";i:1;s:9:\"PREF_MENU\";i:1;s:19:\"PREF_MAIN_MENU_SIDE\";i:2;s:8:\"PREF_SEQ\";i:3;s:8:\"PREF_TOC\";i:2;s:14:\"PREF_SEQ_ICONS\";i:0;s:14:\"PREF_NAV_ICONS\";i:0;s:16:\"PREF_LOGIN_ICONS\";i:0;s:9:\"PREF_FONT\";i:0;s:15:\"PREF_STYLESHEET\";i:0;s:14:\"PREF_NUMBERING\";i:0;s:13:\"PREF_HEADINGS\";i:0;s:16:\"PREF_BREADCRUMBS\";i:1;s:13:\"PREF_OVERRIDE\";i:0;s:9:\"PREF_HELP\";i:1;s:14:\"PREF_MINI_HELP\";i:1;}'); + diff --git a/install/db/atutor_upgrade_1.1_to_1.2.sql b/install/db/atutor_upgrade_1.1_to_1.2.sql new file mode 100644 index 000000000..1280af564 --- /dev/null +++ b/install/db/atutor_upgrade_1.1_to_1.2.sql @@ -0,0 +1,139 @@ +############################################################### +# Database upgrade SQL from ATutor 1.1-Stable to ATutor 1.2 +############################################################### + +# delete the old phpMyChat tables +DROP TABLE `c_ban_users`; +DROP TABLE `c_messages` ; +DROP TABLE `c_reg_users`; +DROP TABLE `c_users`; + +# -------------------------------------------------------- + +# empty the old g_refs and replace them with the new language enables ones + +DELETE FROM `g_refs`; +INSERT INTO `g_refs` VALUES (1, 'g_users_online'), +(2, 'g_local_menu'), +(3, 'g_global_menu'), +(4, 'g_related_topic'), +(5, 'g_jump'), +(6, 'g_top_bypass'), +(7, 'g_sequence'), +(8, 'g_within_sitemap'), +(9, 'g_global_home'), +(10, 'g_breadcrumb'), +(11, 'g_headings'), +(12, 'g_embedded_links'), +(13, 'g_table_of_contents'), +(14, 'g_home'), +(15, 'g_tools'), +(16, 'g_resources'), +(17, 'g_discussions'), +(18, 'g_help'), +(19, 'g_logout'), +(20, 'g_preferences'), +(21, 'g_inbox'), +(22, 'g_local_major_topic'), +(23, 'g_to_sitemap'), +(24, 'g_embedded_glossary'), +(25, 'g_menu_glossary'), +(26, 'g_local_home'), +(27, 'g_print_compiler'), +(28, 'g_my_tracker'), +(29, 'g_links_db'), +(30, 'g_session_start'), +(31, 'g_chat'), +(32, 'g_mytests'), +(33, 'g_new_thread'), +(34, 'g_forum_reply'), +(35, 'g_view_thread'); + +ALTER TABLE `members` ADD `language` VARCHAR( 10 ) NOT NULL ; +ALTER TABLE `g_click_data` CHANGE `timestamp` `timestamp` INT( 11 ) UNSIGNED DEFAULT '0' NOT NULL; + + +#---------------------------------------------------------------- + +#create the language tables and insert the default English language + +CREATE TABLE `lang2` ( + `lang` char(3) NOT NULL default '', + `variable` varchar(30) NOT NULL default '', + `key` varchar(50) NOT NULL default '', + `text` text NOT NULL, + `revised_date` datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY (`lang`,`variable`,`key`), + KEY `lang_variable` (`lang`,`variable`) +) TYPE=MyISAM; + +CREATE TABLE `lang_base` ( + `variable` varchar(30) NOT NULL default '', + `key` varchar(50) NOT NULL default '', + `text` text NOT NULL, + `revised_date` datetime NOT NULL default '0000-00-00 00:00:00', + `context` text NOT NULL, + PRIMARY KEY (`variable`,`key`) +) TYPE=MyISAM; + +CREATE TABLE `lang_base_pages` ( + `variable` varchar(30) NOT NULL default '', + `key` varchar(30) NOT NULL default '', + `page` varchar(50) NOT NULL default '', + PRIMARY KEY (`variable`,`key`,`page`) +) TYPE=MyISAM; + + +# -------------------------------------------------------- + +# replace the old learning concepts with the new language enabled one + +DROP TABLE `learning_concepts`; + +CREATE TABLE `learning_concepts` ( + `course_id` mediumint(8) unsigned NOT NULL default '0', + `tag` varchar(20) NOT NULL default '', + PRIMARY KEY (`tag`,`course_id`), + KEY `course_id` (`course_id`) +) TYPE=MyISAM; + +DELETE FROM `learning_concepts`; + +INSERT INTO `learning_concepts` VALUES (0, 'discussion'), +(0, 'do'), +(0, 'dont'), +(0, 'important'), +(0, 'information'), +(0, 'link'), +(0, 'listen'), +(0, 'project'), +(0, 'question'), +(0, 'read'), +(0, 'test'), +(0, 'think'), +(0, 'write'); +# -------------------------------------------------------- + +DELETE FROM `theme_settings`; + +INSERT INTO `theme_settings` VALUES (1, 'accessibility', 'a:16:{s:19:"PREF_MAIN_MENU_SIDE";i:2;s:14:"PREF_MAIN_MENU";i:0;s:10:"PREF_THEME";i:0;s:12:"PREF_DISPLAY";i:0;s:9:"PREF_TIPS";i:0;s:8:"PREF_SEQ";i:1;s:8:"PREF_TOC";i:2;s:14:"PREF_NUMBERING";i:0;s:11:"PREF_ONLINE";i:0;s:14:"PREF_SEQ_ICONS";i:2;s:14:"PREF_NAV_ICONS";i:2;s:16:"PREF_LOGIN_ICONS";i:2;s:13:"PREF_HEADINGS";i:0;s:16:"PREF_BREADCRUMBS";i:0;s:9:"PREF_FONT";i:0;s:15:"PREF_STYLESHEET";i:0;}'), +(2, 'icons_only', 'a:4:{s:14:"PREF_SEQ_ICONS";i:1;s:14:"PREF_NAV_ICONS";i:1;s:16:"PREF_LOGIN_ICONS";i:1;s:16:"PREF_BREADCRUMBS";i:1;}'), +(3, 'both_icons_and_text', 'a:5:{s:14:"PREF_MAIN_MENU";i:1;s:14:"PREF_SEQ_ICONS";i:0;s:14:"PREF_NAV_ICONS";i:0;s:16:"PREF_LOGIN_ICONS";i:0;s:16:"PREF_BREADCRUMBS";i:1;}'), +(4, 'atutor_defaults', 'a:18:{s:10:"PREF_STACK";a:5:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;}s:19:"PREF_MAIN_MENU_SIDE";i:2;s:8:"PREF_SEQ";i:3;s:14:"PREF_NUMBERING";i:1;s:8:"PREF_TOC";i:1;s:14:"PREF_SEQ_ICONS";i:1;s:14:"PREF_NAV_ICONS";i:0;s:16:"PREF_LOGIN_ICONS";i:0;s:13:"PREF_HEADINGS";i:1;s:16:"PREF_BREADCRUMBS";i:1;s:9:"PREF_FONT";i:0;s:15:"PREF_STYLESHEET";i:0;s:9:"PREF_HELP";i:1;s:14:"PREF_MINI_HELP";i:1;s:18:"PREF_CONTENT_ICONS";i:0;s:14:"PREF_MAIN_MENU";i:1;s:11:"PREF_ONLINE";i:1;s:9:"PREF_MENU";i:1;}'); +# -------------------------------------------------------- + +DROP TABLE users_online; + +CREATE TABLE users_online ( + member_id mediumint(8) unsigned NOT NULL default '0', + course_id mediumint(8) unsigned NOT NULL default '0', + login varchar(20) NOT NULL default '', + expiry int(10) unsigned NOT NULL default '0', + PRIMARY KEY (member_id) +) TYPE=HEAP MAX_ROWS=500; + + + + + + diff --git a/install/db/atutor_upgrade_1.2_to_1.3.sql b/install/db/atutor_upgrade_1.2_to_1.3.sql new file mode 100644 index 000000000..fa7afc475 --- /dev/null +++ b/install/db/atutor_upgrade_1.2_to_1.3.sql @@ -0,0 +1,86 @@ +############################################################### +# Database upgrade SQL from ATutor 1.2.x to ATutor 1.3 +############################################################### + + +# add two new fields to content table, change ints to medints + +ALTER TABLE `content` CHANGE `content_id` `content_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, CHANGE `content_parent_id` `content_parent_id` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL; + +ALTER TABLE `content` ADD `keywords` VARCHAR( 100 ) NOT NULL AFTER `release_date` , ADD `content_path` VARCHAR( 100 ) NOT NULL AFTER `keywords`; +#---------------------------------------------------------------- + +# create table `course_cats` + +CREATE TABLE course_cats ( + cat_id mediumint(8) unsigned NOT NULL auto_increment, + cat_name varchar(100) NOT NULL default '', + cat_parent mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (cat_id) +) TYPE=MyISAM; +#---------------------------------------------------------------- + +# add two new fields to courses table, then set default content_packaging for all records + +ALTER TABLE `courses` ADD `cat_id` MEDIUMINT( 8 ) UNSIGNED DEFAULT '0' NOT NULL AFTER `member_id` , +ADD `content_packaging` ENUM( 'none', 'top', 'all' ) DEFAULT 'top' NOT NULL AFTER `cat_id` ; + +UPDATE `courses` SET `content_packaging`='top' WHERE 1; +#---------------------------------------------------------------- + +# change ints to medints + +ALTER TABLE `g_click_data` CHANGE `from_cid` `from_cid` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , +CHANGE `to_cid` `to_cid` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL; +#---------------------------------------------------------------- + +# add one new record to g_refs + +INSERT INTO `g_refs` ( `g_id` , `reference` ) VALUES ('36', 'g_from_tracker'); +UPDATE `g_refs` SET reference='g_content_packaging' WHERE g_id=27; + +#---------------------------------------------------------------- + +# +# Table structure for table `lang2` +# + +DROP TABLE `lang2`; + +CREATE TABLE `lang2` ( + `lang` char(3) NOT NULL default '', + `variable` varchar(30) NOT NULL default '', + `key` varchar(50) NOT NULL default '', + `text` text NOT NULL, + `revised_date` datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY (`lang`,`variable`,`key`), + KEY `lang_variable` (`lang`,`variable`) +) TYPE=MyISAM; + + + +#---------------------------------------------------------------- + +# change id's to medium ints instead of ints in 'related_content` + +ALTER TABLE `related_content` CHANGE `content_id` `content_id` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , +CHANGE `related_content_id` `related_content_id` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL; + +#---------------------------------------------------------------- + +# change id's to medium ints instead of big ints in 'resource_categories` + +ALTER TABLE `resource_categories` CHANGE `CatID` `CatID` MEDIUMINT( 8 ) UNSIGNED NOT NULL AUTO_INCREMENT, CHANGE `CatParent` `CatParent` MEDIUMINT( 8 ) UNSIGNED DEFAULT NULL; +#---------------------------------------------------------------- + +# this one too + +ALTER TABLE `resource_links` CHANGE `LinkID` `LinkID` MEDIUMINT( 8 ) UNSIGNED NOT NULL AUTO_INCREMENT, CHANGE `CatID` `CatID` MEDIUMINT( 8 ) UNSIGNED DEFAULT '0' NOT NULL; +#---------------------------------------------------------------- + +# revise the theme_settings preferences for two records + +UPDATE `theme_settings` SET `preferences` = 'a:27:{s:10:"PREF_STACK";a:6:{i:0;s:1:"5";i:1;s:1:"0";i:2;s:1:"1";i:3;s:1:"3";i:4;s:1:"4";i:5;s:1:"2";}s:19:"PREF_MAIN_MENU_SIDE";i:2;s:8:"PREF_SEQ";i:1;s:14:"PREF_NUMBERING";i:1;s:8:"PREF_TOC";i:1;s:14:"PREF_SEQ_ICONS";i:2;s:14:"PREF_NAV_ICONS";i:2;s:16:"PREF_LOGIN_ICONS";i:2;s:13:"PREF_HEADINGS";i:0;s:16:"PREF_BREADCRUMBS";i:0;s:9:"PREF_FONT";i:0;s:15:"PREF_STYLESHEET";i:0;s:9:"PREF_HELP";i:0;s:14:"PREF_MINI_HELP";i:1;s:18:"PREF_CONTENT_ICONS";i:2;s:14:"PREF_MAIN_MENU";i:0;s:11:"PREF_ONLINE";i:0;s:9:"PREF_MENU";i:1;s:10:"PREF_THEME";i:0;s:12:"PREF_DISPLAY";i:0;s:9:"PREF_TIPS";i:0;s:13:"PREF_OVERRIDE";i:1;s:9:"PREF_EDIT";i:1;s:10:"PREF_LOCAL";i:0;s:13:"PREF_GLOSSARY";i:0;s:11:"PREF_SEARCH";i:1;s:12:"PREF_RELATED";i:0;}' WHERE `theme_id` = '1' LIMIT 1 ; + +UPDATE `theme_settings` SET `preferences` = 'a:27:{s:10:"PREF_STACK";a:6:{i:0;s:1:"0";i:1;s:1:"1";i:2;s:1:"2";i:3;s:1:"3";i:4;s:1:"4";i:5;s:1:"5";}s:19:"PREF_MAIN_MENU_SIDE";i:2;s:8:"PREF_SEQ";i:3;s:14:"PREF_NUMBERING";i:1;s:8:"PREF_TOC";i:1;s:14:"PREF_SEQ_ICONS";i:1;s:14:"PREF_NAV_ICONS";i:0;s:16:"PREF_LOGIN_ICONS";i:0;s:13:"PREF_HEADINGS";i:1;s:16:"PREF_BREADCRUMBS";i:1;s:9:"PREF_FONT";i:0;s:15:"PREF_STYLESHEET";i:0;s:9:"PREF_HELP";i:1;s:14:"PREF_MINI_HELP";i:1;s:18:"PREF_CONTENT_ICONS";i:0;s:14:"PREF_MAIN_MENU";i:1;s:11:"PREF_ONLINE";i:1;s:9:"PREF_MENU";i:1;s:13:"PREF_OVERRIDE";i:1;s:11:"PREF_SEARCH";i:1;s:10:"PREF_THEME";i:0;s:12:"PREF_DISPLAY";i:0;s:9:"PREF_TIPS";i:0;s:9:"PREF_EDIT";i:1;s:10:"PREF_LOCAL";i:0;s:13:"PREF_GLOSSARY";i:0;s:12:"PREF_RELATED";i:0;}' WHERE `theme_id` = '4' LIMIT 1 ; +#---------------------------------------------------------------- diff --git a/install/db/atutor_upgrade_1.3.2_to_1.4.sql b/install/db/atutor_upgrade_1.3.2_to_1.4.sql new file mode 100644 index 000000000..45abd4a75 --- /dev/null +++ b/install/db/atutor_upgrade_1.3.2_to_1.4.sql @@ -0,0 +1,44 @@ +############################################################### +# Database upgrade SQL from ATutor 1.3.2 to ATutor 1.4 +############################################################### + +# add new fields to course_enrollment table + +ALTER TABLE `course_enrollment` ADD `privileges` SMALLINT UNSIGNED NOT NULL AFTER `approved` , +ADD `role` varchar(35) NOT NULL default '' AFTER `privileges` ; + +ALTER TABLE `content` ADD `inherit_release_date` TINYINT UNSIGNED NOT NULL AFTER `text`; + + +# add new fields to forums table + +ALTER TABLE `forums` ADD `num_topics` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , +ADD `num_posts` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , +ADD `last_post` DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL ; + + +# add new fields to courses table + +ALTER TABLE `courses` ADD `banner_text` TEXT NOT NULL AFTER `copyright` , +ADD `banner_styles` TEXT NOT NULL AFTER `banner_text` ; + +# remove preferences table + +DROP TABLE `preferences`; + +# add new fields to tests and tests_questions: +ALTER TABLE `tests` ADD `content_id` MEDIUMINT UNSIGNED NOT NULL , +ADD `automark` TINYINT UNSIGNED NOT NULL , +ADD `random` TINYINT UNSIGNED NOT NULL , +ADD `difficulty` TINYINT UNSIGNED NOT NULL ; + +ALTER TABLE `tests_questions` ADD `content_id` mediumint(8) NOT NULL AFTER `answer_size` ; + + +# update `theme_settings` data + +UPDATE `theme_settings` SET preferences='a:24:{s:10:"PREF_STACK";a:6:{i:0;s:1:"5";i:1;s:1:"0";i:2;s:1:"1";i:3;s:1:"3";i:4;s:1:"4";i:5;s:1:"2";}s:19:"PREF_MAIN_MENU_SIDE";i:2;s:8:"PREF_SEQ";i:1;s:14:"PREF_NUMBERING";i:1;s:8:"PREF_TOC";i:1;s:14:"PREF_SEQ_ICONS";i:2;s:14:"PREF_NAV_ICONS";i:2;s:16:"PREF_LOGIN_ICONS";i:2;s:13:"PREF_HEADINGS";i:0;s:16:"PREF_BREADCRUMBS";i:0;s:9:"PREF_HELP";i:0;s:14:"PREF_MINI_HELP";i:1;s:18:"PREF_CONTENT_ICONS";i:2;s:14:"PREF_MAIN_MENU";i:0;s:11:"PREF_ONLINE";i:0;s:9:"PREF_MENU";i:1;s:10:"PREF_THEME";i:0;s:12:"PREF_DISPLAY";i:0;s:9:"PREF_TIPS";i:0;s:9:"PREF_EDIT";i:1;s:10:"PREF_LOCAL";i:0;s:13:"PREF_GLOSSARY";i:0;s:11:"PREF_SEARCH";i:1;s:12:"PREF_RELATED";i:0;}' WHERE theme_id=1; +UPDATE `theme_settings` SET preferences='a:4:{s:14:"PREF_SEQ_ICONS";i:1;s:14:"PREF_NAV_ICONS";i:1;s:16:"PREF_LOGIN_ICONS";i:1;s:16:"PREF_BREADCRUMBS";i:1;}' WHERE theme_id=2; +UPDATE `theme_settings` SET preferences='a:5:{s:14:"PREF_MAIN_MENU";i:1;s:14:"PREF_SEQ_ICONS";i:0;s:14:"PREF_NAV_ICONS";i:0;s:16:"PREF_LOGIN_ICONS";i:0;s:16:"PREF_BREADCRUMBS";i:1;}' WHERE theme_id=3; +UPDATE `theme_settings` SET preferences='a:17:{s:10:"PREF_STACK";a:5:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;}s:19:"PREF_MAIN_MENU_SIDE";i:2;s:8:"PREF_SEQ";i:3;s:14:"PREF_NUMBERING";i:1;s:8:"PREF_TOC";i:1;s:14:"PREF_SEQ_ICONS";i:0;s:14:"PREF_NAV_ICONS";i:0;s:16:"PREF_LOGIN_ICONS";i:0;s:13:"PREF_HEADINGS";i:1;s:16:"PREF_BREADCRUMBS";i:1;s:9:"PREF_HELP";i:1;s:14:"PREF_MINI_HELP";i:1;s:18:"PREF_CONTENT_ICONS";i:0;s:14:"PREF_MAIN_MENU";i:1;s:11:"PREF_ONLINE";i:1;s:9:"PREF_MENU";i:1;s:10:"PREF_THEME";s:7:"default";}' WHERE theme_id=4; + diff --git a/install/db/atutor_upgrade_1.3_to_1.3.2.sql b/install/db/atutor_upgrade_1.3_to_1.3.2.sql new file mode 100644 index 000000000..7bc18d4a0 --- /dev/null +++ b/install/db/atutor_upgrade_1.3_to_1.3.2.sql @@ -0,0 +1,12 @@ +############################################################### +# Database upgrade SQL from ATutor 1.3 to ATutor 1.3.2 +############################################################### + +# add new field to course_enrollment table + +ALTER TABLE `course_enrollment` ADD `last_cid` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL; + + +# add new field to content table + +ALTER TABLE `content` ADD `inherit_release_date` TINYINT UNSIGNED DEFAULT '0' NOT NULL; diff --git a/install/db/atutor_upgrade_1.4.1_to_1.4.2.sql b/install/db/atutor_upgrade_1.4.1_to_1.4.2.sql new file mode 100644 index 000000000..98eb63e9f --- /dev/null +++ b/install/db/atutor_upgrade_1.4.1_to_1.4.2.sql @@ -0,0 +1,11 @@ +############################################################### +# Database upgrade SQL from ATutor 1.4.1 to ATutor 1.4.2 +############################################################### + +# Table structure for table `course_cats` +ALTER TABLE `course_cats` ADD `theme` VARCHAR( 30 ) NOT NULL ; + + +# Table structure for table `tests` +ALTER TABLE `tests` ADD `num_takes` TINYINT UNSIGNED NOT NULL ; +ALTER TABLE `tests` ADD `anonymous` TINYINT DEFAULT '0' NOT NULL ; diff --git a/install/db/atutor_upgrade_1.4.2_to_1.4.3.sql b/install/db/atutor_upgrade_1.4.2_to_1.4.3.sql new file mode 100644 index 000000000..dacd992da --- /dev/null +++ b/install/db/atutor_upgrade_1.4.2_to_1.4.3.sql @@ -0,0 +1,156 @@ +############################################################### +# Database upgrade SQL from ATutor 1.4.2 to ATutor 1.4.3 +############################################################### + +CREATE TABLE `languages` ( + `language_code` varchar(5) NOT NULL default '', + `char_set` varchar(20) NOT NULL default '', + `direction` varchar(4) NOT NULL default '', + `reg_exp` varchar(31) NOT NULL default '', + `native_name` varchar(20) NOT NULL default '', + `english_name` varchar(20) NOT NULL default '', + `status` TINYINT UNSIGNED DEFAULT '0' NOT NULL, + PRIMARY KEY (`language_code`,`char_set`) +) TYPE=MyISAM; + +INSERT INTO `languages` VALUES ('en', 'iso-8859-1', 'ltr', 'en([-_][[:alpha:]]{2})?|english', 'English', 'English', 3); + +# -------------------------------------------------------- +# Table structure for table `language_pages` + +CREATE TABLE `language_pages` ( + `term` varchar(30) NOT NULL default '', + `page` varchar(50) NOT NULL default '', + PRIMARY KEY (`term`,`page`) +) TYPE=MyISAM; + +CREATE TABLE `themes` ( + `title` varchar(20) NOT NULL default '', + `version` varchar(10) NOT NULL default '', + `dir_name` varchar(20) NOT NULL default '', + `last_updated` date NOT NULL default '0000-00-00', + `extra_info` varchar(40) NOT NULL default '', + `status` tinyint(3) unsigned NOT NULL default '1', + PRIMARY KEY (`title`) +); + +# insert the default theme +INSERT INTO themes VALUES ('Atutor', '1.4.3', 'default', NOW(), 'This is the default Atutor theme.', 2); + + +# the backups table +CREATE TABLE `backups` ( + `backup_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `date` datetime NOT NULL default '0000-00-00 00:00:00', + `description` varchar(100) NOT NULL default '', + `file_size` int(10) unsigned NOT NULL default '0', + `system_file_name` varchar(50) NOT NULL default '', + `file_name` varchar(50) NOT NULL default '', + `contents` TEXT NOT NULL default '', + PRIMARY KEY (`backup_id`), + KEY `course_id` (`course_id`) +) TYPE=MyISAM; + + +##### changes to the `forums_*` tables ##### + +# the new course forums table +CREATE TABLE `forums_courses` ( + `forum_id` MEDIUMINT UNSIGNED NOT NULL default '0', + `course_id` MEDIUMINT UNSIGNED NOT NULL default '0', + PRIMARY KEY (`forum_id`,`course_id`), + KEY `course_id` (`course_id`) +) TYPE=MyISAM; + +# insert the current forums into the new table +INSERT INTO forums_courses SELECT forum_id, course_id FROM `forums`; + +# remove the old course_id from the forums table and forums_threads +ALTER TABLE `forums` DROP `course_id`; +ALTER TABLE `forums_threads` DROP `course_id`; + +DROP TABLE forums_subscriptions; + +# setup forum subscription +CREATE TABLE `forums_subscriptions` ( + forum_id mediumint(8) unsigned NOT NULL default '0', + member_id mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`forum_id`,`member_id`) +) TYPE=MyISAM; + + +ALTER TABLE `forums_accessed` ADD `subscribe` TINYINT NOT NULL ; + + +#adding alumni status +ALTER TABLE `course_enrollment` CHANGE `approved` `approved` ENUM( 'y', 'n', 'a' ) DEFAULT 'n' NOT NULL; + +UPDATE `theme_settings` SET `preferences` = 'a:25:{s:10:"PREF_STACK";a:8:{i:0;s:1:"0";i:1;s:1:"1";i:2;s:1:"2";i:3;s:1:"3";i:4;s:1:"4";i:5;s:1:"5";i:6;s:1:"6";i:7;s:1:"7";}s:19:"PREF_MAIN_MENU_SIDE";i:2;s:8:"PREF_SEQ";i:3;s:14:"PREF_NUMBERING";i:1;s:8:"PREF_TOC";i:1;s:14:"PREF_SEQ_ICONS";i:1;s:14:"PREF_NAV_ICONS";i:0;s:16:"PREF_LOGIN_ICONS";i:0;s:13:"PREF_HEADINGS";i:1;s:16:"PREF_BREADCRUMBS";i:1;s:9:"PREF_HELP";i:1;s:14:"PREF_MINI_HELP";i:1;s:18:"PREF_CONTENT_ICONS";i:0;s:14:"PREF_MAIN_MENU";i:1;s:11:"PREF_ONLINE";i:1;s:9:"PREF_MENU";i:1;s:10:"PREF_THEME";s:7:"default";s:9:"PREF_EDIT";i:1;s:18:"PREF_JUMP_REDIRECT";i:0;s:10:"PREF_LOCAL";i:1;s:12:"PREF_RELATED";i:1;s:13:"PREF_GLOSSARY";i:1;s:11:"PREF_SEARCH";i:1;s:10:"PREF_POSTS";i:1;s:9:"PREF_POLL";i:1;}' WHERE `theme_id` = '4'; + +###### changes to the `tests_*` tables ##### +CREATE TABLE `tests_questions_assoc` ( + `test_id` mediumint(8) unsigned NOT NULL default '0', + `question_id` mediumint(8) unsigned NOT NULL default '0', + `weight` varchar(4) NOT NULL default '', + `ordering` tinyint(3) unsigned NOT NULL default '0', + `required` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`test_id`,`question_id`), + KEY `test_id` (`test_id`) +) TYPE=MyISAM; + +CREATE TABLE `tests_questions_categories` ( + `category_id` mediumint(8) unsigned NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `title` char(50) NOT NULL default '', + PRIMARY KEY (`category_id`), + KEY `course_id` (`course_id`) +) TYPE=MyISAM; + +ALTER TABLE `tests` ADD INDEX ( `course_id` ); + +INSERT INTO `tests_questions_assoc` SELECT test_id, question_id, weight, ordering, required FROM `tests_questions`; + +ALTER TABLE `tests_questions` CHANGE `test_id` `category_id` MEDIUMINT( 8 ) UNSIGNED DEFAULT '0' NOT NULL; +ALTER TABLE `tests_questions` CHANGE `answer_size` `properties` TINYINT( 4 ) DEFAULT '0' NOT NULL; +ALTER TABLE `tests_questions` DROP `ordering`, DROP `required`, DROP `weight`; +UPDATE `tests_questions` SET `category_id`=0; + +##### new `groups_*` tables ##### +CREATE TABLE `groups` ( +`group_id` MEDIUMINT UNSIGNED NOT NULL auto_increment, +`course_id` MEDIUMINT UNSIGNED NOT NULL default '0', +`title` varchar(20) NOT NULL default '', +PRIMARY KEY ( `group_id` ), +KEY `course_id` (`course_id`) +) TYPE=MyISAM; + + +CREATE TABLE `groups_members` ( +`group_id` MEDIUMINT UNSIGNED NOT NULL default '0', +`member_id` MEDIUMINT UNSIGNED NOT NULL default '0', + PRIMARY KEY (`group_id`,`member_id`) +); + + +CREATE TABLE `tests_groups` ( + `test_id` MEDIUMINT UNSIGNED NOT NULL default '0', + `group_id` MEDIUMINT UNSIGNED NOT NULL default '0', + PRIMARY KEY (`test_id`,`group_id`), + KEY `test_id` (`test_id`) +) TYPE=MyISAM; + + +# Add tracking g for the search tool +INSERT INTO g_refs VALUES (37, 'g_search'); + +# Change automark to selective release field +ALTER TABLE `tests` CHANGE `automark` `result_release` TINYINT( 4 ) UNSIGNED DEFAULT '0' NOT NULL; +ALTER TABLE `tests` ADD `out_of` VARCHAR( 5 ) NOT NULL ; + +UPDATE tests SET result_release=0; +DROP TABLE `lang2`; +DROP TABLE `lang_base`; + +# add RSS feeds to courses +ALTER TABLE `courses` ADD `rss` TINYINT DEFAULT '0' NOT NULL ; \ No newline at end of file diff --git a/install/db/atutor_upgrade_1.4.3_to_1.5.sql b/install/db/atutor_upgrade_1.4.3_to_1.5.sql new file mode 100644 index 000000000..2531c0cd0 --- /dev/null +++ b/install/db/atutor_upgrade_1.4.3_to_1.5.sql @@ -0,0 +1,80 @@ +############################################################### +# Database upgrade SQL from ATutor 1.4.3 to ATutor 1.5 +############################################################### + +ALTER TABLE `courses` ADD `icon` VARCHAR( 20 ) NOT NULL , ADD `home_links` VARCHAR( 255 ) NOT NULL , ADD `main_links` VARCHAR( 255 ) NOT NULL , ADD `side_menu` VARCHAR( 255 ) NOT NULL; + +UPDATE `courses` SET home_links='forum/list.php|glossary/index.php|chat/index.php|tile.php|links/index.php|tools/my_tests.php|sitemap.php|export.php|my_stats.php|polls/index.php|directory.php'; +UPDATE `courses` SET main_links='forum/list.php|glossary/index.php'; +UPDATE `courses` SET side_menu ='menu_menu|related_topics|users_online|glossary|search|poll|posts'; + +#fix for backup file names +ALTER TABLE `backups` CHANGE `file_name` `file_name` VARCHAR( 150 ) NOT NULL; + + +CREATE TABLE `member_track` ( + `member_id` mediumint(8) unsigned NOT NULL default '0', + `course_id` mediumint(8) unsigned NOT NULL default '0', + `content_id` mediumint(8) unsigned NOT NULL default '0', + `counter` mediumint(8) unsigned NOT NULL default '0', + `duration` mediumint(8) unsigned NOT NULL default '0', + `last_accessed` datetime default NULL, + KEY `member_id` (`member_id`), + KEY `content_id` (`content_id`) +) TYPE=MyISAM; + + +CREATE TABLE `admins` ( + `login` VARCHAR( 30 ) NOT NULL , + `password` VARCHAR( 30 ) NOT NULL , + `real_name` VARCHAR( 30 ) NOT NULL , + `email` VARCHAR( 50 ) NOT NULL , + `language` varchar(5) NOT NULL default '', + `privileges` MEDIUMINT UNSIGNED NOT NULL , + `last_login` DATETIME NOT NULL , + PRIMARY KEY ( `login` ) +); + +-- Table structure for table `admin_log` + +CREATE TABLE `admin_log` ( + `login` varchar(30) NOT NULL default '', + `time` datetime NOT NULL default '0000-00-00 00:00:00', + `operation` varchar(20) NOT NULL default '', + `table` varchar(30) NOT NULL default '', + `num_affected` tinyint(3) NOT NULL default '0', + `details` varchar(255) NOT NULL default '', + KEY `login` (`login`) +) TYPE=MyISAM; + +ALTER TABLE `courses` DROP `tracking` ; + +ALTER TABLE `members` ADD `inbox_notify` TINYINT(3) UNSIGNED DEFAULT '0' NOT NULL ; +## instructors: +UPDATE `members` SET `status`=3 WHERE `status`=1; +## students: +UPDATE `members` SET `status`=2 WHERE `status`=0; + +DROP TABLE `learning_concepts`; +DROP TABLE `theme_settings`; + +ALTER TABLE `courses` CHANGE `primary_language` `primary_language` VARCHAR( 5 ) NOT NULL; +ALTER TABLE `members` CHANGE `language` `language` VARCHAR( 5 ) NOT NULL; + +UPDATE `themes` SET status=0; +ALTER TABLE `themes` CHANGE `extra_info` `extra_info` VARCHAR( 255 ) NOT NULL; +REPLACE INTO `themes` VALUES ('ATutor', '1.5', 'default', NOW(), 'This is the default ATutor theme and cannot be deleted as other themes inherit from it. Please do not alter this theme directly as it would complicate upgrading. Instead, create a new theme derived from this one.', 2); + +REPLACE INTO `themes` VALUES ('ATutor Classic', '1.5', 'default_classic', NOW(), 'This is the ATutor Classic theme which makes use of the custom Header and logo images. To customize those images you must edit the theme.cfg.php in this theme\'s directory.', 1); + + +ALTER TABLE `messages` ADD `course_id` MEDIUMINT( 8 ) UNSIGNED DEFAULT '0' NOT NULL AFTER `message_id` ; + + +# Table structure for table `master_list` +CREATE TABLE `master_list` ( + `public_field` CHAR( 30 ) NOT NULL , + `hash_field` CHAR( 40 ) NOT NULL , + `member_id` MEDIUMINT UNSIGNED NOT NULL 0, + PRIMARY KEY ( `public_field` ) +); diff --git a/install/db/atutor_upgrade_1.4_to_1.4.1.sql b/install/db/atutor_upgrade_1.4_to_1.4.1.sql new file mode 100644 index 000000000..4d86e24de --- /dev/null +++ b/install/db/atutor_upgrade_1.4_to_1.4.1.sql @@ -0,0 +1,45 @@ +############################################################### +# Database upgrade SQL from ATutor 1.4 to ATutor 1.4.1 +############################################################### + +# Table structure for table `polls` +CREATE TABLE `polls` ( + `poll_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , + `course_id` MEDIUMINT UNSIGNED NOT NULL , + `question` VARCHAR( 100 ) NOT NULL , + `created_date` DATETIME NOT NULL , + `total` SMALLINT UNSIGNED NOT NULL , + `choice1` VARCHAR( 100 ) NOT NULL , + `count1` SMALLINT UNSIGNED NOT NULL , + `choice2` VARCHAR( 100 ) NOT NULL , + `count2` SMALLINT UNSIGNED NOT NULL , + `choice3` VARCHAR( 100 ) NOT NULL , + `count3` SMALLINT UNSIGNED NOT NULL , + `choice4` VARCHAR( 100 ) NOT NULL , + `count4` SMALLINT UNSIGNED NOT NULL , + `choice5` VARCHAR( 100 ) NOT NULL , + `count5` SMALLINT UNSIGNED NOT NULL , + `choice6` VARCHAR( 100 ) NOT NULL , + `count6` SMALLINT UNSIGNED NOT NULL , + `choice7` VARCHAR( 100 ) NOT NULL , + `count7` SMALLINT UNSIGNED NOT NULL , + PRIMARY KEY ( `poll_id` ) , + INDEX ( `course_id` ) +) TYPE=MyISAM; + +# -------------------------------------------------------- + +# Table structure for table `polls_members` + +CREATE TABLE `polls_members` ( + `poll_id` MEDIUMINT UNSIGNED NOT NULL , + `member_id` MEDIUMINT UNSIGNED NOT NULL , + PRIMARY KEY ( `poll_id` , `member_id` ) +) TYPE=MyISAM; + + +# Change age to date of birth +ALTER TABLE `members` CHANGE `age` `dob` DATE NOT NULL; + +# Add `primary_language` to the `courses` table +ALTER TABLE `courses` ADD `primary_language` VARCHAR( 4 ) DEFAULT 'en' NOT NULL; diff --git a/install/db/atutor_upgrade_1.5.1_to_1.5.2.sql b/install/db/atutor_upgrade_1.5.1_to_1.5.2.sql new file mode 100644 index 000000000..586730f4a --- /dev/null +++ b/install/db/atutor_upgrade_1.5.1_to_1.5.2.sql @@ -0,0 +1,91 @@ +############################################################### +# Database upgrade SQL from ATutor 1.5.1 to ATutor 1.5.2 +############################################################### + +# -------------------------------------------------------- +# Table structure for table `faq_topics` + +CREATE TABLE `faq_topics` ( + `topic_id` mediumint(8) NOT NULL auto_increment, + `course_id` mediumint(8) unsigned NOT NULL default '0', + `name` varchar(250) NOT NULL default '', + KEY `course_id` (`course_id`), + PRIMARY KEY (`topic_id`) +) ; + +# -------------------------------------------------------- +# Table structure for table `faq_entries` +CREATE TABLE `faq_entries` ( + `entry_id` mediumint(8) NOT NULL auto_increment, + `topic_id` mediumint(8) NOT NULL default '0', + `revised_date` datetime NOT NULL default '0000-00-00 00:00:00', + `approved` tinyint(4) NOT NULL default '0', + `question` varchar(250) NOT NULL default '', + `answer` text NOT NULL, + PRIMARY KEY (`entry_id`) +) ; + +# -------------------------------------------------------- +# Table structure for table `feeds` +CREATE TABLE `feeds` ( + `feed_id` mediumint(8) unsigned NOT NULL auto_increment, + `url` varchar(255) NOT NULL default '', + PRIMARY KEY (`feed_id`) +) ; + + + +# Table structure for table `config` + +CREATE TABLE `config` ( + `name` CHAR( 30 ) NOT NULL , + `value` CHAR( 255 ) NOT NULL , +PRIMARY KEY ( `name` ) +); + +# modules + +CREATE TABLE `modules` ( +`dir_name` VARCHAR( 50 ) NOT NULL , +`status` TINYINT NOT NULL , +`privilege` MEDIUMINT UNSIGNED NOT NULL , +`admin_privilege` MEDIUMINT UNSIGNED NOT NULL , +PRIMARY KEY ( `dir_name` ) +); + +INSERT INTO `modules` VALUES ('_core/properties', 2, 1, 0); +INSERT INTO `modules` VALUES ('_standard/statistics', 2, 1, 0); +INSERT INTO `modules` VALUES ('_core/content', 2, 2, 0); +INSERT INTO `modules` VALUES ('_core/glossary', 2, 4, 0); +INSERT INTO `modules` VALUES ('_standard/tests', 2, 8, 0); +INSERT INTO `modules` VALUES ('_standard/chat', 2, 16, 0); +INSERT INTO `modules` VALUES ('_core/file_manager', 2, 32, 0); +INSERT INTO `modules` VALUES ('_standard/links', 2, 64, 0); +INSERT INTO `modules` VALUES ('_standard/forums', 2, 128, 16); +INSERT INTO `modules` VALUES ('_standard/student_tools', 2, 256, 0); +INSERT INTO `modules` VALUES ('_core/enrolment', 2, 512, 0); +INSERT INTO `modules` VALUES ('_standard/course_email', 2, 1024, 0); +INSERT INTO `modules` VALUES ('_standard/announcements', 2, 2048, 0); +# INSERT INTO `modules` VALUES ('acollab', 2, 8192+4096, 0); +INSERT INTO `modules` VALUES ('_standard/polls', 2, 16384, 0); +INSERT INTO `modules` VALUES ('_standard/faq', 2, 32768, 0); +INSERT INTO `modules` VALUES ('_core/users', 2, 0, 2); +INSERT INTO `modules` VALUES ('_core/courses', 2, 0, 4); +INSERT INTO `modules` VALUES ('_core/backups', 2, 1, 8); +INSERT INTO `modules` VALUES ('_core/cats_categories', 2, 0, 32); +INSERT INTO `modules` VALUES ('_core/languages', 2, 0, 64); +INSERT INTO `modules` VALUES ('_core/themes', 2, 0, 128); +INSERT INTO `modules` VALUES ('_standard/rss_feeds', 2, 0, 256); +INSERT INTO `modules` VALUES ('_core/groups', 2, 0, 0); +INSERT INTO `modules` VALUES ('_standard/directory', 2, 0, 0); +INSERT INTO `modules` VALUES ('_standard/tile_search', 2, 0, 0); +INSERT INTO `modules` VALUES ('_standard/sitemap', 2, 0, 0); +INSERT INTO `modules` VALUES ('_standard/tracker', 2, 0, 0); +INSERT INTO `modules` VALUES ('_core/content_packaging', 2, 0, 0); +INSERT INTO `modules` VALUES ('_standard/google_search', 2, 0, 0); + + +ALTER TABLE `admin_log` CHANGE `details` `details` TEXT NOT NULL; + + +ALTER TABLE `courses` CHANGE `home_links` `home_links` TEXT NOT NULL ,CHANGE `main_links` `main_links` TEXT NOT NULL; diff --git a/install/db/atutor_upgrade_1.5.2_to_1.5.3.sql b/install/db/atutor_upgrade_1.5.2_to_1.5.3.sql new file mode 100644 index 000000000..15371b18c --- /dev/null +++ b/install/db/atutor_upgrade_1.5.2_to_1.5.3.sql @@ -0,0 +1,216 @@ +############################################################### +# Database upgrade SQL from ATutor 1.5.2 to ATutor 1.5.3 +############################################################### + +CREATE TABLE `groups_types` ( + `type_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , + `course_id` MEDIUMINT UNSIGNED NOT NULL default '0', + `title` VARCHAR( 80 ) NOT NULL default '', + PRIMARY KEY ( `type_id` ) , + KEY ( `course_id` ) +) TYPE = MYISAM; + +ALTER TABLE `groups` CHANGE `course_id` `type_id` MEDIUMINT( 8 ) UNSIGNED DEFAULT '0' NOT NULL; +ALTER TABLE `groups` ADD `description` TEXT NOT NULL default '' , ADD `modules` VARCHAR(100) NOT NULL default ''; + +UPDATE `modules` SET `privilege`=1048576 WHERE `dir_name`='_core/groups'; +INSERT INTO `modules` VALUES ('_standard/reading_list', 2, 131072, 0); +INSERT INTO `modules` VALUES ('_standard/file_storage', 2, 262144, 0); +INSERT INTO `modules` VALUES ('_standard/assignments', 2, 524288, 0); + +# cron support for modules +#Dec 6, 2007 - duplicated column name +ALTER TABLE `modules` ADD `cron_interval` SMALLINT UNSIGNED DEFAULT '0' NOT NULL , ADD `cron_last_run` INT UNSIGNED DEFAULT '0' NOT NULL ; + + +# forum groups table +CREATE TABLE `forums_groups` ( + `forum_id` mediumint( 8 ) unsigned NOT NULL default '0', + `group_id` mediumint( 8 ) unsigned NOT NULL default '0', + PRIMARY KEY ( `forum_id` , `group_id` ) , + KEY `group_id` ( `group_id` ) +) TYPE = MYISAM ; + +# release date for courses +ALTER TABLE `courses` ADD `release_date` datetime NOT NULL default '0000-00-00 00:00:00'; +ALTER TABLE `courses` ADD `banner` TEXT NOT NULL default ''; + +# -------------------------------------------------------- +# Table structure for table `reading_list` + +CREATE TABLE `reading_list` ( + `reading_id` MEDIUMINT(6) UNSIGNED NOT NULL AUTO_INCREMENT, + `course_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `resource_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `required` enum('required','optional') NOT NULL DEFAULT 'required', + `date_start` DATE NOT NULL DEFAULT '0000-00-00', + `date_end` DATE NOT NULL DEFAULT '0000-00-00', + `comment` text NOT NULL default '', + PRIMARY KEY (`reading_id`), + INDEX (`course_id`) +) TYPE = MYISAM; + +# Table structure for table `external_resources` + +CREATE TABLE `external_resources` ( + `resource_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `course_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `type` TINYINT UNSIGNED NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `author` varchar(150) NOT NULL DEFAULT '', + `publisher` varchar(150) NOT NULL DEFAULT '', + `date` varchar(20) NOT NULL DEFAULT '', + `comments` varchar(255) NOT NULL DEFAULT '', + `id` varchar(50) NOT NULL DEFAULT '', + `url` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`resource_id`), + INDEX (`course_id`) +) TYPE = MYISAM; + +# for the file storage +# -------------------------------------------------------- + +CREATE TABLE `file_storage_groups` ( + `group_id` MEDIUMINT UNSIGNED NOT NULL default '0', + PRIMARY KEY ( `group_id` ) +) TYPE = MYISAM; + + +CREATE TABLE `files` ( + `file_id` mediumint(8) unsigned NOT NULL auto_increment, + `owner_type` tinyint(3) unsigned NOT NULL default '0', + `owner_id` mediumint(8) unsigned NOT NULL default '0', + `member_id` mediumint(8) unsigned NOT NULL default '0', + `folder_id` mediumint(8) unsigned NOT NULL default '0', + `parent_file_id` mediumint(8) unsigned NOT NULL default '0', + `date` datetime NOT NULL default '0000-00-00 00:00:00', + `num_comments` tinyint(3) unsigned NOT NULL default '0', + `num_revisions` tinyint(3) unsigned NOT NULL default '0', + `file_name` varchar(80) NOT NULL default '', + `file_size` int(11) NOT NULL default '0', + `description` text NOT NULL default '', + PRIMARY KEY (`file_id`) +) TYPE=MyISAM; + +CREATE TABLE `files_comments` ( + `comment_id` mediumint(8) unsigned NOT NULL auto_increment, + `file_id` mediumint(8) unsigned NOT NULL default '0', + `member_id` mediumint(8) unsigned NOT NULL default '0', + `date` datetime NOT NULL default '0000-00-00 00:00:00', + `comment` text NOT NULL default '', + PRIMARY KEY (`comment_id`) +) TYPE=MyISAM; + +CREATE TABLE `folders` ( + `folder_id` mediumint(8) unsigned NOT NULL auto_increment, + `parent_folder_id` mediumint(8) unsigned NOT NULL default '0', + `owner_type` tinyint(3) unsigned NOT NULL default '0', + `owner_id` mediumint(8) unsigned NOT NULL default '0', + `title` varchar(30) NOT NULL default '', + PRIMARY KEY (`folder_id`) +) TYPE=MyISAM; + +## assignment manager +CREATE TABLE `assignments` ( + `assignment_id` MEDIUMINT(6) UNSIGNED NOT NULL AUTO_INCREMENT, + `course_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `title` VARCHAR(60) NOT NULL default '', + `assign_to` MEDIUMINT UNSIGNED DEFAULT 0, + `date_due` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `date_cutoff` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `multi_submit` TINYINT DEFAULT '0', + PRIMARY KEY (`assignment_id`), + INDEX (`course_id`) +) TYPE = MYISAM; + +# make the privs field bigger +ALTER TABLE `course_enrollment` CHANGE `privileges` `privileges` INT UNSIGNED DEFAULT '0' NOT NULL; +ALTER TABLE `modules` CHANGE `privilege` `privilege` INT UNSIGNED DEFAULT '0' NOT NULL; + +# second name field +ALTER TABLE `members` ADD `second_name` CHAR( 30 ) NOT NULL default '' AFTER `first_name` ; +ALTER TABLE `members` ADD `private_email` TINYINT DEFAULT '1' NOT NULL ; + +# increase length of users_online `login` field to support a full display name. or close to it. +ALTER TABLE `users_online` CHANGE `login` `login` varchar(255) NOT NULL default ''; + +# Table structure for table `mail_queue` +# since 1.5.3 +CREATE TABLE `mail_queue` ( + `mail_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , + `to_email` VARCHAR( 50 ) NOT NULL default '', + `to_name` VARCHAR( 50 ) NOT NULL default '', + `from_email` VARCHAR( 50 ) NOT NULL default '', + `from_name` VARCHAR( 50 ) NOT NULL default '', + `char_set` VARCHAR( 20 ) NOT NULL default '', + `subject` VARCHAR( 200 ) NOT NULL default '', + `body` TEXT NOT NULL default '', + PRIMARY KEY ( `mail_id` ) +) TYPE = MYISAM; + +#install new themes + +INSERT INTO `themes` VALUES ('Blumin', '1.5.3', 'blumin', NOW(), 'This is the plone look-alike theme.', 1); + +# -------------------------------------------------------- +## Table for `blog_posts` + +CREATE TABLE `blog_posts` ( + `post_id` mediumint(8) unsigned NOT NULL auto_increment, + `member_id` mediumint(8) unsigned NOT NULL default '0', + `owner_type` tinyint(3) unsigned NOT NULL default '0', + `owner_id` mediumint(8) unsigned NOT NULL default '0', + `private` tinyint(3) unsigned NOT NULL default '0', + `date` datetime NOT NULL default '0000-00-00 00:00:00', + `num_comments` tinyint(3) unsigned NOT NULL default '0', + `title` varchar(100) NOT NULL default '', + `body` text NOT NULL default '', + PRIMARY KEY (`post_id`) +) TYPE=MyISAM; + +## Table for `blog_posts_comments` +# -------------------------------------------------------- +CREATE TABLE `blog_posts_comments` ( + `comment_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , + `post_id` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , + `member_id` MEDIUMINT UNSIGNED DEFAULT '0' NOT NULL , + `date` DATETIME NOT NULL default '0000-00-00 00:00:00', + `private` TINYINT UNSIGNED DEFAULT '0' NOT NULL , + `comment` TEXT NOT NULL default '', + PRIMARY KEY ( `comment_id` ) , + INDEX ( `post_id` ) +) TYPE = MYISAM; + +## add blog to the modules (added to 1.5.3.1) +##INSERT INTO `modules` VALUES ('_standard/blogs', 2, 0, 0, 0, 0); + + +## link table updates +# Dec,6, 2007 duplicated column name + ALTER TABLE `resource_categories` RENAME `links_categories` ; +# Dec,6, 2007 duplicated column name + ALTER TABLE `links_categories` + CHANGE `CatID` `cat_id` mediumint(8) unsigned NOT NULL auto_increment , + CHANGE `course_id` `owner_id` mediumint(8) unsigned NOT NULL default '0' , + CHANGE `CatName` `name` varchar(100) NOT NULL default '' , + CHANGE `CatParent` `parent_id` mediumint(8) unsigned default NULL , + ADD `owner_type` tinyint(4) NOT NULL default '0' AFTER `cat_id` ; + +# Dec,6, 2007 duplicated column name +ALTER TABLE `links_categories` + DROP INDEX `course_id` , + ADD INDEX `owner_id` ( `owner_id` ); + +UPDATE `links_categories` SET owner_type=1 WHERE owner_type=0 ; + +# Dec,6, 2007 duplicated column name +ALTER TABLE `resource_links` RENAME `links` ; +ALTER TABLE `links` + CHANGE `LinkID` `link_id` mediumint(8) unsigned NOT NULL auto_increment , + CHANGE `CatID` `cat_id` mediumint(8) unsigned NOT NULL default '0' ; + + +ALTER TABLE `members` CHANGE `gender` `gender` ENUM( 'm', 'f', 'n' ) DEFAULT 'n' NOT NULL; + +# Dec,6, 2007 duplicated column name +ALTER TABLE `handbook_notes` ADD `approved` TINYINT DEFAULT '0' NOT NULL AFTER `page` ; diff --git a/install/db/atutor_upgrade_1.5.3.1_to_1.5.3.2.sql b/install/db/atutor_upgrade_1.5.3.1_to_1.5.3.2.sql new file mode 100644 index 000000000..50b0934d3 --- /dev/null +++ b/install/db/atutor_upgrade_1.5.3.1_to_1.5.3.2.sql @@ -0,0 +1,5 @@ +############################################################### +# Database upgrade SQL from ATutor 1.5.3.1 to ATutor 1.5.3.2 +############################################################### + +UPDATE `modules` SET `cron_interval` = '1440' WHERE `dir_name` = '_core/languages'; diff --git a/install/db/atutor_upgrade_1.5.3.2_to_1.5.3.3.sql b/install/db/atutor_upgrade_1.5.3.2_to_1.5.3.3.sql new file mode 100644 index 000000000..4d0d6f5bc --- /dev/null +++ b/install/db/atutor_upgrade_1.5.3.2_to_1.5.3.3.sql @@ -0,0 +1,27 @@ +############################################################### +# Database upgrade SQL from ATutor 1.5.3.2 to ATutor 1.5.3.3 +############################################################### + +# convert DATETIME fields to TIMESTAMP +ALTER TABLE `admins` CHANGE `last_login` `last_login` TIMESTAMP NULL ; +ALTER TABLE `admin_log` CHANGE `time` `time` TIMESTAMP NOT NULL ; + +ALTER TABLE `backups` CHANGE `date` `date` TIMESTAMP NOT NULL ; +ALTER TABLE `blog_posts` CHANGE `date` `date` TIMESTAMP NOT NULL ; +ALTER TABLE `blog_posts_comments` CHANGE `date` `date` TIMESTAMP NOT NULL ; +ALTER TABLE `content` CHANGE `last_modified` `last_modified` TIMESTAMP NOT NULL ; + +ALTER TABLE `faq_entries` CHANGE `revised_date` `revised_date` TIMESTAMP NOT NULL ; +ALTER TABLE `files` CHANGE `date` `date` TIMESTAMP NOT NULL ; +ALTER TABLE `files_comments` CHANGE `date` `date` TIMESTAMP NOT NULL ; +ALTER TABLE `forums` CHANGE `last_post` `last_post` TIMESTAMP NOT NULL ; +ALTER TABLE `forums_threads` CHANGE `last_comment` `last_comment` TIMESTAMP NOT NULL , + CHANGE `date` `date` TIMESTAMP NOT NULL ; +ALTER TABLE `handbook_notes` CHANGE `date` `date` TIMESTAMP NOT NULL ; +ALTER TABLE `instructor_approvals` CHANGE `request_date` `request_date` TIMESTAMP NOT NULL ; +ALTER TABLE `members` CHANGE `creation_date` `creation_date` TIMESTAMP NOT NULL ; +ALTER TABLE `member_track` CHANGE `last_accessed` `last_accessed` TIMESTAMP NULL ; +ALTER TABLE `messages` CHANGE `date_sent` `date_sent` TIMESTAMP NOT NULL ; +ALTER TABLE `news` CHANGE `date` `date` TIMESTAMP NOT NULL ; +ALTER TABLE `polls` CHANGE `created_date` `created_date` TIMESTAMP NOT NULL ; +ALTER TABLE `tests_results` CHANGE `date_taken` `date_taken` TIMESTAMP NOT NULL ; diff --git a/install/db/atutor_upgrade_1.5.3.3_to_1.5.4.sql b/install/db/atutor_upgrade_1.5.3.3_to_1.5.4.sql new file mode 100644 index 000000000..2913e3857 --- /dev/null +++ b/install/db/atutor_upgrade_1.5.3.3_to_1.5.4.sql @@ -0,0 +1,56 @@ +############################################################### +# Database upgrade SQL from ATutor 1.5.3.3 to ATutor 1.5.4 +############################################################### + +## alter the test questions table to support matching type questions + +ALTER TABLE `tests_questions` ADD `option_0` VARCHAR( 255 ) NOT NULL AFTER `answer_9` , +ADD `option_1` VARCHAR( 255 ) NOT NULL AFTER `option_0` , +ADD `option_2` VARCHAR( 255 ) NOT NULL AFTER `option_1` , +ADD `option_3` VARCHAR( 255 ) NOT NULL AFTER `option_2` , +ADD `option_4` VARCHAR( 255 ) NOT NULL AFTER `option_3` , +ADD `option_5` VARCHAR( 255 ) NOT NULL AFTER `option_4` , +ADD `option_6` VARCHAR( 255 ) NOT NULL AFTER `option_5` , +ADD `option_7` VARCHAR( 255 ) NOT NULL AFTER `option_6` , +ADD `option_8` VARCHAR( 255 ) NOT NULL AFTER `option_7` , +ADD `option_9` VARCHAR( 255 ) NOT NULL AFTER `option_8` ; + +## alter the tests table to support guest tests +ALTER TABLE `tests` ADD `guests` TINYINT NOT NULL DEFAULT '0'; + +# -------------------------------------------------------- +# Table structure for table `course_access` + +CREATE TABLE `course_access` ( + `password` char(8) NOT NULL , + `course_id` mediumint(8) unsigned NOT NULL , + `expiry_date` timestamp NOT NULL , + `enabled` tinyint(4) NOT NULL , + PRIMARY KEY ( `password` ) , + UNIQUE (`course_id`) +) TYPE=MyISAM ; + +## alter the members table to support last login +ALTER TABLE `members` ADD `last_login` TIMESTAMP NOT NULL ; + +## alter the forums table to support minutes to edit +ALTER TABLE `forums` ADD `mins_to_edit` SMALLINT UNSIGNED NOT NULL DEFAULT '0'; + +## table for saving sent inbox messages +CREATE TABLE `messages_sent` ( + `message_id` mediumint( 8 ) unsigned NOT NULL AUTO_INCREMENT , + `course_id` mediumint( 8 ) unsigned NOT NULL default '0', + `from_member_id` mediumint( 8 ) unsigned NOT NULL default '0', + `to_member_id` mediumint( 8 ) unsigned NOT NULL default '0', + `date_sent` timestamp NOT NULL , + `subject` varchar( 150 ) NOT NULL default '', + `body` text NOT NULL , + PRIMARY KEY ( `message_id` ) , + KEY `from_member_id` ( `from_member_id` ) +) TYPE = MYISAM; + +## add the profile_pictures module +INSERT INTO `modules` VALUES ('_standard/profile_pictures', 2, 0, 0, 0, 0); + +## remove inherit_release_date field +ALTER TABLE `content` DROP `inherit_release_date`; diff --git a/install/db/atutor_upgrade_1.5.3_to_1.5.3.1.sql b/install/db/atutor_upgrade_1.5.3_to_1.5.3.1.sql new file mode 100644 index 000000000..82de9edf4 --- /dev/null +++ b/install/db/atutor_upgrade_1.5.3_to_1.5.3.1.sql @@ -0,0 +1,5 @@ +############################################################### +# Database upgrade SQL from ATutor 1.5.3 to ATutor 1.5.3.1 +############################################################### + +INSERT INTO `modules` VALUES ('_standard/blogs', 2, 0, 0, 0, 0); diff --git a/install/db/atutor_upgrade_1.5.4_to_1.5.5.sql b/install/db/atutor_upgrade_1.5.4_to_1.5.5.sql new file mode 100644 index 000000000..8a2902a80 --- /dev/null +++ b/install/db/atutor_upgrade_1.5.4_to_1.5.5.sql @@ -0,0 +1,17 @@ +############################################################### +# Database upgrade SQL from ATutor 1.5.4 to ATutor 1.5.5 +############################################################### + + +## remove login field - #3032 +ALTER TABLE `forums_threads` DROP `login`; + +## refresh test issue - #2362 +ALTER TABLE `tests_questions_assoc` DROP INDEX `test_id`; +ALTER TABLE `tests_results` ADD `status` TINYINT NOT NULL DEFAULT '0'; + +## times tests - #3084 +ALTER TABLE `tests_results` ADD `end_time` TIMESTAMP NOT NULL ; + +## end date - #3089 +ALTER TABLE `courses` ADD `end_date` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `release_date`; diff --git a/install/db/atutor_upgrade_1.5.5_to_1.6.sql b/install/db/atutor_upgrade_1.5.5_to_1.6.sql new file mode 100644 index 000000000..5c4ce2926 --- /dev/null +++ b/install/db/atutor_upgrade_1.5.5_to_1.6.sql @@ -0,0 +1,16 @@ +############################################################### +# Database upgrade SQL from ATutor 1.5.5 to ATutor 1.5.6 +############################################################### + +# one question per page - #3090 +ALTER TABLE `tests_results` ADD `max_pos` TINYINT UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `tests` ADD `display` TINYINT NOT NULL DEFAULT '0'; + +INSERT INTO `themes` VALUES ('Greenmin', '1.6', 'greenmin', NOW(), 'This is the plone look-alike theme in green.', 1); +INSERT INTO `themes` VALUES ('ATutor 1.5', '1.6', 'default15', NOW(), 'This is the 1.5 series default theme.', 1); + +UPDATE `languages` SET char_set='utf-8' WHERE language_code = 'en'; + +# Support SHA1 encryption for admins table +ALTER TABLE `admins` MODIFY COLUMN `password` VARCHAR(40) NOT NULL; +UPDATE `admins` SET password = SHA1(password); \ No newline at end of file diff --git a/install/db/atutor_upgrade_1.5_to_1.5.1.sql b/install/db/atutor_upgrade_1.5_to_1.5.1.sql new file mode 100644 index 000000000..59414c30e --- /dev/null +++ b/install/db/atutor_upgrade_1.5_to_1.5.1.sql @@ -0,0 +1,17 @@ +############################################################### +# Database upgrade SQL from ATutor 1.5 to ATutor 1.5.1 +############################################################### + +# handbook notes: +CREATE TABLE `handbook_notes` ( +`note_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT , +`date` DATETIME NOT NULL , +`section` VARCHAR( 15 ) NOT NULL , +`page` VARCHAR( 50 ) NOT NULL , +`email` VARCHAR( 50 ) NOT NULL , +`note` TEXT NOT NULL , +PRIMARY KEY ( `note_id` ) +); + +#Dec 6, 2007 - duplicated column name +#ALTER TABLE `admins` ADD `language` varchar(5) default '' NOT NULL AFTER `email` ; diff --git a/install/db/atutor_upgrade_1.6.1_to_1.6.2.sql b/install/db/atutor_upgrade_1.6.1_to_1.6.2.sql new file mode 100644 index 000000000..2ba48ddac --- /dev/null +++ b/install/db/atutor_upgrade_1.6.1_to_1.6.2.sql @@ -0,0 +1,164 @@ +# Upgrade SQL for 1.6.1 to 1.6.2 + +# Setup Table for Access4All +CREATE TABLE `primary_resources` ( + `primary_resource_id` mediumint(8) unsigned NOT NULL auto_increment, + `content_id` mediumint(8) unsigned NOT NULL default '0', + `resource` text NOT NULL, + `language_code` varchar(20) default NULL, + PRIMARY KEY (`primary_resource_id`) +) TYPE = MYISAM; + +CREATE TABLE `primary_resources_types` ( + `primary_resource_id` mediumint(8) unsigned NOT NULL, + `type_id` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`primary_resource_id`,`type_id`) +) TYPE = MYISAM; + +CREATE TABLE `resource_types` ( + `type_id` mediumint(8) unsigned NOT NULL auto_increment, + `type` text NOT NULL, + PRIMARY KEY (`type_id`) +) TYPE = MYISAM; + +CREATE TABLE `secondary_resources` ( + `secondary_resource_id` mediumint(8) unsigned NOT NULL auto_increment, + `primary_resource_id` mediumint(8) unsigned NOT NULL, + `secondary_resource` text NOT NULL, + `language_code` varchar(20) default NULL, + PRIMARY KEY (`secondary_resource_id`) +) TYPE = MYISAM; + +CREATE TABLE `secondary_resources_types` ( + `secondary_resource_id` mediumint(8) unsigned NOT NULL, + `type_id` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`secondary_resource_id`,`type_id`) +) TYPE = MYISAM; + +INSERT INTO `resource_types` VALUES +(1, 'auditory'), +(2, 'sign_language'), +(3, 'textual'), +(4, 'visual'); + +INSERT INTO `config` (`name`, `value`) VALUES('encyclopedia', 'http://www.wikipedia.org'); +INSERT INTO `config` (`name`, `value`) VALUES('dictionary', 'http://dictionary.reference.com/'); +INSERT INTO `config` (`name`, `value`) VALUES('thesaurus', 'http://thesaurus.reference.com/'); +INSERT INTO `config` (`name`, `value`) VALUES('atlas', 'http://maps.google.ca/'); +INSERT INTO `config` (`name`, `value`) VALUES('calculator', 'http://www.calculateforfree.com/'); +INSERT INTO `config` (`name`, `value`) VALUES('note_taking', 'http://www.aypwip.org/webnote/'); +INSERT INTO `config` (`name`, `value`) VALUES('abacas', 'http://www.mandarintools.com/abacus.html'); + +#Add the scaffold tools module +INSERT INTO `modules` VALUES ('_standard/support_tools', 2, 0, 2048, 0, 0); + +# End Access4All setup + + +# Content Test Extension +# @author Harris +# @date Sep 08, 08 +CREATE TABLE `content_tests_assoc` ( + `content_id` INTEGER UNSIGNED NOT NULL, + `test_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`content_id`, `test_id`) +) TYPE = MyISAM; + +# Customized test messages associated with the content page +ALTER TABLE `content` ADD COLUMN `test_message` TEXT NOT NULL AFTER `use_customized_head`; +ALTER TABLE `content` ADD COLUMN `allow_test_export` TINYINT(1) UNSIGNED NOT NULL AFTER `test_message`; + +# Extend field "value" for extended default preference setting string +ALTER TABLE `config` MODIFY value TEXT; + +# Extend field "author", "installed_date" for extended patch information +ALTER TABLE `patches` ADD COLUMN `author` VARCHAR(255) NOT NULL AFTER `patch_files`; +ALTER TABLE `patches` ADD COLUMN `installed_date` datetime NOT NULL AFTER `author`; + +# sql file for gradebook module + +CREATE TABLE `grade_scales` ( + `grade_scale_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `member_id` mediumint(8) unsigned NOT NULL default '0', + `scale_name` VARCHAR(255) NOT NULL default '', + `created_date` datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY ( `grade_scale_id` ) +); + +CREATE TABLE `grade_scales_detail` ( + `grade_scale_id` mediumint(8) unsigned NOT NULL, + `scale_value` VARCHAR(50) NOT NULL default '', + `percentage_from` MEDIUMINT NOT NULL default '0', + `percentage_to` MEDIUMINT NOT NULL default '0', + PRIMARY KEY (`grade_scale_id`, `scale_value`) +); + +CREATE TABLE `gradebook_tests` ( + `gradebook_test_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `id` mediumint(8) unsigned NOT NULL default '0' COMMENT 'Values: 0, tests.test_id or assignments.assignment_id. 0 for external tests/assignments. tests.test_id for ATutor tests, assignments.assignment_id for ATutor assignments.', + `type` VARCHAR(50) NOT NULL default '' COMMENT 'Values: ATutor Test, ATutor Assignment, External', + `course_id` mediumint(8) unsigned NOT NULL default '0' COMMENT 'Values: 0 or courses.course_id. Only has value for external tests/assignments. When ATutor internal assignments/tests/surveys, always 0.', + `title` VARCHAR(255) NOT NULL default '' COMMENT 'Values: Null or test name. Always null if ATutor internal assignments/tests/surveys.', + `due_date` datetime NOT NULL default '0000-00-00 00:00:00', + `grade_scale_id` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY ( `gradebook_test_id` ) +); + +CREATE TABLE `gradebook_detail` ( + `gradebook_test_id` mediumint(8) unsigned NOT NULL, + `member_id` mediumint(8) unsigned NOT NULL default '0', + `grade` VARCHAR(255) NOT NULL default '', + PRIMARY KEY (`gradebook_test_id`, `member_id`) +); + +INSERT INTO `grade_scales` (grade_scale_id, member_id, scale_name, created_date) values (1, 0, 'Letter Grade', now()); +INSERT INTO `grade_scales` (grade_scale_id, member_id, scale_name, created_date) values (2, 0, 'Competency 1', now()); +INSERT INTO `grade_scales` (grade_scale_id, member_id, scale_name, created_date) values (3, 0, 'Competency 2', now()); + +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'A+', 90, 100); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'A', 80, 89); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'B', 70, 79); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'C', 60, 69); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'D', 50, 59); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (1, 'E', 0, 49); + +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (2, 'Pass', 75, 100); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (2, 'Fail', 0, 74); + +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (3, 'Excellent', 80, 100); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (3, 'Good', 70, 79); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (3, 'Adequate', 60, 69); +INSERT INTO `grade_scales_detail` (grade_scale_id, scale_value, percentage_from, percentage_to) values (3, 'Inadequate', 0, 59); + +INSERT INTO `modules` (`dir_name`, `status`, `privilege`, `admin_privilege`, `cron_interval`, `cron_last_run`) VALUES('_standard/gradebook', 2, 1048576, 4096, 0, 0); + +DELETE FROM `modules` WHERE dir_name = 'gradebook'; + +UPDATE `courses` set home_links=replace(home_links, 'mods/gradebook/my_gradebook.php', 'mods/_standard/gradebook/my_gradebook.php'), main_links=replace(main_links, 'mods/gradebook/my_gradebook.php', 'mods/_standard/gradebook/my_gradebook.php'); + +# SQL for collecting guest information at test introduction page +CREATE TABLE `guests` ( + `guest_id` VARCHAR(10) NOT NULL, + `name` VARCHAR(255), + `organization` VARCHAR(255), + `location` VARCHAR(255), + `role` VARCHAR(255), + `focus` VARCHAR(255), + PRIMARY KEY (`guest_id`) +) TYPE = MYISAM; + +ALTER TABLE `tests_results` MODIFY member_id VARCHAR(10); +ALTER TABLE `tests` ADD COLUMN `show_guest_form` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `failfeedback`; + + +#move the old student toos to course tool, and add the new student tools +UPDATE `modules` SET `dir_name` = '_standard/course_tools' WHERE `dir_name` = '_standard/student_tools' LIMIT 1 ; +INSERT INTO `modules` (`dir_name` ,`status` ,`privilege` ,`admin_privilege` ,`cron_interval` ,`cron_last_run`)VALUES ('_standard/student_tools', '2', '2097152', '0', '0', '0'); + +CREATE TABLE `fha_student_tools` ( + `course_id` mediumint(8) unsigned NOT NULL, + `links` text NOT NULL , + PRIMARY KEY ( `course_id` ) +); +# Add the forum archiver to standard modules +INSERT INTO `modules` (`dir_name` ,`status` ,`privilege` ,`admin_privilege` ,`cron_interval` ,`cron_last_run`)VALUES ('_standard/farchive', '2', '4194304', '0', '0', '0'); diff --git a/install/db/atutor_upgrade_1.6.2_to_1.6.3.sql b/install/db/atutor_upgrade_1.6.2_to_1.6.3.sql new file mode 100644 index 000000000..3122ee259 --- /dev/null +++ b/install/db/atutor_upgrade_1.6.2_to_1.6.3.sql @@ -0,0 +1,257 @@ +# Setup tables for Social Networking module + +# Activities +CREATE TABLE `social_activities` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `application_id` INTEGER UNSIGNED NOT NULL, + `title` TEXT, + `created_date` TIMESTAMP NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Applications/ Gagdets table +CREATE TABLE `social_applications` ( + `id` INTEGER UNSIGNED, + `url` VARCHAR(255) NOT NULL DEFAULT '', + `title` VARCHAR(255) NOT NULL, + `height` INTEGER UNSIGNED, + `scrolling` INTEGER UNSIGNED, + `screenshot` VARCHAR(255) NOT NULL, + `thumbnail` VARCHAR(255) NOT NULL, + `author` VARCHAR(255) NOT NULL, + `author_email` VARCHAR(128) NOT NULL, + `description` TEXT NOT NULL, + `settings` TEXT NOT NULL, + `views` TEXT NOT NULL, + `last_updated` TIMESTAMP NOT NULL, + PRIMARY KEY (`url`) +) +ENGINE = MyISAM; + +# Application Settings, like storing the perference string. +CREATE TABLE `social_application_settings` ( + `application_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `name` VARCHAR(255) NOT NULL, + `value` TEXT NOT NULL, + PRIMARY KEY (`application_id`, `member_id`, `name`) +) +ENGINE = MyISAM; + +# Application members mapping +CREATE TABLE `social_members_applications` ( + `member_id` INTEGER UNSIGNED NOT NULL, + `application_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`member_id`, `application_id`) +) +ENGINE = MyISAM; + +# Friends table +CREATE TABLE `social_friends` ( + `member_id` INTEGER UNSIGNED NOT NULL, + `friend_id` INTEGER UNSIGNED NOT NULL, + `relationship` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`member_id`, `friend_id`) +) +ENGINE = MyISAM; + +# Friend requests table +CREATE TABLE `social_friend_requests` ( + `member_id` INTEGER UNSIGNED NOT NULL, + `friend_id` INTEGER UNSIGNED NOT NULL, + `relationship` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`member_id`, `friend_id`) +) +ENGINE = MyISAM; + +# Person Positions (jobs) +CREATE TABLE `social_member_position` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `title` VARCHAR(255) NOT NULL, + `company` VARCHAR(255) NOT NULL, + `from` VARCHAR(10) NOT NULL DEFAULT 0, + `to` VARCHAR(10) NOT NULL DEFAULT 0, + `description` TEXT, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Person education +CREATE TABLE `social_member_education` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `university` VARCHAR(255) NOT NULL, + `country` VARCHAR(128), + `province` VARCHAR(128), + `degree` VARCHAR(64), + `field` VARCHAR(64), + `from` VARCHAR(10) NOT NULL DEFAULT 0, + `to` VARCHAR(10) NOT NULL DEFAULT 0, + `description` TEXT NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Person related web sites +CREATE TABLE `social_member_websites` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `url` VARCHAR(255) NOT NULL, + `site_name` VARCHAR(255), + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Tracks visitor counts +CREATE TABLE `social_member_track` ( + `member_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `visitor_id` INTEGER UNSIGNED NOT NULL, + `timestamp` TIMESTAMP NOT NULL, + PRIMARY KEY (`member_id`, `visitor_id`, `timestamp`) +) +ENGINE = MyISAM; + +# Person additional information cojoint with the members table +CREATE TABLE `social_member_additional_information` ( + `member_id` INTEGER UNSIGNED NOT NULL, + `expertise` VARCHAR(255) NOT NULL, + `interests` TEXT, + `associations` TEXT, + `awards` TEXT, + `others` TEXT, + PRIMARY KEY (`member_id`) +) +ENGINE = MyISAM; + +# Privacy Control Preferences +CREATE TABLE `social_privacy_preferences` ( + `member_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `preferences` TEXT NOT NULL, + PRIMARY KEY (`member_id`) +) +ENGINE = MyISAM; + +# Social Group tables +CREATE TABLE `social_groups` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INTEGER UNSIGNED NOT NULL, + `type_id` INTEGER UNSIGNED NOT NULL, + `privacy` INTEGER UNSIGNED NOT NULL, + `name` VARCHAR(255) NOT NULL, + `logo` VARCHAR(255) NOT NULL, + `description` TEXT NOT NULL, + `created_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', + `last_updated` TIMESTAMP NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_activities` ( + `activity_id` INTEGER UNSIGNED NOT NULL, + `group_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`activity_id`, `group_id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_members` ( + `group_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`group_id`, `member_id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_invitations` ( + `sender_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `group_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`sender_id`, `member_id`, `group_id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_requests` ( + `sender_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `group_id` INTEGER UNSIGNED NOT NULL, + PRIMARY KEY (`sender_id`, `member_id`, `group_id`) +) +ENGINE = MyISAM; + +CREATE TABLE `social_groups_types` ( + `type_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `title` VARCHAR(127) NOT NULL, + PRIMARY KEY (`type_id`) +) +ENGINE = MyISAM; + +# CREATE TABLE `social_groups_forums` ( +# `group_id` INTEGER UNSIGNED NOT NULL, +# `forum_id` INTEGER UNSIGNED NOT NULL, +# PRIMARY KEY (`group_id`, `forum_id`) +# ) +# ENGINE = MyISAM; + +# Groups message board +CREATE TABLE `social_groups_board` ( + `id` int(10) unsigned NOT NULL auto_increment, + `member_id` int(10) unsigned NOT NULL, + `group_id` int(10) unsigned NOT NULL, + `body` text NOT NULL, + `created_date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=MyISAM; + + +# Settings +CREATE TABLE `social_user_settings` ( + `member_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `app_settings` TEXT NOT NULL, + PRIMARY KEY (`member_id`) +) +ENGINE = MyISAM; + + +#====== Initial Data ======== +INSERT INTO social_groups_types SET title='business', type_id=1; +INSERT INTO social_groups_types SET title='common_interest', type_id=2; +INSERT INTO social_groups_types SET title='entertainment_arts', type_id=3; +INSERT INTO social_groups_types SET title='geography', type_id=4; +INSERT INTO social_groups_types SET title='internet_technology', type_id=5; +INSERT INTO social_groups_types SET title='organization', type_id=6; +INSERT INTO social_groups_types SET title='music', type_id=7; +INSERT INTO social_groups_types SET title='sports_recreation', type_id=8; + +# END Social Networking setup + +# Module setting +INSERT INTO `modules` VALUES ('_standard/social', 2, 8388608, 0, 0, 0); + +# Login attempt control table +CREATE TABLE `member_login_attempt` ( + `login` varchar(20) NOT NULL, + `attempt` tinyint(3) unsigned default NULL, + `expiry` int(10) unsigned default NULL, + PRIMARY KEY (`login`) +) ENGINE=MyISAM; + +# -------------------------------------------------------- +# Adding feature of blog subsription +# Table structure for table `blog_subscription` +# since 1.6.3 +CREATE TABLE `blog_subscription` ( + `group_id` MEDIUMINT NOT NULL , + `member_id` MEDIUMINT NOT NULL , + PRIMARY KEY (group_id,member_id) +) TYPE=MyISAM; + +# END Adding feature of blog subsription + +# -------------------------------------------------------- +# Adding feature of "detail view" and "icon view" on course home page +# since 1.6.3 +ALTER TABLE `courses` add `home_view` tinyint NOT NULL DEFAULT 0; +ALTER TABLE `fha_student_tools` add `home_view` tinyint NOT NULL DEFAULT 0; + +# END Adding feature of "detail view" and "icon view" on course home page diff --git a/install/db/atutor_upgrade_1.6.3_to_1.6.4.sql b/install/db/atutor_upgrade_1.6.3_to_1.6.4.sql new file mode 100644 index 000000000..ef5a1212d --- /dev/null +++ b/install/db/atutor_upgrade_1.6.3_to_1.6.4.sql @@ -0,0 +1,165 @@ +# Add folder node into `content` table +ALTER TABLE `content` add `content_type` tinyint NOT NULL DEFAULT 0; + +# -------------------------------------------------------- +# Table structure for table `content_forums_assoc` + +CREATE TABLE `content_forums_assoc` ( +`content_id` INTEGER UNSIGNED NOT NULL, +`forum_id` INTEGER UNSIGNED NOT NULL, +PRIMARY KEY ( `content_id` , `forum_id` ) +) +TYPE = MyISAM; + +# -------------------------------------------------------- +# Replace (TEXT NOT NULL) with (TEXT) +ALTER TABLE `admin_log` MODIFY `details` TEXT; + +ALTER TABLE `backups` MODIFY `description` TEXT, MODIFY `file_name` TEXT, MODIFY `contents` TEXT; + +ALTER TABLE `blog_posts` MODIFY `body` TEXT; + +ALTER TABLE `blog_posts_comments` MODIFY `comment` TEXT; + +ALTER TABLE `config` MODIFY `value` TEXT; + +ALTER TABLE `content` MODIFY `keywords` TEXT, MODIFY `content_path` TEXT, MODIFY `text` TEXT, MODIFY `head` TEXT, MODIFY `test_message` TEXT; + +ALTER TABLE `courses` MODIFY `description` TEXT, MODIFY `copyright` TEXT, MODIFY `home_links` TEXT, MODIFY `main_links` TEXT, MODIFY `banner` TEXT; + +ALTER TABLE `faq_topics` MODIFY `name` TEXT; + +ALTER TABLE `faq_entries` MODIFY `question` TEXT, MODIFY `answer` TEXT; + +ALTER TABLE `files` MODIFY `description` TEXT; + +ALTER TABLE `files_comments` MODIFY `comment` TEXT; + +ALTER TABLE `forums` MODIFY `description` TEXT; +-- +ALTER TABLE `forums_threads` MODIFY `body` TEXT; + +ALTER TABLE `glossary` MODIFY `definition` TEXT; + +ALTER TABLE `groups` MODIFY `description` TEXT; + +ALTER TABLE `handbook_notes` MODIFY `note` TEXT; + +ALTER TABLE `instructor_approvals` MODIFY `notes` TEXT; + +ALTER TABLE `links` MODIFY `Description` TEXT; + +ALTER TABLE `members` MODIFY `address` TEXT, MODIFY `preferences` TEXT; + +ALTER TABLE `messages` MODIFY `body` TEXT; + +ALTER TABLE `messages_sent` MODIFY `body` TEXT; + +ALTER TABLE `news` MODIFY `body` TEXT; + +ALTER TABLE `mail_queue` MODIFY `body` TEXT; + +ALTER TABLE `reading_list` MODIFY `comment` TEXT; + +ALTER TABLE `external_resources` MODIFY `comments` TEXT; + +ALTER TABLE `tests` MODIFY `instructions` TEXT, MODIFY `description` TEXT, MODIFY `passfeedback` TEXT, MODIFY `failfeedback` TEXT; + +ALTER TABLE `tests_answers` MODIFY `answer` TEXT, MODIFY `notes` TEXT; + +ALTER TABLE `tests_questions` MODIFY `feedback` TEXT, MODIFY `question` TEXT, MODIFY `choice_0` TEXT +, MODIFY `choice_1` TEXT, MODIFY `choice_2` TEXT, MODIFY `choice_3` TEXT, MODIFY `choice_4` TEXT +, MODIFY `choice_5` TEXT, MODIFY `choice_6` TEXT, MODIFY `choice_7` TEXT, MODIFY `choice_8` TEXT +, MODIFY `choice_9` TEXT, MODIFY `option_0` TEXT, MODIFY `option_1` TEXT, MODIFY `option_2` TEXT +, MODIFY `option_3` TEXT, MODIFY `option_4` TEXT, MODIFY `option_5` TEXT, MODIFY `option_6` TEXT +, MODIFY `option_7` TEXT, MODIFY `option_8` TEXT, MODIFY `option_9` TEXT; + +ALTER TABLE `themes` MODIFY `extra_info` TEXT; + +ALTER TABLE `patches` MODIFY `description` TEXT, MODIFY `sql_statement` TEXT, MODIFY `remove_permission_files` TEXT, +MODIFY `backup_files` TEXT, MODIFY `patch_files` TEXT; + +ALTER TABLE `patches_files` MODIFY `name` TEXT; + +ALTER TABLE `patches_files_actions` MODIFY `code_from` TEXT, MODIFY `code_to` TEXT; + +ALTER TABLE `myown_patches` MODIFY `description` TEXT, MODIFY `sql_statement` TEXT; + +ALTER TABLE `myown_patches_files` MODIFY `code_from` TEXT, MODIFY `code_to` TEXT, MODIFY `uploaded_file` TEXT; + +ALTER TABLE `primary_resources` MODIFY `resource` TEXT; + +ALTER TABLE `resource_types` MODIFY `type` TEXT; + +ALTER TABLE `secondary_resources` MODIFY `secondary_resource` TEXT; + +ALTER TABLE `fha_student_tools` MODIFY `links` TEXT; + +ALTER TABLE `social_applications` MODIFY `description` TEXT, MODIFY `settings` TEXT, MODIFY `views` TEXT; + +ALTER TABLE `social_application_settings` MODIFY `value` TEXT; + +ALTER TABLE `social_member_position` MODIFY `description` TEXT; + +ALTER TABLE `social_member_education` MODIFY `description` TEXT; + +ALTER TABLE `social_privacy_preferences` MODIFY `preferences` TEXT; + +ALTER TABLE `social_groups` MODIFY `description` TEXT; + +ALTER TABLE `social_groups_board` MODIFY `body` TEXT; + +ALTER TABLE `social_user_settings` MODIFY `app_settings` TEXT; + +# added by Bologna CC. +INSERT INTO `modules` VALUES ('_core/tool_manager', 2, 0, 0, 0, 0); + +# -------------------------------------------------------- +# Adding feature of content pre-requisites +# Table structure for table `content_prerequisites` +# since 1.6.4 +CREATE TABLE `content_prerequisites` ( + `content_id` MEDIUMINT NOT NULL, + `type` varchar(50) NOT NULL DEFAULT '', + `item_id` MEDIUMINT NOT NULL, + PRIMARY KEY (content_id,type, item_id) +) TYPE=MyISAM; + +# New Social Tables +CREATE TABLE `social_member_contact` ( + `contact_id` int(10) unsigned NOT NULL auto_increment, + `member_id` int(10) unsigned NOT NULL, + `con_name` varchar(200) NOT NULL, + `con_phone` varchar(15) NOT NULL, + `con_email` varchar(50) NOT NULL, + `con_address` text NOT NULL, + PRIMARY KEY (`contact_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ; + + +CREATE TABLE `social_member_representation` ( + `rep_id` int(10) unsigned NOT NULL auto_increment, + `member_id` int(10) unsigned NOT NULL, + `rep_name` varchar(200) NOT NULL, + `rep_title` varchar(50) NOT NULL, + `rep_phone` varchar(15) NOT NULL, + `rep_email` varchar(50) NOT NULL, + `rep_address` text NOT NULL, + PRIMARY KEY (`rep_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ; + +CREATE TABLE `social_member_personal` ( + `per_id` int(10) unsigned NOT NULL auto_increment, + `member_id` int(10) unsigned NOT NULL, + `per_weight` varchar(200) NOT NULL, + `per_height` varchar(50) NOT NULL, + `per_hair` varchar(15) NOT NULL, + `per_eyes` varchar(50) NOT NULL, + `per_ethnicity` varchar(50) NOT NULL, + `per_languages` varchar(255) NOT NULL, + `per_disabilities` varchar(255) NOT NULL, + PRIMARY KEY (`per_id`) +) ENGINE=MyISAM; + +# Add mobile theme +INSERT INTO `themes` VALUES ('Mobile', '1.6.4', 'mobile', NOW(), 'This is the default theme for mobile devices.', 1); diff --git a/install/db/atutor_upgrade_1.6.4_to_2.0.sql b/install/db/atutor_upgrade_1.6.4_to_2.0.sql new file mode 100644 index 000000000..dca8430ed --- /dev/null +++ b/install/db/atutor_upgrade_1.6.4_to_2.0.sql @@ -0,0 +1,141 @@ + + +UPDATE `modules` SET `dir_name` = '_core/imscp' WHERE `modules`.`dir_name` = '_core/content_packaging' LIMIT 1 ; + +INSERT INTO `modules` VALUES ('_core/modules', 2, 0, 8192, 0, 0); + +# -------------------------------------------------------- +# Adding feature of oauth client +# Table structure for table `oauth_client_servers` +# since 1.6.5 + +CREATE TABLE `oauth_client_servers` ( + `oauth_server_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `oauth_server` VARCHAR(255) NOT NULL default '', + `consumer_key` TEXT NOT NULL , + `consumer_secret` TEXT NOT NULL , + `expire_threshold` INT NOT NULL default 0, + `create_date` datetime NOT NULL, + PRIMARY KEY ( `oauth_server_id` ), + UNIQUE INDEX idx_consumer ( `oauth_server` ) +) TYPE=MyISAM; + +# -------------------------------------------------------- +# Table structure for table `oauth_client_tokens` +# since 1.6.5 + +CREATE TABLE `oauth_client_tokens` ( + `oauth_server_id` MEDIUMINT UNSIGNED NOT NULL, + `token` VARCHAR(50) NOT NULL default '', + `token_type` VARCHAR(50) NOT NULL NOT NULL default '', + `token_secret` TEXT NOT NULL, + `member_id` mediumint(8) unsigned NOT NULL , + `assign_date` datetime NOT NULL, + PRIMARY KEY ( `oauth_server_id`, `token` ) +) TYPE=MyISAM; + +# END Adding feature of oauth client + +# -------------- Photo Album Module Setup ---------------- +INSERT INTO `modules` VALUES ('_standard/photos', 2, 16777216, 0, 0, 0); +# Photo Album Table +CREATE TABLE `pa_albums` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `location` VARCHAR(255) NOT NULL, + `description` TEXT NOT NULL, + `permission` TINYINT(1) UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `photo_id` INTEGER UNSIGNED NOT NULL, + `type_id` TINYINT(1) UNSIGNED NOT NULL, + `created_date` DATETIME NOT NULL, + `last_updated` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Photos Table +CREATE TABLE `pa_photos` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `description` TEXT, + `alt_text` TEXT, + `member_id` INTEGER UNSIGNED NOT NULL, + `album_id` INTEGER UNSIGNED NOT NULL, + `ordering` SMALLINT UNSIGNED NOT NULL, + `created_date` DATETIME NOT NULL, + `last_updated` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Course Album Table +CREATE TABLE `pa_course_album` ( + `course_id` INTEGER UNSIGNED, + `album_id` INTEGER UNSIGNED, + PRIMARY KEY (`course_id`, `album_id`) +) +ENGINE = MyISAM; + +# Photo Album Comments +CREATE TABLE `pa_album_comments` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `album_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `comment` TEXT NOT NULL, + `created_date` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Photo Comments +CREATE TABLE `pa_photo_comments` ( + `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `photo_id` INTEGER UNSIGNED NOT NULL, + `member_id` INTEGER UNSIGNED NOT NULL, + `comment` TEXT NOT NULL, + `created_date` DATETIME NOT NULL, + PRIMARY KEY (`id`) +) +ENGINE = MyISAM; + +# Initiali Config +INSERT INTO `config` VALUES ('pa_max_memory_per_member', '50'); + +# -------------- Photo Album Module Ends ----------------- + +# ----------------Flowplayer Module ------------------------ +INSERT INTO `modules` VALUES ('_standard/flowplayer', 2, 33554432, 0, 0, 0); + +# Add Transformable configuration + +INSERT INTO `config` (`name`, `value`) VALUES('transformable_uri', 'http://localhost/transformable/'); +INSERT INTO `config` (`name`, `value`) VALUES('transformable_web_service_id', '90c3cd6f656739969847f3a99ac0f3c7'); +INSERT INTO `config` (`name`, `value`) VALUES('transformable_oauth_expire', '93600'); + +# End of adding Transformable configuration + +# Add the 1.6 series default theme as a secondary theme for ATutor 2.0 +INSERT INTO `themes` VALUES ('ATutor 1.6', '2.0', 'default16', NOW(), 'This is the 1.6 series default theme.', 1); + +# Add new field themes.type to seperate "Desktop" and "Mobile" themes +ALTER TABLE `themes` ADD `type` varchar(20) NOT NULL default 'Desktop' AFTER `dir_name`; + +# point the module index pages to the new locations +UPDATE `courses` SET main_links=replace(main_links, 'glossary/index.php', 'mods/_core/glossary/index.php'), home_links=replace(home_links, 'glossary/index.php', 'mods/_core/glossary/index.php'); +UPDATE `courses` SET main_links=replace(main_links, 'file_storage/index.php', 'mods/_standard/file_storage/index.php'), home_links=replace(home_links, 'file_storage/index.php', 'mods/_standard/file_storage/index.php'); +UPDATE `courses` SET main_links=replace(main_links, 'tile.php', 'mods/_standard/tile_search/tile.php'), home_links=replace(home_links, 'tile.php', 'mods/_standard/tile_search/tile.php'); +UPDATE `courses` SET main_links=replace(main_links, 'forum/list.php', 'mods/_standard/forums/forum/list.php'), home_links=replace(home_links, 'forum/list.php', 'mods/_standard/forums/forum/list.php'); +UPDATE `courses` SET main_links=replace(main_links, 'chat/index.php', 'mods/_standard/chat/index.php'), home_links=replace(home_links, 'chat/index.php', 'mods/_standard/chat/index.php'); +UPDATE `courses` SET main_links=replace(main_links, 'faq/index.php', 'mods/_standard/faq/index.php'), home_links=replace(home_links, 'faq/index.php', 'mods/_standard/faq/index.php'); +UPDATE `courses` SET main_links=replace(main_links, 'links/index.php', 'mods/_standard/links/index.php'), home_links=replace(home_links, 'links/index.php', 'mods/_standard/links/index.php'); +UPDATE `courses` SET main_links=replace(main_links, 'tools/my_tests.php', 'mods/_standard/tests/my_tests.php'), home_links=replace(home_links, 'tools/my_tests.php', 'mods/_standard/tests/my_tests.php'); +UPDATE `courses` SET main_links=replace(main_links, 'sitemap.php', 'mods/_standard/sitemap/sitemap.php'), home_links=replace(home_links, 'sitemap.php', 'mods/_standard/sitemap/sitemap.php'); +UPDATE `courses` SET main_links=replace(main_links, 'export.php', 'mods/_core/imscp/export.php'), home_links=replace(home_links, 'export.php', 'mods/_core/imscp/export.php'); +UPDATE `courses` SET main_links=replace(main_links, 'my_stats.php', 'mods/_standard/tracker/my_stats.php'), home_links=replace(home_links, 'my_stats.php', 'mods/_standard/tracker/my_stats.php'); +UPDATE `courses` SET main_links=replace(main_links, 'polls/index.php', 'mods/_standard/polls/index.php'), home_links=replace(home_links, 'polls/index.php', 'mods/_standard/polls/index.php'); +UPDATE `courses` SET main_links=replace(main_links, 'directory.php', 'mods/_standard/directory/directory.php'), home_links=replace(home_links, 'directory.php', 'mods/_standard/directory/directory.php'); +UPDATE `courses` SET main_links=replace(main_links, 'groups.php', 'mods/_core/groups/groups.php'), home_links=replace(home_links, 'groups.php', 'mods/_core/groups/groups.php'); +UPDATE `courses` SET main_links=replace(main_links, 'reading_list/index.php', 'mods/_standard/reading_list/index.php'), home_links=replace(home_links, 'reading_list/index.php', 'mods/_standard/reading_list/index.php'); +UPDATE `courses` SET main_links=replace(main_links, 'blogs/index.php', 'mods/_standard/blogs/index.php'), home_links=replace(home_links, 'blogs/index.php', 'mods/_standard/blogs/index.php'); +UPDATE `courses` SET main_links=replace(main_links, 'google_search/index.php', 'mods/_standard/google_search/index.php'), home_links=replace(home_links, 'google_search/index.php', 'mods/_standard/google_search/index.php'); diff --git a/install/db/atutor_upgrade_1.6_to_1.6.1.sql b/install/db/atutor_upgrade_1.6_to_1.6.1.sql new file mode 100644 index 000000000..2d792c449 --- /dev/null +++ b/install/db/atutor_upgrade_1.6_to_1.6.1.sql @@ -0,0 +1,147 @@ +############################################################### +# Database upgrade SQL from ATutor 1.6 to ATutor 1.6.1 +############################################################### + +# support new changes for Test/Survey +ALTER TABLE `tests` +ADD `description` TEXT NOT NULL, +ADD `passscore` MEDIUMINT NOT NULL, +ADD `passpercent` MEDIUMINT NOT NULL, +ADD `passfeedback` TEXT NOT NULL, +ADD `failfeedback` TEXT NOT NULL; + +# support auto enrollment at registration +CREATE TABLE `auto_enroll` ( + `auto_enroll_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `associate_string` VARCHAR(10) NOT NULL, + `name` VARCHAR( 50 ) NOT NULL default '', + PRIMARY KEY ( `auto_enroll_id` ) +) DEFAULT CHARACTER SET = 'utf8'; + +CREATE TABLE `auto_enroll_courses` ( + `auto_enroll_courses_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `auto_enroll_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `course_id` MEDIUMINT UNSIGNED NOT NULL default 0, + PRIMARY KEY ( `auto_enroll_courses_id` ) +) DEFAULT CHARACTER SET = 'utf8'; + +# course directory name +ALTER TABLE `courses` ADD COLUMN `course_dir_name` VARCHAR(255) NOT NULL AFTER `description`; + +# Extend members.password for encrypted password +ALTER TABLE `members` MODIFY password VARCHAR(40); +UPDATE `members` SET password = SHA1(password), creation_date=creation_date, last_login=last_login WHERE CHAR_LENGTH(password) < 40; + +# -------------------------------------------------------- +# Table structure for table `patches` +# since 1.6.1 + +CREATE TABLE `patches` ( + `patches_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `atutor_patch_id` VARCHAR(20) NOT NULL default '', + `applied_version` VARCHAR(10) NOT NULL default '', + `patch_folder` VARCHAR(250) NOT NULL default '', + `description` TEXT NOT NULL, + `available_to` VARCHAR(250) NOT NULL default '', + `sql_statement` text NOT NULL, + `status` varchar(20) NOT NULL default '', + `remove_permission_files` text NOT NULL, + `backup_files` text NOT NULL, + `patch_files` text NOT NULL, + PRIMARY KEY (`patches_id`) +) DEFAULT CHARACTER SET = 'utf8'; + + +# -------------------------------------------------------- +# Table structure for table `patches_files` +# since 1.6.1 + +CREATE TABLE `patches_files` ( + `patches_files_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `patches_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `action` VARCHAR(20) NOT NULL default '', + `name` TEXT NOT NULL, + `location` VARCHAR(250) NOT NULL default '', + PRIMARY KEY (`patches_files_id`) +) DEFAULT CHARACTER SET = 'utf8';; + +# -------------------------------------------------------- +# Table structure for table `patches_files_actions` +# since 1.6.1 + +CREATE TABLE `patches_files_actions` ( + `patches_files_actions_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `patches_files_id` MEDIUMINT UNSIGNED NOT NULL default 0, + `action` VARCHAR(20) NOT NULL default '', + `code_from` TEXT NOT NULL, + `code_to` TEXT NOT NULL, + PRIMARY KEY (`patches_files_actions_id`) +) DEFAULT CHARACTER SET = 'utf8'; + + + +# -------------------------------------------------------- +# New tables for patch creator +# since 1.6.1 + +CREATE TABLE `myown_patches` ( + `myown_patch_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `atutor_patch_id` VARCHAR(20) NOT NULL default '', + `applied_version` VARCHAR(10) NOT NULL default '', + `description` TEXT NOT NULL, + `sql_statement` text NOT NULL, + `status` varchar(20) NOT NULL default '', + `last_modified` datetime NOT NULL, + PRIMARY KEY (`myown_patch_id`) +) DEFAULT CHARACTER SET = 'utf8';; + +CREATE TABLE `myown_patches_dependent` ( + `myown_patches_dependent_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `myown_patch_id` MEDIUMINT UNSIGNED NOT NULL, + `dependent_patch_id` VARCHAR(50) NOT NULL default '', + PRIMARY KEY (`myown_patches_dependent_id`) +) DEFAULT CHARACTER SET = 'utf8'; + +CREATE TABLE `myown_patches_files` ( + `myown_patches_files_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, + `myown_patch_id` MEDIUMINT UNSIGNED NOT NULL, + `action` VARCHAR(20) NOT NULL default '', + `name` VARCHAR(250) NOT NULL, + `location` VARCHAR(250) NOT NULL default '', + `code_from` TEXT NOT NULL, + `code_to` TEXT NOT NULL, + `uploaded_file` TEXT NOT NULL, + PRIMARY KEY (`myown_patches_files_id`) +) DEFAULT CHARACTER SET = 'utf8'; + +# -------------------------------------------------------- +# Include Patcher as a standard module +# since 1.6.1 + +INSERT INTO `modules` +SELECT '_standard/patcher', 2, 0, MAX(admin_privilege)*2, 0, 0 FROM `modules`; + +# -------------------------------------------------------- +# Support customized head +# since 1.6.1 + +ALTER TABLE `content` +ADD head TEXT NOT NULL, +ADD use_customized_head TINYINT(4) NOT NULL; + +# -------------------------------------------------------- +# courses.created_date is modified to datetime +# remove unused fields: courses.preferences, courses.header, courses.footer, courses.banner_text, courses.banner_styles +# since 1.6.1 + +ALTER TABLE `courses` MODIFY created_date DATETIME; +ALTER TABLE `courses` DROP preferences, DROP header, DROP footer, DROP banner_text, DROP banner_styles; + +#--------------------------------------------------------- +# Adds the fluid theme to the default theme provided in the public distribution +INSERT INTO `themes` VALUES ('Fluid', '1.6.1', 'fluid', NOW(), 'Theme that implements the Fluid reorderer used to drag-and-drop the menu from side-to-side.', 1); + +# -------------------------------------------------------- +# Increase course icon filename size +# http://www.atutor.ca/atutor/mantis/view.php?id=3319 +ALTER TABLE `courses` MODIFY COLUMN `icon` VARCHAR(75); diff --git a/install/images/bad.gif b/install/images/bad.gif new file mode 100644 index 0000000000000000000000000000000000000000..80a2e7b6fa8a49e725aa477e393342560d7b568d GIT binary patch literal 174 zcmV;f08#%(Nk%w1VGaNd0J9GOArlj*C@3W!9)~6-IVB}FA|fs$BRe}g|4vR%6BGYU zOlLGBV6DG5`PnA^8LW000jFEC2ui01f~R000DA@I7#(eFs<&M!CcgXu#kw zo>UBsGf+w6C<75HPLotdJl8m#!P1j4TEfet=*SU<%BMm`BPb25oN)lf3=qzR3^Ax2 czPUxAAa;-|CC5S_5iDGqI-t7*&>s;1JHM(qx&QzG literal 0 HcmV?d00001 diff --git a/install/images/feedback.gif b/install/images/feedback.gif new file mode 100644 index 0000000000000000000000000000000000000000..a909231895a52750bf3f1008f0d1d52409ddee0c GIT binary patch literal 543 zcmcJMO;Zwa0LGt4(Mn)}5ZD)%|GvPw0txQM;*y|-rXdw&nToArO&=j=dKp{rU?*qF zaE6(f4mx$}3-kfJIgL$?U!aq>PMt{l4n2ow<~jXle$UM8w2@n*;KDC-)9G~Lax6|0 zZjk0ej80MsqOkt3!pp;gl9W_B7+xq>=jY0=p1;hB>4Y$(OCyFnI+4mJ1yza0rm{st z(J41o7%OZ%+aOWV%LTD#I`fQ=4xp){nZnv&vR%k4Tu|%WfcU5?zh*%7v+vx!?b$;Y{2qU5Q#u*u6!i z9!|>F9(m5+HiGrg$i122>j8^>uyT{XZ26Pb8&cttozo%<<+;`L-Fac$+vpi1mBr%H qv@msXyZ5SiYk5M=_w5Xf#+9M$;Ep|@$IJ_-_1diCcf)o18s=X^Qf7nz literal 0 HcmV?d00001 diff --git a/install/images/question.gif b/install/images/question.gif new file mode 100644 index 0000000000000000000000000000000000000000..02fd44948886b81e0da26fbe0133350a90fd15fc GIT binary patch literal 136 zcmZ?wbhEHb6k-r!*vtR|$BrHQPc%^c$->CMz{sEj(g`wyfyLC|1ZUPTeH&#ieS3S=Y}PXo34YUwH<+#`a3bw4zF6d7QsX+EalF|LTZs*w>m;xn?p$_Mq0C0y@4BgvsIm; zl`fgwUW-x-5^X06R953{~$XpFSZ6h4`htVcbF?lLI4Q5a>no9?^vq{fuN zSCp97|6`;*I5!RFmA>bm+89o=muiFDtaoV5&5t-|OkM!*XWSlsy;i|x+SG}gMv0Mj zf8=Syj6nt2T2me0v5s0qX~VUu%T}g~O{0^SKRqt>%;T#%On(Fz05SM}Em6z4^Z)<= M07*qoM6N<$f{zx(Jpcdz literal 0 HcmV?d00001 diff --git a/install/include/classes/TableConversion.class.php b/install/include/classes/TableConversion.class.php new file mode 100644 index 000000000..72f78f57b --- /dev/null +++ b/install/include/classes/TableConversion.class.php @@ -0,0 +1,1981 @@ +sys_default_lang = 'iso-8859-1'; + $this->table_prefix = $table_prefix; + } + + /** + * This function runs all the table that uses the system default language + */ + function convertTableBySysDefault(){ + global $errors; + + $temp_table = new CourseCategoriesTable($this->table_prefix, 'course_cats', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'course_cats was not converted.'; + $_SESSION['redo_conversion'][$course_title]['CourseCategoriesTable'] = array($this->table_prefix, 'course_cats', $char_set, $course_id); + } + + $temp_table = new MembersTable($this->table_prefix, 'members', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'members was not converted.'; + $_SESSION['redo_conversion'][$course_title]['MembersTable'] = array($this->table_prefix, 'members', $char_set, $course_id); + } + } + + /** + * This function runs all the table that uses the system default language + * Particular for 1.6.1, since 1.6 didn't convret all table + */ + function convertTableBySysDefault_161(){ + global $errors; + + $temp_table = new AdminsTable($this->table_prefix, 'admins', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'admins was not converted.'; + $_SESSION['redo_conversion'][$course_title]['AdminsTable'] = array($this->table_prefix, 'admins', $this->sys_default_lang, $course_id); + } + + $temp_table = new AdminLogTable($this->table_prefix, 'admin_log', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'admin_log was not converted.'; + $_SESSION['redo_conversion'][$course_title]['AdminLogTable'] = array($this->table_prefix, 'admin_log', $this->sys_default_lang, $course_id); + } + + $temp_table = new AutoEnrollTable($this->table_prefix, 'auto_enroll', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'auto_enroll was not converted.'; + $_SESSION['redo_conversion'][$course_title]['AutoEnrollTable'] = array($this->table_prefix, 'auto_enroll', $this->sys_default_lang, $course_id); + } + + $temp_table = new AutoEnrollCoursesTable($this->table_prefix, 'auto_enroll_courses', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'auto_enroll_courses was not converted.'; + $_SESSION['redo_conversion'][$course_title]['AutoEnrollCourses'] = array($this->table_prefix, 'auto_enroll_courses', $this->sys_default_lang, $course_id); + } + + $temp_table = new ConfigTable($this->table_prefix, 'config', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'config was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ConfigTable'] = array($this->table_prefix, 'config', $this->sys_default_lang, $course_id); + } + + $temp_table = new CourseAccessTable($this->table_prefix, 'course_access', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'course_access was not converted.'; + $_SESSION['redo_conversion'][$course_title]['CourseAccessTable'] = array($this->table_prefix, 'course_access', $this->sys_default_lang, $course_id); + } + + $temp_table = new CourseStatsTable($this->table_prefix, 'course_stats', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'course_stats was not converted.'; + $_SESSION['redo_conversion'][$course_title]['CourseStatsTable'] = array($this->table_prefix, 'course_stats', $this->sys_default_lang, $course_id); + } + + $temp_table = new FeedsTable($this->table_prefix, 'feeds', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'feeds was not converted.'; + $_SESSION['redo_conversion'][$course_title]['FeedsTable'] = array($this->table_prefix, 'feeds', $this->sys_default_lang, $course_id); + } + + $temp_table = new FileStorageGroupsTable($this->table_prefix, 'file_storage_groups', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'file_storage_groups was not converted.'; + $_SESSION['redo_conversion'][$course_title]['FileStorageGroupsTable'] = array($this->table_prefix, 'file_storage_groups', $this->sys_default_lang, $course_id); + } + + $temp_table = new ForumsAccessedTable($this->table_prefix, 'forums_accessed', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'forums_accessed was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ForumsAccessedTable'] = array($this->table_prefix, 'forums_accessed', $this->sys_default_lang, $course_id); + } + + $temp_table = new ForumsCoursesTable($this->table_prefix, 'forums_courses', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'forums_courses was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ForumsCoursesTable'] = array($this->table_prefix, 'forums_courses', $this->sys_default_lang, $course_id); + } + + $temp_table = new ForumsGroupsTable($this->table_prefix, 'forums_groups', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'forums_groups was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ForumsGroupsTable'] = array($this->table_prefix, 'forums_groups', $this->sys_default_lang, $course_id); + } + + $temp_table = new ForumsSubscriptionsTable($this->table_prefix, 'forums_subscriptions', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'forums_subscriptions was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ForumsSubscriptionsTable'] = array($this->table_prefix, 'forums_subscriptions', $this->sys_default_lang, $course_id); + } + + $temp_table = new GroupsMembersTable($this->table_prefix, 'groups_members', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'groups_members was not converted.'; + $_SESSION['redo_conversion'][$course_title]['GroupsMembersTable'] = array($this->table_prefix, 'groups_members', $this->sys_default_lang, $course_id); + } + + $temp_table = new HandbookNotesTable($this->table_prefix, 'handbook_notes', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'handbook_notes was not converted.'; + $_SESSION['redo_conversion'][$course_title]['HandbookNotesTable'] = array($this->table_prefix, 'handbook_notes', $this->sys_default_lang, $course_id); + } + + $temp_table = new InstructorApprovalsTable($this->table_prefix, 'instructor_approvals', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'instructor_approvals was not converted.'; + $_SESSION['redo_conversion'][$course_title]['InstructorApprovalsTable'] = array($this->table_prefix, 'instructor_approvals', $this->sys_default_lang, $course_id); + } + + $temp_table = new LanguagesTable($this->table_prefix, 'languages', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'languages was not converted.'; + $_SESSION['redo_conversion'][$course_title]['LanguagesTable'] = array($this->table_prefix, 'languages', $this->sys_default_lang, $course_id); + } + + $temp_table = new LanguagePagesTable($this->table_prefix, 'language_pages', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'language_pages was not converted.'; + $_SESSION['redo_conversion'][$course_title]['LanguagePagesTable'] = array($this->table_prefix, 'language_pages', $this->sys_default_lang, $course_id); + } + + $temp_table = new LanguageTextTable($this->table_prefix, 'language_text', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'language_text was not converted.'; + $_SESSION['redo_conversion'][$course_title]['LanguageTextTable'] = array($this->table_prefix, 'language_text', $this->sys_default_lang, $course_id); + } + + $temp_table = new MailQueueTable($this->table_prefix, 'mail_queue', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'mail_queue was not converted.'; + $_SESSION['redo_conversion'][$course_title]['MailQueueTable'] = array($this->table_prefix, 'mail_queue', $this->sys_default_lang, $course_id); + } + + $temp_table = new MasterListTable($this->table_prefix, 'master_list', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'master_list was not converted.'; + $_SESSION['redo_conversion'][$course_title]['MasterListTable'] = array($this->table_prefix, 'master_list', $this->sys_default_lang, $course_id); + } + + $temp_table = new MemberTrackTable($this->table_prefix, 'member_track', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'member_track was not converted.'; + $_SESSION['redo_conversion'][$course_title]['MemberTrackTable'] = array($this->table_prefix, 'member_track', $this->sys_default_lang, $course_id); + } + + $temp_table = new ModulesTable($this->table_prefix, 'modules', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'modules was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ModulesTable'] = array($this->table_prefix, 'modules', $this->sys_default_lang, $course_id); + } + + $temp_table = new PollsMembersTable($this->table_prefix, 'polls_members', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'polls_members was not converted.'; + $_SESSION['redo_conversion'][$course_title]['PollsMembersTable'] = array($this->table_prefix, 'polls_members', $this->sys_default_lang, $course_id); + } + + $temp_table = new RelatedContentTable($this->table_prefix, 'related_content', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'related_content was not converted.'; + $_SESSION['redo_conversion'][$course_title]['RelatedContentTable'] = array($this->table_prefix, 'related_content', $this->sys_default_lang, $course_id); + } + + $temp_table = new TestsGroupsTable($this->table_prefix, 'tests_groups', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'tests_groups was not converted.'; + $_SESSION['redo_conversion'][$course_title]['TestsGroupsTable'] = array($this->table_prefix, 'tests_groups', $this->sys_default_lang, $course_id); + } + + $temp_table = new TestsQuestionsAssocTable($this->table_prefix, 'tests_questions_assoc', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'tests_questions_assoc was not converted.'; + $_SESSION['redo_conversion'][$course_title]['TestsQuestionsAssocTable'] = array($this->table_prefix, 'tests_questions_assoc', $this->sys_default_lang, $course_id); + } + + $temp_table = new TestsResultsTable($this->table_prefix, 'tests_results', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'tests_results was not converted.'; + $_SESSION['redo_conversion'][$course_title]['TestsResultsTable'] = array($this->table_prefix, 'tests_results', $this->sys_default_lang, $course_id); + } + + $temp_table = new ThemesTable($this->table_prefix, 'themes', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'themes was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ThemesTable'] = array($this->table_prefix, 'themes', $this->sys_default_lang, $course_id); + } + + $temp_table = new UsersOnlineTable($this->table_prefix, 'users_online', $this->sys_default_lang); + if (!$temp_table->convert()){ + $errors[]= $this->table_prefix.'users_online was not converted.'; + $_SESSION['redo_conversion'][$course_title]['UsersOnlineTable'] = array($this->table_prefix, 'users_online', $this->sys_default_lang, $course_id); + } + } + + /** + * This function runs through all the table that are class dependent. + */ + function convertTableByClass($course_title, $char_set, $course_id){ + global $errors; + //Run through all ATutor table and convert only those rows with the above courses. + //todo: implement a driver class inside the TableConversion class. + $temp_table = new AssignmentsTable($this->table_prefix, 'assignments', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'assignments was not converted.'; + $_SESSION['redo_conversion'][$course_title]['AssignmentsTable'] = array($this->table_prefix, 'assignments', $char_set, $course_id); + } + + $temp_table = new BackupsTable($this->table_prefix, 'backups', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'backups was not converted.'; + $_SESSION['redo_conversion'][$course_title]['BackupsTable'] = array($this->table_prefix, 'backups', $char_set, $course_id); + } + + $temp_table = new BlogPostsTable($this->table_prefix, 'blog_posts', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'blog_posts was not converted.'; + $_SESSION['redo_conversion'][$course_title]['BlogPostsTable'] = array($this->table_prefix, 'blog_posts', $char_set, $course_id); + } + + $temp_table = new ContentTable($this->table_prefix, 'content', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'content was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ContentTable'] = array($this->table_prefix, 'content', $char_set, $course_id); + } + + $temp_table = new CoursesTable($this->table_prefix, 'courses', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'courses was not converted.'; + $_SESSION['redo_conversion'][$course_title]['CoursesTable'] = array($this->table_prefix, 'courses', $char_set, $course_id); + } + + $temp_table = new CourseEnrollmentTable($this->table_prefix, 'course_enrollment', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'course_enrollment was not converted.'; + $_SESSION['redo_conversion'][$course_title]['CourseEnrollmentTable'] = array($this->table_prefix, 'course_enrollment', $char_set, $course_id); + } + + $temp_table = new ExternalResourcesTable($this->table_prefix, 'external_resources', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'external_resources was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ExternalResourcesTable'] = array($this->table_prefix, 'external_resources', $char_set, $course_id); + } + + $temp_table = new FaqTopicsTable($this->table_prefix, 'faq_topics', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'faq_topics was not converted.'; + $_SESSION['redo_conversion'][$course_title]['FaqTopicsTable'] = array($this->table_prefix, 'faq_topics', $char_set, $course_id); + } + + $temp_table = new FoldersTable($this->table_prefix, 'folders', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'folders was not converted.'; + $_SESSION['redo_conversion'][$course_title]['FoldersTable'] = array($this->table_prefix, 'folders', $char_set, $course_id); + } + + $temp_table = new FilesTable($this->table_prefix, 'files', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'files was not converted.'; + $_SESSION['redo_conversion'][$course_title]['FilesTable'] = array($this->table_prefix, 'files', $char_set, $course_id); + } + + $temp_table = new ForumsTable($this->table_prefix, 'forums', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'forums was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ForumsTable'] = array($this->table_prefix, 'forums', $char_set, $course_id); + } + + $temp_table = new GlossaryTable($this->table_prefix, 'glossary', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'glossary was not converted.'; + $_SESSION['redo_conversion'][$course_title]['GlossaryTable'] = array($this->table_prefix, 'glossary', $char_set, $course_id); + } + + $temp_table = new GroupsTypesTable($this->table_prefix, 'groups_types', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'groups_types was not converted.'; + $_SESSION['redo_conversion'][$course_title]['GroupsTypesTable'] = array($this->table_prefix, 'groups_types', $char_set, $course_id); + } + + $temp_table = new LinksCategoriesTable($this->table_prefix, 'links_categories', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'links_categories was not converted.'; + $_SESSION['redo_conversion'][$course_title]['LinksCategoriesTable'] = array($this->table_prefix, 'links_categories', $char_set, $course_id); + } + + $temp_table = new MessagesTable($this->table_prefix, 'messages', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'messages was not converted.'; + $_SESSION['redo_conversion'][$course_title]['MessagesTable'] = array($this->table_prefix, 'messages', $char_set, $course_id); + } + + $temp_table = new MessagesSentTable($this->table_prefix, 'messages_sent', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'messages_sent was not converted.'; + $_SESSION['redo_conversion'][$course_title]['MessagesSentTable'] = array($this->table_prefix, 'messages_sent', $char_set, $course_id); + } + + $temp_table = new NewsTable($this->table_prefix, 'news', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'news was not converted.'; + $_SESSION['redo_conversion'][$course_title]['NewsTable'] = array($this->table_prefix, 'news', $char_set, $course_id); + } + + $temp_table = new PollsTable($this->table_prefix, 'polls', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'polls was not converted.'; + $_SESSION['redo_conversion'][$course_title]['PollsTable'] = array($this->table_prefix, 'polls', $char_set, $course_id); + } + + $temp_table = new ReadingListTable($this->table_prefix, 'reading_list', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'reading_list was not converted.'; + $_SESSION['redo_conversion'][$course_title]['ReadingListTable'] = array($this->table_prefix, 'reading_list', $char_set, $course_id); + } + + $temp_table = new TestsTable($this->table_prefix, 'tests', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'tests was not converted.'; + $_SESSION['redo_conversion'][$course_title]['TestsTable'] = array($this->table_prefix, 'tests', $char_set, $course_id); + } + + $temp_table = new TestQuestionsTable($this->table_prefix, 'tests_questions', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'tests_questions was not converted.'; + $_SESSION['redo_conversion'][$course_title]['TestQuestionsTable'] = array($this->table_prefix, 'tests_questions', $char_set, $course_id); + } + + $temp_table = new TestsQuestionsCategoriesTable($this->table_prefix, 'tests_questions_categories', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'tests_questions_categories was not converted.'; + $_SESSION['redo_conversion'][$course_title]['TestsQuestionsCategoriesTable'] = array($this->table_prefix, 'tests_questions_categories', $char_set, $course_id); + } + } + + /** + * This function runs through all the table that are class dependent. + * Particular for the tables that haven't been converted during 1.5.5 to 1.6 + */ + function convertTableByClass_161($course_title, $char_set, $course_id){ + global $errors; + //Run through all ATutor table and convert only those rows with the above courses. + //todo: implement a driver class inside the TableConversion class. +/* $temp_table = new MessagesTable($this->table_prefix, 'messages', $char_set, $course_id); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$this->table_prefix.'messages was not converted.'; + $_SESSION['redo_conversion'][$course_title]['MessagesTable'] = array($this->table_prefix, 'messages', $char_set, $course_id); + } +*/ + } + + /** + * This function will alter all table's charset to UTF-8 + */ + function alter_all_charset(){ + global $errors; + $sql = 'SHOW TABLES'; + $result = mysql_query($sql); + if (mysql_numrows($result) > 0) { + while ($row = mysql_fetch_array($result)){ + $sql = 'ALTER TABLE `'.$row[0].'` CONVERT TO CHARACTER SET utf8'; + mysql_query($sql); + } + } + } + } + + +/** +* This class will handle utf8 conversion on all tables associated with a specific course. +* This class can be potentially upgraded to a automated table parser to optimize codes, instead of having +* different abstract classes for each individual table inside ATutor. +* Note: Keeping in mind that this class will not be used a lot after 1.6 conversion. +* @access public +* @author Harris Wong +* @precondition MySQL connected, mbstring lib enabled. +* @date Nov 28, 2007 +*/ +class ATutorTable{ + /** variables */ + var $table; + var $table_prefix; + var $from_encoding; + var $courseID; + var $to_encoding; + + /** + * Constructor + * @param table prefix + * @param table is the table name of which we want to covert + * @param from_encoding is the encoding which the content will be converted from. + * @param foreign_ID is the primary key/foreign key of the table. $foreign_ID will be the primary key when + * the table has a "course_id" column, foreign key when it doesn't. + * foreign_ID is an empty string if this table does not depend on courses, such as members, + * course categories tables. + */ + function ATutorTable($table_prefix, $table, $from_encoding, $foreign_ID=''){ + $this->table_prefix = $table_prefix; + $this->table = $table; + $this->from_encoding = $from_encoding; + $this->foreign_ID= $foreign_ID; + $this->to_encoding = "UTF-8"; + //check if mb_string library is enabled, die o/w + if (!extension_loaded('mbstring')){ + die("Please have mbstring library enabled"); + } + + //Alter table + $this->alterTable(); + } + + + /** + * alterTable + * Perform mysql ALTER table function, to switch to UTF-8 tables. + */ + function alterTable(){ + $query = 'ALTER TABLE `'.$this->table_prefix.$this->table.'` CONVERT TO CHARACTER SET utf8'; + mysql_query($query); + } + + + /** + * getContent + * This method will get all the contents from this table with the given courseID. + * @param courseDependent = false when this table isn't related to course encoding, true if it is related (default) + * @return result set, and null on failure or 0 rows + */ + function getContent($courseDependent = true){ + if ($courseDependent) { + $sql = "SELECT * FROM `".$this->table_prefix.$this->table."` WHERE course_id=".$this->foreign_ID; + } else { + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table; + } + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + /** + * convert + * Abstract class that convert the table contents to UTF8 + * @return mysql_query's return object + */ + function convert(){/* Abstract */} + + + /** + * executeSQL + * This runs the sql statement + * @param value_array contains all the new values mapped by their column names + * @param primary_key is the primary key of this table. + */ + function generate_sql($value_array, $primary_key_col, $primary_key){ + $sql = "UPDATE `".$this->table_prefix.$this->table."` SET "; + $i = 1; + foreach($value_array as $column_name=>$column_value){ + $column_value = mysql_real_escape_string($column_value); + $column_name = mysql_real_escape_string($column_name); + $sql .= "`$column_name`='$column_value'"; + if ($i < sizeof($value_array)) { + $sql .= ', '; + } + $i++; + } + //If there are more than 1 key + if (is_array($primary_key_col)){ + $j = 1; + $sql .= " WHERE "; + foreach ($primary_key_col as $k=>$v){ + $v = mysql_real_escape_string($v); + $sql .= $v.'='.$primary_key[$k]; + if ($j < sizeof($primary_key_col)){ + $sql .= " AND "; + } + $j++; + } + } else { + $sql .= " WHERE `$primary_key_col`="; + if (preg_match('/^[0-9]+$/', $primary_key)==1){ + $sql .= $primary_key; + } else { + //prim key is a string, put it around a pair of quotes + $sql .= "'$primary_key'"; + } + } +// echo "
    "; + return $sql; + } +} + + +/** + * Class for Admins + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class AdminsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'login'; + //Convert all neccessary entries + $value_array['real_name'] = mb_convert_encoding($row['real_name'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + echo mysql_error(); + } + return $result; + } +} + +/** + * Class for AdminLog + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class AdminLogTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'time'; + //Convert all neccessary entries + $value_array['operation'] = mb_convert_encoding($row['operation'], $this->to_encoding, $this->from_encoding); + $value_array['table'] = mb_convert_encoding($row['table'], $this->to_encoding, $this->from_encoding); + $value_array['details'] = mb_convert_encoding($row['details'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for Assignments + */ +class AssignmentsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'assignment_id'; + //Convert all neccessary entries + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + + //Convert Folders table, that is related to assignments. + $folders = new FoldersTable($this->table_prefix, 'folders', $this->from_encoding, $row[$key_col]); + $result &= $folders->convert(WORKSPACE_ASSIGNMENT); + //Convert Files table, that is related to assignments. + $files_table = new FilesTable($this->table_prefix, 'files', $this->from_encoding, $row[$key_col]); + $result &= $files_table->convert(WORKSPACE_ASSIGNMENT); + + //Generate SQL + //echo (mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col]))) ; + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + //Needs to alter related tables + if (!$rs) { + new FoldersTable($this->table_prefix, 'folders', ''); + new FilesTable($this->table_prefix, 'files', ''); + } + return $result; + } +} + +/** + * Class for auto_enroll + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class AutoEnrollTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'auto_enroll_id'; + //Convert all neccessary entries + $value_array['associate_string'] = mb_convert_encoding($row['associate_string'], $this->to_encoding, $this->from_encoding); + $value_array['name'] = mb_convert_encoding($row['name'], $this->to_encoding, $this->from_encoding); + + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for auto_enroll_courses + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class AutoEnrollCoursesTable extends ATutorTable{ + //Nothing to convert in this table except the table structure. + function convert(){ + return true; + } +} + + +/** + * Class for Backups + */ +class BackupsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'backup_id'; + //Convert all neccessary entries + $value_array['date'] = $row['date']; + $value_array['description'] = mb_convert_encoding($row['description'], $this->to_encoding, $this->from_encoding); + $value_array['system_file_name'] = mb_convert_encoding($row['system_file_name'], $this->to_encoding, $this->from_encoding); + $value_array['file_name'] = mb_convert_encoding($row['file_name'], $this->to_encoding, $this->from_encoding); + $value_array['contents'] = mb_convert_encoding($row['contents'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for Blog posts + */ +class BlogPostsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'post_id'; + //Convert all neccessary entries + $value_array['date'] = $row['date']; + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + $value_array['body'] = mb_convert_encoding($row['body'], $this->to_encoding, $this->from_encoding); + //Convert sub post comment. + $commentPosts = new BlogPostsCommentsTable($this->table_prefix, 'blog_posts_comments', $this->from_encoding, $row[$key_col]); + $result &= $commentPosts->convert(); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + //Needs to alter related tables + if (!$rs) { + new BlogPostsCommentsTable($this->table_prefix, 'blog_posts_comments', ''); + } + return $result; + } +} + +/** + * Class for Blog posts comments + * Used only by BlogPostsTable + * Foreign key = post_id + */ +class BlogPostsCommentsTable extends ATutorTable{ + //Overrider + function getContent(){ + $sql = "SELECT * FROM `".$this->table_prefix.$this->table."` WHERE post_id=".$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'comment_id'; + //Convert all neccessary entries + $value_array['date'] = $row['date']; + $value_array['text'] = mb_convert_encoding($row['text'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for config + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class ConfigTable extends ATutorTable{ + function convert(){ + //nothing to convert + return true; + } +} + +/** + * Class for course_access + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class CourseAccessTable extends ATutorTable{ + function convert(){ + //nothing to convert + return true; + } +} + +/** + * Class for course_stats + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class CourseStatsTable extends ATutorTable{ + function convert(){ + //nothing to convert + return true; + } +} + +/** + * Class for Content + */ +class ContentTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'content_id'; + //Convert all neccessary entries + $value_array['last_modified'] = $row['last_modified']; + $value_array['keywords'] = mb_convert_encoding($row['keywords'], $this->to_encoding, $this->from_encoding); + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + $value_array['text'] = mb_convert_encoding($row['text'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for Courses + */ +class CoursesTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'course_id'; + //Convert all neccessary entries + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + $value_array['description'] = mb_convert_encoding($row['description'], $this->to_encoding, $this->from_encoding); +// $value_array['preferences'] = mb_convert_encoding($row['preferences'], $this->to_encoding, $this->from_encoding); + $value_array['copyright'] = mb_convert_encoding($row['copyright'], $this->to_encoding, $this->from_encoding); + $value_array['banner'] = mb_convert_encoding($row['banner'], $this->to_encoding, $this->from_encoding); + /* The following should not needed to be converted after they are deprecated */ +// $value_array['header'] = mb_convert_encoding($row['header'], $this->to_encoding, $this->from_encoding); +// $value_array['footer'] = mb_convert_encoding($row['footer'], $this->to_encoding, $this->from_encoding); +// $value_array['banner_text'] = mb_convert_encoding($row['banner_text'], $this->to_encoding, $this->from_encoding); +// $value_array['banner_styles'] = mb_convert_encoding($row['banner_styles'], $this->to_encoding, $this->from_encoding); + + //Generate SQL +// echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for Courses enrollment + */ +class CourseEnrollmentTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'course_id'; + $key_col2 = 'member_id'; + //Convert all neccessary entries + $value_array['role'] = mb_convert_encoding($row['role'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, array($key_col, $key_col2), array($row[$key_col], $row[$key_col2])); + $result &= mysql_query($this->generate_sql($value_array, array($key_col, $key_col2), array($row[$key_col], $row[$key_col2]))); + } + return $result; + } +} + + +/** + * Class for Course Categories + * Course Categories are created by admins, the language encoding should be based on + * the admin's language setting for >= 1.5.1 + * Otherwise, default it to iso-8859-1. + * Note: This class is independent from courses + */ +class CourseCategoriesTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'cat_id'; + //Convert all neccessary entries + $value_array['cat_name'] = mb_convert_encoding($row['cat_name'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for External resources + */ +class ExternalResourcesTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'resource_id'; + //Convert all neccessary entries + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + $value_array['author'] = mb_convert_encoding($row['author'], $this->to_encoding, $this->from_encoding); + $value_array['publisher'] = mb_convert_encoding($row['publisher'], $this->to_encoding, $this->from_encoding); + $value_array['comments'] = mb_convert_encoding($row['comments'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for Faq topics + */ +class FaqTopicsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'topic_id'; + //Convert all neccessary entries + $value_array['name'] = mb_convert_encoding($row['name'], $this->to_encoding, $this->from_encoding); + //Convert faq entries + $faqEntries = new FaqEntriesTable($this->table_prefix, 'faq_entries', $this->from_encoding, $row[$key_col]); + $faqEntries->convert(); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + //Needs to alter related tables + if (!$rs) { + new FaqEntriesTable($this->table_prefix, 'faq_entries', ''); + } + return $result; + } +} + +/** + * Class for Faq Entries + * Used only by FaqTopicsTable + * Foreign key = topic_id + */ +class FaqEntriesTable extends ATutorTable{ + //Overrider + function getContent(){ + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table.'` WHERE topic_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'entry_id'; + //Convert all neccessary entries + $value_array['revised_date'] = $row['revised_date']; + $value_array['question'] = mb_convert_encoding($row['question'], $this->to_encoding, $this->from_encoding); + $value_array['answer'] = mb_convert_encoding($row['answer'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for feeds + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class FeedsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'feed_id'; + //Convert all neccessary entries + $value_array['url'] = mb_convert_encoding($row['url'], $this->to_encoding, $this->from_encoding); + + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for Forums + */ +class ForumsTable extends ATutorTable{ + //Overrider + function getContent(){ + $sql = 'SELECT this_forum.* FROM `'.$this->table_prefix.$this->table.'` this_forum NATURAL JOIN `'.$this->table_prefix.'forums_courses` this_course WHERE this_course.course_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'forum_id'; + //Convert all neccessary entries + $value_array['last_post'] = $row['last_post']; + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + $value_array['description'] = mb_convert_encoding($row['description'], $this->to_encoding, $this->from_encoding); + //Convert faq entries + $forumThread= new ForumsThreadsTable($this->table_prefix, 'forums_threads', $this->from_encoding, $row[$key_col]); + $result &= $forumThread->convert(); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result = mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + //Needs to alter related tables + if (!$rs) { + new ForumsThreadsTable($this->table_prefix, 'forums_threads', ''); + } + return $result; + } +} + +/** + * Class for forums_accessed + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class ForumsAccessedTable extends ATutorTable{ + function convert(){ + //nothing to convert + return true; + } +} + +/** + * Class for forums_courses + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class ForumsCoursesTable extends ATutorTable{ + function convert(){ + //nothing to convert + return true; + } +} + +/** + * Class for forums_groups + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class ForumsGroupsTable extends ATutorTable{ + function convert(){ + //nothing to convert + return true; + } +} + +/** + * Class for forums_subscriptions + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class ForumsSubscriptionsTable extends ATutorTable{ + function convert(){ + //nothing to convert + return true; + } +} + +/** + * Class for Forums threads + * Used only by ForumsTable + * Foreign key = forum_id + */ +class ForumsThreadsTable extends ATutorTable{ + //Overrider + function getContent(){ + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table.'` WHERE forum_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'post_id'; + //Convert all neccessary entries + $value_array['last_comment'] = $row['last_comment']; + $value_array['subject'] = mb_convert_encoding($row['subject'], $this->to_encoding, $this->from_encoding); + $value_array['body'] = mb_convert_encoding($row['body'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for Folders + * Associated with Groups, Links + */ + class FoldersTable extends ATutorTable{ + /* + * Overrider + * owner_id means category_id, owner_type refers to the different link type defined in the constants.inc.php. + * @param $owner_type are defined by the constances, which are course, groups, self + */ + function getContent($owner_type){ + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table.'` WHERE owner_type='.$owner_type.' AND owner_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + /* + * @param $owner_type are defined by the constances, which are course, groups, self; defaulted to be WORKSPACE_COURSE + */ + function convert($owner_type=WORKSPACE_COURSE){ + $rs = $this->getContent($owner_type); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'folder_id'; + //Convert all neccessary entries + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } + } + +/** + * Class for Files + * Associated with Groups, Links + */ + class FilesTable extends ATutorTable{ + /* + * Overrider + * owner_id means category_id, owner_type refers to the different link type defined in the constants.inc.php. + * @param $owner_type are defined by the constances, which are course, groups, self + */ + function getContent($owner_type){ + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table.'` WHERE owner_type='.$owner_type.' AND owner_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + /* + * @param $owner_type are defined by the constances, which are course, groups, self; defaulted to be WORKSPACE_COURSE + */ + function convert($owner_type=WORKSPACE_COURSE){ + $rs = $this->getContent($owner_type); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'file_id'; + //Convert all neccessary entries + $value_array['date'] = $row['date']; + $value_array['file_name'] = mb_convert_encoding($row['file_name'], $this->to_encoding, $this->from_encoding); + $value_array['description'] = mb_convert_encoding($row['description'], $this->to_encoding, $this->from_encoding); + //Convert faq entries + $filesComments= new FilesCommentsTable($this->table_prefix, 'files_comments', $this->from_encoding, $row[$key_col]); + $result &= $filesComments->convert(); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + //Needs to alter related tables + if (!$rs) { + new FilesCommentsTable($this->table_prefix, 'files_comments', ''); + } + return $result; + } + } + + +/** + * Class for Files comments + * Used only by FilesTable + * Foreign key = file_id + */ +class FilesCommentsTable extends ATutorTable{ + //Overrider + function getContent(){ + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table.'` WHERE file_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'comment_id'; + //Convert all neccessary entries + $value_array['date'] = $row['date']; + $value_array['comment'] = mb_convert_encoding($row['comment'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for file_storage_groups + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class FileStorageGroupsTable extends ATutorTable{ + function convert(){ + //nothing to convert + return true; + } +} + +/** + * Class for Glossary + */ +class GlossaryTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'word_id'; + //Convert all neccessary entries + $value_array['word'] = mb_convert_encoding($row['word'], $this->to_encoding, $this->from_encoding); + $value_array['definition'] = mb_convert_encoding($row['definition'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for Group types + */ +class GroupsTypesTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'type_id'; + //Convert all neccessary entries + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + + //Convert group table + $groups = new GroupsTable($this->table_prefix, 'groups', $this->from_encoding, $row[$key_col]); + $result &= $groups->convert(); + //Convert links table, that has owner_type=group + $linkscats = new LinksCategoriesTable($this->table_prefix, 'links_categories', $this->from_encoding, $row[$key_col]); + $result &= $linkscats->convert(LINK_CAT_GROUP); + //Convert folder tables, that has owner_type=group + $folders = new FoldersTable($this->table_prefix, 'folders', $this->from_encoding, $row[$key_col]); + $result &= $folders->convert(WORKSPACE_GROUP); + //Convert file tables, that has owner_type=group + $files_table = new FilesTable($this->table_prefix, 'files', $this->from_encoding, $row[$key_col]); + $result &= $files_table->convert(WORKSPACE_GROUP); + + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + //Needs to alter related tables + if (!$rs) { + new GroupsTable($this->table_prefix, 'groups', ''); + new LinksCategoriesTable($this->table_prefix, 'links_categories', ''); + new FoldersTable($this->table_prefix, 'folders', ''); + new FilesTable($this->table_prefix, 'files', ''); + } + return $result; + } +} + +/** + * Class for Groups + * Used only by GroupTypesTable + * Foreign key = type_id + */ +class GroupsTable extends ATutorTable{ + //Overrider + function getContent(){ + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table.'` WHERE type_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'group_id'; + //Convert all neccessary entries + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + $value_array['description'] = mb_convert_encoding($row['description'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for groups_members + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class GroupsMembersTable extends ATutorTable{ + function convert(){ + //nothing to convert + return true; + } +} + +/** + * Class for handbooks_notes + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class HandbookNotesTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'note_id'; + //Convert all neccessary entries + $value_array['section'] = mb_convert_encoding($row['section'], $this->to_encoding, $this->from_encoding); + $value_array['page'] = mb_convert_encoding($row['page'], $this->to_encoding, $this->from_encoding); + $value_array['note'] = mb_convert_encoding($row['note'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for instructor_approvals + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class InstructorApprovalsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'member_id'; + //Convert all neccessary entries + $value_array['notes'] = mb_convert_encoding($row['notes'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for lanaguages + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class LanguagesTable extends ATutorTable{ + function convert(){ + //will only have english language remains. + return true; + } +} + +/** + * Class for lanaguage_pages + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class LanguagePagesTable extends ATutorTable{ + function convert(){ + //will only have iso88591, which is in ascii + return true; + } +} + +/** + * Class for language_text + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class LanguageTextTable extends ATutorTable{ + function convert(){ + //will only have iso88591, which is in ascii + return true; + } +} + +/** + * Class for Links Categories + * Links' owner_id can be of courses, groups, self. + */ +class LinksCategoriesTable extends ATutorTable{ + /* + * Overrider + * owner_id means category_id, owner_type refers to the different link type defined in the constants.inc.php. + * @param $owner_type are defined by the constances + */ + function getContent($owner_type){ + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table.'` WHERE owner_type='.$owner_type.' AND owner_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + /* + * @param $owner_type are defined by the constances, which are course, groups, self; defaulted to be LINK_CAT_COURSE + */ + function convert($owner_type=LINK_CAT_COURSE){ + $rs = $this->getContent($owner_type); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'cat_id'; + //Convert all neccessary entries + $value_array['name'] = mb_convert_encoding($row['name'], $this->to_encoding, $this->from_encoding); + //Convert links table + $linkscats = new LinksTable($this->table_prefix, 'links', $this->from_encoding, $row[$key_col]); + $result &= $linkscats->convert(); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + //Needs to alter related tables + if (!$rs) { + new LinksTable($this->table_prefix, 'links', ''); + } + return $result; + } +} + +/** + * Class for Links + * Used only by LinksCategoriesTable + * Foreign key = cat_id + */ + class LinksTable extends ATutorTable{ + //Overrider + function getContent(){ + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table.'` WHERE cat_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'link_id'; + //Convert all neccessary entries + $value_array['LinkName'] = mb_convert_encoding($row['LinkName'], $this->to_encoding, $this->from_encoding); + $value_array['Description'] = mb_convert_encoding($row['Description'], $this->to_encoding, $this->from_encoding); + $value_array['SubmitName'] = mb_convert_encoding($row['SubmitName'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } + } + +/** + * Class for mail_queue + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class MailQueueTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'mail_id'; + //Convert all neccessary entries + $value_array['to_name'] = mb_convert_encoding($row['to_name'], $this->to_encoding, $this->from_encoding); + $value_array['from_name'] = mb_convert_encoding($row['from_name'], $this->to_encoding, $this->from_encoding); + $value_array['subject'] = mb_convert_encoding($row['subject'], $this->to_encoding, $this->from_encoding); + $value_array['body'] = mb_convert_encoding($row['body'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for master_list + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class MasterListTable extends ATutorTable{ + function convert(){ + //nothong to convert + return true; + } +} + +/** + * Class for Members + * Note: This class is independent from courses + */ +class MembersTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'member_id'; + //Convert all neccessary entries + $value_array['first_name'] = mb_convert_encoding($row['first_name'], $this->to_encoding, $this->from_encoding); + $value_array['second_name'] = mb_convert_encoding($row['second_name'], $this->to_encoding, $this->from_encoding); + $value_array['last_name'] = mb_convert_encoding($row['last_name'], $this->to_encoding, $this->from_encoding); + $value_array['address'] = mb_convert_encoding($row['address'], $this->to_encoding, $this->from_encoding); + $value_array['city'] = mb_convert_encoding($row['city'], $this->to_encoding, $this->from_encoding); + $value_array['province'] = mb_convert_encoding($row['province'], $this->to_encoding, $this->from_encoding); + $value_array['creation_date'] = $row['creation_date']; + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for member_track + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class MemberTrackTable extends ATutorTable{ + function convert(){ + //nothong to convert + return true; + } +} + +/** + * Class for Messages + */ +class MessagesTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'message_id'; + //Convert all neccessary entries + $value_array['date_sent'] = $row['date_sent']; + $value_array['subject'] = mb_convert_encoding($row['subject'], $this->to_encoding, $this->from_encoding); + $value_array['body'] = mb_convert_encoding($row['body'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for Messages Sent + */ +class MessagesSentTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'message_id'; + //Convert all neccessary entries + $value_array['date_sent'] = $row['date_sent']; + $value_array['subject'] = mb_convert_encoding($row['subject'], $this->to_encoding, $this->from_encoding); + $value_array['body'] = mb_convert_encoding($row['body'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for modules + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class ModulesTable extends ATutorTable{ + function convert(){ + //nothong to convert + return true; + } +} + + +/** + * Class for News + */ +class NewsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'news_id'; + //Convert all neccessary entries + $value_array['date'] = $row['date']; + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + $value_array['body'] = mb_convert_encoding($row['body'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for Polls + */ +class PollsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'poll_id'; + //Convert all neccessary entries + $value_array['created_date'] = $row['created_date']; + $value_array['question'] = mb_convert_encoding($row['question'], $this->to_encoding, $this->from_encoding); + $value_array['choice1'] = mb_convert_encoding($row['choice1'], $this->to_encoding, $this->from_encoding); + $value_array['choice2'] = mb_convert_encoding($row['choice2'], $this->to_encoding, $this->from_encoding); + $value_array['choice3'] = mb_convert_encoding($row['choice3'], $this->to_encoding, $this->from_encoding); + $value_array['choice4'] = mb_convert_encoding($row['choice4'], $this->to_encoding, $this->from_encoding); + $value_array['choice5'] = mb_convert_encoding($row['choice5'], $this->to_encoding, $this->from_encoding); + $value_array['choice6'] = mb_convert_encoding($row['choice6'], $this->to_encoding, $this->from_encoding); + $value_array['choice7'] = mb_convert_encoding($row['choice7'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + +/** + * Class for PollsMembers + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class PollsMembersTable extends ATutorTable{ + function convert(){ + //nothong to convert + return true; + } +} + +/** + * Class for RelatedContent + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class RelatedContentTable extends ATutorTable{ + function convert(){ + //nothong to convert + return true; + } +} + +/** + * Class for Readlig list + */ +class ReadingListTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'reading_id'; + //Convert all neccessary entries + $value_array['comment'] = mb_convert_encoding($row['comment'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for Tests + */ +class TestsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'test_id'; + //Convert all neccessary entries + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + $value_array['instructions'] = mb_convert_encoding($row['instructions'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for Test questions + */ +class TestQuestionsTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'question_id'; + //Convert all neccessary entries + $value_array['question'] = mb_convert_encoding($row['question'], $this->to_encoding, $this->from_encoding); + $value_array['feedback'] = mb_convert_encoding($row['feedback'], $this->to_encoding, $this->from_encoding); + $value_array['question'] = mb_convert_encoding($row['question'], $this->to_encoding, $this->from_encoding); + $value_array['choice_0'] = mb_convert_encoding($row['choice_0'], $this->to_encoding, $this->from_encoding); + $value_array['choice_1'] = mb_convert_encoding($row['choice_1'], $this->to_encoding, $this->from_encoding); + $value_array['choice_2'] = mb_convert_encoding($row['choice_2'], $this->to_encoding, $this->from_encoding); + $value_array['choice_3'] = mb_convert_encoding($row['choice_3'], $this->to_encoding, $this->from_encoding); + $value_array['choice_4'] = mb_convert_encoding($row['choice_4'], $this->to_encoding, $this->from_encoding); + $value_array['choice_5'] = mb_convert_encoding($row['choice_5'], $this->to_encoding, $this->from_encoding); + $value_array['choice_6'] = mb_convert_encoding($row['choice_6'], $this->to_encoding, $this->from_encoding); + $value_array['choice_7'] = mb_convert_encoding($row['choice_7'], $this->to_encoding, $this->from_encoding); + $value_array['choice_8'] = mb_convert_encoding($row['choice_8'], $this->to_encoding, $this->from_encoding); + $value_array['choice_9'] = mb_convert_encoding($row['choice_9'], $this->to_encoding, $this->from_encoding); + $value_array['option_0'] = mb_convert_encoding($row['option_0'], $this->to_encoding, $this->from_encoding); + $value_array['option_1'] = mb_convert_encoding($row['option_1'], $this->to_encoding, $this->from_encoding); + $value_array['option_2'] = mb_convert_encoding($row['option_2'], $this->to_encoding, $this->from_encoding); + $value_array['option_3'] = mb_convert_encoding($row['option_3'], $this->to_encoding, $this->from_encoding); + $value_array['option_4'] = mb_convert_encoding($row['option_4'], $this->to_encoding, $this->from_encoding); + $value_array['option_5'] = mb_convert_encoding($row['option_5'], $this->to_encoding, $this->from_encoding); + $value_array['option_6'] = mb_convert_encoding($row['option_6'], $this->to_encoding, $this->from_encoding); + $value_array['option_7'] = mb_convert_encoding($row['option_7'], $this->to_encoding, $this->from_encoding); + $value_array['option_8'] = mb_convert_encoding($row['option_8'], $this->to_encoding, $this->from_encoding); + $value_array['option_9'] = mb_convert_encoding($row['option_9'], $this->to_encoding, $this->from_encoding); + + //Convert links table + $tests_answers = new TestsAnswersTable($this->table_prefix, 'tests_answers', $this->from_encoding, $row[$key_col]); + $result &= $tests_answers->convert(); + + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + //Needs to alter related tables + if (!$rs) { + new TestsAnswersTable($this->table_prefix, 'tests_answers', ''); + } + return $result; + } +} + + +/** + * Class for Test answers + * Used only by TestQuestionTable + * Foreign key = question_id, since question is one-to-many answers mapping. + */ + class TestsAnswersTable extends ATutorTable{ + //Overrider + function getContent(){ + $sql = 'SELECT * FROM `'.$this->table_prefix.$this->table.'` WHERE question_id='.$this->foreign_ID; + $result = mysql_query($sql); + if ($result && mysql_num_rows($result)>0){ + return $result; + } + return false; + } + + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col1 = 'question_id'; + $key_col2 = 'result_id'; + $key_col3 = 'member_id'; + //Convert all neccessary entries + $value_array['answer'] = mb_convert_encoding($row['answer'], $this->to_encoding, $this->from_encoding); + $value_array['notes'] = mb_convert_encoding($row['notes'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, array($key_col1, $key_col2, $key_col3), + array($row[$key_col1], $row[$key_col2], $row[$key_col3]))); + } + return $result; + } + } + +/** + * Class for tests_groups + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class TestsGroupsTable extends ATutorTable{ + function convert(){ + //nothong to convert + return true; + } +} + +/** + * Class for tests_questions_assoc + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class TestsQuestionsAssocTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'test_id'; + $key_col2 = 'question_id'; + //Convert all neccessary entries + $value_array['weight'] = mb_convert_encoding($row['weight'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, array($key_col, $key_col2), array($row[$key_col], $row[$key_col2]))); + } + return $result; + } +} + +/** + * Class for Tests questions category + */ +class TestsQuestionsCategoriesTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'category_id'; + //Convert all neccessary entries + $value_array['title'] = mb_convert_encoding($row['title'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for tests_results + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class TestsResultsTable extends ATutorTable{ + function convert(){ + //nothong to convert + return true; + } +} + + +/** + * Class for themes + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class ThemesTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'title'; + //Convert all neccessary entries + $value_array['extra_info'] = mb_convert_encoding($row['extra_info'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} + + +/** + * Class for users_online + * Default language iso-8859-1. + * Note: This class is independent from courses + */ +class UsersOnlineTable extends ATutorTable{ + function convert(){ + $rs = $this->getContent(false); + $result = true; + while ($rs!=false && $row = mysql_fetch_assoc($rs)){ + //Store the key for updating purposes. + $key_col = 'member_id'; + //Convert all neccessary entries + $value_array['login'] = mb_convert_encoding($row['login'], $this->to_encoding, $this->from_encoding); + //Generate SQL + //echo $this->generate_sql($value_array, $key_col, $row[$key_col]); + $result &= mysql_query($this->generate_sql($value_array, $key_col, $row[$key_col])); + } + return $result; + } +} +?> diff --git a/install/include/classes/sqlutility.php b/install/include/classes/sqlutility.php new file mode 100644 index 000000000..923205da4 --- /dev/null +++ b/install/include/classes/sqlutility.php @@ -0,0 +1,155 @@ + add the current + // substring to the returned array + if (!$i) { + $ret[] = $sql; + return true; + } + // Backquotes or no backslashes before + // quotes: it's indeed the end of the + // string -> exit the loop + else if ($string_start == '`' || $sql[$i-1] != '\\') { + $string_start = ''; + $in_string = false; + break; + } + // one or more Backslashes before the presumed + // end of string... + else { + // first checks for escaped backslashes + $j = 2; + $escaped_backslash = false; + while ($i-$j > 0 && $sql[$i-$j] == '\\') { + $escaped_backslash = !$escaped_backslash; + $j++; + } + // ... if escaped backslashes: it's really the + // end of the string -> exit the loop + if ($escaped_backslash) { + $string_start = ''; + $in_string = false; + break; + } + // ... else loop + else { + $i++; + } + } // end if...elseif...else + } // end for + } // end if (in string) + // We are not in a string, first check for delimiter... + else if ($char == ';') { + // if delimiter found, add the parsed part to the returned array + $ret[] = substr($sql, 0, $i); + $sql = ltrim(substr($sql, min($i + 1, $sql_len))); + $sql_len = strlen($sql); + if ($sql_len) { + $i = -1; + } else { + // The submited statement(s) end(s) here + return true; + } + } // end else if (is delimiter) + // ... then check for start of a string,... + else if (($char == '"') || ($char == '\'') || ($char == '`')) { + $in_string = true; + $string_start = $char; + } // end else if (is start of string) + + // for start of a comment (and remove this comment if found)... + else if ($char == '#' || ($char == ' ' && $i > 1 && $sql[$i-2] . $sql[$i-1] == '--')) { + // starting position of the comment depends on the comment type + $start_of_comment = (($sql[$i] == '#') ? $i : $i-2); + // if no "\n" exits in the remaining string, checks for "\r" + // (Mac eol style) + $end_of_comment = (strpos(' ' . $sql, "\012", $i+2)) + ? strpos(' ' . $sql, "\012", $i+2) + : strpos(' ' . $sql, "\015", $i+2); + if (!$end_of_comment) { + // no eol found after '#', add the parsed part to the returned + // array and exit + $ret[] = trim(substr($sql, 0, $i-1)); + return true; + } else { + $sql = substr($sql, 0, $start_of_comment) . ltrim(substr($sql, $end_of_comment)); + $sql_len = strlen($sql); + $i--; + } // end if...else + } // end else if (is comment) + } // end for + + // add any rest to the returned array + if (!empty($sql) && trim($sql) != '') { + $ret[] = $sql; + } + return true; + } + + /** + * add a prefix.'_' to all tablenames in a query + * + * @param string $query valid MySQL query string + * @param string $prefix prefix to add to all table names + * @return mixed FALSE on failure + */ + function prefixQuery($query, $prefix) + { + $pattern = "/^(INSERT INTO|REPLACE INTO|CREATE TABLE|ALTER TABLE|UPDATE)(\s)+([`]?)([^`\s]+)\\3(\s)+/siU"; + $pattern2 = "/^(DROP TABLE)(\s)+([`]?)([^`\s]+)\\3(\s)?$/siU"; + if (preg_match($pattern, $query, $matches) || preg_match($pattern2, $query, $matches)) { + $replace = "\\1 ".$prefix."\\4\\5"; + $matches[0] = preg_replace($pattern, $replace, $query); + + if ($matches[1] == 'INSERT INTO') { + $parts = explode(' ', $matches[0]); + $size_of_parts = count($parts); + if ($parts[$size_of_parts-2] == 'FROM') { + $parts[$size_of_parts-1] = $prefix . str_replace('`', '', $parts[$size_of_parts-1]); + $matches[0] = implode(' ', $parts); + } + } else if ($matches[1] == 'ALTER TABLE') { + $parts = explode(' ', $matches[0]); + if ($parts[3] == 'RENAME') { + $parts[4] = $prefix . str_replace('`', '', $parts[4]); + $matches[0] = implode(' ', $parts); + } + } + + return $matches; + } + return false; + } +} +?> \ No newline at end of file diff --git a/install/include/common.inc.php b/install/include/common.inc.php new file mode 100644 index 000000000..fea2998c7 --- /dev/null +++ b/install/include/common.inc.php @@ -0,0 +1,263 @@ +'.$table . '
    created successfully.'; + } else { + if (mysql_errno($db) == 1050) { + $progress[] = 'Table '.$table . ' already exists. Skipping.'; + } else { + $errors[] = 'Table ' . $table . ' creation failed.'; + } + } + } + elseif($prefixed_query[1] == 'INSERT INTO'){ + mysql_query($prefixed_query[0],$db); + }elseif($prefixed_query[1] == 'REPLACE INTO'){ + mysql_query($prefixed_query[0],$db); + }elseif($prefixed_query[1] == 'ALTER TABLE'){ + if (mysql_query($prefixed_query[0],$db) !== false) { + $progress[] = 'Table '.$table.' altered successfully.'; + } else { + if (mysql_errno($db) == 1060) + $progress[] = 'Table '.$table . ' fields already exists. Skipping.'; + elseif (mysql_errno($db) == 1091) + $progress[] = 'Table '.$table . ' fields already dropped. Skipping.'; + else + $errors[] = 'Table '.$table.' alteration failed.'; + } + + }elseif($prefixed_query[1] == 'DROP TABLE'){ + mysql_query($prefixed_query[1] . ' ' .$table,$db); + }elseif($prefixed_query[1] == 'UPDATE'){ + mysql_query($prefixed_query[0],$db); + } + } + } + return true; + } + +function print_errors( $errors ) { + ?> +
    + + + + +
    +

    Error

    + '; + foreach ($errors as $p) { + echo '
  • '.$p.'
  • '; + } + echo ''; + ?> +

    + +
    + + + + +

    Feedback

    + '; + foreach ($feedback as $p) { + echo '
  • '.$p.'
  • '; + } + echo ''; + ?>
    +
    + $value) { + if (substr($key, 0, strlen('step')) == 'step') { + continue; + } else if ($key == 'step') { + continue; + } else if ($key == 'action') { + continue; + } else if ($key == 'submit') { + continue; + } + + $_POST['step'.$step][$key] = urlencode($stripslashes($value)); + } +} + + +function print_hidden($current_step) { + for ($i=1; $i<$current_step; $i++) { + if (is_array($_POST['step'.$i])) { + foreach($_POST['step'.$i] as $key => $value) { + echo ''."\n"; + } + } + } +} + +function print_progress($step) { + global $install_steps; + + echo '

    Installation Progress

    '; + + $num_steps = count($install_steps); + for ($i=0; $i<$num_steps; $i++) { + if ($i == $step) { + echo 'Step '.$i.': '.$install_steps[$i]['name'].''; + } else { + echo ''; + if ($step > $i) { + echo 'Step Done! '; + } else { + echo ' '; + } + echo 'Step '.$i.': '.$install_steps[$i]['name'].''; + } + if ($i+1 < $num_steps) { + echo '
    '; + } + } + echo '


    '; + + echo '

    '.$install_steps[$step]['name'].'

    '; +} + + +if (version_compare(phpversion(), '5.0') < 0) { + function scandir($dirstr) { + $files = array(); + $fh = opendir($dirstr); + while (false !== ($filename = readdir($fh))) { + array_push($files, $filename); + } + closedir($fh); + return $files; + } +} + +/** + * Print the HTML of the meta forward codes + */ +function print_meta_redirect(){ + $body = 'ATutor appears to have been installed already.
    '; + $body .= 'Click here to login.'; + + $html = "\n"; + $html .= ''.$body.''."\n"; + $html .= "\n"; + + return $html; +} + +/** + * This function is used for printing variables for debugging. + * @access public + * @param mixed $var The variable to output + * @param string $title The name of the variable, or some mark-up identifier. + * @author Joel Kronenberg + */ +function debug($var, $title='') { + echo '
    ';
    +	if ($title) {
    +		echo '

    '.$title.'

    '; + } + + ob_start(); + print_r($var); + $str = ob_get_contents(); + ob_end_clean(); + + $str = str_replace('<', '<', $str); + + $str = str_replace('[', '[', $str); + $str = str_replace(']', ']', $str); + $str = str_replace('=>', '=>', $str); + $str = str_replace('Array', 'Array', $str); + echo $str; + echo '
    '; +} +?> \ No newline at end of file diff --git a/install/include/config_template.php b/install/include/config_template.php new file mode 100644 index 000000000..30dbfacdd --- /dev/null +++ b/install/include/config_template.php @@ -0,0 +1,132 @@ +"; + +?> \ No newline at end of file diff --git a/install/include/footer.php b/install/include/footer.php new file mode 100644 index 000000000..1424228ce --- /dev/null +++ b/install/include/footer.php @@ -0,0 +1,17 @@ + + +
     
    + + + \ No newline at end of file diff --git a/install/include/header.php b/install/include/header.php new file mode 100644 index 000000000..8fd5a4900 --- /dev/null +++ b/install/include/header.php @@ -0,0 +1,46 @@ + 'Introduction'); +$install_steps[1] = array('name' => 'Terms of Use'); +$install_steps[2] = array('name' => 'Database'); +$install_steps[3] = array('name' => 'Accounts & Preferences'); +$install_steps[4] = array('name' => 'Content Directory'); +$install_steps[5] = array('name' => 'Save Configuration'); +$install_steps[6] = array('name' => 'Anonymous Usage Collection'); +$install_steps[7] = array('name' => 'Done!'); + +?> + + + ATutor Installation + + + + +
    +

    ATutor Installation

    + +
    +
    \ No newline at end of file diff --git a/install/include/step1.php b/install/include/step1.php new file mode 100644 index 000000000..63d7b9420 --- /dev/null +++ b/install/include/step1.php @@ -0,0 +1,38 @@ + +

    ATutor is licensed under the terms of the GNU General Public License (GPL), which essentially allows for the free distribution and modification of ATutor. ATutor has its own license that governs its use outside the bounds of the GPL.

    + +

    Please see atutor.ca for additional details.

    + +

    If you do not agree to the Terms of Use then you may not install and use ATutor.

    + +
    + + + +
    + -
    +
    +
    \ No newline at end of file diff --git a/install/include/step2.php b/install/include/step2.php new file mode 100644 index 000000000..9c5725d9c --- /dev/null +++ b/install/include/step2.php @@ -0,0 +1,144 @@ +=') === FALSE) { + $errors[] = 'MySQL version '.$row['version'].' was detected. ATutor requires version 4.1.10 or later.'; + } + + if (!isset($errors)){ + if (!mysql_select_db($_POST['db_name'], $db)) { + $sql = "CREATE DATABASE `$_POST[db_name]` CHARACTER SET utf8 COLLATE utf8_general_ci"; + $result = mysql_query($sql, $db); + if (!$result) { + $errors[] = 'Unable to select or create database '.$_POST['db_name'].'.'; + } else { + $progress[] = 'Database '.$_POST['db_name'].' created successfully.'; + mysql_select_db($_POST['db_name'], $db); + } + } else { + /* Check if the database that existed is in UTF-8, if not, ask for retry */ + $sql = "SHOW CREATE DATABASE `$_POST[db_name]`"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + if (!preg_match('/CHARACTER SET utf8/i', $row['Create Database'])){ + $sql2 = 'ALTER DATABASE `'.$_POST['db_name'].'` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci'; + $result2 = mysql_query($sql2); + if (!$result2){ + $errors[] = 'Database '.$_POST['db_name'].' is not in UTF8. Please set the database character set to UTF8 before continuing by using the following query:
    ALTER DATABASE `'.$_POST['db_name'].'` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci.
    To use ALTER DATABASE, you need the ALTER privilege on the database. You can also check the MySQL manual here.'; + } + } + } + } + + if (!isset($errors)) { + $progress[] = 'Connected to database '.$_POST['db_name'].' successfully.'; + $errors = array(); + + /* @See include/classes/dbmanager.php */ + queryFromFile('db/atutor_schema.sql'); + queryFromFile('db/atutor_language_text.sql'); + + if (!$errors) { + print_progress($step); + + unset($_POST['submit']); + unset($_POST['action']); + store_steps($step); + print_feedback($progress); + + echo '
    + '; + print_hidden(3); + echo '

    '; + return; + } + } + + } +} + +print_progress($step); + + +echo '

    Please enter your database information:

    '; + + +if (isset($progress)) { + print_feedback($progress); +} + +if (isset($errors)) { + print_errors($errors); +} + +?> + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    *
    + Hostname of the database server. Default: localhost
    *
    + The port to the database server. Default: 3306
    *
    + The username to the database server.
    *
    + The password to the database server.
    *
    + The name of the database to use. It will be created if it does not exist.
    Default: atutor
    ?

    + The prefix to add to table names to avoid conflicts with existing tables.
    + Default: AT_
    + +

    + +
    diff --git a/install/include/step3.php b/install/include/step3.php new file mode 100644 index 000000000..87f34f9e0 --- /dev/null +++ b/install/include/step3.php @@ -0,0 +1,277 @@ + 0){ + $sql = "INSERT INTO ".$_POST['step2']['tb_prefix']."config VALUES ('just_social', '1')"; + $result = mysql_query($sql ,$db); + } + + //if fresh install, use SET NAME to set the mysql connection to UTF8 + $sql = "INSERT INTO ".$_POST['step2']['tb_prefix']."config VALUES ('set_utf8', '1')"; + mysql_query($sql ,$db); + + unset($_POST['admin_username']); + unset($_POST['form_admin_password_hidden']); + unset($_POST['admin_email']); + unset($_POST['account_username']); + unset($_POST['form_account_password_hidden']); + unset($_POST['account_email']); + unset($_POST['home_url']); + unset($_POST['email']); + unset($_POST['site_name']); + unset($_POST['just_social']); + + unset($errors); + unset($_POST['submit']); + unset($action); + store_steps($step); + $step++; + return; + } +} + +print_progress($step); + +if (isset($errors)) { + print_errors($errors); +} + +if (isset($_POST['step1']['old_version']) && $_POST['upgrade_action']) { + $defaults['admin_username'] = urldecode($_POST['step1']['admin_username']); + $defaults['admin_email'] = urldecode($_POST['step1']['admin_email']); + + $defaults['site_name'] = urldecode($_POST['step1']['site_name']); + $defaults['header_img'] = urldecode($_POST['step1']['header_img']); + $defaults['header_logo'] = urldecode($_POST['step1']['header_logo']); + $defaults['home_url'] = urldecode($_POST['step1']['home_url']); +} else { + $defaults = $_defaults; +} + +?> + + + + +
    + + + + + + + '; + } else { + echo ''; + } + ?> +
    + + + + + + + + + + + + + + + + + + + +
    Super Administrator Account
    The Super Administrator account is used for managing ATutor. The Super Administrator can also create additional Administrators each with their own privileges and roles. Administrator accounts cannot enroll in courses.
    *
    + May contain only letters, numbers, or underscores.
    *
    *
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    System Preferences
    *
    + The name of your course server website.
    Default:
    *
    + The email that will be used as the return email when needed.
    *
    + Deploy ATutor as just a Social Networking platform? (without LMS)
    + /> + /> +
    ?

    + This will be the URL for the 'Home' link in the Public Area. Leave empty to have this link not appear.
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Personal Account
    You will need a personal account to view and create courses.
    *
    + May contain only letters, numbers, and underscores.
    *
    *
    *
    *
    +
    +
    +
    +
    \ No newline at end of file diff --git a/install/include/step4.php b/install/include/step4.php new file mode 100644 index 000000000..e8c61cfc8 --- /dev/null +++ b/install/include/step4.php @@ -0,0 +1,126 @@ + +

    You will need a personal account to view and, optionally, create courses.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    + May contain only letters, numbers, or underscores.
    20 character maximum.

    + Use a combination of letters, numbers and symbols.
    15 character maximum.
    Instructor Account:
    + Do you want this to be an instructor account allowing you to create courses?
    + Default: Yes
    />, />
    Welcome Course:
    + Do you want the basic Welcome Course created? Only possible if an instructor account above is created.
    + Default: Yes
    />, />
    + +

    + +
    \ No newline at end of file diff --git a/install/include/step5.php b/install/include/step5.php new file mode 100644 index 000000000..3bf49afe0 --- /dev/null +++ b/install/include/step5.php @@ -0,0 +1,273 @@ +Content Directory
    entered does not exist.'; + } else if (!is_dir($_POST['content_dir'])) { + $errors[] = 'Content Directory is not a directory.'; + } else if (!is_writable($_POST['content_dir'])){ + $errors[] = 'The Content Directory is not writable.'; + } else { + + $_POST['content_dir'] = realpath(urldecode($_POST['content_dir'])); + + if (!is_dir($_POST['content_dir'].'/import')) { + if (!@mkdir($_POST['content_dir'].'/import')) { + $errors[] = ''.$_POST['content_dir'].'/import directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/import')){ + $errors[] = ''.$_POST['content_dir'].'/import directory is not writable.'; + } + + if (!is_dir($_POST['content_dir'].'/chat')) { + if (!@mkdir($_POST['content_dir'].'/chat')) { + $errors[] = ''.$_POST['content_dir'].'/chat directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/chat')){ + $errors[] = ''.$_POST['content_dir'].'/chat directory is not writable.'; + } + + if (!is_dir($_POST['content_dir'].'/backups')) { + if (!@mkdir($_POST['content_dir'].'/backups')) { + $errors[] = ''.$_POST['content_dir'].'/backups directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/backups')){ + $errors[] = ''.$_POST['content_dir'].'/backups directory is not writable.'; + } + if (!is_dir($_POST['content_dir'].'/feeds')) { + if (!@mkdir($_POST['content_dir'].'/feeds')) { + $errors[] = ''.$_POST['content_dir'].'/feeds directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/feeds')){ + $errors[] = ''.$_POST['content_dir'].'/feeds directory is not writable.'; + } + + if (!is_dir($_POST['content_dir'].'/file_storage')) { + if (!@mkdir($_POST['content_dir'].'/file_storage')) { + $errors[] = ''.$_POST['content_dir'].'/file_storage directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/file_storage')){ + $errors[] = ''.$_POST['content_dir'].'/file_storage directory is not writable.'; + } + + if (!is_dir($_POST['content_dir'].'/profile_pictures')) { + if (!@mkdir($_POST['content_dir'].'/profile_pictures')) { + $errors[] = ''.$_POST['content_dir'].'/profile_pictures directory does not exist and cannot be created.'; + } else { + mkdir($_POST['content_dir'].'/profile_pictures/originals'); + mkdir($_POST['content_dir'].'/profile_pictures/thumbs'); + mkdir($_POST['content_dir'].'/profile_pictures/profile'); + } + } else if (!is_writable($_POST['content_dir'].'/profile_pictures')){ + $errors[] = ''.$_POST['content_dir'].'/profile_pictures directory is not writable.'; + } + if (!is_dir($_POST['content_dir'].'/patcher')) { + if (!@mkdir($_POST['content_dir'].'/patcher')) { + $errors[] = ''.$_POST['content_dir'].'/patcher directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/patcher')){ + $errors[] = ''.$_POST['content_dir'].'/patcher directory is not writable.'; + } + if (!is_dir($_POST['content_dir'].'/social')) { + if (!@mkdir($_POST['content_dir'].'/social')) { + $errors[] = ''.$_POST['content_dir'].'/social directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/social')){ + $errors[] = ''.$_POST['content_dir'].'/social directory is not writable.'; + } + if (!is_dir($_POST['content_dir'].'/photos')) { + if (!@mkdir($_POST['content_dir'].'/photos')) { + $errors[] = ''.$_POST['content_dir'].'/photos directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/photos')){ + $errors[] = ''.$_POST['content_dir'].'/photos directory is not writable.'; + } + if (!is_dir($_POST['content_dir'].'/module')) { + if (!@mkdir($_POST['content_dir'].'/module')) { + $errors[] = ''.$_POST['content_dir'].'/module directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/module')){ + $errors[] = ''.$_POST['content_dir'].'/module directory is not writable.'; + } + if (!is_dir($_POST['content_dir'].'/theme')) { + if (!@mkdir($_POST['content_dir'].'/theme')) { + $errors[] = ''.$_POST['content_dir'].'/theme directory does not exist and cannot be created.'; + } + } else if (!is_writable($_POST['content_dir'].'/theme')){ + $errors[] = ''.$_POST['content_dir'].'/theme directory is not writable.'; + } + + // save blank index.html pages to those directories + @copy('../images/index.html', $_POST['content_dir'] . '/import/index.html'); + @copy('../images/index.html', $_POST['content_dir'] . '/chat/index.html'); + @copy('../images/index.html', $_POST['content_dir'] . '/backups/index.html'); + @copy('../images/index.html', $_POST['content_dir'] . '/feeds/index.html'); + @copy('../images/index.html', $_POST['content_dir'] . '/file_storage/index.html'); + @copy('../images/index.html', $_POST['content_dir'] . '/profile_pictures/index.html'); + @copy('../images/index.html', $_POST['content_dir'] . '/index.html'); + } + + if (!isset($errors)) { + unset($errors); + unset($_POST['submit']); + unset($action); + + if (substr($_POST['content_dir'], -1) !='\\'){ + $_POST['content_dir'] .= DIRECTORY_SEPARATOR; + } + + // kludge to fix the missing slashes when magic_quotes_gpc is On + if ($addslashes != 'mysql_real_escape_string') { + $_POST['content_dir'] = addslashes($_POST['content_dir']); + } + + store_steps($step); + $step++; + return; + } else { + // kludge to fix the missing slashes when magic_quotes_gpc is On + if ($addslashes != 'mysql_real_escape_string') { + $_POST['content_dir'] = addslashes($_POST['content_dir']); + } + } +} + +print_progress($step); + +if (isset($errors)) { + print_errors($errors); +} + +if (isset($_POST['step1']['old_version'])) { + //get real path to old content + + $old_install = realpath('../../' . DIRECTORY_SEPARATOR . $_POST['step1']['old_path']); + $old_config_cd = urldecode($_POST['step1']['content_dir']); // this path may not exist + $new_install = realpath('../'); + + $path_info = pathinfo($old_config_cd); + $content_dir_name = $path_info['basename']; + + if ($new_install . DIRECTORY_SEPARATOR . $content_dir_name . DIRECTORY_SEPARATOR == $old_config_cd) { + // case 2 + $copy_from = $old_install . DIRECTORY_SEPARATOR . $content_dir_name; + } else { + // case 3 + 4 + // it's outside + $copy_from = ''; + } + + $_defaults['content_dir'] = $old_config_cd; + +} else { + $defaults = $_defaults; + $blurb = ''; + + // the following code checks to see if get.php is being executed, then sets $_POST['get_file'] appropriately: + $headers = array(); + $path = substr($_SERVER['PHP_SELF'], 0, -strlen('install/install.php')) . 'get.php/?test'; + $port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80; + + $host = parse_url($_SERVER['HTTP_HOST']); + + if (isset($host['path'])) { + $host = $host['path']; + } else if (isset($host['host'])) { + $host = $host['host']; + } else { + $_SERVER['HTTP_HOST']; + } + if ($port == 443) { + // if we're using SSL, but don't know if support is compiled into PHP: + $fd = @fopen('https://'.$host.$path, 'rb'); + if ($fd === false) { + $content = false; + } else { + $content = @fread($fd, filesize($filename)); + @fclose($fd); + } + + if (strlen($content) == 0) { + $headers[] = 'ATutor-Get: OK'; + } else { + $headers[] = ''; + } + } else { + $fp = @fsockopen($host, $port, $errno, $errstr, 15); + + if($fp) { + $head = 'HEAD '.@$path. " HTTP/1.0\r\nHost: ".@$host."\r\n\r\n"; + fputs($fp, $head); + while(!feof($fp)) { + if ($header = trim(fgets($fp, 1024))) { + $headers[] = $header; + } + } + } + } + if (in_array('ATutor-Get: OK', $headers)) { + $get_file = 'TRUE'; + } else { + $get_file = 'FALSE'; + } +} + +?> +
    +
    + + + + + + + + + + + +
    The content directory at will be used for this installation's content. Please create it if it does not already exist.
    + + + + + + + +
    * +

    It has been detected that your webserver does not support the protected content directory feature. The content directory stores all of the courses' files.

    +

    Due to that restriction your content directory must exist within your ATutor installation directory and cannot be moved. Its path is specified below. Please create it if it does not already exist.

    +

    +
    + + + + + +
    * +

    Please specify where the content directory should be. The content directory stores all of the courses' files. As a security measure, the content directory should be placed outside of your ATutor installation (for example, to a non-web-accessible location that is not publically available).

    + +

    On a Windows machine, the path should look like C:\content, while on Unix it should look like /var/content.

    + +

    The directory you specify must be created if it does not already exist and be writeable by the webserver. On Unix machines issue the command chmod a+rwx content, additionally the path may not contain any symbolic links.

    + +
    + +

    +
    \ No newline at end of file diff --git a/install/include/step6.php b/install/include/step6.php new file mode 100644 index 000000000..552453212 --- /dev/null +++ b/install/include/step6.php @@ -0,0 +1,111 @@ +' . $file . '
    is not writeable.'; + }else{ + $progress[] = '' . $file . ' is writeable.'; + } +} else { + $errors[] = '' . $file . ' does not exist.'; +} + +print_progress($step); + +echo '
    '; + +if (isset($errors)) { + if (isset($progress)) { + print_feedback($progress); + } + print_errors($errors); + + echo''; + + unset($_POST['step']); + unset($_POST['action']); + unset($errors); + print_hidden($step); + + echo '

    Note: To change permissions on Unix use chmod a+rw then the file name.

    '; + + echo '

    '; + +} else { + require('include/config_template.php'); + + $comments = '/*'.str_pad(' This file was generated by the ATutor '.$new_version. ' installation script.', 70, ' ').'*/ +/*'.str_pad(' File generated '.date('Y-m-d H:m:s'), 70, ' ').'*/'; + + if (!write_config_file('../include/config.inc.php', $comments)) { + echo ''; + + print_feedback($progress); + + $errors[] = 'include/config.inc.php cannot be written! Please verify that the file exists and is writeable. On Unix issue the command chmod a+rw include/config.inc.php to make the file writeable. On Windows edit the file\'s properties ensuring that the Read-only attribute is not checked and that Everyone access permissions are given to that file.'; + print_errors($errors); + + echo '

    Note: To change permissions on Unix use chmod a+rw then the file name.

    '; + + echo '

    '; + + } else { + /* if header img and logo were carried forward AND the upgrade was from 1.4.3 to 1.5 then */ + if (($_POST['step1']['header_img'] != '' || $_POST['step1']['header_logo'] != '') + && $new_version == '1.5' && $_POST['step1']['old_version'] == '1.4.3') + { + $db = mysql_connect($_POST['step1']['db_host'] . ':' . $_POST['step1']['db_port'], $_POST['step1']['db_login'], urldecode($_POST['step1']['db_password'])); + mysql_select_db($_POST['step1']['db_name'], $db); + + $sql = "INSERT INTO ".$_POST['step1']['tb_prefix']."themes VALUES ('ATutor_alt', '1.5', 'default_oldheader', NOW() , 'Backwards compatible default theme', 2)"; + @mysql_query($sql, $db); + + $sql = "UPDATE ".$_POST['step1']['tb_prefix']."themes SET status=0, version='1.5' WHERE dir_name = 'default'"; + @mysql_query($sql, $db); + } + + echo ''; + print_hidden($step); + + $progress[] = 'Data has been saved successfully.'; + + $cdir = urldecode(trim($_POST['step4']['content_dir'])); + + @chmod('../include/config.inc.php', 0444); + + print_feedback($progress); + + echo '

    '; + + } +} + +?> + +
    \ No newline at end of file diff --git a/install/include/step7.php b/install/include/step7.php new file mode 100644 index 000000000..94d2cd3ca --- /dev/null +++ b/install/include/step7.php @@ -0,0 +1,185 @@ + 1) { + $build = 'unknown'; + $build_date = date('Y-m-d H:i:s'); +} else { + $svn_data = explode(' ', $svn_data); + + $build = $svn_data[0]; + $build_date = $svn_data[4] .' '. $svn_data[5]; +} + +if (!$build) { + $build = 'unknown'; +} + +$os = php_uname('s') . ' '. php_uname('r'). ' '. php_uname('v'). ' '. php_uname('m'); + + +if (isset($_POST['submit'])) { + unset($_POST['submit']); + unset($action); + + if ($_POST['log_yes']) { + + $request = '&upgrade=' . urlencode($stripslashes($_POST['log_upgrade'])); + $request .= '&version=' . urlencode($stripslashes($new_version)); + $request .= '&build=' . urlencode($stripslashes($build)); + $request .= '&build_date=' . urlencode($stripslashes($build_date)); + $request .= '&os=' . urlencode($stripslashes($_POST['log_os'])); + $request .= '&server=' . urlencode($stripslashes($_POST['log_server'])); + $request .= '&php=' . urlencode($stripslashes($_POST['log_php'])); + $request .= '&mysql=' . urlencode($stripslashes($_POST['log_mysql'])); + + if ($_POST['step1']['old_path'] != '') { + // get some usage data from this upgrade: + $db = @mysql_connect($_POST['step1']['db_host'] . ':' . $_POST['step1']['db_port'], $_POST['step1']['db_login'], urldecode($_POST['step1']['db_password'])); + @mysql_select_db($_POST['step1']['db_name'], $db); + + $db_size = 0; // db size in bytes + $sql = 'SHOW TABLE STATUS'; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $db_size += $row['Data_length']+$row['Index_length']; + } + + $sql = "SELECT COUNT(*) AS cnt FROM ".$_POST['step1']['tb_prefix']."courses"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $num_courses = $row['cnt']; + + $sql = "SELECT COUNT(*) AS cnt FROM ".$_POST['step1']['tb_prefix']."members"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $num_users = $row['cnt']; + + $sql = "SELECT COUNT(*) AS cnt FROM ".$_POST['step1']['tb_prefix']."admins"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $num_users += $row['cnt']; + + $sql = "SELECT GROUP_CONCAT(language_code) AS langs FROM ".$_POST['step1']['tb_prefix']."languages"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $languages = $row['langs']; + + $request .= '&db=' . $db_size; // db size in bytes + $request .= '&courses=' . $num_courses; // number of courses + $request .= '&users=' . $num_users; // number of users (including admins) + $request .= '&langs=' . $languages; // comma separated list of installed languages + } + + if ($_POST['log_url_yes']) { + $request .= '&url=' . urlencode($stripslashes($_POST['log_url'])); + } + + $header = "POST /install_log.php HTTP/1.1\r\n"; + $header .= "Host: atutor.ca\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($request) . "\r\n\r\n"; + $fp = fsockopen('www.atutor.ca', 80, $errno, $errstr, 30); + + if ($fp) { + fputs($fp, $header . $request . "\r\n\r\n"); + fclose($fp); + } + } + + store_steps($step); + $step++; + return; +} + +print_progress($step); + +?> +
    + +'; + } else { + echo ''; + } + print_hidden($step); + ?> +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Submit the following information to the atutor.ca server anonymously? The information we gather helps us plan our development resources to better suit the needs of the community. You may optionally choose to send the URL of your ATutor installation.
    ATutor Version: (build )
    Operating System:
    Web Server:
    PHP Version:
    MySQL Version:
    ?
    Optional URL:

    +
    + +
    +

    + +
    \ No newline at end of file diff --git a/install/include/step8.php b/install/include/step8.php new file mode 100644 index 000000000..5b7f9458e --- /dev/null +++ b/install/include/step8.php @@ -0,0 +1,33 @@ + +

    Congratulations on your installation of ATutor !

    + +

    You may now login using your personal and administrator accounts you created in Step 3.

    + +

    For security reasons once you have confirmed that ATutor has installed correctly, you should delete the install/ directory, +and reset the permissions on the config.inc.php file to read only.

    + +

    See the Support Forums on atutor.ca for additional help & support.

    + +
    + +
    +
    + +
    +
    \ No newline at end of file diff --git a/install/include/upgrade_header.php b/install/include/upgrade_header.php new file mode 100644 index 000000000..4034226d9 --- /dev/null +++ b/install/include/upgrade_header.php @@ -0,0 +1,55 @@ + 'Introduction'); +$install_steps[1] = array('name' => 'Locate Old Version'); +$install_steps[2] = array('name' => 'Database'); +$install_steps[3] = array('name' => 'Conversion'); +$install_steps[4] = array('name' => 'New '.$new_version.' Configuration Options'); +$install_steps[5] = array('name' => 'Content Directory'); +$install_steps[6] = array('name' => 'Content Files'); +$install_steps[7] = array('name' => 'Save Configuration'); +$install_steps[8] = array('name' => 'Anonymous Usage Collection'); +$install_steps[9] = array('name' => 'Done!'); + + +?> + + +ATutor Upgrade + + + + + +
    +

    ATutor Upgrade

    + +
    +
    +
    \ No newline at end of file diff --git a/install/include/ustep1.php b/install/include/ustep1.php new file mode 100644 index 000000000..50d946a68 --- /dev/null +++ b/install/include/ustep1.php @@ -0,0 +1,269 @@ +'.VERSION . ' in path '.$_POST['old_path'].'.'; + } + if (!version_compare(VERSION, $new_version, '<')) { + $errors[] = 'The version upgrading ('.VERSION.') is not older than the new version ('.$new_version.').'; + } + + if (!$errors) { + $progress[] = 'Will be upgrading from version '.VERSION.' to version '.$new_version.'.'; + print_feedback($progress); + + require('../../'.$_POST['old_path'] . '/include/config.inc.php'); + + if (is_array($IllegalExtentions)) { + $IllegalExtentions = implode(',', $IllegalExtentions); + } + + echo '
    '; + echo ''; + echo ''; + + echo ''; + echo ''; + echo ''; + if (defined('DB_PORT')) { + echo ''; + } else { + echo ''; + } + echo ''; + + if (defined('TABLE_PREFIX')) { + echo ''; + } else { + echo ''; + } + if (defined('SITE_NAME')) { + echo ''; + } else { + echo ''; + } + if (defined('HEADER_IMAGE')) { + echo ''; + } else { + echo ''; + } + if (defined('HEADER_LOGO')) { + echo ''; + } else { + echo ''; + } + if (defined('HOME_URL')) { + echo ''; + } else { + echo ''; + } + + if (defined('MAIL_USE_SMTP')) { + echo ''; + } else { + echo ''; + } + if (defined('AT_FORCE_GET_FILE')) { + echo ''; + } else { + echo ''; + } + echo ''; + + if (defined('ADMIN_USERNAME')) { + echo ''; + } else { + echo ''; + } + + if (defined('ADMIN_EMAIL')) { + echo ''; + } else { + echo ''; + } + if (defined('EMAIL')) { + echo ''; + } else if (defined('ADMIN_EMAIL')) { + echo ''; + } else { + echo ''; + } + if (defined('EMAIL_NOTIFY')) { + echo ''; + } else { + echo ''; + } + if (defined('ALLOW_INSTRUCTOR_REQUESTS')) { + echo ''; + } else { + echo ''; + } + + if (defined('AT_EMAIL_CONFIRMATION')) { + echo ''; + } else { + echo ''; + } + + if (defined('AT_MASTER_LIST')) { + echo ''; + } else { + echo ''; + } + if (defined('AT_ENABLE_HANDBOOK_NOTES')) { + echo ''; + } else { + echo ''; + } + if (defined('AUTO_APPROVE_INSTRUCTORS')) { + echo ''; + } else { + echo ''; + } + + if (isset($MaxFileSize)) { + echo ''; + } else { + echo ''; + } + if (isset($MaxCourseSize)) { + echo ''; + } else { + echo ''; + } + if (isset($MaxCourseFloat)) { + echo ''; + } else { + echo ''; + } + + if (isset($IllegalExtentions)) { + echo ''; + } else { + echo ''; + } + if (defined('CACHE_DIR')) { + echo ''; + } else { + echo ''; + } + + if (defined('AT_ENABLE_CATEGORY_THEMES')) { + echo ''; + } else { + echo ''; + } + + if (defined('AT_COURSE_BACKUPS')) { + echo ''; + } else { + echo ''; + } + + if (defined('AT_CONTENT_DIR')) { + echo ''; + } else { + echo ''; + } + echo ''; + echo ''; + echo '

    '; + return; + } + } else { + $errors[] = 'Directory was found, but the old configuration file cannot be found.'; + } + } else { + $errors[] = 'Directory does not exist relative to the new installation.'; + } +} + +if (isset($progress)) { + print_feedback($progress); +} + +if (isset($errors)) { + print_errors($errors); +} + +?> +

    Please specify the location of the old ATutor installation.

    + +
      +
    1. Release Candidate (RC) installations cannot be upgraded.
    2. +
    3. Depending on the size of the existing courses, some steps (particularly 2 and 6) of the upgrade may require considerable time to complete.
    4. +
    5. All installed language packs will be deleted.
    6. +
    7. Some installed themes may not be supported by this version.
    8. +
    9. All extra modules will have to be reinstalled before they can be enabled again.
    10. +
    + +

    Select the old ATutor installation directory below.

    + + $value) { + if ($value == '.' || $value == '..') { + unset($dirs[$key]); + } + if ($current_dir == $value) { + unset($dirs[$key]); + continue; + } + if (!is_dir($path . $value) || !file_exists($path . $value . '/include/config.inc.php')) { + unset($dirs[$key]); + } + } +?> + +
    + + + + + + + + +
    *
    + The old directory must be at the same level as the current directory.
    + + + + None found. + +
    + +
    + + +

    + + +
    \ No newline at end of file diff --git a/install/include/ustep2.php b/install/include/ustep2.php new file mode 100644 index 000000000..779afed15 --- /dev/null +++ b/install/include/ustep2.php @@ -0,0 +1,167 @@ +'.$_POST['db_name'].'
    .'; + } + + $sql = "SELECT VERSION() AS version"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + if (version_compare($row['version'], '4.0.2', '>=') === FALSE) { + $errors[] = 'MySQL version '.$row['version'].' was detected. ATutor requires version 4.0.2 or later.'; + } + + if (!$errors) { + $progress[] = 'Connected to database '.$_POST['db_name'].' successfully.'; + unset($errors); + + //Save all the course primary language into session variables iff it has not been set. + if (!isset($_SESSION['course_info'])){ + $sql = "SELECT a.course_id, a.title, l.language_code, l.char_set FROM ".$_POST['tb_prefix']."courses a left join ".$_POST['tb_prefix']."languages l ON l.language_code = a.primary_language"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)){ + $_SESSION['course_info'][$row['course_id']] = array('char_set'=>$row['char_set'], 'language_code'=>$row['language_code']); + } + } + + $sql = "DELETE FROM ".$_POST['tb_prefix']."language_text WHERE `variable`<>'_c_template' AND `variable`<>'_c_msgs'"; + @mysql_query($sql, $db); + + $sql = "DELETE FROM ".$_POST['tb_prefix']."languages WHERE language_code<>'en'"; + @mysql_query($sql, $db); + + //get list of all update scripts minus sql extension + $files = scandir('db'); + foreach ($files as $file) { + if(count($file = explode('_',$file))==5) { + $file[4] = substr($file[4],0,-3); + $update_files[$file[2]] = $file; + } + } + + $curr_ver = $_POST['old_version']; + ksort($update_files); + foreach ($update_files as $up_file) { + if(version_compare($curr_ver, $up_file[4], '<')) { + update_one_ver($up_file); + } + } + + /* reset all the accounts to English */ + $sql = "UPDATE ".$_POST['tb_prefix']."members SET language='en', creation_date=creation_date, last_login=last_login"; + @mysql_query($sql, $db); + + /* set all the courses to 'en' as primary language if empty. added 1.4.1 */ + $sql = "UPDATE ".$_POST['tb_prefix']."courses SET primary_language='en' WHERE primary_language=''"; + @mysql_query($sql, $db); + + queryFromFile('db/atutor_language_text.sql'); + + if (!$errors) { + print_progress($step); + + unset($_POST['submit']); + store_steps(1); + print_feedback($progress); + + echo '
    + + '; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + print_hidden(2); + echo '

    '; + return; + } + } + } + + print_progress($step); + + unset($_POST['submit']); + if (isset($progress)) { + print_feedback($progress); + } + + if (isset($errors)) { + print_errors($errors); + } + + + echo '
    + '; + store_steps(1); + print_hidden(2); + + if ($found_lang) { +?> + + + + + + + + +

    All installed language packs and changes made to the default English language will be deleted. You will have to re-install any language packs by downloading the latest versions from ATutor.ca. Some language packs may not currently be available.

    ,

    + '; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + echo '

    '; + return; +?> \ No newline at end of file diff --git a/install/include/ustep3.php b/install/include/ustep3.php new file mode 100644 index 000000000..b94f01082 --- /dev/null +++ b/install/include/ustep3.php @@ -0,0 +1,303 @@ + 1"; + mysql_query($sql, $db); + + $sql = "SELECT MAX(admin_privilege) AS max FROM ".$_POST['step1']['tb_prefix']."modules"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $priv = $row['max'] * 2; + + $sql = "UPDATE ".$_POST['step1']['tb_prefix']."modules SET `admin_privilege`=$priv WHERE `dir_name`='_core/enrolment'"; + mysql_query($sql, $db); + } + if (version_compare($_POST['step1']['old_version'], '1.5.5', '<')) { + $sql = "UPDATE ".$_POST['step1']['tb_prefix']."tests_results SET status=1, date_taken=date_taken, end_time=date_taken"; + mysql_query($sql, $db); + } + if (version_compare($_POST['step1']['old_version'], '1.6.4', '<')) { + /* convert all content nodes to the IMS standard. (adds null nodes for all top pages) */ + include('ustep_content_conversion.php'); + + // fix all the wrong ordering + $sql = "SELECT content_id, content_parent_id, ordering, course_id FROM ".$_POST['step1']['tb_prefix']."content ORDER BY course_id, content_parent_id, ordering"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + if ($current_course_id != $row['course_id']) { + $current_course_id = $row['course_id']; + unset($current_parent_id); + unset($ordering); + } + if ($current_parent_id != $row['content_parent_id']) { + $current_parent_id = $row['content_parent_id']; + $ordering = 1; + } + + if ($row['ordering'] != $ordering) { + $sql = "UPDATE ".$_POST['step1']['tb_prefix']."content SET ordering=$ordering WHERE content_id=$row[content_id]"; + mysql_query($sql, $db); + } + + echo "\n"; + + $ordering++; + } + + /* Convert db to a tree */ + $sql = 'SELECT distinct course_id FROM '.$_POST['step1']['tb_prefix'].'content'; + $result_course = mysql_query($sql, $db); + while ($row_course = mysql_fetch_assoc($result_course)){ + + $sql = 'SELECT * FROM '.$_POST['step1']['tb_prefix'].'content WHERE course_id='.$row_course['course_id']; + $result = mysql_query($sql, $db); + $content_array = array(); + + while ($row = mysql_fetch_assoc($result)){ + $content_array[$row['content_parent_id']][$row['ordering']] = $row['content_id']; + } + + $tree = buildTree($content_array[0], $content_array); + + /* Restructure the tree */ + $tree = rebuild($tree); + + + /* Update the Db based on this new tree */ + reconstruct($tree, '', 0, $_POST['step1']['tb_prefix']); + } + } + + /* deal with the extra modules: */ + /* for each module in the modules table check if that module still exists in the mod directory. */ + /* if that module does not exist then check the old directory and prompt to have it copied */ + /* or delete it from the modules table. or maybe disable it instead? */ + if (version_compare($_POST['step1']['old_version'], '1.5.1', '>')) { + define('TABLE_PREFIX', $_POST['step1']['tb_prefix']); + require(AT_INCLUDE_PATH . '../mods/_core/modules/classes/Module.class.php'); + $moduleFactory = new ModuleFactory(FALSE); + $module_list =& $moduleFactory->getModules(AT_MODULE_STATUS_DISABLED | AT_MODULE_STATUS_ENABLED); + $keys = array_keys($module_list); + foreach($keys as $dir_name) { + $module =& $module_list[$dir_name]; + $module->setIsMissing($module->isExtra()); + } + } + + + if (!isset($errors)) { + unset($errors); + unset($_POST['submit']); + unset($action); + store_steps($step); + $step++; + return; + } +} + +print_progress($step); + +if (isset($errors)) { + print_errors($errors); +} + + +?> + +
    + + + + + +

    Below are new configuration options that are available for this version.

    + +
    + + + + + + + + + + + + + + + + + + + +
    Super Administrator
    The Super Administrator account is used for managing ATutor. Since ATutor version 1.5 the Super Administrator can also create additional Administrators each with their own privileges and roles.
    *
    + May contain only letters, numbers, or underscores.
    *
    *
    + +
    + + + + + + + + + +
    System Preferences
    *
    + The email that will be used as the return email when needed and when instructor account requests are made.
    + + +

    Groups made prior to 1.5.3 are not backwards compatible and will be removed.

    + +

    There are no new configuration options for this version.

    + + +
    +
    +
    +
    \ No newline at end of file diff --git a/install/include/ustep4.php b/install/include/ustep4.php new file mode 100644 index 000000000..32075e6c5 --- /dev/null +++ b/install/include/ustep4.php @@ -0,0 +1,113 @@ +'.$file.' copied successfully.'; + } else { + $errors[] = 'Course content directory '.$file.' NOT copied.'; + } + } else { + // a regular file + copy($_POST['step5']['copy_from'] . $file, $content_dir .$file); + } + } + } + +} else { + $progress[] = 'Using existing content directory '.$content_dir.'.'; +} +///////////////////// +// Create the patcher content directory +if (!is_dir($content_dir.'/patcher')) { + if (!@mkdir($content_dir.'/patcher')) { + $errors[] = ''.$content_dir.'/patcher directory does not exist and cannot be created.'; + } +} else if (!is_writable($content_dir.'/patcher')){ + $errors[] = ''.$content_dir.'/patcher directory is not writable.'; +} +//////////////////////// + +// Create the module content directory +if (!is_dir($content_dir.'/module')) { + if (!@mkdir($content_dir.'/module')) { + $errors[] = ''.$content_dir.'/module directory does not exist and cannot be created.'; + } +} else if (!is_writable($content_dir.'/module')){ + $errors[] = ''.$content_dir.'/module directory is not writable.'; +} +//////////////////////// + +// Create the theme content directory +if (!is_dir($content_dir.'/theme')) { + if (!@mkdir($content_dir.'/theme')) { + $errors[] = ''.$content_dir.'/theme directory does not exist and cannot be created.'; + } +} else if (!is_writable($content_dir.'/theme')){ + $errors[] = ''.$content_dir.'/theme directory is not writable.'; +} +//////////////////////// + +echo '
    '; +if (isset($progress)) { + print_feedback($progress); +} +if (isset($errors)) { + print_errors($errors); +} + +if ($_POST['step1']['cache_dir'] != '') { + define('CACHE_DIR', urldecode($_POST['step1']['cache_dir'])); + define('CACHE_ON', 1); + require('../include/phpCache/phpCache.inc.php'); + cache_gc(NULL, 1, true); +} + +?> +
    + + + +

    +
    \ No newline at end of file diff --git a/install/include/ustep5.php b/install/include/ustep5.php new file mode 100644 index 000000000..0e20cecd8 --- /dev/null +++ b/install/include/ustep5.php @@ -0,0 +1,102 @@ +' . $file . ' is not writeable. Use chmod a+rw '.$file.' to change permissions.'; + }else{ + $progress[] = '' . $file . ' is writeable.'; + } +} else { + $errors[] = '' . $file . ' does not exist.'; +} + +print_progress($step); + +echo '
    '; + +if (isset($errors)) { + if (isset($progress)) { + print_feedback($progress); + } + print_errors($errors); + + echo''; + + unset($_POST['step']); + unset($_POST['action']); + unset($errors); + print_hidden($step); + + echo '

    Note: To change permissions on Unix use chmod a+rw then the file name.

    '; + + echo '

    '; + +} else { + + if (!copy('../../'.$_POST['step1']['old_path'] . '/include/config.inc.php', '../include/config.inc.php')) { + echo ''; + + print_feedback($progress); + + $errors[] = 'include/config.inc.php cannot be written! Please verify that the file exists and is writeable. On Unix issue the command chmod a+rw include/config.inc.php to make the file writeable. On Windows edit the file\'s properties ensuring that the Read-only attribute is not checked and that Everyone access permissions are given to that file.'; + print_errors($errors); + + echo '

    Note: To change permissions on Unix use chmod a+rw then the file name.

    '; + + echo '

    '; + + } else { + echo ''; + print_hidden($step); + + $progress[] = 'Data has been saved successfully.'; + + if (version_compare($_POST['step1']['old_version'], '1.5.2', '<')) { + require('include/config_template.php'); + + $comments = '/*'.str_pad(' This file was generated by the ATutor 1.5.2 installation script.', 70, ' ').'*/ + /*'.str_pad(' File generated '.date('Y-m-d H:m:s'), 70, ' ').'*/'; + + $_POST['db_login'] = urldecode($_POST['db_login']); + $_POST['db_password'] = urldecode($_POST['db_password']); + + write_config_file('../include/config.inc.php', $comments); + } + @chmod('../include/config.inc.php', 0444); + + print_feedback($progress); + + echo '

    '; + + } +} + +?> + +
    \ No newline at end of file diff --git a/install/include/ustep6.php b/install/include/ustep6.php new file mode 100644 index 000000000..31875c4f2 --- /dev/null +++ b/install/include/ustep6.php @@ -0,0 +1,30 @@ + +

    Congratulations on your upgrade of ATutor !

    + +

    It is important that you login as the ATutor administrator to review and set any new System Configuration options.

    +

    For security reasons, after you have confirmed the installation was successful, it is also important that you delete the install/ directory and reset the /include/config.inc.php file to read-only. On Linux/Unix systems, use chmod a-w include/config.inc.php.

    +

    See the Support Forums on atutor.ca for additional help & support.

    + +
    + +
    +
    + +
    +
    \ No newline at end of file diff --git a/install/include/ustep7.php b/install/include/ustep7.php new file mode 100644 index 000000000..796663357 --- /dev/null +++ b/install/include/ustep7.php @@ -0,0 +1,566 @@ +'.$_POST['db_name'].'.'; + } + + $sql = "SELECT VERSION() AS version"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + if (version_compare($row['version'], '4.0.2', '>=') === FALSE) { + $errors[] = 'MySQL version '.$row['version'].' was detected. ATutor requires version 4.0.2 or later.'; + } + + if (!$errors) { + print_progress($step); + + /* + * Check if version is > 1.6, if so, this entire step can be skipped + * OR if db table are already all in utf8. + */ + if (version_compare($_POST['step1']['old_version'], '1.6.1', '>') === TRUE || + check_db_default_charset($db) === TRUE) { + $progress[] = 'Version '.$_POST['step1']['old_version'].' found.'; + $progress[] = 'UTF-8 Conversion is not needed, skipping.'; + print_feedback($progress); + echo '
    + '; + print_hidden(2); + echo '

    '; + return; + } + + unset($_POST['submit']); + if (isset($progress)) { + print_feedback($progress); + } + + $progress[] = 'Connected to database '.$_POST['db_name'].' successfully.'; + unset($errors); + + //If this is a Retry on step3 failure, load step2 post values back to jump right back to where it left off. + if (isset($_POST['step2'])){ + $temp = $_POST; + $_POST = $_POST['step2']; + $_POST['step1'] = $temp['step1']; + } + + //Conversion type set + if ($_POST['con_step']=='2'){ + //Get list of unqiue encoding; skip utf8 + $char_encodings = array(); + foreach($_SESSION['course_info'] AS $course_id=>$temp){ + if (strtolower($temp['char_set'])!='utf-8' && strtolower($temp['char_set'])!='utf8' + && !in_array($temp['char_set'], $char_encodings)){ + $char_encodings[] = $temp['char_set']; + } + } + echo '
    + '; + + print_hidden(2); + /* + * If we are converting the entire database, then all we need is just 1 language encoding; + * if we are converting each class individually, then we need to output all the classes and allow language + * options to choose from + */ + $convert_type = $_POST['convert_type']; + $recommanded_conversion = $_POST['recommanded_conversion']; + $confirm_con_step = $_POST['confirm_con_step']; + //check if the user-selected convert_type is the same as the recommanded conversion type, if not, give a warning msg + if ($convert_type!=$recommanded_conversion && $confirm_con_step==""){ + //The html fragment that sets the suggested conversion type on bold + $suggestion = array('class="suggested"', 'checked="checked"'); + $suggestion_skip=""; + $suggestion_all=""; + $suggestion_courses=""; + echo '

    You have selected a different conversion option from the recommanded one. Please verify your option and click "Yes, please continue." to continue.

    Please be aware that invalid conversion may lose your data.

    '; + generateCourseLangTable($_POST['tb_prefix'], $_SESSION['course_info']); + //Print the selected options from the previous user option. + switch($convert_type){ + case "all": + $suggestion_all[1] =& $suggestion[1] ; + break; + case "skip": + $suggestion_skip[1] =& $suggestion[1]; + break; + case "courses": + $suggestion_courses[1] =& $suggestion[1]; + break; + } + switch($recommanded_conversion){ + case "all": + $suggestion_all[0] =& $suggestion[0] ; + break; + case "skip": + $suggestion_skip[0] =& $suggestion[0]; + break; + case "courses": + $suggestion_courses[0] =& $suggestion[0]; + break; + } + + + echo '
    '; + echo '
    '; + + echo '
    '; + echo '
    '; + + echo '
    '; + echo '
    '; + + print_post_for_step9($_POST); + echo ''; + echo ''; + echo ''; + echo '

    '; + return; + } + + if ($convert_type=='all'){ + echo "

    You have chosen the Convert all content option. All ATutor's content will be converted to UTF-8 from the encoding listed below.

    "; + echo "

    Note: This might take up to several minutes, please be patient while you wait.


    "; + echo "
    "; + } elseif ($convert_type=='courses'){ + echo "

    You have chosen the conversion by course. Each of the following courses' content will be converted to UTF-8 with respect to its Course Primary Language.

    "; + echo '

    Notes:

      '; + echo '
    • Please backup your ATutor database before clicking next.
    • '; + echo '
    • This conversion will convert only course related tables, other tables will be converted from the ATutor default (ISO-8859-1) to UTF-8.
    • '; + echo '
    • If the "Course Primary Language" listed below is not the language that you wish to convert from, please change the "Course Primary Language" under "Course Properties" to the language you want to convert from.
    • '; + echo '
    '; + generateCourseLangTable($_POST['tb_prefix'], $_SESSION['course_info']); + } elseif ($convert_type=='skip'){ + //When 'skip' has been chosen, check if version# < 1.6, if so, convert database structure(not content); o/w skip to next step. + if (version_compare($_POST['step1']['old_version'], '1.6', '<') === TRUE) { + unset($progress); + $progress[] = "Will not be converting database content."; + $progress[] = "Will be converting database table column structure (charset, collation) to UTF-8."; + print_feedback($progress); + print_post_for_step9($_POST); + echo ''; + echo ''; + echo '

    '; + return; + } elseif (version_compare($_POST['step1']['old_version'], '1.6.1', '<') === TRUE) { + //Check if version#=1.6 if so, then this 1.6 database must already been upgraded before. + //change all table to utf-8. + $conversionDriver = new ConversionDriver($_POST['tb_prefix']); + $conversionDriver->alter_all_charset(); + unset($progress); + $progress[] = "All tables' charset are now in UTF-8."; + print_feedback($progress); + print_post_for_step9($_POST); + echo ''; + echo ''; + echo '

    '; + return; + } + echo "

    Skipping UTF-8 Conversion.

    No content will be converted.

    "; + echo ''; //skip to next step + echo '

    '; + return; + } else { + $errors[] = "No conversion type selected."; + print_errors($errors); + print_post_for_step9($_POST); + echo ''; + echo '

    '; + return; + } + print_post_for_step9($_POST); + echo ''; + echo ''; + echo '

    '; + return; + } elseif ($_POST['con_step'] == '3'){ + //Check if this is a refresh request, if so, don't convert the db. + if (isset($_SESSION['conversion_completed']) && $_SESSION['conversion_completed']==true){ + $progress[] ='Database has already been converted, click next to continue.'; + print_feedback($progress); + echo '
    + + + '; + print_hidden(2); + print_post_for_step9($_POST); + echo '

    '; + return; + } + + $result = ''; + // Get course code to map encoding/charset + if ($_POST['convert_type'] == 'all' || $_POST['convert_type'] == 'courses' ){ + $query = "SELECT course_id, title FROM ".$_POST['tb_prefix']."courses"; + $result = mysql_query($query); + if (mysql_num_rows($result) <= 0){ + return false; + } + } else { + //'Skip' was selected, convert table structure only + queryFromFile('db/atutor_convert_db_to_utf8.sql'); + $progress[] = 'Database table structure has been converted to UTF-8.'; + print_feedback($progress); + if (isset($errors)){ + print_errors($errors); + echo '
    + '; + print_hidden(2); + print_post_for_step9($_POST); + echo '

    '; + return; + } + echo '
    + '; + echo ''; + print_hidden(2); + print_post_for_step9($_POST); + echo '

    '; + return; + } + + /* + * redo_conversion SESSION variable keep tracks of the table that failed conversion. + * If it is set, then run only those tables inside the redo_conversion SESSION variable. + */ + if (isset($_SESSION['redo_conversion'])){ + unset($errors); + foreach($_SESSION['redo_conversion'] as $course_title=>$class_obj){ + foreach($class_obj as $class_name=>$class_param){ + $temp_table = new $class_name ($class_param[0], $class_param[1], $class_param[2], $class_param[3]); + if (!$temp_table->convert()){ + $errors[]= $course_title.': '.$class_param[0].$class_param[1].' was not converted.'; + } else { + unset($_SESSION['redo_conversion'][$class_name]); + $progress[] = "$class_param[1] has now been converted."; + } + } + } + } else { + /* + * Convert course independent materials such as user information, categories + * Decide which language to use + */ + $conversionDriver = new ConversionDriver($_POST['tb_prefix']); + if (version_compare($_POST['step1']['old_version'], '1.6', '<') === TRUE) { + $conversionDriver->convertTableBySysDefault(); + } + $conversionDriver->convertTableBySysDefault_161(); + + /* + * Loop through all the courses, and convert all the course's content + * Flush out the process in the mean time. + */ + echo '
    '; + while ($row = mysql_fetch_assoc($result)){ + $course_id = $row['course_id']; + //Get charset + if (isset($_POST['encoding_code'])&& $_POST['encoding_code']!=""){ + $char_set = $_POST['encoding_code']; + } else { + $char_set = $_SESSION['course_info'][$course_id]['char_set']; + } + $row['title'] = mb_convert_encoding($row['title'], "UTF-8", $char_set); + + //If this is already in UTF-8, skip conversion + if (strtolower($char_set)=="utf-8" || strtolower($char_set)=="utf8"){ + $progress[] = 'Course ('.$row['title'].') has been skipped, course\'s content are already in UTF-8.'; + continue; + } + $progress[] = 'Course ('.$row['title'].') has been converted from '.$char_set; + + //Run through all ATutor table and convert only those rows with the above courses. + //todo: implement a driver class inside the TableConversion class. + ob_start(); + echo "Converting $row[title]...
    "; + ob_flush(); + flush(); + ob_end_flush(); + if (version_compare($_POST['step1']['old_version'], '1.6', '<') === TRUE) { + $conversionDriver->convertTableByClass($row['title'], $char_set, $course_id); + } + $conversionDriver->convertTableByClass_161($row['title'], $char_set, $course_id); + } + echo '
    '; + } + //Check if there are any errors, if not, jump to next step + if (!$errors) { + unset($_POST['submit']); +// store_steps(1); + //Will not allow refresh on this screen, because it will re-convert the database. + $_SESSION['conversion_completed'] = true; + + print_feedback($progress); + + if (isset($errors)){ + print_errors($errors); + echo '
    + + '; + print_hidden(2); + print_post_for_step9($_POST); + echo '

    '; + return; + } + + echo '
    + + + '; + print_hidden(2); + print_post_for_step9($_POST); + echo '

    '; + return; + } + } elseif ($_POST['con_step'] == '4'){ + //Convert the database charset to UTF8. + if (!check_db_default_charset($db)){ + $sql = 'ALTER DATABASE `'.$_POST['db_name'].'` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci'; + if (mysql_query($sql)===false){ + //Alter failed, probably the user doesn't have permission. + $errors[] = 'Failed to change the database default character set to UTF-8. Please set the database character set to UTF8 before continuing by using the following query: ALTER DATABASE `'.$_POST['db_name'].'` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci. To use ALTER DATABASE, you need the ALTER privilege on the database.'; + } + } + $progress[] ='Database default charset is now in UTF-8, click next to continue.'; + print_feedback($progress); + echo '
    + '; + print_hidden(2); + print_post_for_step9($_POST); + echo '

    '; + return; + } else { + /* If the installation has been stopped right after the conversion step. The session variable + * that prevents refreshes will still be set, have to unset it to carry on the installation. + */ + if (isset($_SESSION['conversion_completed'])){ + unset($_SESSION['conversion_completed']); + } + + $html = '
    + '; +// store_steps(1); + + $html .= '

    ATutor 2.0 upgrade requires you to convert ATutor content to UTF-8 if you are upgrading from a version earlier than ATutor 1.6. The courses are listed below with their associated primary language.

    Please choose one of the conversion options listed below, the recommanded option(in blue) is already selected for you.

    For a more detailed description for each of these conversions, please visit our ATutor Wiki page

    '; + + //The html fragment that sets the suggested conversion type on bold + $suggestion = array('class="suggested"', 'checked="checked"'); + $suggestion_skip=""; + $suggestion_all=""; + $suggestion_courses=""; + $recommanded_conversion = ""; + + $html .= ''; + $html .= ''; + + //Get all courses and their associated language from $_POST + $prev_language = ""; //Keep tracks of the course primary language in each loop. + $is_same_language = true; + if (isset($_SESSION['course_info'])){ + foreach ($_SESSION['course_info'] as $course_id=>$row){ + if ($prev_language==""){ + $prev_language = $row['char_set']; + } elseif ($is_same_language==true) { + if($prev_language != $row['char_set']){ + $is_same_language &= false; + } + } + //Get title + $query = 'SELECT title, primary_language FROM '.$_POST['tb_prefix'].'courses WHERE course_id='.$course_id; + $result = mysql_query($query); + if ($result && mysql_numrows($result) > 0){ + $rs_row = mysql_fetch_assoc($result); + //if the char_set of the course is not defined, the array will be emptied. + $html .= ''; + } + } + } + if ($is_same_language == true){ + if (strtolower($prev_language)=="utf8" || strtolower($prev_language)=="utf-8" ){ + $suggestion_skip =& $suggestion; + $recommanded_conversion = "skip"; + } else { + $suggestion_all =& $suggestion; + $recommanded_conversion = "all"; + $html .= ''; + } + } else { + $suggestion_courses =& $suggestion; + $recommanded_conversion = "courses"; + } + $html .= '
    Course TitleCourse Primary Language
    '; + $html .= @mb_convert_encoding($rs_row['title'], "UTF-8", $row['char_set']); + $html .= ''.$row['char_set']; + if ($row['char_set']==''){ + $errors[] = "$rs_row[title] has a primary language that does not appear to be installed on the system. Before continuing, either set the primary language to one that is installed, or install the missing language pack."; + $html .= ' Missing "'.$rs_row['primary_language'].'"'; + } + $html .= '

    '; + + $html .= '

    Convert all content: Use this option if you are using just one non-UTF-8 language pack in your ATutor.

    '; + $html .= '

    Convert content by courses: Use this option when you are using more than one UTF-8 or non-UTF-8 language pack in your ATutor.

    '; + $html .= '

    Skip conversion: Use this option when you have only UTF-8 language packs in your ATutor.

    '; + $html .= '
    '; + + $html .= '
    '; + $html .= '
    '; + + $html .= '
    '; + $html .= '
    '; + + $html .= '
    '; + $html .= '
    '; + + //States flags for ustep9 + $html .= ''; + $html .= ''; + if (isset($errors)){ + print_errors($errors); + $html .= '

    '; + } else { + $html .= '

    '; + } + echo $html; + print_hidden(2); + print_post_for_step9($_POST); + echo '
    '; + return; + } + } +} + + //Failed + if (isset($errors)) { + print_errors($errors); + } + + echo '
    + '; + store_steps(2); + print_hidden(3); + print_post_for_step9($_POST); + echo '

    '; + return; + + +/** --------------------------------------------------------------------------- + * Functions declaraion + * --------------------------------------------------------------------------- + */ +/** + * Runs through the database and get all the courses' titles and languages out + * @param table_prefix is the database table prefix + * @course_info is the array that stores the course_id->[charset, encoding] mapping. + */ + function generateCourseLangTable($table_prefix, $course_info){ + echo ''; + echo ''; + //Get all courses and their associated languages out + foreach ($course_info AS $course_id=>$row){ + $query = 'SELECT title FROM '.$table_prefix.'courses WHERE course_id='.$course_id; + $result = mysql_query($query); + if ($result && mysql_numrows($result) > 0){ + $rs_row = mysql_fetch_assoc($result); + } else { + return; + } + echo ''; + } else { + echo mb_convert_encoding($rs_row['title'], "UTF-8", $row['char_set']); + echo ''; + } + } + echo '
    Course TitleCourse Primary Language
    '; + if ($row['char_set']==''){ + echo $rs_row['title']; + echo 'Undefined
    '.$row['char_set'].'
    '; + } + +/** + * This function prints out the post values that need to be carried over along + * the entire step 9. + * @param $_post the post parameter + */ +function print_post_for_step9($_POST){ + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; +} + + +/** + * This function checks if the database's and tbale's default charset are UTF-8. + * @return true if db default charset is UTF-8, false otherwise. + */ +function check_db_default_charset($db){ + //Check database's default charset + $sql = "SHOW CREATE DATABASE `$_POST[db_name]`"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + if (!preg_match('/CHARACTER SET utf8/i', $row['Create Database'])){ + return false; + } + + //check tables + $sql = "SHOW TABLES"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_row($result)){ + $row[0]; + $sql2 = "SHOW CREATE TABLE `$row[0]`"; + $result2 = mysql_query($sql2, $db); + $row2 = mysql_fetch_row($result); + + if (!preg_match('/DEFAULT CHARSET\=utf8/i', $row[1])){ + return false; + } + } + + return true; +} +?> \ No newline at end of file diff --git a/install/include/ustep_content_conversion.php b/install/include/ustep_content_conversion.php new file mode 100644 index 000000000..5ccc6ffe2 --- /dev/null +++ b/install/include/ustep_content_conversion.php @@ -0,0 +1,130 @@ +children + * should remain the same throughout the recursion. + * @return A tree structure representation of the content entries. + * @author Harris Wong + */ +function buildTree($current, $content_array){ + $folder = array(); + foreach($current as $order=>$content_id){ + //if has children + if (isset($content_array[$content_id])){ + $wrapper[$content_id] = buildTree($content_array[$content_id], $content_array); + } + + //no children. + if ($wrapper){ + $folder['order_'.$order] = $wrapper; + unset($wrapper); + } else { + $folder['order_'.$order] = $content_id; + } + } + return $folder; +} + + +/** + * Transverse the content tree structure, and reconstruct it with the IMS spec. + * This tree has the structure of [order=>array(id)], so the first layer is its order, second is the id + * if param merge is true, if node!=null, merge it to top layer, and + offset to all others + * @param mixed Tree from the buildTree() function, or sub-tree + * @param mixed the current tree. + * @return A new content tree that meets the IMS specification. + * @author Harris Wong + */ +function rebuild($tree, $node=''){ + $order_offset = 0; + $folder = array(); + if (!is_array($tree)){ + return $tree; + } + if ($node!=''){ + $tree['order_0'] = $node; + $order_offset += 1; + } + //go through the tree + foreach($tree as $k=>$v){ + if (preg_match('/order\_([\d]+)/', $k, $match)==1){ + //if this is the order layer + $folder['order_'.($match[1]+$order_offset)] = rebuild($v); + } else { + //if this is the content layer + if(is_array($v)){ + $folder[$k] = rebuild($v, $k); + } + } + } + return $folder; +} + + +/** + * Transverse the tree and update/insert entries based on the updated structure. + * @param array The tree from rebuild(), and the subtree from the recursion. + * @param int the ordering of this subtree respect to its parent. + * @param int parent content id + * @return null (nothing to return, it updates the db only) + */ +function reconstruct($tree, $order, $content_parent_id, $table_prefix){ + global $db; + + //a content page. + if (!is_array($tree)){ + $sql = 'UPDATE '.$table_prefix."content SET ordering=$order, content_parent_id=$content_parent_id WHERE content_id=$tree"; + if (!mysql_query($sql, $db)){ + //throw error + echo mysql_error(); + } + return; + } + foreach ($tree as $k=>$v){ + if (preg_match('/order\_([\d]+)/', $k, $match)==1){ + //order layer + reconstruct($v, $match[1], $content_parent_id, $table_prefix); //inherit the previous layer id + } else { + //content folder layer + $sql = 'SELECT * FROM '.$table_prefix."content WHERE content_id=$k"; + $result = mysql_query($sql, $db); + $old_content_row = mysql_fetch_assoc($result); + $sql = 'INSERT INTO '.$table_prefix.'content (course_id, content_parent_id, ordering, last_modified, revision, formatting, release_date, keywords, content_path, title, use_customized_head, allow_test_export, content_type) VALUES (' + .$old_content_row['course_id'] . ', ' + .$content_parent_id . ', ' + .$order . ', ' + .'\''. $old_content_row['last_modified'] . '\', ' + .$old_content_row['revision'] . ', ' + .$old_content_row['formatting'] . ', ' + .'\''. $old_content_row['release_date'] . '\', ' + .'\''. mysql_real_escape_string($old_content_row['keywords']) . '\', ' + .'\''. mysql_real_escape_string($old_content_row['content_path']) . '\', ' + .'\''. mysql_real_escape_string($old_content_row['title']) . '\', ' + .$old_content_row['use_customized_head'] . ', ' + .$old_content_row['allow_test_export'] . ', ' + . '1)'; + + if (mysql_query($sql, $db)){ + $folder_id = mysql_insert_id(); + reconstruct($v, '', $folder_id, $table_prefix); + } else { + //throw error + echo mysql_error(); + } + } + } +} +?> + diff --git a/install/include/ustep_pwd_encryt.php b/install/include/ustep_pwd_encryt.php new file mode 100644 index 000000000..9f8a617e1 --- /dev/null +++ b/install/include/ustep_pwd_encryt.php @@ -0,0 +1,32 @@ +
    "; + +$sql = "SELECT member_id, first_name, last_name, password FROM ".TABLE_PREFIX."members"; +$result = mysql_query($sql, $db); + +while ($row = mysql_fetch_assoc($result)) +{ + if (strlen($row["password"]) < 40) + { + print "updating member ".$row["first_name"]." ".$row["last_name"].": from ".$row["password"]." to " .sha1($row["password"])."
    "; + $sql_update = "UPDATE ".TABLE_PREFIX."members set password = '".sha1($row["password"])."', creation_date = creation_date WHERE member_id=".$row["member_id"]; + $result_update = mysql_query($sql_update, $db); + } +} + +print "
    Done!"; +?> diff --git a/install/index.php b/install/index.php new file mode 100644 index 000000000..c8d46893a --- /dev/null +++ b/install/index.php @@ -0,0 +1,412 @@ +'; +$good = 'Good'; +$warning = 'Warning'; + +$no_good = FALSE; +$not_as_good = FALSE; +?> + + + + + +
    Icon Legend
    Pass, can proceed
    Warning but can proceed
    Failed, can not proceed
    + +

    Welcome to the ATutor Installation

    +

    This process will step you through your ATutor installation or upgrade.

    +

    During this process be sure not to use your browser's Refresh or Reload feature as it may complicate the installation process.

    + +

    Before you continue you may want to review the ATutor Handbook for more detailed instructions.

    + +

    Requirements

    +

    Please review the requirements below before proceeding.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    File IntegrityDetectedStatus
    Case Sensitivity'; + echo $good; + } else if (file_exists('../include/classes/CSVExport.class.php')) { + echo 'Enforced'; + echo $good; + } else { + echo 'Enforced'; + echo $bad; + $no_good = TRUE; + } ?>
    PHP OptionsDetectedStatus
    PHP 4.3.0+=')) { + echo $good; + } else { + echo $bad; + $no_good = TRUE; + } ?>
    zlib'; + echo $good; + } else { + echo 'Disabled'; + echo $bad; + $no_good = TRUE; + } ?>
    mbstring'; + echo $good; + } else { + echo 'Disabled'; + echo $bad; + $not_as_good = TRUE; + } ?>
    mysql'; + echo $good; + } else { + echo 'Disabled'; + echo $bad; + $no_good = TRUE; + } ?>
    safe_mode = Off'; + echo $bad; + $no_good = TRUE; + } else { + echo 'Off'; + echo $good; + } ?>
    file_uploads = On'; + echo $good; + } else { + echo 'Off'; + echo $bad; + $no_good = TRUE; + } ?>
    GD'; + echo $good; + } else { + echo 'Disabled'; + echo $warning; + } ?>
    JPEG Support'; + echo $good; + } else { + echo 'Disabled'; + echo $warning; + } ?>
    upload_max_filesize >= 2 MB
    post_max_size >= 8 MB
    sessions'; + echo $good; + } else { + echo 'Disabled'; + echo $bad; + $no_good = TRUE; + } ?>
    session.auto_start = 0'; + echo $bad; + $no_good = TRUE; + } else { + echo '0'; + echo $good; + } ?>
    session.save_path'; + echo $good; + } else { + echo 'Directory Not Writeable'; + echo $bad; + $no_good = TRUE; + } + ?>
    curl'; + echo $good; + } else { + echo 'Disabled'; + echo $warning; + } + ?>
    . in include_path'; + echo $good; + } else { + echo 'Disabled'; + echo $bad; + $no_good = TRUE; + } + ?>
    Mail configuration'; + echo $warning; + } else { + echo 'Enabled'; + echo $good; + } + ?>
    MySQL OptionsDetectedStatus
    MySQL 4.1.10+'; + echo $good; + } else { + echo 'Not Found'; + echo $bad; + $no_good = TRUE; + } ?>
    +
    + + + + + + +
    Your server does not meet the minimum requirements!
    + Please correct the above errors to continue.
    + + + + + + + + + + +
    ATutor has indicated that the 'mbstring' library is missing from the PHP.
    + We strongly encourage you to install the 'mbstring' library before continuing, however, if you choose not to install the library from PHP, a third party library within ATutor will be used.

    + For production systems, we strongly encourage you to install the PHP with mbstring support.

    + You may choose to by pass the mbstring check for the installation at your own risk by clicking continue.
    New Installation » +
    +
    + + +
    +
    +
    + + + + +
    Or
    + + + + + + + + + +
    Upgrading from previous ATutor must have mbstring library installed.
    Upgrade an Existing Installation » + +
    + + + + + + +
    New Installation »
    + + +
    + + + + +
    Or
    + + + + + +
    Upgrade an Existing Installation »
    + + +
    + + + diff --git a/install/install.php b/install/install.php new file mode 100644 index 000000000..3d59cb3c2 --- /dev/null +++ b/install/install.php @@ -0,0 +1,73 @@ + \ No newline at end of file diff --git a/install/not_installed.php b/install/not_installed.php new file mode 100644 index 000000000..b0ff87baa --- /dev/null +++ b/install/not_installed.php @@ -0,0 +1,30 @@ + + + +

    ATutor does not appear to be installed. Continue on to the installation.

    + + + \ No newline at end of file diff --git a/install/stylesheet.css b/install/stylesheet.css new file mode 100644 index 000000000..0297cfe77 --- /dev/null +++ b/install/stylesheet.css @@ -0,0 +1,482 @@ +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2008 by Greg Gay, Joel Kronenberg, Heidi Hazelton */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/************************************************************************/ +/* $Id$ */ + +/* main body attributes */ +body { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: black; + background-color: white; + background-image: url(); + margin-bottom: 0px; + margin-top: 0px; + margin-left: 0px; + margin-right: 0px; + font-size: small; +} + +code { + font-size: x-small; + color: black; +} + +kbd { + padding: 0px 1px 0px 1px; + border-width: 1px 2px 2px 1px; + border-style: solid; + background: #faf6f6; + color: #000; + border-color: #edd #baa #baa #eed; + font-size: small; + white-space: nowrap; +} + +/* paragraph attributes */ +p { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: black; + margin-bottom: 10px; + margin-top: 0px; + margin-left: 5px; + margin-right: 0px; + font-size: small; +} + +p.heading { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-weight: bold; + font-size: 1em; + color: black; + margin-bottom: 10px; + margin-top: 0px; + margin-left: 5px; + margin-right: 0px; +} + +p.error { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-weight: bold; + color: red; + margin-bottom: 10px; + margin-top: 0px; + margin-left: 5px; + margin-right: 0px; +} + +h1#header { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: white; + float: left; + display: inline; + margin: 10px; +} + + +/* list attributes */ +ul { + font-family: Verdana, Arial, Helvetica, sans-serif; + margin-bottom: 5px; + margin-top: 0px; + margin-right: 0px; +} + +li { + font-family: Verdana, Arial, Helvetica, sans-serif; + margin-bottom: 0px; + margin-top: 0px; + margin-right: 0px; +} +li.important{ + color: red; +} + +/* link attributes */ +a:link { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: #384F89; + border-bottom: 1px solid #384F89; + text-decoration: none; +} +a:visited { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: #51286C; +} +a:hover { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: black; +} + +/* formfield attributes */ +.formfield { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: black; + border: #003399 solid 1px; + padding: 2px; +} + +.formfield:focus { + border: #0033FF solid 1px; +} + +form { + font-family: Verdana, Arial, Helvetica, sans-serif; + margin-bottom: 0px; + margin-top: 0px; + margin-left: 0px; + margin-right: 0px; +} + +/* main submit button */ +.button { + font-family: Verdana, Arial, Helvetica, sans-serif; + background-color: #F8F8F8; + font-weight: bold; + border: #354A81 solid 1px; +} +.button:hover, .button:focus { + cursor: pointer; + border: #354AFF solid 1px; + color: #0000FF; +} + +/* small submit button at top */ +.button2 { + font-family: Verdana, Arial, Helvetica, sans-serif; + background-color: #F8F8F8; + font-size: 0.7em; + border-top: #354A81 solid 1px; + border-bottom: #354A81 solid 1px; + border-right: #354A81 solid 1px; + border-left: #354A81 solid 1px; +} + +/* table border */ +.tableborder { + border-left: 1px #98AAB1 solid; + border-right: 1px #98AAB1 solid; + border-top: 1px #98AAB1 solid; +} + +/* user menu at top */ +.topbar { + font-family: Verdana, Arial, Helvetica, sans-serif; + background-color: #D1D7DC; + text-align: center; + padding: 3px; +} + +/* breadcrumbs */ +td.breadcrumbs { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: #FFFFFF; + background-color: #006699; + height: 26px; +} +.breadcrumbs { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: white + text-decoration: none; + letter-spacing: 1px; +} +a.breadcrumbs { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: #CCCCCC; + text-decoration: none; +} +a.breadcrumbs:hover { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: #CCCC00; + text-decoration: none; +} +a.breadcrumbs:visited { + font-family: Verdana, Arial, Helvetica, sans-serif; + color : #CCCCCC; + text-decoration: none; +} + +/* +td.rowpic { + background-color: #FFFFFF; + background-image: url('images/cellpic2.jpg'); + background-repeat: repeat-y; +} +*/ + +/* the menu */ +td.row1, table.row1 { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-weight: normal; + background-color: #EFEFEF; + vertical-align: top; + padding: 8px; + border-bottom: 1px #98AAB1 solid; +} + +/* menu titles*/ +td.cat, th { + font-family: Verdana, Arial, Helvetica, sans-serif; + background-repeat: repeat-x; + background-color:#D1D7DC; + height: 25px; + text-align: center; + font-weight: bold; + padding-top: 3px; + padding-left: 5px; + padding-right: 5px; + white-space: nowrap; +} +td.cat2 { + font-family: Verdana, Arial, Helvetica, sans-serif; + text-align: center; + font-weight: bold; + padding: 1px; + border: 1px #98AAB1 solid; +} +td.cat2b { + font-family: Verdana, Arial, Helvetica, sans-serif; + text-align: center; + font-weight: bold; + height: 27px; + padding: 1px; + border: 1px #98AAB1 solid; +} +td.cat2c { + font-family: Verdana, Arial, Helvetica, sans-serif; + text-align: center; + font-weight: bold; + height: 27px; + padding: 1px; + border: 1px #98AAB1 solid; +} +td.cat2d { + font-family: Verdana, Arial, Helvetica, sans-serif; + text-align: center; + font-weight: bold; + height: 27px; + padding: 1px; + border: 1px #98AAB1 solid; +} +td.cat2e { + font-family: Verdana, Arial, Helvetica, sans-serif; + text-align: center; + font-weight: bold; + height: 27px; + padding: 1px; + border: 1px #98AAB1 solid; +} +table.cat2 { +font-family: Verdana, Arial, Helvetica, sans-serif; + /*Remove this attribute, to fix Opera menu display problem*/ + /*height: 27px;*/ + text-align: center; + font-weight: bold; + border: 1px #98AAB1 solid; +} +table.cat3 { + font-family: Verdana, Arial, Helvetica, sans-serif; + background-color: ; +} + +/* various line breaks */ +td.row2 { + background-color: #98AAB1; + font-weight: normal; + height: 1px; +} +td.row3 { + background-color: #006699; + font-weight: normal; + height: 1px; +} +td.row4 { + background-color: #f3f3f2; + font-weight: normal; + height: 1px; +} + +/* align text to the left */ +.left { + text-align: left; +} + +/* pipe attribute */ +.spacer { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: #006699; + font-size: 0.65em; +} + +/* previous/next text attribute */ +.bigspacer { + color: #006699; +} + +.bodyline { + background-color: #FFFFFF; + border: 1px #98AAB1 solid; +} +/* date attributes */ +small.date { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: #006699; + margin-bottom: 0px; + margin-top: 0px; + margin-left: 5px; + margin-right: 0px; + font-size: 0.7em; +} + +.menuimage { + vertical-align: middle; + margin-top: 0px; +} + +/* page breakline */ +hr { + color: #98AAB1; + background-color: white; + height: 1px; +} + +/* box around a forum reply */ +.block { + position: relative; + font-size: 0.9em; + background-color: #FFFFFF; + border: 1px dotted #98AAB1; + padding: 5px; + margin-right: 5px; + margin-left: 15px; + margin-bottom: 0px; +} + + +select.dropdown { + font-family: Verdana, Arial, Helvetica, sans-serif; + background-color: #F1F3F1; +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0px; + margin-bottom: 0px; + margin-left: 5px; +} + +h3 { + border-bottom: 1px solid #354A81; +} + + +.content { + padding: 20px; + font-size: small; +} + +/* error box */ +table.errbox{ + font-family: Verdana, Arial, Helvetica, sans-serif; + background-color: #FF0000; + border: 2px #EEEEEE solid; +} +tr.errbox { + font-family: Verdana, Arial, Helvetica, sans-serif; + background-color: white; + vertical-align:top; + font-size: smaller; +} +h3.err { + color: red; + border: 0px; +} + +h3.good { + color: green; + border: 0px; +} + + + /* feedback box */ +table.fbkbox{ + font-family: Verdana, Arial, Helvetica, sans-serif; + background-color: #30B626; + border: 2px #EEEEEE solid; +} +tr.fbkbox{ + font-family: Verdana, Arial, Helvetica, sans-serif; + background-color: white; + vertical-align: top; + font-size: smaller; +} +img#logo { + padding: 10px; + background-color: #eeeeee; + border: 1px solid #cccccc; + margin: 10px; + float: right; +} + +div.install { + border: 1px solid #354A81; +} + +div.install h3 { + background-color: #354A81; + margin-left: 0px; + padding: 2px; + color: white; + border: 0px; +} +div.install p { + font-size: small; +} +div.suggested { + font-weight: bold; + color: blue; + border: 1px solid #e3e3e3; +} + +/* for data tables */ +table.data { + border: 1px solid #f0f0f0; + width: 90%; + margin-left: auto; + margin-right: auto; + border-spacing: 0px; + border-collapse: collapse; +} + +table.data th { + background-color: #fafafa; + border-bottom: 1px solid #f0f0f0; + padding: 2px; + white-space: nowrap; + border-top: 1px solid #f0f0f0; +} + +table.data td { + padding: 3px; +} + +.required { + font-weight: bold; + color: red; + padding-right: 0px; +} +div.optional { + font-weight: bold; + color: red; + font-size: large; + float: left; + position: relative; + margin-top: -3px; + height: 15px; + padding-right: 3px; +} \ No newline at end of file diff --git a/install/update_config.php b/install/update_config.php new file mode 100644 index 000000000..08066dac0 --- /dev/null +++ b/install/update_config.php @@ -0,0 +1,58 @@ + \ No newline at end of file diff --git a/install/upgrade.php b/install/upgrade.php new file mode 100644 index 000000000..97f801d67 --- /dev/null +++ b/install/upgrade.php @@ -0,0 +1,85 @@ +Upgrade from here using the Upgrade button.'; + require('include/footer.php'); + exit; + } + // in: select directory + // out: confirm verions + require('include/ustep1.php'); +} +if ($step == 2) { + // in: update database + // out: - + require('include/ustep2.php'); +} +if ($step == 3) { + // in: database info + // out: convert database from to UTF-8 + require('include/ustep7.php'); +} +if ($step == 4) { + // in: display version specific notices + // out: update database with new options + require('include/ustep3.php'); +} +if ($step == 5) { + // in: determine where the old content dir is and if it has to be copied + // out: try to create the directory and set permissions + require('include/step5.php'); +} +if ($step == 6) { + // in: copy the content if needed + // out: - + require('include/ustep4.php'); +} +if ($step == 7) { + // in: copy the config file + // out: - + require('include/ustep5.php'); +} +/* anonymous data collection */ +if ($step == 8) { + require('include/step7.php'); +} + +if ($step == 9) { + require('include/ustep6.php'); +} +require('include/footer.php'); +?> \ No newline at end of file diff --git a/jscripts/ATutor.js b/jscripts/ATutor.js new file mode 100644 index 000000000..c6d95ef17 --- /dev/null +++ b/jscripts/ATutor.js @@ -0,0 +1,479 @@ +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002 - 2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/************************************************************************/ +// $Id: $ + +var ATutor = ATutor || {}; +ATutor.users = ATutor.users || {}; +ATutor.users.preferences = ATutor.users.preferences || {}; + +(function() { + /** + * A function to open a popup window + */ + ATutor.poptastic = function (url) { + var newwindow=window.open(url,'popup','height=600,width=600,scrollbars=yes,resizable=yes'); + if (window.focus) { + newwindow.focus(); + } + }; + + /** + * Cookie handling + */ + var getexpirydate = function (nodays) { + var Today = new Date(); + var nomilli = Date.parse(Today); + Today.setTime(nomilli + nodays * 24 * 60 * 60 * 1000); + return Today.toUTCString(); + }; + + ATutor.setcookie = function (name, value, duration) { + document.cookie = name + "=" + escape(value) + ";path=/;expires=" + getexpirydate(duration);; + if( !ATutor.getcookie(name) ){ + return false; + } else { + return true; + } + }; + + ATutor.getcookie = function (cookiename) { + var cookiestring = "" + document.cookie; + var index1 = cookiestring.indexOf(cookiename); + if (index1 == -1 || cookiename == "") { + return ""; + } + var index2 = cookiestring.indexOf( ';', index1); + if (index2 == -1) { + index2 = cookiestring.length; + } + return unescape(cookiestring.substring(index1 + cookiename.length + 1, index2)); + }; + + //styles block for user preferences + //used by ATutor.users.preferences.setStyles + ATutor.users.preferences.user_styles = + ''; + + /** + * Substitutes styles into styles block above and then places those styles on the page + */ + ATutor.users.preferences.setStyles = function (bg_color, fg_color, hl_color, font, font_size) { + var font_style = font ? 'font-family:' + font + ' !important;\n' : ''; + var font_size_style = font_size ? 'font-size:' + font_size + 'em !important;\n' : ''; + var bg_color_style = bg_color ? 'background-color: #' + bg_color + ' !important;\n' : ''; + var fg_color_style = fg_color ? 'color: #' + fg_color + ' !important;\n' : ''; + var hl_color_style = hl_color ? 'background-color: #' + hl_color + '! important;\n' : ''; + + var pref_style = ATutor.users.preferences.user_styles.replace(/FONT_FAMILY/g, font_style).replace(/FONT_SIZE/g, font_size_style).replace(/BG_COLOR/g, bg_color_style).replace(/FG_COLOR/g, fg_color_style).replace(/HL_COLOR/g, hl_color_style); + jQuery('#pref_style').replaceWith(pref_style); + if (window.opener) jQuery('#pref_style', window.opener.document).replaceWith(pref_style); + }; + + /** + * Adds click hander to links with id pref_wiz_launcher + */ + ATutor.users.preferences.addPrefWizClickHandler = function () { + var launcherArray = jQuery(".pref_wiz_launcher"); + launcherArray.click(function() { + var query_string = ""; + if (ATutor.users.preferences.course_id !== "") { + query_string = 'course_id=' + ATutor.users.preferences.course_id; + } + if (query_string !== "") { + query_string = "?" + query_string; + } + window.open(ATutor.base_href + 'users/pref_wizard/index.php' + query_string,'prefWizWindow','menubar=0,scrollbars=1,resizable=1,width=640,height=580'); + return false; + }); + }; + +})(); diff --git a/jscripts/ATutorCourse.js b/jscripts/ATutorCourse.js new file mode 100644 index 000000000..86a5892e4 --- /dev/null +++ b/jscripts/ATutorCourse.js @@ -0,0 +1,155 @@ +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002 - 2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/** ********************************************************************* */ +// $Id: $ +var ATutor = ATutor || {}; +ATutor.course = ATutor.course || {}; + +(function() { + + var element_collapse_icon; + var element_expand_icon; + + var setExpandIcon = function (clickedElement, title) { + clickedElement.attr("src", element_expand_icon) + clickedElement.attr("alt", ATutor.course.show + " " + title); + clickedElement.attr("title", ATutor.course.show + " " + title); + ATutor.setcookie("m_"+title, 0, 1); + }; + + var setCollapseIcon = function (clickedElement, title) { + clickedElement.attr("src", element_collapse_icon); + clickedElement.attr("alt", ATutor.course.hide + " " + title); + clickedElement.attr("title", ATutor.course.hide + " " + title); + ATutor.setcookie("m_"+title, null, 1);; + + }; + + // toggle elements in side menu (via the +/- icon in each side menu element) + function showHideSubmenu() + { + var clickedElement = jQuery(this); + var title = jQuery("span", clickedElement.parent()).html(); + if (clickedElement.attr("src") == element_collapse_icon) { + setExpandIcon(clickedElement, title); + } + else { + setCollapseIcon(clickedElement, title); + } + clickedElement.parent().next().slideToggle(); + } + + //modifies the menu html to add title, expand/collapse image, alt text. + var printSubmenus = function () { + var sideMenuBoxHeadings = jQuery("h4.box"); + for (var titleIndex = 0; titleIndex < sideMenuBoxHeadings.length; titleIndex++) { + var heading = jQuery(sideMenuBoxHeadings[titleIndex]); + var title = jQuery("span", heading).html(); + var inputElement = jQuery("input", heading); + var menu = jQuery(heading.next()); + if (ATutor.getcookie("m_" + title) == "0") { + setExpandIcon(inputElement, title); + menu.hide(); + } else { + setCollapseIcon(inputElement, title); + menu.show(); + } + inputElement.click(showHideSubmenu); + + } + }; + + var menu_show_icon; + var menu_hide_icon; + + //Initialize the submenus + ATutor.course.doSideMenus = function () { + element_collapse_icon = ATutor.base_href + "themes/" + ATutor.course.theme + "/images/mswitch_minus.gif"; + element_expand_icon = ATutor.base_href + "themes/" + ATutor.course.theme + "/images/mswitch_plus.gif"; + printSubmenus(); + }; + + var hideMenu = function (effect) { + var menuImage = jQuery("#menutoggle > a > img"); + menuImage.attr("src", menu_show_icon); + menuImage.attr("alt", ATutor.course.show); + menuImage.attr("title", ATutor.course.show); + + if (effect) { + jQuery("#side-menu").slideUp("slow"); + } else { + jQuery("#side-menu").hide(); + } + + ATutor.setcookie("side-menu", "none", 1); + }; + + var showMenu = function (effect) { + var menuImage = jQuery("#menutoggle > a > img"); + menuImage.attr("src", menu_hide_icon); + menuImage.attr("alt", ATutor.course.hide); + menuImage.attr("title", ATutor.course.hide); + + if (effect) { + jQuery("#side-menu").slideDown("slow"); + } else { + jQuery("#side-menu").show(); + } + ATutor.setcookie("side-menu", "", 1); + }; + + var showHideMenu = function () { + var clickedElement = jQuery("img", this); + if (clickedElement.attr("src") == menu_hide_icon) { + hideMenu(true); + } + else { + showMenu(true); + } + }; + + var printMenuToggle = function (effect) { + var state = ATutor.getcookie("side-menu"); + if (state && state=="none") { + hideMenu(effect); + } else { + showMenu(effect); + } + var menuLink = jQuery("#menutoggle > a"); + menuLink.click(showHideMenu); + }; + + ATutor.course.doMenuToggle = function (effect) { + menu_show_icon = ATutor.base_href + "themes/" + ATutor.course.theme + "/images/showmenu.gif"; + menu_hide_icon = ATutor.base_href + "themes/" + ATutor.course.theme + "/images/hidemenu.gif"; + printMenuToggle(); + }; + + ATutor.course.toggleFolder = function (cid, expand, collapse, course_id) { + var tree_collapse_icon = ATutor.base_href + "images/tree/tree_collapse.gif"; + var tree_expand_icon = ATutor.base_href + "images/tree/tree_expand.gif"; + if (jQuery("#tree_icon"+cid).attr("src") == tree_collapse_icon) { + jQuery("#tree_icon"+cid).attr("src", tree_expand_icon); + jQuery("#tree_icon"+cid).attr("alt", expand); + jQuery("#tree_icon"+cid).attr("title", expand); + ATutor.setcookie("c"+course_id+"_"+cid, null, 1); + } + else { + jQuery("#tree_icon"+cid).attr("src", tree_collapse_icon); + jQuery("#tree_icon"+cid).attr("alt", collapse); + jQuery("#tree_icon"+cid).attr("title", collapse); + ATutor.setcookie("c"+course_id+"_"+cid, "1", 1); + } + + jQuery("#folder"+cid).slideToggle(); + }; + +})(); diff --git a/jscripts/ATutor_js.php b/jscripts/ATutor_js.php new file mode 100644 index 000000000..e4177d1f1 --- /dev/null +++ b/jscripts/ATutor_js.php @@ -0,0 +1,53 @@ + +ATutor = ATutor || {}; +ATutor.course = ATutor.course || {}; + +(function () { + + ATutor.base_href = ""; + ATutor.course.show = ""; + ATutor.course.hide = ""; + ATutor.course.theme = ""; + + //everything in the document.ready block executes after the page is fully loaded + jQuery(document).ready( function () { + ATutor.users.preferences.setStyles( + '', + '', + '', + '', + ''); + + ATutor.users.preferences.addPrefWizClickHandler(); + ATutor.users.preferences.course_id = ""; + 0)) { +?> + var myName = self.name; + if (myName != "prefWizWindow" && myName != "progWin") { + ATutor.course.doSideMenus(); + ATutor.course.doMenuToggle(); + } + + }); +})(); + + diff --git a/jscripts/ATutor_tiny_mce_plugins/insert_tag/editor_plugin.js b/jscripts/ATutor_tiny_mce_plugins/insert_tag/editor_plugin.js new file mode 100644 index 000000000..cc633d0df --- /dev/null +++ b/jscripts/ATutor_tiny_mce_plugins/insert_tag/editor_plugin.js @@ -0,0 +1 @@ +"use strict";(function(){tinymce.PluginManager.requireLangPack('insert_tag');tinymce.create('tinymce.plugins.Insert_tagPlugin',{init:function(ed,url){var placeCursor=function(){ed.selection.select(ed.dom.select('span#remove_me')[0]);ed.dom.remove(ed.dom.select('span#remove_me')[0])};var insertionFunction=function(insertionString){return function(){if(ed.selection.isCollapsed()){ed.selection.setContent('['+insertionString+'][/'+insertionString+']');placeCursor()}else{ed.selection.setContent('['+insertionString+']'+ed.selection.getContent()+'[/'+insertionString+']')}}};ed.addCommand('mceInsertTermTag',insertionFunction("?"));ed.addButton('insert_term_tag',{title:'insert_tag.termdesc',cmd:'mceInsertTermTag',image:url+'/img/term.png'});ed.addCommand('mceInsertCodeTag',insertionFunction("code"));ed.addButton('insert_code_tag',{title:'insert_tag.codedesc',cmd:'mceInsertCodeTag',image:url+'/img/code.png'});ed.addCommand('mceInsertTexTag',insertionFunction("tex"));ed.addButton('insert_tex_tag',{title:'insert_tag.texdesc',cmd:'mceInsertTexTag',image:url+'/img/tex.png'});ed.addCommand('mceInsertMediaTag',function(){if(ed.selection.isCollapsed()){ed.selection.setContent('[media|640|480]http://[/media]');placeCursor()}else{ed.selection.setContent('[media|640|480]http://'+ed.selection.getContent()+'[/media]')}});ed.addButton('insert_media_tag',{title:'insert_tag.mediadesc',cmd:'mceInsertMediaTag',image:url+'/img/media.png'})},getInfo:function(){return{longname:'Insert tag plugin',author:'ATutor',authorurl:'http://www.atutor.ca',infourl:'http://www.atutor.ca',version:"0.9beta"}}});tinymce.PluginManager.add('insert_tag',tinymce.plugins.Insert_tagPlugin)})(); \ No newline at end of file diff --git a/jscripts/ATutor_tiny_mce_plugins/insert_tag/editor_plugin_src.js b/jscripts/ATutor_tiny_mce_plugins/insert_tag/editor_plugin_src.js new file mode 100644 index 000000000..9936cf4d8 --- /dev/null +++ b/jscripts/ATutor_tiny_mce_plugins/insert_tag/editor_plugin_src.js @@ -0,0 +1,126 @@ +/** + * $Id: $ + * + * @author Laurel A. Williams + * @copyright Copyright © 2008, ATutor, All rights reserved. + */ + +/*global tinymce*/ + +"use strict"; +(function () { + + // Load plugin specific language pack + tinymce.PluginManager.requireLangPack('insert_tag'); + + tinymce.create('tinymce.plugins.Insert_tagPlugin', { + + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init : function (ed, url) { + + /** + * Places the cursor in the appropriate insertion point between [][/] + * tags. It deletes the with the id="remove_me", which was + * placed between the [][/] tags, leaving the caret between + * the tags as desired. + */ + var placeCursor = function () { + ed.selection.select(ed.dom.select('span#remove_me')[0]); + ed.dom.remove(ed.dom.select('span#remove_me')[0]); + }; + + /** + * A function which generates a function to insert the appropriate + * tags, either [?], [code] or [tex]. + * + * Note the slightly hacky insertion of a span with id="remove_me" + * which is used to place the cursor in the correct insertion point. + */ + var insertionFunction = function (insertionString) { + return function () { + if (ed.selection.isCollapsed()) { + ed.selection.setContent('['+ insertionString + + '][/' + insertionString + ']'); + placeCursor(); + } + else { + ed.selection.setContent('['+ insertionString + ']' + + ed.selection.getContent() + '[/' + insertionString + ']'); + } + }; + }; + + //[?] tag + ed.addCommand('mceInsertTermTag', insertionFunction("?")); + ed.addButton('insert_term_tag', { + title : 'insert_tag.termdesc', + cmd : 'mceInsertTermTag', + image : url + '/img/term.png' + }); + + //[code] tag - has not been added to interface because it doesn't work + ed.addCommand('mceInsertCodeTag', insertionFunction("code")); + ed.addButton('insert_code_tag', { + title : 'insert_tag.codedesc', + cmd : 'mceInsertCodeTag', + image : url + '/img/code.png' + }); + + //[tex] tag + ed.addCommand('mceInsertTexTag', insertionFunction("tex")); + ed.addButton('insert_tex_tag', { + title : 'insert_tag.texdesc', + cmd : 'mceInsertTexTag', + image : url + '/img/tex.png' + }); + + //[media] tag + // a bit more complex tag, so formed inline instead of using insertionFunction + ed.addCommand('mceInsertMediaTag', function () { + if (ed.selection.isCollapsed()) { + ed.selection.setContent('[media|640|480]http://[/media]'); + placeCursor(); + } + else { + ed.selection.setContent('[media|640|480]http://' + + ed.selection.getContent() + '[/media]'); + + } + }); + ed.addButton('insert_media_tag', { + title : 'insert_tag.mediadesc', + cmd : 'mceInsertMediaTag', + image : url + '/img/media.png' + }); + + }, + + + /** + * Returns information about the plugin as a name/value array. The + * current keys are longname, author, authorurl, infourl and version. + * + * @return {Object} Name/value array containing information about the + * plugin. + */ + getInfo : function () { + return { + longname : 'Insert tag plugin', + author : 'ATutor', + authorurl : 'http://www.atutor.ca', + infourl : 'http://www.atutor.ca', + version : "0.9beta" + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('insert_tag', tinymce.plugins.Insert_tagPlugin); +})(); \ No newline at end of file diff --git a/jscripts/ATutor_tiny_mce_plugins/insert_tag/img/code.png b/jscripts/ATutor_tiny_mce_plugins/insert_tag/img/code.png new file mode 100644 index 0000000000000000000000000000000000000000..fc0301a71089fad467f7c57c7fae7109e706e858 GIT binary patch literal 359 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6qGD+ zjVKAuPb(=;EJ|f?Ovz75Rq)JBOiv9;O-!jQJeg_(RP@Eu#WAGf){~ICyiEoIdMB*& z7^EMtr83`a=zGAnt1-_(zBYxqfZ1F?JwWZkrJ%Vtgc@Juy+-QPpyd*{nI{h6!F zA(`l`!O5KX<$`F7^0{(veU*od9_L+p(z0I*e+x9(cuIK7`Ua(WyW2nZ%avAaO}x`u zpu8fVQJ8(YP}L!!?=t&R(?usGwkAmJT{$84oLrVO)3guPKPDDReRuY72);J8aM_>4 zR{?2rKS|cTeW10PYrW?82-UMyOV_^Cns55(+Q!mX2i{6v-*DCVTV(7>J+7@is zF-vulqjD^xa~bP0l+XkK D$YqSg literal 0 HcmV?d00001 diff --git a/jscripts/ATutor_tiny_mce_plugins/insert_tag/img/media.png b/jscripts/ATutor_tiny_mce_plugins/insert_tag/img/media.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ce7bb198a3b268bd634d2b26e9b710f3797d37 GIT binary patch literal 653 zcmV;80&@L{P)WO3(`_cf+b25@DJ#zdQm}8GzWtq2-QnZ8W6mB^kfeK5f%S{ zUW%tGMCwrwic~ZrQcG=4f?5bkV+3dRk8hw6bk~y$KX#b!y*J4EJ~>;dRASqrSu;ZpM>?P}K~6AT zWv6Dmq?v&9LdXC(m%WCO6ma_di$R(v$@ad_>@R41N3N5lSJq9@6CGhX84-$%Xrd_6 z;){?{E|Ytt5$S-&Au>t4wDlIxdkfe-a22LMj``McG};r8@{GsRPm*+8fFey6C)@ifDBXVyTw(N@Xd41b45OFg6x_QA zpwLiigyy~cVoPxW^r~C7ZQpr%>1$*HKmv~AY-qJw4;gUecS--wnqslISSS=^KA&Ic n@BK|Onfz#3R%n{$a)0j^sqv5F(1NTL00000NkvXXu0mjf3S}fX literal 0 HcmV?d00001 diff --git a/jscripts/ATutor_tiny_mce_plugins/insert_tag/img/term.png b/jscripts/ATutor_tiny_mce_plugins/insert_tag/img/term.png new file mode 100644 index 0000000000000000000000000000000000000000..4ad60d2c1362992d4e2759c2f85c8694d2126caf GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQak|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XECYN(Tp1W%KLCNh4Gmv`Vizv_{_x?SgTse{f-ege{5Wvn zmw>>VgoMu%CVUGB_^6=p?!bWq4Gj(d|NmF#5_}5OAY2mU7YyX00ftSrcQ}C3DV{El zAr-fBE_m}DP~c#>5cul;wo`xkqlLwJ59!1kHS*jk(AmWBd6tlP0-v2xosR9-f}~Sl z4$1F$;rs4TpJp`K6vn8!Ga$jKK#3I;rD|Fe>ZIS36xAo_^hDt zZo-6b1qELO1l|}JyblQY$iVRWz=2;54j&F2IMC40@c;k+xoFVdQ&MBb@08wn8MF0Q* literal 0 HcmV?d00001 diff --git a/jscripts/ATutor_tiny_mce_plugins/insert_tag/langs/en.js b/jscripts/ATutor_tiny_mce_plugins/insert_tag/langs/en.js new file mode 100644 index 000000000..c20be8d54 --- /dev/null +++ b/jscripts/ATutor_tiny_mce_plugins/insert_tag/langs/en.js @@ -0,0 +1,6 @@ +tinyMCE.addI18n('en.insert_tag',{ + termdesc : 'Insert term tag', + codedesc : 'Insert code tag', + mediadesc : 'Insert media tag', + texdesc : 'Insert tex tag' +}); \ No newline at end of file diff --git a/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/editor_plugin.js b/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/editor_plugin.js new file mode 100644 index 000000000..df23a982e --- /dev/null +++ b/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/editor_plugin.js @@ -0,0 +1 @@ +"use strict";(function(){tinymce.PluginManager.requireLangPack('swap_toolbar');tinymce.create('tinymce.plugins.Swap_toolbarPlugin',{init:function(ed,url){ed.addCommand('mceSwapToComplex',function(){tinyMCE.execCommand('mceRemoveControl',false,ed.id);ATutor.tinymce.initComplex();tinyMCE.execCommand('mceAddControl',false,ed.id);jQuery("#complexeditor").val('1')});ed.addButton('swap_toolbar_complex',{title:'swap_toolbar_complex.desc',cmd:'mceSwapToComplex',image:url+'/img/bullet_arrow_down.png'});ed.addCommand('mceSwapToSimple',function(){tinyMCE.execCommand('mceRemoveControl',false,ed.id);ATutor.tinymce.initSimple();tinyMCE.execCommand('mceAddControl',false,ed.id);jQuery("#complexeditor").val('0')});ed.addButton('swap_toolbar_simple',{title:'swap_toolbar_simple.desc',cmd:'mceSwapToSimple',image:url+'/img/bullet_arrow_up.png'})},getInfo:function(){return{longname:'Swap toolbar plugin',author:'ATutor',authorurl:'http://www.atutor.ca',infourl:'http://www.atutor.ca',version:"0.9beta"}}});tinymce.PluginManager.add('swap_toolbar',tinymce.plugins.Swap_toolbarPlugin)})(); \ No newline at end of file diff --git a/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/editor_plugin_src.js b/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/editor_plugin_src.js new file mode 100644 index 000000000..ddb62dd38 --- /dev/null +++ b/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/editor_plugin_src.js @@ -0,0 +1,76 @@ +/** + * $Id: $ + * + * @author Laurel A. Williams + * @copyright Copyright © 2008, ATutor, All rights reserved. + */ + +/*global tinymce*/ + +"use strict"; +(function () { + + // Load plugin specific language pack + tinymce.PluginManager.requireLangPack('swap_toolbar'); + + tinymce.create('tinymce.plugins.Swap_toolbarPlugin', { + + /** + * Initializes the plugin, this will be executed after the plugin has been created. + * This call is done before the editor instance has finished it's initialization so use the onInit event + * of the editor instance to intercept that event. + * + * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. + * @param {string} url Absolute URL to where the plugin is located. + */ + init : function (ed, url) { + + ed.addCommand('mceSwapToComplex', function () { + tinyMCE.execCommand('mceRemoveControl', false, ed.id); + ATutor.tinymce.initComplex(); + tinyMCE.execCommand('mceAddControl', false, ed.id); + jQuery("#complexeditor").val('1'); + }); + + ed.addButton('swap_toolbar_complex', { + title : 'swap_toolbar_complex.desc', + cmd : 'mceSwapToComplex', + image : url + '/img/bullet_arrow_down.png' + }); + + ed.addCommand('mceSwapToSimple', function () { + tinyMCE.execCommand('mceRemoveControl', false, ed.id); + ATutor.tinymce.initSimple(); + tinyMCE.execCommand('mceAddControl', false, ed.id); + jQuery("#complexeditor").val('0'); + }); + + ed.addButton('swap_toolbar_simple', { + title : 'swap_toolbar_simple.desc', + cmd : 'mceSwapToSimple', + image : url + '/img/bullet_arrow_up.png' + }); + }, + + + /** + * Returns information about the plugin as a name/value array. The + * current keys are longname, author, authorurl, infourl and version. + * + * @return {Object} Name/value array containing information about the + * plugin. + */ + getInfo : function () { + return { + longname : 'Swap toolbar plugin', + author : 'ATutor', + authorurl : 'http://www.atutor.ca', + infourl : 'http://www.atutor.ca', + version : "0.9beta" + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('swap_toolbar', tinymce.plugins.Swap_toolbarPlugin); +})(); \ No newline at end of file diff --git a/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/img/bullet_arrow_down.png b/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/img/bullet_arrow_down.png new file mode 100644 index 0000000000000000000000000000000000000000..9b23c06d7b4f4689dc8c9fd4e9d4d1f199fe376f GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kw&{<9vg>5sxM-`@i?U z_W%6<>VNmY^FQu?=k0EK8(;qS{{MRQfQxhfe|^4DBSLrMi_=~IrT?G*6aRH>fZ%Fr xHSdzT`JeW`iC!n;=N{%MvhUm!=_W-^hCUIl<$=usRzPbQJYD@<);T3K0RSKzPZ9tC literal 0 HcmV?d00001 diff --git a/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/img/bullet_arrow_up.png b/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/img/bullet_arrow_up.png new file mode 100644 index 0000000000000000000000000000000000000000..24df0f42129c291ddb3dd50c8ba2884dc23a2c43 GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-%M_H=O!(Kw&{<9vg>5sxM7Y}K&+ zaVqV>2dg?$?}z`N|7UG5-|D8TLf!k;{Mi5T|C#@x_qjwjYRvdu`ttwR|Kb1QzwCco x|Ef}l>({^Sf7bts|6%{R{?h*$`OZ2jjF;IsFRaMi76-J3!PC{xWt~$(698OLQAz*+ literal 0 HcmV?d00001 diff --git a/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/langs/en.js b/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/langs/en.js new file mode 100644 index 000000000..173d0f0e2 --- /dev/null +++ b/jscripts/ATutor_tiny_mce_plugins/swap_toolbar/langs/en.js @@ -0,0 +1,7 @@ +tinyMCE.addI18n('en.swap_toolbar_simple',{ + desc : 'Hide toolbars' +}); + +tinyMCE.addI18n('en.swap_toolbar_complex',{ + desc : 'More toolbars' +}); diff --git a/jscripts/TILE.js b/jscripts/TILE.js new file mode 100644 index 000000000..40894ac44 --- /dev/null +++ b/jscripts/TILE.js @@ -0,0 +1,275 @@ + + function queryPopUp(width,height,q,wname,url) { + var winopts = "resizable=yes,scrollbars=yes,toolbar=no,location=no,height=" + height + ",width=" + width; + var query = url + "&query=" + q; + return window.open(query,wname,winopts); +} + +function popUp(width,height,url,wname,smallwindow) { + var windowWidth = 0, windowHeight = 0; + if (smallwindow) { + windowWidth = width; + windowHeight = height; + } + else { + if (screen && screen.width && screen.height) { + // Desktop + windowWidth = screen.width; + windowHeight = screen.height * 0.85; + } + else if (window.innerWidth && window.innerHeight) { + //Non-IE + windowWidth = window.innerWidth; + windowHeight = window.innerHeight; + } + else if (document.documentElement && + (document.documentElement.offsetWidth && + document.documentElement.offsetHeight)) { + //IE 6+ in 'standards compliant mode' + windowWidth = document.documentElement.offsetWidth; + windowHeight = document.documentElement.offsetHeight; + } + else if (document.body && + (document.body.offsetWidth && document.body.offsetHeight)) { + //IE 4 compatible + windowWidth = document.body.offsetWidth; + windowHeight = document.body.offsetHeight; + } + else { + windowWidth = width; + windowHeight = height; + } + } + windowWidth *= 0.95; + var winopts = "resizable=yes,scrollbars=yes,toolbar=yes,location=no,height=" + windowHeight + ",width=" + windowWidth; + var helpWindowzzz = window.open(url,wname,winopts) + helpWindowzzz.focus(); + helpWindowzzz.moveTo(0, 0); + +} + function changeColour(c1, c2, c3) { + var frm = document.forms[1]; + var c1Num = frm.elements[c1].selectedIndex; + var c2Num = frm.elements[c2].selectedIndex; + var c3Num = frm.elements[c3].selectedIndex; + if (c1Num == c2Num) { + if (c1Num == 0) { + frm.elements[c2].selectedIndex = 1; + c2Num = 1; + } else { + frm.elements[c2].selectedIndex = 0; + c2Num = 0; + } + } else if (c1Num == c3Num) { + if (c1Num == 0) { + frm.elements[c3].selectedIndex = 1; + c3Num = 1; + } else { + frm.elements[c3].selectedIndex = 0; + c3Num = 0; + } + } + + if (c2Num == c3Num) { + if (c1Num == 0 || c1Num == 1) { + frm.elements[c3].selectedIndex = 2; + } else if (c2Num == 0) { + frm.elements[c3].selectedIndex = 1; + } else if (c2Num == 1) { + frm.elements[c3].selectedIndex = 0; + } + } +} + +function setPreviewSize(fontVal) { + var docSize = (document.getElementById('defaultfontsize').value * document.getElementById('font_times').value)+'pt'; + var docBase = document.getElementById('previewText'); + docBase.style.fontSize = docSize; + docBase = document.getElementById('highlightedPreview'); + docBase.style.fontSize = docSize; +} +function setPreviewFace() { + var faceSet = document.getElementById('fontface'); + var faceVal = document.getElementById('fontface').value; + + if (faceVal == "") faceVal = document.getElementById('defaultfontface').value;; + + var docBase = document.getElementById('previewText'); + docBase.style.fontFamily = faceVal; + docBase = document.getElementById('highlightedPreview'); + docBase.style.fontFamily = faceVal; +} +function setPreviewColours() { + var fgSet = document.getElementById('fg'); + var fgVal = document.getElementById('fg').value; + var bgSet = document.getElementById('bg'); + var bgVal = document.getElementById('bg').value; + var hlSet = document.getElementById('hl'); + var hlVal = document.getElementById('hl').value; + + if (fgVal == "") fgVal = document.getElementById('defaultfg').value; + if (bgVal == "") bgVal = document.getElementById('defaultbg').value; + if (hlVal == "") hlVal = document.getElementById('defaulthl').value; + + fgVal = '\#'+fgVal.substr(0,6); + bgVal = '\#'+bgVal.substr(0,6); + hlVal = '\#'+hlVal.substr(0,6); + + var docBase = document.getElementById('previewText'); + docBase.style.color = fgVal; + docBase.style.backgroundColor = bgVal; + + docBase = document.getElementById('highlightedPreview'); + docBase.style.backgroundColor = hlVal; +} + function checkATTSignLang() { + var frm = document.forms[0]; + var value = null; + if (frm.attSignLang[0].checked) + value = frm.attSignLang[0].value; + else if (frm.attSignLang[1].checked) + value = frm.attSignLang[1].value; + + if (value == "false") + frm.attSignLangVal.disabled=true; + else if (value == "true") + frm.attSignLangVal.disabled=false; +} + function checkAudioDesc() { + var frm = document.forms[0]; + var value = null; + if (frm.audioDesc[0].checked) + value = frm.audioDesc[0].value; + else if (frm.audioDesc[1].checked) + value = frm.audioDesc[1].value; + + if (value == "false") { + frm.audioDescLang.disabled=true; + frm.audioDescType[0].disabled=true; + frm.audioDescType[1].disabled=true; + } + else if (value == "true") { + frm.audioDescLang.disabled=false; + frm.audioDescType[0].disabled=false; + frm.audioDescType[1].disabled=false; + } +} + +function checkVisualText() { + var frm = document.forms[0]; + var value = null; + if (frm.visualText[0].checked) + value = frm.visualText[0].value; + else if (frm.visualText[1].checked) + value = frm.visualText[1].value; + + if (value == "false") { + frm.altTextLang.disabled=true; + frm.longDescLang.disabled=true; + } + else if (value == "true") { + frm.altTextLang.disabled=false; + frm.longDescLang.disabled=false; + } +} + function checkCaptions() { + var frm = document.forms[0]; + var value = null; + if (frm.caption[0].checked) + value = frm.caption[0].value; + else if (frm.caption[1].checked) + value = frm.caption[1].value; + + if (value == "false") { + frm.captionType[0].disabled=true; + frm.captionType[1].disabled=true; + frm.captionLang.disabled=true; + frm.enhancedCaption[0].disabled=true; + frm.enhancedCaption[1].disabled=true; + frm.reducedSpeed[0].disabled=true; + frm.reducedSpeed[1].disabled=true; + frm.captionRate.disabled=true; + } + else if (value == "true") { + frm.captionType[0].disabled=false; + frm.captionType[1].disabled=false; + frm.captionLang.disabled=false; + frm.enhancedCaption[0].disabled=false; + frm.enhancedCaption[1].disabled=false; + frm.reducedSpeed[0].disabled=false; + frm.reducedSpeed[1].disabled=false; + frm.captionRate.disabled=false; + checkCaptionRate(); + } +} + +function checkCaptionRate() { + var frm = document.forms[0]; + var value = null; + if (frm.reducedSpeed[0].checked) + value = frm.reducedSpeed[0].value; + else if (frm.reducedSpeed[1].checked) + value = frm.reducedSpeed[1].value; + + if (value == "false") + frm.captionRate.disabled=true; + else if (value == "true") + frm.captionRate.disabled=false; +} + +function checkATASignLang() { + var frm = document.forms[0]; + var value = null; + if (frm.ataSignLang[0].checked) + value = frm.ataSignLang[0].value; + else if (frm.ataSignLang[1].checked) + value = frm.ataSignLang[1].value; + + if (value == "false") + frm.ataSignLangVal.disabled=true; + else if (value == "true") + frm.ataSignLangVal.disabled=false; +} + +function allDigits(str) { + var digits = "0123456789"; + var result = true; + for (var i = 0; i < str.length; i++) { + if (digits.indexOf(str.substr(i, 1)) < 0 ) { + result = false; + break; + } + } + return result; +} + +function checkCaptionRateValue() { + var frm = document.forms[0]; + var value = null; + var result = true; + if (!frm.captionRate.disabled) { + if (!allDigits(frm.captionRate.value)) { + alert('Please enter a number for the "Caption Rate" field.'); + frm.captionRate.focus(); + result = false; + } + else { + value = parseInt(frm.captionRate.value); + if (isNaN(value) || value < 1 || value > 300) { + alert('Please enter a number between 1 and 300 for the "Caption Rate" field.'); + frm.captionRate.focus(); + result = false; + } + } + } + return result; +} + var cssFilter=/^http:\/\/.+\..{2,3}\/.+/; +function checkCSS() { + var theForm = document.forms[0]; + if (!cssFilter.test(theForm.ssURL.value)) { + alert('Please enter a valid URL to a CSS file.'); + return false; + } + return true; +} diff --git a/jscripts/calendar.js b/jscripts/calendar.js new file mode 100644 index 000000000..e01409959 --- /dev/null +++ b/jscripts/calendar.js @@ -0,0 +1,1750 @@ +// ***************************************************************************** +// Simple Calendar Widget - Cross-Browser Javascript pop-up calendar. +// +// Copyright (C) 2005-2007 Anthony Garrett +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, it is available at +// the GNU web site (http://www.gnu.org/) or by writing to the +// Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// ***************************************************************************** +// +// Contact: Sorry, I can't offer support for this but if you find a problem +// (or just want to tell me how useful you find it), please send +// me an email at scwfeedback@tarrget.info (Note the two Rs in +// tarrget). I will try to fix problems quickly but this is a +// spare time thing for me. +// +// Credits: I wrote this from scratch myself but I couldn't have done it +// without the superb "JavaScript The Definitive Guide" by David +// Flanagan (Pub. O'Reilly ISBN 0-596-00048-0). I also recognise +// a contribution from my experience with PopCalendar 4.1 by +// Liming(Victor) Weng. +// +// Link back: Please give me credit and link back to my page. To ensure that +// search engines give my page a higher ranking you can add the +// following HTML to any indexed page on your web site: +// +// +// Simple Calendar Widget by Anthony Garrett +// +// +// Features: Easily customised +// (output date format, colours, language, year range and +// week start day) +// Accepts a date as input +// (see comments below for formats). +// Cross-browser code tested against; +// Internet Explorer 6.0.28 Mozilla 1.7.1 +// Opera 7.52+ Firefox 0.9.1+ +// Konqueror 3.4.0 Flock 0.4.9 +// +// How to add the Calendar to your page: +// This script needs to be defined for your page so, immediately +// after the BODY tag add the following line; +// +// +// +// Your root directory of the web site should also contain an empty +// file called "scwblank.html". See +// http://www.tarrget.info/calendar/IEnightmare.html +// for a full explanation. +// +// How to use the Calendar once it is defined for your page: +// +// Simply choose an event to trigger the calendar (like an onClick +// or an onMouseOver) and an element to work on (for the calendar +// to take its initial date from and write its output date to) then +// write it like this; +// +// <>="scwShow(<>,event);" +// +// e.g. onClick="scwShow(scwID('myElement'),event);" +// or onMouseOver="scwShow(this,event);" +// +// NOTE: If you wish to use the calendar with an Anchor tag, do +// not use the syntax: href="javascript:scwShow(...)" +// Instead you should use the following; +// +// +// <> +// +// +// If you are using a text node then specify the text's parent node +// in the function call. The date should be the only text under that +// node; +// +// e.g.

    <>

    +// +// You can also disable days of the week by adding arguments to the +// call to scwShow. The values should be Sunday = 0 through to +// Saturday = 6. A call to scwShow with Friday and Monday disabled +// would look something like this; +// +// scwShow(<>,event,5,1); +// +// Finally you can use the following technique to run a function +// when the calendar closes: +// +// scwNextAction=<>.runsAfterSCW(this,<>); +// scwShow(<>,event <<,optional arguments above>>); +// +// Where <> is a function defined on the calling page +// and <> is the list of arguments being passed to that +// function. +// +// No event? No problem! +// +// Normally the calendar will be triggered by an event but if you wish to +// control it in code and the event is not available to you, simply pass +// an element as the second parameter; +// +// E.G. scwShow(<>,<>); +// as in: scwShow(this,this); +// +// ------------------------------------------------------------------ +// Here's an extremely trivial but fully functioning example page +// showing two of the ways to trigger the calendar; +// +// +// Basic Example +// +// +//

    06-Dec-2006

    +// +//

    +// +// 08-Dec-2006 +// +// +// +// +// ***************************************************************************** +// +// See http://www.tarrget.info/calendar/scw.htm for a complete version history +// +// Version Date By Description +// ======= ==== =============== =========== +// 3.58 2007-04-04 Anthony Garrett Resolved an error caused when the date +// range does not include the current year. +// Thanks to Steve Davis for letting me know. +// +// Fixed "Today" selector display which +// was incorrectly visible when year range +// ended last year. (Also the result of +// investigations based on Steve Davis' +// feedback). +// +// 3.59 2007-06-13 Anthony Garrett Added Verdana to font list of +// calendar's CSS. Resolves rendering +// bug in Safari Beta 3 for Windows. +// +// 3.60 2007-07-31 Anthony Garrett Fixed javascript error that occurred +// when the target element had no value +// attribute. The error had no impact +// on the behaviour of the script. Thanks +// to John Phelps for reporting this bug. +// +// 3.70 2007-09-21 Anthony Garrett Updated the event trapping to make it +// less intrusive on the page body. +// NOTE: This requires that a calendar's +// second parameter should be the calling +// event (not the calling object as in +// previous versions). +// Thanks to Steve Davis for the bug report +// that led to this change. +// +// Fixed a bug that caused undelimited +// dates to be handled incorrectly. They +// are now parsed against the full date +// output format then checked for validity. +// Thanks to Dan Wood for raising this bug. +// +// Replaced the date input sequence user +// configuration setting with parsing the +// sequence from the full format. New users +// are often confused by the sequence and +// in practice (to allow the calendar's date +// output to be used for input) the sequence +// must always match the full format element +// order. +// +// Extended IFRAME backing to all calendar objects +// in order to improve calendar display over +// some embedded applets and objects. Thanks to +// Stanko Kupcevic for his feedback on this. +// NOTE: It is not possible to protect any +// JavaScript object displayed over an +// embedded DYNAMIC (and, therefore refreshed) +// object because browsers usually do not +// directly control the screen handling within +// the object. The best advice therefore remains +// to design pages in such a way that the calendar +// does not overlap embedded objects. +// +// 3.71 2008-12-14 Anthony Garrett Restored the ability to use an element +// as the second parameter when opening a +// calendar while retaining the option +// of passing an event. Thanks to Thierry Blind +// and Sergey Snovsky for the feedback. +// +// ***************************************************************************** + +// ************************************ +// Start of Simple Calendar Widget Code +// ************************************ + +// This date is used throughout to determine today's date. + + var scwDateNow = new Date(Date.parse(new Date().toDateString())); + +//****************************************************************************** +//------------------------------------------------------------------------------ +// Customisation section +//------------------------------------------------------------------------------ +//****************************************************************************** + + // Set the bounds for the calendar here... + // If you want the year to roll forward you can use something like this... + // var scwBaseYear = scwDateNow.getFullYear()-5; + // alternatively, hard code a date like this... + // var scwBaseYear = 1990; + + var scwBaseYear = scwDateNow.getFullYear()-10; + + // How many years do want to be valid and to show in the drop-down list? + + var scwDropDownYears = 20; + + // All language-dependent changes can be made here... + + // If you wish to work in a single language (other than English) then + // just replace the English (in the function scwSetLanguage below) with + // your own text. + + // Using multiple languages: + // In order to keep this script to a resonable size I have not included + // languages here. You can set language fields in a function that you + // should call scwSetLanguage the script will use your languages. + // I have included all the translations that have been sent to me in + // such a function on the demonstration page. + + var scwLanguage; + + function scwSetDefaultLanguage() + {try + {scwSetLanguage();} + catch (exception) + {// English + scwToday = 'Today:'; + scwDrag = 'click here to drag'; + scwArrMonthNames = ['Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec']; + scwArrWeekInits = ['S','M','T','W','T','F','S']; + scwInvalidDateMsg = 'The entered date is invalid.\n'; + scwOutOfRangeMsg = 'The entered date is out of range.'; + scwDoesNotExistMsg = 'The entered date does not exist.'; + scwInvalidAlert = ['Invalid date (',') ignored.']; + scwDateDisablingError = ['Error ',' is not a Date object.']; + scwRangeDisablingError = ['Error ', + ' should consist of two elements.']; + } + }; + + // Note: Always start the scwArrWeekInits array with your string for + // Sunday whatever scwWeekStart (below) is set to. + + // scwWeekStart determines the start of the week in the display + // Set it to: 0 (Zero) for Sunday, 1 (One) for Monday etc.. + + var scwWeekStart = 1; + + // The week start day for the display is taken as the week start + // for week numbering. This ensures that only one week number + // applies to one line of the calendar table. + // [ISO 8601 begins the week with Day 1 = Monday.] + + // If you want to see week numbering on the calendar, set + // this to true. If not, false. + + var scwWeekNumberDisplay = false; + + // Week numbering rules are generally based on a day in the week + // that determines the first week of the year. ISO 8601 uses + // Thursday (day four when Sunday is day zero). You can alter + // the base day here. + + // See http://www.cl.cam.ac.uk/~mgk25/iso-time.html for more information + + var scwWeekNumberBaseDay = 4; + + // Each of the calendar's alert message types can be disabled + // independently here. + + var scwShowInvalidDateMsg = true, + scwShowOutOfRangeMsg = true, + scwShowDoesNotExistMsg = true, + scwShowInvalidAlert = true, + scwShowDateDisablingError = true, + scwShowRangeDisablingError = true; + + // Set the allowed input date delimiters here... + // E.g. To set the rising slash, hyphen, full-stop (aka stop or point), + // comma and space as delimiters use + // var scwArrDelimiters = ['/','-','.',',',' ']; + + var scwArrDelimiters = ['/','-','.',',',' ']; + + // Set the format for the displayed 'Today' date and for the output + // date here. + // + // The format is described using delimiters of your choice (as set + // in scwArrDelimiters above) and case insensitive letters D, M and Y. + // + // NOTE: If no delimiters are input then the date output format is used + // to parse the value. This allows less flexiblility in the input + // value than using delimiters but an accurately entered date + // remains parsable. + // + // Definition Returns + // ---------- ------- + // D date in the month without zero filling + // DD date in the month left zero filled + // M month number without zero filling + // MM month number left zero filled + // MMM month string from scwArrMonthNames + // YY year number in two digits + // YYYY year number in four digits + + // Displayed "Today" date format + + //var scwDateDisplayFormat = 'dd-mm-yy'; // e.g. 'MMM-DD-YYYY' for the US + var scwDateDisplayFormat = 'dd/mmm/yyyy'; // e.g. 'MMM-DD-YYYY' for the US + + // Output date format + + //var scwDateOutputFormat = 'DD MMM, YYYY'; // e.g. 'MMM-DD-YYYY' for the US + //var scwDateOutputFormat = 'DD-MMM-YYYY'; // e.g. 'MMM-DD-YYYY' for the US + var scwDateOutputFormat = 'YYYY-MM-DD'; // e.g. 'MMM-DD-YYYY' for the US + + // Note: The delimiters used should be in scwArrDelimiters. + + // scwZindex controls how the pop-up calendar interacts with the rest + // of the page. It is usually adequate to leave it as 1 (One) but I + // have made it available here to help anyone who needs to alter the + // level in order to ensure that the calendar displays correctly in + // relation to all other elements on the page. + + var scwZindex = 1; + + // Personally I like the fact that entering 31-Sep-2005 displays + // 1-Oct-2005, however you may want that to be an error. If so, + // set scwBlnStrict = true. That will cause an error message to + // display and the selected month is displayed without a selected + // day. Thanks to Brad Allan for his feedback prompting this feature. + + var scwBlnStrict = false; + + // If you wish to disable any displayed day, e.g. Every Monday, + // you can do it by setting the following array. The array elements + // match the displayed cells. + // + // You could put something like the following in your calling page + // to disable all weekend days; + // + // for (var i=0;i' + + '.scw {padding:1px;vertical-align:middle;}' + + 'iframe.scw {position:absolute;z-index:' + scwZindex + + ';top:0px;left:0px;visibility:hidden;' + + 'width:1px;height:1px;}' + + 'table.scw {padding:0px;visibility:hidden;' + + 'position:absolute;cursor:default;' + + 'width:200px;top:0px;left:0px;' + + 'z-index:' + (scwZindex+1) + + ';text-align:center;}' + + '' ); + + // This style sheet can be extracted from the script and edited into regular + // CSS (by removing all occurrences of + and '). That can be used as the + // basis for themes. Classes are described in comments within the style + // sheet. + + document.writeln( + '' + ); + +//****************************************************************************** +//------------------------------------------------------------------------------ +// End of customisation section +//------------------------------------------------------------------------------ +//****************************************************************************** + +// Variables required by both scwShow and scwShowMonth + + var scwTargetEle, + scwTriggerEle, + scwMonthSum = 0, + scwBlnFullInputDate = false, + scwPassEnabledDay = new Array(), + scwSeedDate = new Date(), + scwParmActiveToday = true, + scwWeekStart = scwWeekStart%7, + scwToday, + scwDrag, + scwArrMonthNames, + scwArrWeekInits, + scwInvalidDateMsg, + scwOutOfRangeMsg, + scwDoesNotExistMsg, + scwInvalidAlert, + scwDateDisablingError, + scwRangeDisablingError; + + // Add a method to format a date into the required pattern + + Date.prototype.scwFormat = + function(scwFormat) + {var charCount = 0, + codeChar = '', + result = ''; + + for (var i=0;i<=scwFormat.length;i++) + {if (i 0) {result += codeChar;} + } + + if (i 0) + {scwTriggerEle.scwTextNode = scwChildNodes[i]; + scwTriggerEle.scwLength = scwChildNodes[i].nodeValue.length; + break; + } + } + } + } + } + + // Set the language-dependent elements + + scwSetDefaultLanguage(); + + scwID('scwDragText').innerHTML = scwDrag; + + scwID('scwMonths').options.length = 0; + for (var i=0;iscwSeedDate + ) + {scwSeedDate = new Date(scwBaseYear + Math.floor(scwDropDownYears / 2), 5, 1);} + } + else + {function scwInputFormat() + {var scwArrSeed = new Array(), + scwArrInput = scwDateValue.split(new RegExp('[\\'+scwArrDelimiters.join('\\')+']+','g')); + + // "Escape" all the user defined date delimiters above - + // several delimiters will need it and it does no harm for + // the others. + + // Strip any empty array elements (caused by delimiters) + // from the beginning or end of the array. They will + // still appear in the output string if in the output + // format. + + if (scwArrInput[0]!=null) + {if (scwArrInput[0].length==0) {scwArrInput.splice(0,1);} + if (scwArrInput[scwArrInput.length-1].length==0) {scwArrInput.splice(scwArrInput.length-1,1);} + } + + scwBlnFullInputDate = false; + + scwDateOutputFormat = scwDateOutputFormat.toUpperCase(); + + // List all the allowed letters in the date format + var template = ['D','M','Y']; + + // Prepare the sequence of date input elements + var result = new Array(); + + for (var i=0;i-1) + {result[scwDateOutputFormat.search(template[i])] = template[i];} + } + + var scwDateSequence = result.join(''); + + // Separate the elements of the date input + switch (scwArrInput.length) + {case 1: + {if (scwDateOutputFormat.indexOf('Y')>-1 && + scwArrInput[0].length>scwDateOutputFormat.lastIndexOf('Y')) + {scwArrSeed[0] = parseInt(scwArrInput[0].substring(scwDateOutputFormat.indexOf('Y'), + scwDateOutputFormat.lastIndexOf('Y')+1),10); + } + else {scwArrSeed[0] = 0;} + + if (scwDateOutputFormat.indexOf('M')>-1 && + scwArrInput[0].length>scwDateOutputFormat.lastIndexOf('M')) + {scwArrSeed[1] = scwArrInput[0].substring(scwDateOutputFormat.indexOf('M'), + scwDateOutputFormat.lastIndexOf('M')+1); + } + else {scwArrSeed[1] = '6';} + + if (scwDateOutputFormat.indexOf('D')>-1 && + scwArrInput[0].length>scwDateOutputFormat.lastIndexOf('D')) + {scwArrSeed[2] = parseInt(scwArrInput[0].substring(scwDateOutputFormat.indexOf('D'), + scwDateOutputFormat.lastIndexOf('D')+1),10); + } + else {scwArrSeed[2] = 1;} + + if (scwArrInput[0].length==scwDateOutputFormat.length) {scwBlnFullInputDate = true;} + break; + } + case 2: + {// Year and Month entry + scwArrSeed[0] = + parseInt(scwArrInput[scwDateSequence. + replace(/D/i,''). + search(/Y/i)],10); // Year + scwArrSeed[1] = scwArrInput[scwDateSequence. + replace(/D/i,''). + search(/M/i)]; // Month + scwArrSeed[2] = 1; // Day + break; + } + case 3: + {// Day Month and Year entry + + scwArrSeed[0] = + parseInt(scwArrInput[scwDateSequence. + search(/Y/i)],10); // Year + scwArrSeed[1] = scwArrInput[scwDateSequence. + search(/M/i)]; // Month + scwArrSeed[2] = + parseInt(scwArrInput[scwDateSequence. + search(/D/i)],10); // Day + + scwBlnFullInputDate = true; + break; + } + default: + {// A stuff-up has led to more than three elements in + // the date. + scwArrSeed[0] = 0; // Year + scwArrSeed[1] = 0; // Month + scwArrSeed[2] = 0; // Day + } + } + + // These regular expressions validate the input date format + // to the following rules; + // Day 1-31 (optional zero on single digits) + // Month 1-12 (optional zero on single digits) + // or case insensitive name + // Year One, Two or four digits + + // Months names are as set in the language-dependent + // definitions and delimiters are set just below there + + var scwExpValDay = new RegExp('^(0?[1-9]|[1-2][0-9]|3[0-1])$'), + scwExpValMonth = new RegExp('^(0?[1-9]|1[0-2]|' + + scwArrMonthNames.join('|') + + ')$','i'), + scwExpValYear = new RegExp('^([0-9]{1,2}|[0-9]{4})$'); + + // Apply validation and report failures + + if (scwExpValYear.exec(scwArrSeed[0]) == null || + scwExpValMonth.exec(scwArrSeed[1]) == null || + scwExpValDay.exec(scwArrSeed[2]) == null + ) + {if (scwShowInvalidDateMsg) + {alert(scwInvalidDateMsg + + scwInvalidAlert[0] + scwDateValue + + scwInvalidAlert[1]);} + scwBlnFullInputDate = false; + scwArrSeed[0] = scwBaseYear + + Math.floor(scwDropDownYears/2); // Year + scwArrSeed[1] = '6'; // Month + scwArrSeed[2] = 1; // Day + } + + // Return the Year in scwArrSeed[0] + // Month in scwArrSeed[1] + // Day in scwArrSeed[2] + + return scwArrSeed; + }; + + // Parse the string into an array using the allowed delimiters + + scwArrSeedDate = scwInputFormat(); + + // So now we have the Year, Month and Day in an array. + + // If the year is one or two digits then the routine assumes a + // year belongs in the 21st Century unless it is less than 50 + // in which case it assumes the 20th Century is intended. + + if (scwArrSeedDate[0]<100) {scwArrSeedDate[0] += (scwArrSeedDate[0]>50)?1900:2000;} + + // Check whether the month is in digits or an abbreviation + + if (scwArrSeedDate[1].search(/\d+/)!=0) + {month = scwArrMonthNames.join('|').toUpperCase(). + search(scwArrSeedDate[1].substr(0,3). + toUpperCase()); + scwArrSeedDate[1] = Math.floor(month/4)+1; + } + + scwSeedDate = new Date(scwArrSeedDate[0],scwArrSeedDate[1]-1,scwArrSeedDate[2]); + } + + // Test that we have arrived at a valid date + + if (isNaN(scwSeedDate)) + {if (scwShowInvalidDateMsg) {alert(scwInvalidDateMsg + scwInvalidAlert[0] + scwDateValue + scwInvalidAlert[1]);} + scwSeedDate = new Date(scwBaseYear + Math.floor(scwDropDownYears/2),5,1); + scwBlnFullInputDate=false; + } + else + {// Test that the date is within range, + // if not then set date to a sensible date in range. + + if ((new Date(scwBaseYear,0,1)) > scwSeedDate) + {if (scwBlnStrict && scwShowOutOfRangeMsg) {alert(scwOutOfRangeMsg);} + scwSeedDate = new Date(scwBaseYear,0,1); + scwBlnFullInputDate=false; + } + else + {if ((new Date(scwBaseYear+scwDropDownYears,0,0)) scwDisabledDates[i][1])) {scwDisabledDates[i].reverse();} + } + else + {if (scwShowRangeDisablingError) {alert(scwDateDisablingError[0] + scwDisabledDates[i] + scwDateDisablingError[1]);}} + } + } + + // Calculate the number of months that the entered (or + // defaulted) month is after the start of the allowed + // date range. + + scwMonthSum = 12*(scwSeedDate.getFullYear()-scwBaseYear)+scwSeedDate.getMonth(); + + scwID('scwYears' ).options.selectedIndex = Math.floor(scwMonthSum/12); + scwID('scwMonths').options.selectedIndex = (scwMonthSum%12); + + // Check whether or not dragging is allowed and display drag handle if necessary + + scwID('scwDrag').style.display=(scwAllowDrag)?'':'none'; + + // Display the month + + scwShowMonth(0); + + // Position the calendar box + + // The object sniffing for Opera allows for the fact that Opera + // is the only major browser that correctly reports the position + // of an element in a scrollable DIV. This is because IE and + // Firefox omit the DIV from the offsetParent tree. + + scwTargetEle=scwEle; + + var offsetTop =parseInt(scwEle.offsetTop ,10) + parseInt(scwEle.offsetHeight,10), + offsetLeft=parseInt(scwEle.offsetLeft,10); + + if (!window.opera) + {while (scwEle.tagName!='BODY' && scwEle.tagName!='HTML') + {offsetTop -=parseInt(scwEle.scrollTop, 10); + offsetLeft-=parseInt(scwEle.scrollLeft,10); + scwEle=scwEle.parentNode; + } + scwEle=scwTargetEle; + } + + do {scwEle=scwEle.offsetParent; + offsetTop +=parseInt(scwEle.offsetTop, 10); + offsetLeft+=parseInt(scwEle.offsetLeft,10); + } + while (scwEle.tagName!='BODY' && scwEle.tagName!='HTML'); + + scwID('scw').style.top =offsetTop +'px'; + scwID('scw').style.left=offsetLeft+'px'; + + scwID('scwIframe').style.top=offsetTop +'px'; + scwID('scwIframe').style.left=offsetLeft+'px'; + scwID('scwIframe').style.width=(scwID('scw').offsetWidth-(scwID('scwIE')?2:4))+'px'; + scwID('scwIframe').style.height=(scwID('scw').offsetHeight-(scwID('scwIE')?2:4))+'px'; + scwID('scwIframe').style.visibility='inherit'; + + // Show it on the page + scwID('scw').style.visibility='inherit'; + }; + + function scwHide() + {scwID('scw').style.visibility='hidden'; + scwID('scwIframe').style.visibility='hidden'; + if (typeof scwNextAction!='undefined' && scwNextAction!=null) + {scwNextActionReturn = scwNextAction(); + // Explicit null set to prevent closure causing memory leak + scwNextAction = null; + } + }; + + function scwCancel(scwEvt) + {if (scwClickToHide) {scwHide();} + scwStopPropagation(scwEvt); + }; + + function scwStopPropagation(scwEvt) + {if (scwEvt.stopPropagation) + {scwEvt.stopPropagation();} // Capture phase + else {scwEvt.cancelBubble = true;} // Bubbling phase + }; + + function scwBeginDrag(event) + {var elementToDrag = scwID('scw'); + + var deltaX = event.clientX, + deltaY = event.clientY, + offsetEle = elementToDrag; + + do {deltaX -= parseInt(offsetEle.offsetLeft,10); + deltaY -= parseInt(offsetEle.offsetTop ,10); + offsetEle = offsetEle.offsetParent; + } + while (offsetEle.tagName!='BODY' && + offsetEle.tagName!='HTML'); + + if (document.addEventListener) + {document.addEventListener('mousemove',moveHandler,true); // Capture phase + document.addEventListener('mouseup', upHandler, true); // Capture phase + } + else {elementToDrag.attachEvent('onmousemove',moveHandler); // Bubbling phase + elementToDrag.attachEvent('onmouseup', upHandler); // Bubbling phase + elementToDrag.setCapture(); + } + + scwStopPropagation(event); + + function moveHandler(scwEvt) + {if (!scwEvt) scwEvt = window.event; + + elementToDrag.style.left = (scwEvt.clientX - deltaX) + 'px'; + elementToDrag.style.top = (scwEvt.clientY - deltaY) + 'px'; + + scwID('scwIframe').style.left = (scwEvt.clientX - deltaX) + 'px'; + scwID('scwIframe').style.top = (scwEvt.clientY - deltaY) + 'px'; + + scwStopPropagation(scwEvt); + }; + + function upHandler(scwEvt) + {if (!scwEvt) scwEvt = window.event; + + if (document.removeEventListener) + {document.removeEventListener('mousemove',moveHandler,true); // Capture phase + document.removeEventListener('mouseup', upHandler, true); // Capture phase + } + else {elementToDrag.detachEvent('onmouseup', upHandler); // Bubbling phase + elementToDrag.detachEvent('onmousemove',moveHandler); // Bubbling phase + elementToDrag.releaseCapture(); + } + + scwStopPropagation(scwEvt); + }; + }; + + function scwShowMonth(scwBias) + {// Set the selectable Month and Year + // May be called: from the left and right arrows + // (shift month -1 and +1 respectively) + // from the month selection list + // from the year selection list + // from the showCal routine + // (which initiates the display). + + var scwShowDate = new Date(Date.parse(new Date().toDateString())), + scwStartDate = new Date(); + + // Set the time to the middle of the day so that the handful of + // regions that have daylight saving shifts that change the day + // of the month (i.e. turn the clock back at midnight or forward + // at 23:00) do not mess up the date display in the calendar. + + scwShowDate.setHours(12); + + scwSelYears = scwID('scwYears'); + scwSelMonths = scwID('scwMonths'); + + if (scwSelYears.options.selectedIndex>-1) + {scwMonthSum=12*(scwSelYears.options.selectedIndex)+scwBias; + if (scwSelMonths.options.selectedIndex>-1) {scwMonthSum+=scwSelMonths.options.selectedIndex;} + } + else + {if (scwSelMonths.options.selectedIndex>-1) {scwMonthSum+=scwSelMonths.options.selectedIndex;}} + + scwShowDate.setFullYear(scwBaseYear + Math.floor(scwMonthSum/12),(scwMonthSum%12),1); + + // If the Week numbers are displayed, shift the week day names to the right. + scwID('scwWeek_').style.display=(scwWeekNumberDisplay)?'':'none'; + + // Opera has a bug with setting the selected index. + // It requires the following work-around to force SELECTs to display correctly. + if (window.opera) + {scwID('scwMonths').style.display = 'inherit'; + scwID('scwYears' ).style.display = 'inherit'; + } + + // Set the drop down boxes. + scwTemp = (12*parseInt((scwShowDate.getFullYear()-scwBaseYear),10)) + parseInt(scwShowDate.getMonth(),10); + + if (scwTemp > -1 && scwTemp < (12*scwDropDownYears)) + {scwSelYears.options.selectedIndex=Math.floor(scwMonthSum/12); + scwSelMonths.options.selectedIndex=(scwMonthSum%12); + + scwCurMonth = scwShowDate.getMonth(); + + scwShowDate.setDate((((scwShowDate. + getDay()-scwWeekStart)<0)?-6:1)+ + scwWeekStart-scwShowDate.getDay()); + + // This statement moved by Michael Cerveny to make version 3.55 + var scwCompareDateValue = new Date(scwShowDate.getFullYear(), + scwShowDate.getMonth(), + scwShowDate.getDate()).valueOf(); + + scwStartDate = new Date(scwShowDate); + + if (scwID('scwFoot')) + {var scwFoot = scwID('scwFoot'); + + function scwFootOutput() {scwSetOutput(scwDateNow);}; + + if (scwDisabledDates.length==0) + {if (scwActiveToday && scwParmActiveToday) + {scwFoot.onclick = scwFootOutput; + scwFoot.className = 'scwFoot'; + + if (scwID('scwIE')) + {scwFoot.onmouseover = scwChangeClass; + scwFoot.onmouseout = scwChangeClass; + } + + } + else + {scwFoot.onclick = null; + scwFoot.className = 'scwFootDisabled'; + + if (scwID('scwIE')) + {scwFoot.onmouseover = null; + scwFoot.onmouseout = null; + } + + if (document.addEventListener) + {scwFoot.addEventListener('click',scwStopPropagation,false);} + else {scwFoot.attachEvent('onclick',scwStopPropagation);} + } + } + else + {for (var k=0;k= scwDisabledDates[k][0].valueOf() && + scwDateNow.valueOf() <= scwDisabledDates[k][1].valueOf() + ) + ) + ) + ) + {scwFoot.onclick = null; + scwFoot.className = 'scwFootDisabled'; + + if (scwID('scwIE')) + {scwFoot.onmouseover = null; + scwFoot.onmouseout = null; + } + + if (document.addEventListener) + {scwFoot.addEventListener('click',scwStopPropagation,false);} + else {scwFoot.attachEvent('onclick',scwStopPropagation);} + break; + } + else + {scwFoot.onclick=scwFootOutput; + scwFoot.className='scwFoot'; + + if (scwID('scwIE')) + {scwFoot.onmouseover = scwChangeClass; + scwFoot.onmouseout = scwChangeClass; + } + } + } + } + } + + function scwSetOutput(scwOutputDate) + {if (typeof scwTargetEle.value == 'undefined') + {scwTriggerEle.scwTextNode.replaceData(0,scwTriggerEle.scwLength,scwOutputDate.scwFormat(scwDateOutputFormat));} + else {scwTargetEle.value = scwOutputDate.scwFormat(scwDateOutputFormat);} + scwHide(); + }; + + function scwCellOutput(scwEvt) + {var scwEle = scwEventTrigger(scwEvt), + scwOutputDate = new Date(scwStartDate); + + if (scwEle.nodeType==3) scwEle=scwEle.parentNode; + + scwOutputDate.setDate(scwStartDate.getDate() + parseInt(scwEle.id.substr(8),10)); + + scwSetOutput(scwOutputDate); + }; + + function scwChangeClass(scwEvt) + {var scwEle = scwEventTrigger(scwEvt); + + if (scwEle.nodeType==3) {scwEle=scwEle.parentNode;} + + switch (scwEle.className) + {case 'scwCells': + scwEle.className = 'scwCellsHover'; + break; + case 'scwCellsHover': + scwEle.className = 'scwCells'; + break; + case 'scwCellsExMonth': + scwEle.className = 'scwCellsExMonthHover'; + break; + case 'scwCellsExMonthHover': + scwEle.className = 'scwCellsExMonth'; + break; + case 'scwCellsWeekend': + scwEle.className = 'scwCellsWeekendHover'; + break; + case 'scwCellsWeekendHover': + scwEle.className = 'scwCellsWeekend'; + break; + case 'scwFoot': + scwEle.className = 'scwFootHover'; + break; + case 'scwFootHover': + scwEle.className = 'scwFoot'; + break; + case 'scwInputDate': + scwEle.className = 'scwInputDateHover'; + break; + case 'scwInputDateHover': + scwEle.className = 'scwInputDate'; + } + + return true; + } + + function scwEventTrigger(scwEvt) + {if (!scwEvt) {scwEvt = event;} + return scwEvt.target||scwEvt.srcElement; + }; + + function scwWeekNumber(scwInDate) + {// The base day in the week of the input date + var scwInDateWeekBase = new Date(scwInDate); + + scwInDateWeekBase.setDate(scwInDateWeekBase.getDate() + - scwInDateWeekBase.getDay() + + scwWeekNumberBaseDay + + ((scwInDate.getDay()> + scwWeekNumberBaseDay)?7:0)); + + // The first Base Day in the year + var scwFirstBaseDay = new Date(scwInDateWeekBase.getFullYear(),0,1); + + scwFirstBaseDay.setDate(scwFirstBaseDay.getDate() + - scwFirstBaseDay.getDay() + + scwWeekNumberBaseDay + ); + + if (scwFirstBaseDay < new Date(scwInDateWeekBase.getFullYear(),0,1)) + {scwFirstBaseDay.setDate(scwFirstBaseDay.getDate()+7);} + + // Start of Week 01 + var scwStartWeekOne = new Date(scwFirstBaseDay + - scwWeekNumberBaseDay + + scwInDate.getDay()); + + if (scwStartWeekOne > scwFirstBaseDay) + {scwStartWeekOne.setDate(scwStartWeekOne.getDate()-7);} + + // Subtract the date of the current week from the date of the + // first week of the year to get the number of weeks in + // milliseconds. Divide by the number of milliseconds + // in a week then round to no decimals in order to remove + // the effect of daylight saving. Add one to make the first + // week, week 1. Place a string zero on the front so that + // week numbers are zero filled. + + var scwWeekNo = '0' + (Math.round((scwInDateWeekBase - scwFirstBaseDay)/604800000,0) + 1); + + // Return the last two characters in the week number string + + return scwWeekNo.substring(scwWeekNo.length-2, scwWeekNo.length); + }; + + // Treewalk to display the dates. + // I tried to use getElementsByName but IE refused to cooperate + // so I resorted to this method which works for all tested + // browsers. + + var scwCells = scwID('scwCells'); + + for (i=0;i + (new Date(scwBaseYear+ + scwDropDownYears,0,0, + scwShowDate.getHours())) + ) + ) || + (scwOutOfMonthDisable && + (scwShowDate < + (new Date(scwShowDate.getFullYear(), + scwCurMonth,1, + scwShowDate.getHours())) + || + scwShowDate > + (new Date(scwShowDate.getFullYear(), + scwCurMonth+1,0, + scwShowDate.getHours())) + ) + ) + )?true:false; + + scwCell.style.visibility = + (scwOutOfMonthHide && + (scwShowDate < + (new Date(scwShowDate.getFullYear(), + scwCurMonth,1, + scwShowDate.getHours())) + || + scwShowDate > + (new Date(scwShowDate.getFullYear(), + scwCurMonth+1,0, + scwShowDate.getHours())) + ) + )?'hidden':'inherit'; + + for (var k=0;k= scwDisabledDates[k][0].valueOf() && + scwCompareDateValue <= scwDisabledDates[k][1].valueOf() + ) + {scwDisabled = true;} + } + } + + if (scwDisabled || + !scwEnabledDay[j-1+(7*((i*scwCells.childNodes.length)/6))] || + !scwPassEnabledDay[(j-1+(7*(i*scwCells.childNodes.length/6)))%7] + ) + {scwRows.childNodes[j].onclick = null; + + if (scwID('scwIE')) + {scwRows.childNodes[j].onmouseover = null; + scwRows.childNodes[j].onmouseout = null; + } + + scwCell.className= + (scwShowDate.getMonth()!=scwCurMonth) + ?'scwCellsExMonthDisabled' + :(scwBlnFullInputDate && + scwShowDate.toDateString()== + scwSeedDate.toDateString()) + ?'scwInputDateDisabled' + :(scwShowDate.getDay()%6==0) + ?'scwCellsWeekendDisabled' + :'scwCellsDisabled'; + + scwCell.style.borderColor = + (scwFormatTodayCell && scwShowDate.toDateString()==scwDateNow.toDateString()) + ?scwTodayCellBorderColour + :(scwCell.currentStyle) + ?scwCell.currentStyle['backgroundColor'] + :(window.getComputedStyle) + ?document.defaultView.getComputedStyle(scwCell,null).getPropertyValue('background-color') + :''; + } + else + {scwRows.childNodes[j].onclick=scwCellOutput; + + if (scwID('scwIE')) + {scwRows.childNodes[j].onmouseover = scwChangeClass; + scwRows.childNodes[j].onmouseout = scwChangeClass; + } + + scwCell.className= + (scwShowDate.getMonth()!=scwCurMonth) + ?'scwCellsExMonth' + :(scwBlnFullInputDate && + scwShowDate.toDateString()== + scwSeedDate.toDateString()) + ?'scwInputDate' + :(scwShowDate.getDay()%6==0) + ?'scwCellsWeekend' + :'scwCells'; + + scwCell.style.borderColor = + (scwFormatTodayCell && scwShowDate.toDateString() == scwDateNow.toDateString()) + ?scwTodayCellBorderColour + :(scwCell.currentStyle) + ?scwCell.currentStyle['backgroundColor'] + :(window.getComputedStyle) + ?document.defaultView.getComputedStyle(scwCell,null).getPropertyValue('background-color') + :''; + } + + scwShowDate.setDate(scwShowDate.getDate()+1); + scwCompareDateValue = new Date(scwShowDate.getFullYear(),scwShowDate.getMonth(),scwShowDate.getDate()).valueOf(); + } + } + } + } + } + + // Opera has a bug with setting the selected index. + // It requires the following work-around to force SELECTs to display correctly. + // Also Opera's poor dynamic rendering prior to 9.5 requires + // the visibility to be reset to prevent garbage in the calendar + // when the displayed month is changed. + + if (window.opera) + {scwID('scwMonths').style.display = 'inline'; + scwID('scwYears' ).style.display = 'inline'; + scwID('scw').style.visibility='hidden'; + scwID('scw').style.visibility='inherit'; + } + }; + +// ************************* +// End of Function Library +// ************************* +// *************************** +// Start of Calendar structure +// *************************** + + document.writeln(""); + document.writeln(""); + document.write( + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
    " + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
    " + + "" + + "" + + "" + + "" + + "" + + "
    " + + "
    " + + "" + + "" + + ""); + + for (i=0;i<7;i++) + {document.write( + ""); + } + + document.write("" + + "" + + ""); + + for (i=0;i<6;i++) + {document.write( + "" + + ""); + for (j=0;j<7;j++) + {document.write( + ""); + } + + document.write( + ""); + } + + document.write( + ""); + + if ((new Date(scwBaseYear + scwDropDownYears, 0, 0)) > scwDateNow && + (new Date(scwBaseYear, 0, 0)) < scwDateNow) + {document.write( + "" + + "" + + "" + + "" + + ""); + } + + document.write( + "
    " + + "
    " + + "
    "); + + if (document.addEventListener) + {scwID('scw' ).addEventListener('click',scwCancel,false); + scwID('scwHeadLeft' ).addEventListener('click',scwStopPropagation,false); + scwID('scwMonths' ).addEventListener('click',scwStopPropagation,false); + scwID('scwMonths' ).addEventListener('change',scwStopPropagation,false); + scwID('scwYears' ).addEventListener('click',scwStopPropagation,false); + scwID('scwYears' ).addEventListener('change',scwStopPropagation,false); + scwID('scwHeadRight').addEventListener('click',scwStopPropagation,false); + } + else {scwID('scw' ).attachEvent('onclick',scwCancel); + scwID('scwHeadLeft' ).attachEvent('onclick',scwStopPropagation); + scwID('scwMonths' ).attachEvent('onclick',scwStopPropagation); + scwID('scwMonths' ).attachEvent('onchange',scwStopPropagation); + scwID('scwYears' ).attachEvent('onclick',scwStopPropagation); + scwID('scwYears' ).attachEvent('onchange',scwStopPropagation); + scwID('scwHeadRight').attachEvent('onclick',scwStopPropagation); + } + +// *************************** +// End of Calendar structure +// *************************** +// **************************************** +// Start of document level event definition +// **************************************** + + if (document.addEventListener) + {document.addEventListener('click',scwHide, false);} + else {document.attachEvent('onclick',scwHide);} + +// **************************************** +// End of document level event definition +// **************************************** +// ************************************ +// End of Simple Calendar Widget Code +// ************************************ \ No newline at end of file diff --git a/jscripts/help.js b/jscripts/help.js new file mode 100644 index 000000000..1e92548a1 --- /dev/null +++ b/jscripts/help.js @@ -0,0 +1,25 @@ +function toggleToc() { + //var tocmain = document.getElementById('help'); + var toc = document.getElementById('help'); + var showlink=document.getElementById('showlink'); + var hidelink=document.getElementById('hidelink'); + if(toc.style.display == 'none') { + toc.style.display = tocWas; + hidelink.style.display=''; + showlink.style.display='none'; + //tocmain.className = ''; + + var help = document.getElementById('help-title'); + help.className = ''; + + } else { + tocWas = toc.style.display; + toc.style.display = 'none'; + hidelink.style.display='none'; + showlink.style.display=''; + //tocmain.className = 'tochidden'; + + var help = document.getElementById('help-title'); + help.className = 'line'; + } +} diff --git a/jscripts/infusion/InfusionAll.js b/jscripts/infusion/InfusionAll.js new file mode 100644 index 000000000..1497083a0 --- /dev/null +++ b/jscripts/infusion/InfusionAll.js @@ -0,0 +1,33 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var window=this,undefined,_jQuery=window.jQuery,_$=window.$,jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context)},quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,isSimple=/^.[^:#\[\.,]*$/;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;this.context=selector;return this}if(typeof selector==="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1]){selector=jQuery.clean([match[1]],context)}else{var elem=document.getElementById(match[3]);if(elem&&elem.id!=match[3]){return jQuery().find(selector)}var ret=jQuery(elem||[]);ret.context=document;ret.selector=selector;return ret}}else{return jQuery(context).find(selector)}}else{if(jQuery.isFunction(selector)){return jQuery(document).ready(selector)}}if(selector.selector&&selector.context){this.selector=selector.selector;this.context=selector.context}return this.setArray(jQuery.isArray(selector)?selector:jQuery.makeArray(selector))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(num){return num===undefined?Array.prototype.slice.call(this):this[num]},pushStack:function(elems,name,selector){var ret=jQuery(elems);ret.prevObject=this;ret.context=this.context;if(name==="find"){ret.selector=this.selector+(this.selector?" ":"")+selector}else{if(name){ret.selector=this.selector+"."+name+"("+selector+")"}}return ret},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this},each:function(callback,args){return jQuery.each(this,callback,args)},index:function(elem){return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this)},attr:function(name,value,type){var options=name;if(typeof name==="string"){if(value===undefined){return this[0]&&jQuery[type||"attr"](this[0],name)}else{options={};options[name]=value}}return this.each(function(i){for(name in options){jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name))}})},css:function(key,value){if((key=="width"||key=="height")&&parseFloat(value)<0){value=undefined}return this.attr(key,value,"curCSS")},text:function(text){if(typeof text!=="object"&&text!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text))}var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8){ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this])}})});return ret},wrapAll:function(html){if(this[0]){var wrap=jQuery(html,this[0].ownerDocument).clone();if(this[0].parentNode){wrap.insertBefore(this[0])}wrap.map(function(){var elem=this;while(elem.firstChild){elem=elem.firstChild}return elem}).append(this)}return this},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html)})},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html)})},append:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType==1){this.appendChild(elem)}})},prepend:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType==1){this.insertBefore(elem,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this)})},after:function(){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this.nextSibling)})},end:function(){return this.prevObject||jQuery([])},push:[].push,sort:[].sort,splice:[].splice,find:function(selector){if(this.length===1){var ret=this.pushStack([],"find",selector);ret.length=0;jQuery.find(selector,this[0],ret);return ret}else{return this.pushStack(jQuery.unique(jQuery.map(this,function(elem){return jQuery.find(selector,elem)})),"find",selector)}},clone:function(events){var ret=this.map(function(){if(!jQuery.support.noCloneEvent&&!jQuery.isXMLDoc(this)){var html=this.outerHTML;if(!html){var div=this.ownerDocument.createElement("div");div.appendChild(this.cloneNode(true));html=div.innerHTML}return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(events===true){var orig=this.find("*").andSelf(),i=0;ret.find("*").andSelf().each(function(){if(this.nodeName!==orig[i].nodeName){return }var events=jQuery.data(orig[i],"events");for(var type in events){for(var handler in events[type]){jQuery.event.add(this,type,events[type][handler],events[type][handler].data)}}i++})}return ret},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i)})||jQuery.multiFilter(selector,jQuery.grep(this,function(elem){return elem.nodeType===1})),"filter",selector)},closest:function(selector){var pos=jQuery.expr.match.POS.test(selector)?jQuery(selector):null,closer=0;return this.map(function(){var cur=this;while(cur&&cur.ownerDocument){if(pos?pos.index(cur)>-1:jQuery(cur).is(selector)){jQuery.data(cur,"closest",closer);return cur}cur=cur.parentNode;closer++}})},not:function(selector){if(typeof selector==="string"){if(isSimple.test(selector)){return this.pushStack(jQuery.multiFilter(selector,this,true),"not",selector)}else{selector=jQuery.multiFilter(selector,this)}}var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector})},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector==="string"?jQuery(selector):jQuery.makeArray(selector))))},is:function(selector){return !!selector&&jQuery.multiFilter(selector,this).length>0},hasClass:function(selector){return !!selector&&this.is("."+selector)},val:function(value){if(value===undefined){var elem=this[0];if(elem){if(jQuery.nodeName(elem,"option")){return(elem.attributes.value||{}).specified?elem.value:elem.text}if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0){return null}for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0)}else{if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0)});if(!values.length){this.selectedIndex=-1}}else{this.value=value}}})},html:function(value){return value===undefined?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(value)},replaceWith:function(value){return this.after(value).remove()},eq:function(i){return this.slice(i,+i+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(args,table,callback){if(this[0]){var fragment=(this[0].ownerDocument||this[0]).createDocumentFragment(),scripts=jQuery.clean(args,(this[0].ownerDocument||this[0]),fragment),first=fragment.firstChild;if(first){for(var i=0,l=this.length;i1||i>0?fragment.cloneNode(true):fragment)}}if(scripts){jQuery.each(scripts,evalScript)}}return this;function root(elem,cur){return table&&jQuery.nodeName(elem,"table")&&jQuery.nodeName(cur,"tr")?(elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody"))):elem}}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src){jQuery.ajax({url:elem.src,async:false,dataType:"script"})}else{jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"")}if(elem.parentNode){elem.parentNode.removeChild(elem)}}function now(){return +new Date}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(typeof target==="boolean"){deep=target;target=arguments[1]||{};i=2}if(typeof target!=="object"&&!jQuery.isFunction(target)){target={}}if(length==i){target=this;--i}for(;i-1}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name]}callback.call(elem);for(var name in options){elem.style[name]=old[name]}},css:function(elem,name,force,extra){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;if(extra==="border"){return }jQuery.each(which,function(){if(!extra){val-=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0}if(extra==="margin"){val+=parseFloat(jQuery.curCSS(elem,"margin"+this,true))||0}else{val-=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0}})}if(elem.offsetWidth!==0){getWH()}else{jQuery.swap(elem,props,getWH)}return Math.max(0,Math.round(val))}return jQuery.curCSS(elem,name,force)},curCSS:function(elem,name,force){var ret,style=elem.style;if(name=="opacity"&&!jQuery.support.opacity){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret}if(name.match(/float/i)){name=styleFloat}if(!force&&style&&style[name]){ret=style[name]}else{if(defaultView.getComputedStyle){if(name.match(/float/i)){name="float"}name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle){ret=computedStyle.getPropertyValue(name)}if(name=="opacity"&&ret==""){ret="1"}}else{if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase()});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft}}}}return ret},clean:function(elems,context,fragment){context=context||document;if(typeof context.createElement==="undefined"){context=context.ownerDocument||context[0]&&context[0].ownerDocument||document}if(!fragment&&elems.length===1&&typeof elems[0]==="string"){var match=/^<(\w+)\s*\/?>$/.exec(elems[0]);if(match){return[context.createElement(match[1])]}}var ret=[],scripts=[],div=context.createElement("div");jQuery.each(elems,function(i,elem){if(typeof elem==="number"){elem+=""}if(!elem){return }if(typeof elem==="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">"});var tags=elem.replace(/^\s+/,"").substring(0,10).toLowerCase();var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
    "]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||!jQuery.support.htmlSerialize&&[1,"div
    ","
    "]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--){div=div.lastChild}if(!jQuery.support.tbody){var hasBody=/"&&!hasBody?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j){if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length){tbody[j].parentNode.removeChild(tbody[j])}}}if(!jQuery.support.leadingWhitespace&&/^\s/.test(elem)){div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild)}elem=jQuery.makeArray(div.childNodes)}if(elem.nodeType){ret.push(elem)}else{ret=jQuery.merge(ret,elem)}});if(fragment){for(var i=0;ret[i];i++){if(jQuery.nodeName(ret[i],"script")&&(!ret[i].type||ret[i].type.toLowerCase()==="text/javascript")){scripts.push(ret[i].parentNode?ret[i].parentNode.removeChild(ret[i]):ret[i])}else{if(ret[i].nodeType===1){ret.splice.apply(ret,[i+1,0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))))}fragment.appendChild(ret[i])}}return scripts}return ret},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8){return undefined}var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&elem.parentNode){elem.parentNode.selectedIndex}if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode){throw"type property can't be changed"}elem[name]=value}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name)){return elem.getAttributeNode(name).nodeValue}if(name=="tabIndex"){var attributeNode=elem.getAttributeNode("tabIndex");return attributeNode&&attributeNode.specified?attributeNode.value:elem.nodeName.match(/(button|input|object|select|textarea)/i)?0:elem.nodeName.match(/^(a|area)$/i)&&elem.href?0:undefined}return elem[name]}if(!jQuery.support.style&¬xml&&name=="style"){return jQuery.attr(elem.style,"cssText",value)}if(set){elem.setAttribute(name,""+value)}var attr=!jQuery.support.hrefNormalized&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr}if(!jQuery.support.opacity&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+""=="NaN"?"":"alpha(opacity="+value*100+")")}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase()});if(set){elem[name]=value}return elem[name]},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"")},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||typeof array==="string"||jQuery.isFunction(array)||array.setInterval){ret[0]=array}else{while(i){ret[--i]=array[i]}}}return ret},inArray:function(elem,array){for(var i=0,length=array.length;i0?this.clone(true):this).get();jQuery.fn[original].apply(jQuery(insert[i]),elems);ret=ret.concat(elems)}return this.pushStack(ret,name,selector)}});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1){this.removeAttribute(name)}},addClass:function(classNames){jQuery.className.add(this,classNames)},removeClass:function(classNames){jQuery.className.remove(this,classNames)},toggleClass:function(classNames,state){if(typeof state!=="boolean"){state=!jQuery.className.has(this,classNames)}jQuery.className[state?"add":"remove"](this,classNames)},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).length){jQuery("*",this).add([this]).each(function(){jQuery.event.remove(this);jQuery.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){jQuery(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments)}});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0}var expando="jQuery"+now(),uuid=0,windowData={};jQuery.extend({cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id){id=elem[expando]=++uuid}if(name&&!jQuery.cache[id]){jQuery.cache[id]={}}if(data!==undefined){jQuery.cache[id][name]=data}return name?jQuery.cache[id][name]:id},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id]){break}if(!name){jQuery.removeData(elem)}}}else{try{delete elem[expando]}catch(e){if(elem.removeAttribute){elem.removeAttribute(expando)}}delete jQuery.cache[id]}},queue:function(elem,type,data){if(elem){type=(type||"fx")+"queue";var q=jQuery.data(elem,type);if(!q||jQuery.isArray(data)){q=jQuery.data(elem,type,jQuery.makeArray(data))}else{if(data){q.push(data)}}}return q},dequeue:function(elem,type){var queue=jQuery.queue(elem,type),fn=queue.shift();if(!type||type==="fx"){fn=queue[0]}if(fn!==undefined){fn.call(elem)}}});jQuery.fn.extend({data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length){data=jQuery.data(this[0],key)}return data===undefined&&parts[1]?this.data(parts[0]):data}else{return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value)})}},removeData:function(key){return this.each(function(){jQuery.removeData(this,key)})},queue:function(type,data){if(typeof type!=="string"){data=type;type="fx"}if(data===undefined){return jQuery.queue(this[0],type)}return this.each(function(){var queue=jQuery.queue(this,type,data);if(type=="fx"&&queue.length==1){queue[0].call(this)}})},dequeue:function(type){return this.each(function(){jQuery.dequeue(this,type)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var chunker=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,done=0,toString=Object.prototype.toString;var Sizzle=function(selector,context,results,seed){results=results||[];context=context||document;if(context.nodeType!==1&&context.nodeType!==9){return[]}if(!selector||typeof selector!=="string"){return results}var parts=[],m,set,checkSet,check,mode,extra,prune=true;chunker.lastIndex=0;while((m=chunker.exec(selector))!==null){parts.push(m[1]);if(m[2]){extra=RegExp.rightContext;break}}if(parts.length>1&&origPOS.exec(selector)){if(parts.length===2&&Expr.relative[parts[0]]){set=posProcess(parts[0]+parts[1],context)}else{set=Expr.relative[parts[0]]?[context]:Sizzle(parts.shift(),context);while(parts.length){selector=parts.shift();if(Expr.relative[selector]){selector+=parts.shift()}set=posProcess(selector,set)}}}else{var ret=seed?{expr:parts.pop(),set:makeArray(seed)}:Sizzle.find(parts.pop(),parts.length===1&&context.parentNode?context.parentNode:context,isXML(context));set=Sizzle.filter(ret.expr,ret.set);if(parts.length>0){checkSet=makeArray(set)}else{prune=false}while(parts.length){var cur=parts.pop(),pop=cur;if(!Expr.relative[cur]){cur=""}else{pop=parts.pop()}if(pop==null){pop=context}Expr.relative[cur](checkSet,pop,isXML(context))}}if(!checkSet){checkSet=set}if(!checkSet){throw"Syntax error, unrecognized expression: "+(cur||selector)}if(toString.call(checkSet)==="[object Array]"){if(!prune){results.push.apply(results,checkSet)}else{if(context.nodeType===1){for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&(checkSet[i]===true||checkSet[i].nodeType===1&&contains(context,checkSet[i]))){results.push(set[i])}}}else{for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&checkSet[i].nodeType===1){results.push(set[i])}}}}}else{makeArray(checkSet,results)}if(extra){Sizzle(extra,context,results,seed);if(sortOrder){hasDuplicate=false;results.sort(sortOrder);if(hasDuplicate){for(var i=1;i":function(checkSet,part,isXML){var isPartStr=typeof part==="string";if(isPartStr&&!/\W/.test(part)){part=isXML?part:part.toUpperCase();for(var i=0,l=checkSet.length;i=0)){if(!inplace){result.push(elem)}}else{if(inplace){curLoop[i]=false}}}}return false},ID:function(match){return match[1].replace(/\\/g,"")},TAG:function(match,curLoop){for(var i=0;curLoop[i]===false;i++){}return curLoop[i]&&isXML(curLoop[i])?match[1]:match[1].toUpperCase()},CHILD:function(match){if(match[1]=="nth"){var test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(match[2]=="even"&&"2n"||match[2]=="odd"&&"2n+1"||!/\D/.test(match[2])&&"0n+"+match[2]||match[2]);match[2]=(test[1]+(test[2]||1))-0;match[3]=test[3]-0}match[0]=done++;return match},ATTR:function(match,curLoop,inplace,result,not,isXML){var name=match[1].replace(/\\/g,"");if(!isXML&&Expr.attrMap[name]){match[1]=Expr.attrMap[name]}if(match[2]==="~="){match[4]=" "+match[4]+" "}return match},PSEUDO:function(match,curLoop,inplace,result,not){if(match[1]==="not"){if(match[3].match(chunker).length>1||/^\w/.test(match[3])){match[3]=Sizzle(match[3],null,null,curLoop)}else{var ret=Sizzle.filter(match[3],curLoop,inplace,true^not);if(!inplace){result.push.apply(result,ret)}return false}}else{if(Expr.match.POS.test(match[0])||Expr.match.CHILD.test(match[0])){return true}}return match},POS:function(match){match.unshift(true);return match}},filters:{enabled:function(elem){return elem.disabled===false&&elem.type!=="hidden"},disabled:function(elem){return elem.disabled===true},checked:function(elem){return elem.checked===true},selected:function(elem){elem.parentNode.selectedIndex;return elem.selected===true},parent:function(elem){return !!elem.firstChild},empty:function(elem){return !elem.firstChild},has:function(elem,i,match){return !!Sizzle(match[3],elem).length},header:function(elem){return/h\d/i.test(elem.nodeName)},text:function(elem){return"text"===elem.type},radio:function(elem){return"radio"===elem.type},checkbox:function(elem){return"checkbox"===elem.type},file:function(elem){return"file"===elem.type},password:function(elem){return"password"===elem.type},submit:function(elem){return"submit"===elem.type},image:function(elem){return"image"===elem.type},reset:function(elem){return"reset"===elem.type},button:function(elem){return"button"===elem.type||elem.nodeName.toUpperCase()==="BUTTON"},input:function(elem){return/input|select|textarea|button/i.test(elem.nodeName)}},setFilters:{first:function(elem,i){return i===0},last:function(elem,i,match,array){return i===array.length-1},even:function(elem,i){return i%2===0},odd:function(elem,i){return i%2===1},lt:function(elem,i,match){return imatch[3]-0},nth:function(elem,i,match){return match[3]-0==i},eq:function(elem,i,match){return match[3]-0==i}},filter:{PSEUDO:function(elem,match,i,array){var name=match[1],filter=Expr.filters[name];if(filter){return filter(elem,i,match,array)}else{if(name==="contains"){return(elem.textContent||elem.innerText||"").indexOf(match[3])>=0}else{if(name==="not"){var not=match[3];for(var i=0,l=not.length;i=0)}}},ID:function(elem,match){return elem.nodeType===1&&elem.getAttribute("id")===match},TAG:function(elem,match){return(match==="*"&&elem.nodeType===1)||elem.nodeName===match},CLASS:function(elem,match){return(" "+(elem.className||elem.getAttribute("class"))+" ").indexOf(match)>-1},ATTR:function(elem,match){var name=match[1],result=Expr.attrHandle[name]?Expr.attrHandle[name](elem):elem[name]!=null?elem[name]:elem.getAttribute(name),value=result+"",type=match[2],check=match[4];return result==null?type==="!=":type==="="?value===check:type==="*="?value.indexOf(check)>=0:type==="~="?(" "+value+" ").indexOf(check)>=0:!check?value&&result!==false:type==="!="?value!=check:type==="^="?value.indexOf(check)===0:type==="$="?value.substr(value.length-check.length)===check:type==="|="?value===check||value.substr(0,check.length+1)===check+"-":false},POS:function(elem,match,i,array){var name=match[2],filter=Expr.setFilters[name];if(filter){return filter(elem,i,match,array)}}}};var origPOS=Expr.match.POS;for(var type in Expr.match){Expr.match[type]=RegExp(Expr.match[type].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var makeArray=function(array,results){array=Array.prototype.slice.call(array);if(results){results.push.apply(results,array);return results}return array};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(e){makeArray=function(array,results){var ret=results||[];if(toString.call(array)==="[object Array]"){Array.prototype.push.apply(ret,array)}else{if(typeof array.length==="number"){for(var i=0,l=array.length;i";var root=document.documentElement;root.insertBefore(form,root.firstChild);if(!!document.getElementById(id)){Expr.find.ID=function(match,context,isXML){if(typeof context.getElementById!=="undefined"&&!isXML){var m=context.getElementById(match[1]);return m?m.id===match[1]||typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id").nodeValue===match[1]?[m]:undefined:[]}};Expr.filter.ID=function(elem,match){var node=typeof elem.getAttributeNode!=="undefined"&&elem.getAttributeNode("id");return elem.nodeType===1&&node&&node.nodeValue===match}}root.removeChild(form)})();(function(){var div=document.createElement("div");div.appendChild(document.createComment(""));if(div.getElementsByTagName("*").length>0){Expr.find.TAG=function(match,context){var results=context.getElementsByTagName(match[1]);if(match[1]==="*"){var tmp=[];for(var i=0;results[i];i++){if(results[i].nodeType===1){tmp.push(results[i])}}results=tmp}return results}}div.innerHTML="";if(div.firstChild&&typeof div.firstChild.getAttribute!=="undefined"&&div.firstChild.getAttribute("href")!=="#"){Expr.attrHandle.href=function(elem){return elem.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var oldSizzle=Sizzle,div=document.createElement("div");div.innerHTML="

    ";if(div.querySelectorAll&&div.querySelectorAll(".TEST").length===0){return }Sizzle=function(query,context,extra,seed){context=context||document;if(!seed&&context.nodeType===9&&!isXML(context)){try{return makeArray(context.querySelectorAll(query),extra)}catch(e){}}return oldSizzle(query,context,extra,seed)};Sizzle.find=oldSizzle.find;Sizzle.filter=oldSizzle.filter;Sizzle.selectors=oldSizzle.selectors;Sizzle.matches=oldSizzle.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var div=document.createElement("div");div.innerHTML="
    ";if(div.getElementsByClassName("e").length===0){return }div.lastChild.className="e";if(div.getElementsByClassName("e").length===1){return }Expr.order.splice(1,0,"CLASS");Expr.find.CLASS=function(match,context,isXML){if(typeof context.getElementsByClassName!=="undefined"&&!isXML){return context.getElementsByClassName(match[1])}}})()}function dirNodeCheck(dir,cur,doneName,checkSet,nodeCheck,isXML){var sibDir=dir=="previousSibling"&&!isXML;for(var i=0,l=checkSet.length;i0){match=elem;break}}}elem=elem[dir]}checkSet[i]=match}}}var contains=document.compareDocumentPosition?function(a,b){return a.compareDocumentPosition(b)&16}:function(a,b){return a!==b&&(a.contains?a.contains(b):true)};var isXML=function(elem){return elem.nodeType===9&&elem.documentElement.nodeName!=="HTML"||!!elem.ownerDocument&&isXML(elem.ownerDocument)};var posProcess=function(selector,context){var tmpSet=[],later="",match,root=context.nodeType?[context]:context;while((match=Expr.match.PSEUDO.exec(selector))){later+=match[0];selector=selector.replace(Expr.match.PSEUDO,"")}selector=Expr.relative[selector]?selector+"*":selector;for(var i=0,l=root.length;i0||elem.offsetHeight>0};Sizzle.selectors.filters.animated=function(elem){return jQuery.grep(jQuery.timers,function(fn){return elem===fn.elem}).length};jQuery.multiFilter=function(expr,elems,not){if(not){expr=":not("+expr+")"}return Sizzle.matches(expr,elems)};jQuery.dir=function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1){matched.push(cur)}cur=cur[dir]}return matched};jQuery.nth=function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir]){if(cur.nodeType==1&&++num==result){break}}return cur};jQuery.sibling=function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem){r.push(n)}}return r};return ;window.Sizzle=Sizzle})();jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8){return }if(elem.setInterval&&elem!=window){elem=window}if(!handler.guid){handler.guid=this.guid++}if(data!==undefined){var fn=handler;handler=this.proxy(fn);handler.data=data}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){return typeof jQuery!=="undefined"&&!jQuery.event.triggered?jQuery.event.handle.apply(arguments.callee.elem,arguments):undefined});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var namespaces=type.split(".");type=namespaces.shift();handler.type=namespaces.slice().sort().join(".");var handlers=events[type];if(jQuery.event.specialAll[type]){jQuery.event.specialAll[type].setup.call(elem,data,namespaces)}if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem,data,namespaces)===false){if(elem.addEventListener){elem.addEventListener(type,handle,false)}else{if(elem.attachEvent){elem.attachEvent("on"+type,handle)}}}}handlers[handler.guid]=handler;jQuery.event.global[type]=true});elem=null},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8){return }var events=jQuery.data(elem,"events"),ret,index;if(events){if(types===undefined||(typeof types==="string"&&types.charAt(0)==".")){for(var type in events){this.remove(elem,type+(types||""))}}else{if(types.type){handler=types.handler;types=types.type}jQuery.each(types.split(/\s+/),function(index,type){var namespaces=type.split(".");type=namespaces.shift();var namespace=RegExp("(^|\\.)"+namespaces.slice().sort().join(".*\\.")+"(\\.|$)");if(events[type]){if(handler){delete events[type][handler.guid]}else{for(var handle in events[type]){if(namespace.test(events[type][handle].type)){delete events[type][handle]}}}if(jQuery.event.specialAll[type]){jQuery.event.specialAll[type].teardown.call(elem,namespaces)}for(ret in events[type]){break}if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem,namespaces)===false){if(elem.removeEventListener){elem.removeEventListener(type,jQuery.data(elem,"handle"),false)}else{if(elem.detachEvent){elem.detachEvent("on"+type,jQuery.data(elem,"handle"))}}}ret=null;delete events[type]}}})}for(ret in events){break}if(!ret){var handle=jQuery.data(elem,"handle");if(handle){handle.elem=null}jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle")}}},trigger:function(event,data,elem,bubbling){var type=event.type||event;if(!bubbling){event=typeof event==="object"?event[expando]?event:jQuery.extend(jQuery.Event(type),event):jQuery.Event(type);if(type.indexOf("!")>=0){event.type=type=type.slice(0,-1);event.exclusive=true}if(!elem){event.stopPropagation();if(this.global[type]){jQuery.each(jQuery.cache,function(){if(this.events&&this.events[type]){jQuery.event.trigger(event,data,this.handle.elem)}})}}if(!elem||elem.nodeType==3||elem.nodeType==8){return undefined}event.result=undefined;event.target=elem;data=jQuery.makeArray(data);data.unshift(event)}event.currentTarget=elem;var handle=jQuery.data(elem,"handle");if(handle){handle.apply(elem,data)}if((!elem[type]||(jQuery.nodeName(elem,"a")&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false){event.result=false}if(!bubbling&&elem[type]&&!event.isDefaultPrevented()&&!(jQuery.nodeName(elem,"a")&&type=="click")){this.triggered=true;try{elem[type]()}catch(e){}}this.triggered=false;if(!event.isPropagationStopped()){var parent=elem.parentNode||elem.ownerDocument;if(parent){jQuery.event.trigger(event,data,parent,true)}}},handle:function(event){var all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);event.currentTarget=this;var namespaces=event.type.split(".");event.type=namespaces.shift();all=!namespaces.length&&!event.exclusive;var namespace=RegExp("(^|\\.)"+namespaces.slice().sort().join(".*\\.")+"(\\.|$)");handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||namespace.test(handler.type)){event.handler=handler;event.data=handler.data;var ret=handler.apply(this,arguments);if(ret!==undefined){event.result=ret;if(ret===false){event.preventDefault();event.stopPropagation()}}if(event.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(event){if(event[expando]){return event}var originalEvent=event;event=jQuery.Event(originalEvent);for(var i=this.props.length,prop;i;){prop=this.props[--i];event[prop]=originalEvent[prop]}if(!event.target){event.target=event.srcElement||document}if(event.target.nodeType==3){event.target=event.target.parentNode}if(!event.relatedTarget&&event.fromElement){event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement}if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0)}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode)){event.which=event.charCode||event.keyCode}if(!event.metaKey&&event.ctrlKey){event.metaKey=event.ctrlKey}if(!event.which&&event.button){event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)))}return event},proxy:function(fn,proxy){proxy=proxy||function(){return fn.apply(this,arguments)};proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy},special:{ready:{setup:bindReady,teardown:function(){}}},specialAll:{live:{setup:function(selector,namespaces){jQuery.event.add(this,namespaces[0],liveHandler)},teardown:function(namespaces){if(namespaces.length){var remove=0,name=RegExp("(^|\\.)"+namespaces[0]+"(\\.|$)");jQuery.each((jQuery.data(this,"events").live||{}),function(){if(name.test(this.type)){remove++}});if(remove<1){jQuery.event.remove(this,namespaces[0],liveHandler)}}}}}};jQuery.Event=function(src){if(!this.preventDefault){return new jQuery.Event(src)}if(src&&src.type){this.originalEvent=src;this.type=src.type}else{this.type=src}this.timeStamp=now();this[expando]=true};function returnFalse(){return false}function returnTrue(){return true}jQuery.Event.prototype={preventDefault:function(){this.isDefaultPrevented=returnTrue;var e=this.originalEvent;if(!e){return }if(e.preventDefault){e.preventDefault()}e.returnValue=false},stopPropagation:function(){this.isPropagationStopped=returnTrue;var e=this.originalEvent;if(!e){return }if(e.stopPropagation){e.stopPropagation()}e.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=returnTrue;this.stopPropagation()},isDefaultPrevented:returnFalse,isPropagationStopped:returnFalse,isImmediatePropagationStopped:returnFalse};var withinElement=function(event){var parent=event.relatedTarget;while(parent&&parent!=this){try{parent=parent.parentNode}catch(e){parent=this}}if(parent!=this){event.type=event.data;jQuery.event.handle.apply(this,arguments)}};jQuery.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(orig,fix){jQuery.event.special[fix]={setup:function(){jQuery.event.add(this,orig,withinElement,fix)},teardown:function(){jQuery.event.remove(this,orig,withinElement)}}});jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data)})},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments)});return this.each(function(){jQuery.event.add(this,type,one,fn&&data)})},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn)})},trigger:function(type,data){return this.each(function(){jQuery.event.trigger(type,data,this)})},triggerHandler:function(type,data){if(this[0]){var event=jQuery.Event(type);event.preventDefault();event.stopPropagation();jQuery.event.trigger(event,data,this[0]);return event.result}},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off)}var type="GET";if(params){if(jQuery.isFunction(params)){callback=params;params=null}else{if(typeof params==="object"){params=jQuery.param(params);type="POST"}}}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified"){self.html(selector?jQuery("
    ").append(res.responseText.replace(//g,"")).find(selector):res.responseText)}if(callback){self.each(callback,[res.responseText,status,res])}}});return this},serialize:function(){return jQuery.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?jQuery.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val,i){return{name:elem.name,value:val}}):{name:elem.name,value:val}}).get()}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f)}});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type})},getScript:function(url,callback){return jQuery.get(url,null,callback,"script")},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json")},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={}}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type})},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!=="string"){s.data=jQuery.param(s.data)}if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre)){s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?"}}else{if(!s.data||!s.data.match(jsre)){s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?"}}s.dataType="json"}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data){s.data=(s.data+"").replace(jsre,"="+jsonp+"$1")}s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp]}catch(e){}if(head){head.removeChild(script)}}}if(s.dataType=="script"&&s.cache==null){s.cache=false}if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"")}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null}if(s.global&&!jQuery.active++){jQuery.event.trigger("ajaxStart")}var parts=/^(\w+:)?\/\/([^\/?#]+)/.exec(s.url);if(s.dataType=="script"&&type=="GET"&&parts&&(parts[1]&&parts[1]!=location.protocol||parts[2]!=location.host)){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset){script.charset=s.scriptCharset}if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();script.onload=script.onreadystatechange=null;head.removeChild(script)}}}head.appendChild(script);return undefined}var requestDone=false;var xhr=s.xhr();if(s.username){xhr.open(type,s.url,s.async,s.username,s.password)}else{xhr.open(type,s.url,s.async)}try{if(s.data){xhr.setRequestHeader("Content-Type",s.contentType)}if(s.ifModified){xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default)}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){if(s.global&&!--jQuery.active){jQuery.event.trigger("ajaxStop")}xhr.abort();return false}if(s.global){jQuery.event.trigger("ajaxSend",[xhr,s])}var onreadystatechange=function(isTimeout){if(xhr.readyState==0){if(ival){clearInterval(ival);ival=null;if(s.global&&!--jQuery.active){jQuery.event.trigger("ajaxStop")}}}else{if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null}status=isTimeout=="timeout"?"timeout":!jQuery.httpSuccess(xhr)?"error":s.ifModified&&jQuery.httpNotModified(xhr,s.url)?"notmodified":"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s)}catch(e){status="parsererror"}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified")}catch(e){}if(s.ifModified&&modRes){jQuery.lastModified[s.url]=modRes}if(!jsonp){success()}}else{jQuery.handleError(s,xhr,status)}complete();if(isTimeout){xhr.abort()}if(s.async){xhr=null}}}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0){setTimeout(function(){if(xhr&&!requestDone){onreadystatechange("timeout")}},s.timeout)}}try{xhr.send(s.data)}catch(e){jQuery.handleError(s,xhr,null,e)}if(!s.async){onreadystatechange()}function success(){if(s.success){s.success(data,status)}if(s.global){jQuery.event.trigger("ajaxSuccess",[xhr,s])}}function complete(){if(s.complete){s.complete(xhr,status)}if(s.global){jQuery.event.trigger("ajaxComplete",[xhr,s])}if(s.global&&!--jQuery.active){jQuery.event.trigger("ajaxStop")}}return xhr},handleError:function(s,xhr,status,e){if(s.error){s.error(xhr,status,e)}if(s.global){jQuery.event.trigger("ajaxError",[xhr,s,e])}},active:0,httpSuccess:function(xhr){try{return !xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223}catch(e){}return false},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]}catch(e){}return false},httpData:function(xhr,type,s){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror"){throw"parsererror"}if(s&&s.dataFilter){data=s.dataFilter(data,type)}if(typeof data==="string"){if(type=="script"){jQuery.globalEval(data)}if(type=="json"){data=window.eval("("+data+")")}}return data},param:function(a){var s=[];function add(key,value){s[s.length]=encodeURIComponent(key)+"="+encodeURIComponent(value)}if(jQuery.isArray(a)||a.jquery){jQuery.each(a,function(){add(this.name,this.value)})}else{for(var j in a){if(jQuery.isArray(a[j])){jQuery.each(a[j],function(){add(j,this)})}else{add(j,jQuery.isFunction(a[j])?a[j]():a[j])}}}return s.join("&").replace(/%20/g,"+")}});var elemdisplay={},timerId,fxAttrs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function genFx(type,num){var obj={};jQuery.each(fxAttrs.concat.apply([],fxAttrs.slice(0,num)),function(){obj[this]=type});return obj}jQuery.fn.extend({show:function(speed,callback){if(speed){return this.animate(genFx("show",3),speed,callback)}else{for(var i=0,l=this.length;i").appendTo("body");display=elem.css("display");if(display==="none"){display="block"}elem.remove();elemdisplay[tagName]=display}jQuery.data(this[i],"olddisplay",display)}}for(var i=0,l=this.length;i=0;i--){if(timers[i].elem==this){if(gotoEnd){timers[i](true)}timers.splice(i,1)}}});if(!gotoEnd){this.dequeue()}return this}});jQuery.each({slideDown:genFx("show",1),slideUp:genFx("hide",1),slideToggle:genFx("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(name,props){jQuery.fn[name]=function(speed,callback){return this.animate(props,speed,callback)}});jQuery.extend({speed:function(speed,easing,fn){var opt=typeof speed==="object"?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&!jQuery.isFunction(easing)&&easing};opt.duration=jQuery.fx.off?0:typeof opt.duration==="number"?opt.duration:jQuery.fx.speeds[opt.duration]||jQuery.fx.speeds._default;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false){jQuery(this).dequeue()}if(jQuery.isFunction(opt.old)){opt.old.call(this)}};return opt},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum}},timers:[],fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig){options.orig={}}}});jQuery.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(force){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;var self=this;function t(gotoEnd){return self.step(gotoEnd)}t.elem=this.elem;if(t()&&jQuery.timers.push(t)&&!timerId){timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim){if(this.options.curAnim[i]!==true){done=false}}if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){jQuery(this.elem).hide()}if(this.options.hide||this.options.show){for(var p in this.options.curAnim){jQuery.attr(this.elem.style,p,this.options.orig[p])}}this.options.complete.call(this.elem)}return false}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now)},_default:function(fx){if(fx.elem.style&&fx.elem.style[fx.prop]!=null){fx.elem.style[fx.prop]=fx.now+fx.unit}else{fx.elem[fx.prop]=fx.now}}}});if(document.documentElement.getBoundingClientRect){jQuery.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return jQuery.offset.bodyOffset(this[0])}var box=this[0].getBoundingClientRect(),doc=this[0].ownerDocument,body=doc.body,docElem=doc.documentElement,clientTop=docElem.clientTop||body.clientTop||0,clientLeft=docElem.clientLeft||body.clientLeft||0,top=box.top+(self.pageYOffset||jQuery.boxModel&&docElem.scrollTop||body.scrollTop)-clientTop,left=box.left+(self.pageXOffset||jQuery.boxModel&&docElem.scrollLeft||body.scrollLeft)-clientLeft;return{top:top,left:left}}}else{jQuery.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return jQuery.offset.bodyOffset(this[0])}jQuery.offset.initialized||jQuery.offset.initialize();var elem=this[0],offsetParent=elem.offsetParent,prevOffsetParent=elem,doc=elem.ownerDocument,computedStyle,docElem=doc.documentElement,body=doc.body,defaultView=doc.defaultView,prevComputedStyle=defaultView.getComputedStyle(elem,null),top=elem.offsetTop,left=elem.offsetLeft;while((elem=elem.parentNode)&&elem!==body&&elem!==docElem){computedStyle=defaultView.getComputedStyle(elem,null);top-=elem.scrollTop,left-=elem.scrollLeft;if(elem===offsetParent){top+=elem.offsetTop,left+=elem.offsetLeft;if(jQuery.offset.doesNotAddBorder&&!(jQuery.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(elem.tagName))){top+=parseInt(computedStyle.borderTopWidth,10)||0,left+=parseInt(computedStyle.borderLeftWidth,10)||0}prevOffsetParent=offsetParent,offsetParent=elem.offsetParent}if(jQuery.offset.subtractsBorderForOverflowNotVisible&&computedStyle.overflow!=="visible"){top+=parseInt(computedStyle.borderTopWidth,10)||0,left+=parseInt(computedStyle.borderLeftWidth,10)||0}prevComputedStyle=computedStyle}if(prevComputedStyle.position==="relative"||prevComputedStyle.position==="static"){top+=body.offsetTop,left+=body.offsetLeft}if(prevComputedStyle.position==="fixed"){top+=Math.max(docElem.scrollTop,body.scrollTop),left+=Math.max(docElem.scrollLeft,body.scrollLeft)}return{top:top,left:left}}}jQuery.offset={initialize:function(){if(this.initialized){return }var body=document.body,container=document.createElement("div"),innerDiv,checkDiv,table,td,rules,prop,bodyMarginTop=body.style.marginTop,html='
    ';rules={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(prop in rules){container.style[prop]=rules[prop]}container.innerHTML=html;body.insertBefore(container,body.firstChild);innerDiv=container.firstChild,checkDiv=innerDiv.firstChild,td=innerDiv.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(checkDiv.offsetTop!==5);this.doesAddBorderForTableAndCells=(td.offsetTop===5);innerDiv.style.overflow="hidden",innerDiv.style.position="relative";this.subtractsBorderForOverflowNotVisible=(checkDiv.offsetTop===-5);body.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(body.offsetTop===0);body.style.marginTop=bodyMarginTop;body.removeChild(container);this.initialized=true},bodyOffset:function(body){jQuery.offset.initialized||jQuery.offset.initialize();var top=body.offsetTop,left=body.offsetLeft;if(jQuery.offset.doesNotIncludeMarginInBodyOffset){top+=parseInt(jQuery.curCSS(body,"marginTop",true),10)||0,left+=parseInt(jQuery.curCSS(body,"marginLeft",true),10)||0}return{top:top,left:left}}};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,"marginTop");offset.left-=num(this,"marginLeft");parentOffset.top+=num(offsetParent,"borderTopWidth");parentOffset.left+=num(offsetParent,"borderLeftWidth");results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left}}return results},offsetParent:function(){var offsetParent=this[0].offsetParent||document.body;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,"position")=="static")){offsetParent=offsetParent.offsetParent}return jQuery(offsetParent)}});jQuery.each(["Left","Top"],function(i,name){var method="scroll"+name;jQuery.fn[method]=function(val){if(!this[0]){return null}return val!==undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val}):this[0]==window||this[0]==document?self[i?"pageYOffset":"pageXOffset"]||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method]}});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom",lower=name.toLowerCase();jQuery.fn["inner"+name]=function(){return this[0]?jQuery.css(this[0],lower,false,"padding"):null};jQuery.fn["outer"+name]=function(margin){return this[0]?jQuery.css(this[0],lower,false,margin?"margin":"border"):null};var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(document.documentElement["client"+name],document.body["scroll"+name],document.documentElement["scroll"+name],document.body["offset"+name],document.documentElement["offset"+name]):size===undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,typeof size==="string"?size:size+"px")}})})();jQuery.ui||(function($){var _remove=$.fn.remove,isFF2=$.browser.mozilla&&(parseFloat($.browser.version)<1.9);$.ui={version:"1.7",plugin:{add:function(module,option,set){var proto=$.ui[module].prototype;for(var i in set){proto.plugins[i]=proto.plugins[i]||[];proto.plugins[i].push([option,set[i]])}},call:function(instance,name,args){var set=instance.plugins[name];if(!set||!instance.element[0].parentNode){return }for(var i=0;i0){return true}el[scroll]=1;has=(el[scroll]>0);el[scroll]=0;return has},isOverAxis:function(x,reference,size){return(x>reference)&&(x<(reference+size))},isOver:function(y,x,top,left,height,width){return $.ui.isOverAxis(y,top,height)&&$.ui.isOverAxis(x,left,width)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(isFF2){var attr=$.attr,removeAttr=$.fn.removeAttr,ariaNS="http://www.w3.org/2005/07/aaa",ariaState=/^aria-/,ariaRole=/^wairole:/;$.attr=function(elem,name,value){var set=value!==undefined;return(name=="role"?(set?attr.call(this,elem,name,"wairole:"+value):(attr.apply(this,arguments)||"").replace(ariaRole,"")):(ariaState.test(name)?(set?elem.setAttributeNS(ariaNS,name.replace(ariaState,"aaa:"),value):attr.call(this,elem,name.replace(ariaState,"aaa:"))):attr.apply(this,arguments)))};$.fn.removeAttr=function(name){return(ariaState.test(name)?this.each(function(){this.removeAttributeNS(ariaNS,name.replace(ariaState,""))}):removeAttr.call(this,name))}}$.fn.extend({remove:function(){$("*",this).add(this).each(function(){$(this).triggerHandler("remove")});return _remove.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var scrollParent;if(($.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){scrollParent=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test($.curCSS(this,"position",1))&&(/(auto|scroll)/).test($.curCSS(this,"overflow",1)+$.curCSS(this,"overflow-y",1)+$.curCSS(this,"overflow-x",1))}).eq(0)}else{scrollParent=this.parents().filter(function(){return(/(auto|scroll)/).test($.curCSS(this,"overflow",1)+$.curCSS(this,"overflow-y",1)+$.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!scrollParent.length?$(document):scrollParent}});$.extend($.expr[":"],{data:function(elem,i,match){return !!$.data(elem,match[3])},focusable:function(element){var nodeName=element.nodeName.toLowerCase(),tabIndex=$.attr(element,"tabindex");return(/input|select|textarea|button|object/.test(nodeName)?!element.disabled:"a"==nodeName||"area"==nodeName?element.href||!isNaN(tabIndex):!isNaN(tabIndex))&&!$(element)["area"==nodeName?"parents":"closest"](":hidden").length},tabbable:function(element){var tabIndex=$.attr(element,"tabindex");return(isNaN(tabIndex)||tabIndex>=0)&&$(element).is(":focusable")}});function getter(namespace,plugin,method,args){function getMethods(type){var methods=$[namespace][plugin][type]||[];return(typeof methods=="string"?methods.split(/,?\s+/):methods)}var methods=getMethods("getter");if(args.length==1&&typeof args[0]=="string"){methods=methods.concat(getMethods("getterSetter"))}return($.inArray(method,methods)!=-1)}$.widget=function(name,prototype){var namespace=name.split(".")[0];name=name.split(".")[1];$.fn[name]=function(options){var isMethodCall=(typeof options=="string"),args=Array.prototype.slice.call(arguments,1);if(isMethodCall&&options.substring(0,1)=="_"){return this}if(isMethodCall&&getter(namespace,name,options,args)){var instance=$.data(this[0],name);return(instance?instance[options].apply(instance,args):undefined)}return this.each(function(){var instance=$.data(this,name);(!instance&&!isMethodCall&&$.data(this,name,new $[namespace][name](this,options))._init());(instance&&isMethodCall&&$.isFunction(instance[options])&&instance[options].apply(instance,args))})};$[namespace]=$[namespace]||{};$[namespace][name]=function(element,options){var self=this;this.namespace=namespace;this.widgetName=name;this.widgetEventPrefix=$[namespace][name].eventPrefix||name;this.widgetBaseClass=namespace+"-"+name;this.options=$.extend({},$.widget.defaults,$[namespace][name].defaults,$.metadata&&$.metadata.get(element)[name],options);this.element=$(element).bind("setData."+name,function(event,key,value){if(event.target==element){return self._setData(key,value)}}).bind("getData."+name,function(event,key){if(event.target==element){return self._getData(key)}}).bind("remove",function(){return self.destroy()})};$[namespace][name].prototype=$.extend({},$.widget.prototype,prototype);$[namespace][name].getterSetter="option"};$.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(key,value){var options=key,self=this;if(typeof key=="string"){if(value===undefined){return this._getData(key)}options={};options[key]=value}$.each(options,function(key,value){self._setData(key,value)})},_getData:function(key){return this.options[key]},_setData:function(key,value){this.options[key]=value;if(key=="disabled"){this.element[value?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",value)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(type,event,data){var callback=this.options[type],eventName=(type==this.widgetEventPrefix?type:this.widgetEventPrefix+type);event=$.Event(event);event.type=eventName;if(event.originalEvent){for(var i=$.event.props.length,prop;i;){prop=$.event.props[--i];event[prop]=event.originalEvent[prop]}}this.element.trigger(event,data);return !($.isFunction(callback)&&callback.call(this.element[0],event,data)===false||event.isDefaultPrevented())}};$.widget.defaults={disabled:false};$.ui.mouse={_mouseInit:function(){var self=this;this.element.bind("mousedown."+this.widgetName,function(event){return self._mouseDown(event)}).bind("click."+this.widgetName,function(event){if(self._preventClickEvent){self._preventClickEvent=false;event.stopImmediatePropagation();return false}});if($.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);($.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(event){event.originalEvent=event.originalEvent||{};if(event.originalEvent.mouseHandled){return }(this._mouseStarted&&this._mouseUp(event));this._mouseDownEvent=event;var self=this,btnIsLeft=(event.which==1),elIsCancel=(typeof this.options.cancel=="string"?$(event.target).parents().add(event.target).filter(this.options.cancel).length:false);if(!btnIsLeft||elIsCancel||!this._mouseCapture(event)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){self.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(event)&&this._mouseDelayMet(event)){this._mouseStarted=(this._mouseStart(event)!==false);if(!this._mouseStarted){event.preventDefault();return true}}this._mouseMoveDelegate=function(event){return self._mouseMove(event)};this._mouseUpDelegate=function(event){return self._mouseUp(event)};$(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);($.browser.safari||event.preventDefault());event.originalEvent.mouseHandled=true;return true},_mouseMove:function(event){if($.browser.msie&&!event.button){return this._mouseUp(event)}if(this._mouseStarted){this._mouseDrag(event);return event.preventDefault()}if(this._mouseDistanceMet(event)&&this._mouseDelayMet(event)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,event)!==false);(this._mouseStarted?this._mouseDrag(event):this._mouseUp(event))}return !this._mouseStarted},_mouseUp:function(event){$(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(event.target==this._mouseDownEvent.target);this._mouseStop(event)}return false},_mouseDistanceMet:function(event){return(Math.max(Math.abs(this._mouseDownEvent.pageX-event.pageX),Math.abs(this._mouseDownEvent.pageY-event.pageY))>=this.options.distance)},_mouseDelayMet:function(event){return this.mouseDelayMet},_mouseStart:function(event){},_mouseDrag:function(event){},_mouseStop:function(event){},_mouseCapture:function(event){return true}};$.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);(function($){$.fn.bgIframe=$.fn.bgiframe=function(s){if($.browser.msie&&parseInt($.browser.version)<=6){s=$.extend({top:"auto",left:"auto",width:"auto",height:"auto",opacity:true,src:"javascript:false;"},s||{});var prop=function(n){return n&&n.constructor==Number?n+"px":n},html=' +
    +
    + + + +
    +
    + + + + + diff --git a/jscripts/infusion/components/uiOptions/html/UIOptionsPreview.html b/jscripts/infusion/components/uiOptions/html/UIOptionsPreview.html new file mode 100644 index 000000000..b50caec27 --- /dev/null +++ b/jscripts/infusion/components/uiOptions/html/UIOptionsPreview.html @@ -0,0 +1,85 @@ + + + + + UI Options Preview Content + + + + + + + + + + + + +

    Web Pages

    + Mint Leaf +

    A web page or webpage is a resource of information that is suitable for the World Wide Web and can be accessed through a web browser. This information is usually in HTML or XHTML format, and may provide navigation to other web pages via hypertext links.

    +

    Color, typography, illustration and interaction

    +

    Web pages usually include instructions as to the colors of text and backgrounds and very often also contain links to images and sometimes other media to be included in the final view.

    +

    Elements of a webpage

    +
      +
    1. Textual + +
    2. +
    3. Non-textual +
        +
      • Static and Animated imagery
      • +
      • Audio
      • +
      • Video
      • +
      +
    4. +
    5. Interactive + +
    6. +
    7. Hidden
    8. +
    +

    Breakdown of webpage markup

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TagMeaningDeprecated
    <B>
    Make an element as BOLD
    <I>
    Show an element in ITALICS
    <EM>
    EMPHASIZE an element
    <STRONG>
    Use STRONG EMPHASIS for an element
    TagMeaningDeprecated
    + + diff --git a/jscripts/infusion/components/uiOptions/images/500x327_mint_truffle.jpg b/jscripts/infusion/components/uiOptions/images/500x327_mint_truffle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..50e0acdeeb0de593c26a96f4d210d27f43215255 GIT binary patch literal 57689 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 M>FVdQ&MBb@02ftci2wiq literal 0 HcmV?d00001 diff --git a/jscripts/infusion/components/uiOptions/images/expand_collapse.png b/jscripts/infusion/components/uiOptions/images/expand_collapse.png new file mode 100644 index 0000000000000000000000000000000000000000..909a6e4d636e27dbd0b35a2b9828dcdb064cd7df GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^0zj<5!3-pI!a4o{DVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5XR04cLTt9sHz|YTr_3G7=Cr=(b zc1%J-;@h`xH*Vaxbm@|)s3=g?|Ns9F@Zo9JriCu;xFrGGpk=`MVyS^*{B%(5uD6>R({?T_2>N{T&QZOxsdY1S w|15j|(&(G*r@ONc{!jTJzw=+ecLO8Cj2@=xGmmZG2{fF+)78&qol`;+0LO}B0ssI2 literal 0 HcmV?d00001 diff --git a/jscripts/infusion/components/uiOptions/images/h1.png b/jscripts/infusion/components/uiOptions/images/h1.png new file mode 100644 index 0000000000000000000000000000000000000000..a1e35fc56db1edbc5f9b760d880b4d14aaf9b806 GIT binary patch literal 899 zcmV-}1AP36P)U4;TQ};W&)qoB9l`~KJ=F0O|FP^@9 zMoze#S4xl!#*$2Ha-+BXo<;2K>o-d`mQXTaEBpKV{SW<(yPI2&A5bcAA;4s-@muj``CN<bGDtBR zk5DOKJ(J0V3(j&zoo~KJJ(mnwDT9!Z0W^Uqo^USm>UXu`dsI=MGw&rO!w52fCL}{7 zgkU+lLI#8M-E}aIz>SW+EWe`XP&=j~pG5}D?@Q-A097k*2Zih*dpiH0% zgzZpgx*j4Xj0SGW@N~GoxxTl%hcbaCu)4MyjiL)O7#}!yqj7ZW&h4DBUq5~(G4cV} zoC#|iYu)*7G#<}&=8O-;M7?5ySgPVUCK-}A4#RL^aY5pL8NgoXUVnUaOg-P8Ym*qh z?{V{Ubm4^SNGW~a&$J$#41OORo+W2Wsk|JS0e~fxI|3oN=lek`2*Va>Ko&UVtkr6n zG`*ro1DA6@2*?ByqaGXpfE6y3cYe<4)-OG}`+KwkD(4PQ2M@F96z;s7xSFOZX`ppx zW_F{3z%n1G^+|wYT~xp?OyM>cnbt3r$m?U&4%itECJbEnisv&J@Z=uQ5Fgl8Pwx3E ZzyOu6djr{n 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 RAGi2HPD)v_PTVBqKLFR15UBtF literal 0 HcmV?d00001 diff --git a/jscripts/infusion/components/uiOptions/images/mintleaf.png b/jscripts/infusion/components/uiOptions/images/mintleaf.png new file mode 100644 index 0000000000000000000000000000000000000000..2d5e678e7b3616180953750b91a7e79dbb9f7107 GIT binary patch literal 23238 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:1;container.css("line-height",spacing+"em")};var setMinSize=function(container,size){if(size&&size>0){container.css("font-size",size+"pt");replaceClass(container,"[class*=fl-font-size-]",/\bfl-font-size-[0-9]{1,2}\s+/g,"fl-font-size-100")}else{container.css("font-size","")}};var addStyles=function(container,settings,classnameMap){addClassForSetting(container,"textFont",settings.textFont,classnameMap);addClassForSetting(container,"textSpacing",settings.textSpacing,classnameMap);addClassForSetting(container,"theme",settings.theme,classnameMap);addClassForSetting(container,"layout",settings.layout,classnameMap)};var styleElements=function(elements,setting,classname){if(setting){elements.addClass(classname)}else{elements.removeClass(classname)}};var styleLinks=function(container,settings,classnameMap){var links=$("a",container);styleElements(links,settings.linksUnderline,classnameMap.linksUnderline);styleElements(links,settings.linksBold,classnameMap.linksBold);styleElements(links,settings.linksLarger,classnameMap.linksLarger)};var styleInputs=function(container,settings,classnameMap){styleElements($("input",container),settings.inputsLarger,classnameMap.inputsLarger)};var initModel=function(that){if(that.options.savedSettings){that.model=that.options.savedSettings;return }that.model=that.settingsStore.fetch()||fluid.copy(that.defaultSiteSettings)};var clearClashingClasses=function(container,classnameMap){var settingsWhichMayClash=["textFont","textSpacing","theme","layout"];var classesToRemove="fl-noBackgroundImages";var selector=".fl-noBackgroundImages";for(var i=0;i0){startIndex=cookie.indexOf(cookiePrefix);if(startIndex>-1){startIndex=startIndex+cookiePrefix.length;endIndex=cookie.indexOf(";",startIndex);if(endIndex=that.min&&value<=that.max)};that.isValid=function(value){return !(isNaN(parseInt(value,10))||isNaN(value))};that.updateModel=function(model,source){if(that.isInRange(model)){that.events.modelChanged.fire(model,that.model,source);that.model=model;that.locate("thumb").attr("aria-valuenow",that.model)}};return that};fluid.defaults("fluid.textfieldSlider",{selectors:{textfield:".flc-textfieldSlider-field",slider:".flc-textfieldSlider-slider",thumb:".ui-slider-handle"},events:{modelChanged:null},sliderOptions:{orientation:"horizontal"},min:0,max:100,value:null})})(jQuery,fluid_1_1);(function($,fluid){var createSelectNode=function(id,selection,list,names){return{ID:id,selection:{valuebinding:selection},optionlist:{valuebinding:list},optionnames:{valuebinding:names}}};var createSimpleBindingNode=function(id,binding){return{ID:id,valuebinding:binding}};var generateTree=function(that,rendererModel){var children=[];children.push(createSelectNode("text-font","selections.textFont","labelMap.textFont.values","labelMap.textFont.names"));children.push(createSelectNode("text-spacing","selections.textSpacing","labelMap.textSpacing.values","labelMap.textSpacing.names"));children.push(createSelectNode("theme","selections.theme","labelMap.theme.values","labelMap.theme.names"));var bgiExplodeOpts={selectID:"background-images",rowID:"background-images-row:",inputID:"background-images-choice",labelID:"background-images-label"};children.push(createSelectNode("background-images","selections.backgroundImages","labelMap.backgroundImages.values","labelMap.backgroundImages.names"));children=children.concat(fluid.explodeSelectionToInputs(that.options.controlValues.backgroundImages,bgiExplodeOpts));var layoutExplodeOpts={selectID:"layout",rowID:"layout-row:",inputID:"layout-choice",labelID:"layout-label"};children.push(createSelectNode("layout","selections.layout","labelMap.layout.values","labelMap.layout.names"));children=children.concat(fluid.explodeSelectionToInputs(that.options.controlValues.layout,layoutExplodeOpts));var tocExplodeOpts={selectID:"toc",rowID:"toc-row:",inputID:"toc-choice",labelID:"toc-label"};children.push(createSelectNode("toc","selections.toc","labelMap.toc.values","labelMap.toc.names"));children=children.concat(fluid.explodeSelectionToInputs(that.options.controlValues.layout,tocExplodeOpts));children.push(createSimpleBindingNode("links-underline","selections.linksUnderline"));children.push(createSimpleBindingNode("links-bold","selections.linksBold"));children.push(createSimpleBindingNode("links-larger","selections.linksLarger"));children.push(createSimpleBindingNode("inputs-larger","selections.inputsLarger"));return{children:children}};var bindHandlers=function(that){var saveButton=that.locate("save");saveButton.click(that.save);that.locate("reset").click(that.reset);that.locate("cancel").click(that.cancel);var form=fluid.findForm(saveButton);$(form).submit(function(){that.save()})};var initPreview=function(that){var previewFrame=that.locate("previewFrame");var previewEnhancer;that.events.modelChanged.addListener(function(model){setTimeout(function(){if(previewEnhancer){previewEnhancer.updateModel(model)}},0)});previewFrame.load(function(){var previewFrameContents=previewFrame.contents();var options={savedSettings:that.model,tableOfContents:that.uiEnhancer.options.tableOfContents,settingsStore:{type:"fluid.uiEnhancer.tempStore"}};previewEnhancer=fluid.uiEnhancer(previewFrameContents,options)})};var createLabelMap=function(options){var labelMap={};for(var item in options.controlValues){labelMap[item]={names:options.strings[item],values:options.controlValues[item]}}return labelMap};var createRenderOptions=function(that){that.model.toc=String(that.model.toc);that.model.backgroundImages=String(that.model.backgroundImages);var aggregateModel=fluid.assembleModel({selections:{model:that.model,applier:that.applier},labelMap:{model:createLabelMap(that.options)}});return{model:aggregateModel.model,applier:aggregateModel.applier,autoBind:true}};var initSliders=function(that){var createOptions=function(settingName){return{listeners:{modelChanged:function(value){that.applier.requestChange(settingName,value)}},value:that.model[settingName]}};var options=createOptions("textSize");fluid.merge(null,options,that.options.textMinSize.options);fluid.initSubcomponents(that,"textMinSize",[that.options.selectors.textMinSizeCtrl,options]);options=createOptions("lineSpacing");fluid.merge(null,options,that.options.lineSpacing.options);fluid.initSubcomponents(that,"lineSpacing",[that.options.selectors.lineSpacingCtrl,options])};var mergeSiteDefaults=function(options,siteDefaults){for(var settingName in options.controlValues){var setting=String(siteDefaults[settingName]);var settingValues=options.controlValues[settingName];if(setting){var index=$.inArray(setting,settingValues);if(index===-1){var defaultIndex=$.inArray("default",settingValues);if(defaultIndex===-1){settingValues.push(setting)}else{settingValues[defaultIndex]=setting}}}}};var setupUIOptions=function(that){that.applier.modelChanged.addListener("*",function(newModel,oldModel,changeRequest){that.events.modelChanged.fire(newModel,oldModel,changeRequest.source)});mergeSiteDefaults(that.options,that.uiEnhancer.defaultSiteSettings);that.events.afterRender.addListener(function(){initSliders(that);bindHandlers(that);initPreview(that)});var rendererOptions=createRenderOptions(that);var template=fluid.selfRender(that.container,generateTree(that,rendererOptions.model),rendererOptions);that.events.afterRender.fire();return template};fluid.uiOptions=function(container,options){var that=fluid.initView("fluid.uiOptions",container,options);that.uiEnhancer=$(document).data("uiEnhancer");that.model=fluid.copy(that.uiEnhancer.model);that.applier=fluid.makeChangeApplier(that.model);var savedModel=that.uiEnhancer.model;var template;that.save=function(){that.events.onSave.fire(that.model);savedModel=fluid.copy(that.model);that.uiEnhancer.updateModel(savedModel)};that.reset=function(){that.events.onReset.fire();that.updateModel(fluid.copy(that.uiEnhancer.defaultSiteSettings),that);that.refreshView()};that.cancel=function(){that.events.onCancel.fire();that.updateModel(fluid.copy(savedModel),that);that.refreshView()};that.refreshView=function(){var rendererOptions=createRenderOptions(that);fluid.reRender(template,that.container,generateTree(that,rendererOptions.model),rendererOptions);that.events.afterRender.fire()};that.updateModel=function(newModel,source){that.events.modelChanged.fire(newModel,that.model,source);fluid.clear(that.model);fluid.model.copyModel(that.model,newModel)};template=setupUIOptions(that);return that};fluid.defaults("fluid.uiOptions",{textMinSize:{type:"fluid.textfieldSlider",options:{min:6,max:30}},lineSpacing:{type:"fluid.textfieldSlider",options:{min:1,max:10}},selectors:{controls:".flc-uiOptions-control",textMinSizeCtrl:".flc-uiOptions-min-text-size",lineSpacingCtrl:".flc-uiOptions-line-spacing",cancel:".flc-uiOptions-cancel",reset:".flc-uiOptions-reset",save:".flc-uiOptions-save",previewFrame:".flc-uiOptions-preview-frame"},events:{modelChanged:null,onSave:null,onCancel:null,onReset:null,afterRender:null},strings:{textFont:["Serif","Sans-Serif","Arial","Verdana","Courier","Times"],textSpacing:["Regular","Wide","Wider","Widest"],theme:["Low Contrast","Medium Contrast","Medium Contrast Grey Scale","High Contrast","High Contrast Inverted"],backgroundImages:["Yes","No"],layout:["Yes","No"],toc:["Yes","No"]},controlValues:{textFont:["serif","sansSerif","arial","verdana","courier","times"],textSpacing:["default","wide1","wide2","wide3"],theme:["lowContrast","default","mediumContrast","highContrast","highContrastInverted"],backgroundImages:["true","false"],layout:["simple","default"],toc:["true","false"]}})})(jQuery,fluid_1_1); \ No newline at end of file diff --git a/jscripts/infusion/components/undo/js/Undo.js b/jscripts/infusion/components/undo/js/Undo.js new file mode 100644 index 000000000..4a447929a --- /dev/null +++ b/jscripts/infusion/components/undo/js/Undo.js @@ -0,0 +1 @@ +fluid_1_1=fluid_1_1||{};(function($,fluid){var STATE_INITIAL="state_initial",STATE_CHANGED="state_changed",STATE_REVERTED="state_reverted";function defaultRenderer(that,targetContainer){var markup="[undo][redo]";var markupNode=$(markup);targetContainer.append(markupNode);return markupNode}function refreshView(that){if(that.state===STATE_INITIAL){that.locate("undoContainer").hide();that.locate("redoContainer").hide()}else{if(that.state===STATE_CHANGED){that.locate("undoContainer").show();that.locate("redoContainer").hide()}else{if(that.state===STATE_REVERTED){that.locate("undoContainer").hide();that.locate("redoContainer").show()}}}}var bindHandlers=function(that){that.locate("undoControl").click(function(){if(that.state!==STATE_REVERTED){fluid.model.copyModel(that.extremalModel,that.component.model);that.component.updateModel(that.initialModel,that);that.state=STATE_REVERTED;refreshView(that);that.locate("redoControl").focus()}return false});that.locate("redoControl").click(function(){if(that.state!==STATE_CHANGED){that.component.updateModel(that.extremalModel,that);that.state=STATE_CHANGED;refreshView(that);that.locate("undoControl").focus()}return false});return{modelChanged:function(newModel,oldModel,source){if(source!==that){that.state=STATE_CHANGED;fluid.model.copyModel(that.initialModel,oldModel);refreshView(that)}}}};fluid.undoDecorator=function(component,userOptions){var that=fluid.initView("undo",null,userOptions);that.container=that.options.renderer(that,component.container);fluid.initDomBinder(that);fluid.tabindex(that.locate("undoControl"),0);fluid.tabindex(that.locate("redoControl"),0);that.component=component;that.initialModel={};that.extremalModel={};fluid.model.copyModel(that.initialModel,component.model);fluid.model.copyModel(that.extremalModel,component.model);that.state=STATE_INITIAL;refreshView(that);var listeners=bindHandlers(that);that.returnedOptions={listeners:listeners};return that};fluid.defaults("undo",{selectors:{undoContainer:".flc-undo-undoContainer",undoControl:".flc-undo-undoControl",redoContainer:".flc-undo-redoContainer",redoControl:".flc-undo-redoControl"},renderer:defaultRenderer})})(jQuery,fluid_1_1); \ No newline at end of file diff --git a/jscripts/infusion/components/uploader/ReadMe.txt b/jscripts/infusion/components/uploader/ReadMe.txt new file mode 100644 index 000000000..eec74c10a --- /dev/null +++ b/jscripts/infusion/components/uploader/ReadMe.txt @@ -0,0 +1,103 @@ +Infusion Uploader Read Me + +1) Upgrading +2) Known Issues +3) Troubleshooting +4) Running the Uploader with out a Server + +-------------------------------------- + +UPGRADING from previous versions: + +Before upgrading from Infusion 0.5 or earlier, please refer to the Uploader API documentation and the +latest example code. The Fluid Uploader was extensively refactored in the 0.6 release, and a fresh new +API has been introduced. + +Please refer to the Uploader API documentation on the Fluid Wiki and the +Infusion 1.0 example code before using the Uploader with an existing integration. +http://wiki.fluidproject.org/display/fluid/Uploader+API + +-------------------------------------- + +KNOWN ISSUES: + +* To support Flash 10 (released on 9/26/2008), the Uploader required a new version of the SWFUpload + Flash component (2.2.0 beta 3). This new version, still in beta, still has numerous bugs. We have + worked around many of the bugs and inconsistencies in the SWFUpload code, but there are still + significant compromises and issues in this release. For this reason we do not consider this version + of the Uploader to be production-ready. + + In the previous version of the Uploader, the Flash component worked completely "behind the scenes". + To support Flash 10, the Uploader displays a Flash-based "Browse files..." button in place of a + HTML button. The Flash-based button presents the following quirks: + + In Firefox and IE, the Flash-based Browse button does not size correctly when the text/page + is resized or zoomed. + + In Firefox (FF): + - The AIRA role for the Browse button is read correctly as "button" but the text of the button, + "Browse Files", is ignored. + - The Flash-based Browse button traps keyboard navigation, refusing to give up focus without a + mouse click. + + In Internet Explorer (IE): + - AIRA is not supported by Internet Explorer. + + We are exploring work-arounds for most of these issues, and will have a patch out as soon + as possible to fix them. + +* In previous versions of the Uploader the upload process would stop immediately at the moment that + the Stop Upload button was clicked. + + With Infusion 0.8, we wait for the current file to complete or to error before we stop the upload + process. This avoids a serious bug in the SWFUploader where the Upload process could get stuck when + the Upload process as resumed. + + +-------------------------------------- + +TROUBLE SHOOTING: + +* When running the Uploader sample code on a local system without a server, check to make + sure that you have followed the instructions below under "RUNNING THE UPLOADER ON A + LOCAL SYSTEM WITHOUT A SERVER". + +* If you see this error in your console: + [Exception... "'Invalid function name' when calling method: [nsIDOMEventListener::handleEvent]" + nsresult: "0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)" location: "" data: no] + + The flashUrl option is probably wrong. Check that first. + + +-------------------------------------- + +RUNNING THE UPLOADER ON A LOCAL SYSTEM WITHOUT A SERVER + +Running the Uploader locally without a server is intended for basic testing purposes only. The +DemoUploadManager provides a simulated conversation with the server, but it doesn't represent a +fully accurate picture of the component's behaviour when used in a real deployment environment. + +So see the Uploader in action with a real server, have a look at Fluid's Image Gallery demo: + +http://build.fluidproject.org:8080/sakai-imagegallery2-web/site/AddImages/ + + +Additionally, you may need to modify some of your Flash settings to allow the local SWFUpload +object to access your file system. To do so, follow these directions: + +1. Open your browser +2. Browse to: + http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html +3. In the Flash Settings panel, click "Edit locations..." +4. Select "Add location..." +5. Click "Browse for folder..." +6. Select the local /src/webapp/lib/swfupload/flash/ directory that contains the swfupload.swf file +7. Restart your browser + +You should be good to go! + +However, if you move your installation, you'll need to do this all over again. There are settings +that will allow the file to be run from any location on your local machine but these instructions +are the minimum settings and therefor pose the least security risk. + +These settings are global and do not need to repeated for every browser on a given system. diff --git a/jscripts/infusion/components/uploader/css/Uploader.css b/jscripts/infusion/components/uploader/css/Uploader.css new file mode 100644 index 000000000..d66650a08 --- /dev/null +++ b/jscripts/infusion/components/uploader/css/Uploader.css @@ -0,0 +1,76 @@ +.fl-uploader{width:500px;position:relative;display:block;clear:both;padding:1em 2em;} +.fl-uploader-manually-degrade,.fl-uploader-manually-enhance{display:block;width:434px;clear:both;float:none;text-align:center;padding:1em;} +.fl-ProgEnhance-basic div input{margin-top:.6em;} +.fl-ProgEnhance-enhanced{display:none;} +.fl-progEnhance-basic div input{margin-top:.6em;} +.fl-progEnhance-enhanced{display:none;} +.fl-uploader td,.fl-uploader th{border:none;} +.fl-uploader table{border-collapse:separate;margin:0;} +.fl-uploader form{height:auto!important;} +.fl-uploader button{background-color:#4F99D3;border-color:#2A3990;border-width:1px;border-style:solid;font-weight:bolder;font-size:1.06em;color:#FFF;height:32px;padding-left:2em;padding-right:2em;margin-right:1em;outline:none;cursor:pointer;} +.fl-uploader button:focus,.fl-uploader button.focus{outline:2px solid #142B8C;} +.fl-uploader button:hover{background-color:#115F8F;} +.fl-uploader button.fl-uploader-button-default{background-color:#74B74A;border-color:#006838;} +.fl-uploader button.fl-uploader-button-default:hover{background-color:#519325;border-color:#142B8C;} +.fl-uploader button.fl-uploader-button-cancel{background-color:#D03E4F;border-color:#BF1E2D;} +.fl-uploader button.fl-uploader-button-cancel:hover{background-color:#9E182B;border-color:#142B8C;} +.fl-uploader button.fl-uploader-dim{background-color:#999;border-color:#666;} +.fl-uploader button.fl-uploader-dim:hover{background-color:#999;border-color:#666;} +.fl-uploader button span.text-description{display:none;} +.fl-uploader a.fl-uploader-browse{background-image:url(../images/add.png);background-attachment:scroll;background-repeat:no-repeat;background-position:5px center;padding:4px 7px 4px 26px;border:none;white-space:nowrap;color:#427ABE;font-weight:normal;cursor:pointer;} +html>body .fl-uploader a.fl-uploader-browse{padding:3px 6px 3px 25px;border:1px solid transparent;} +.fl-uploader a.fl-uploader-browse:hover{border:1px solid #CCC;background-color:#FFF;} +.fl-uploader a.fl-uploader-browse:focus,.fl-uploader a.fl-uploader-browse.focus{outline:2px solid #142B8C;background-color:#FFF;} +.fl-uploader .fl-uploader-browse-overlay{position:absolute;z-index:1;} +.fl-uploader-queue-wrapper caption{display:none;} +.fl-uploader-queue-header{background-color:transparent;color:#666;} +.fl-uploader-queue-header th{text-align:left;font-size:.94em;font-weight:normal;padding:6px;} +.fl-uploader-queue-footer{position:relative;background-color:#E6E6E6;border:1px solid #999;} +.fl-uploader-queue-footer table{width:100%;position:relative;z-index:6;} +.fl-uploader-queue-footer td{padding:8px 6px;vertical-align:middle;font-size:1.07em;font-weight:bolder;color:#666;} +.fl-uploader-footer-buttons{text-align:right;} +.fl-scroller{display:block;position:relative;background-color:white;overflow:auto;overflow-x:hidden;overflow-y:auto;border-width:1px 1px 0;border-style:solid;border-color:#999;} +.fl-scroller .fl-scroller-inner{position:relative;overflow:hidden;} +.fl-uploader-queue{position:relative;font-size:.9em;color:#333;z-index:6;} +html>body .fl-uploader-queue{width:100%;} +.fl-uploader-queue:focus{border:1px solid #142B8C;} +.fl-uploader-queue tr:focus,.fl-uploader-queue tr.fl-uploader-file-focus{background-color:#FFF9DC;} +.fl-uploader-queue tr{background-color:transparent;} +.fl-uploader-queue tr.fl-uploader-file-state-ready:hover,.fl-uploader-queue tr.fl-uploader-file-hover{background-color:#FFF9DC;} +.fl-uploader-queue tr.fl-uploader-dim{color:#666;} +.fl-uploader-queue td,.fl-uploader-queue th{text-align:left;padding:3px 6px;border-bottom:1px solid #BBCFDC;} +.fl-uploader-queue-wrapper .fl-uploader-file-name{width:322px;overflow:hidden;} +.fl-uploader-queue-wrapper .fl-uploader-file-size{text-align:right;width:4em;white-space:nowrap;} +.fl-uploader-queue .fl-uploader-file-action{border:1px solid transparent;height:20px;width:20px;overflow:hidden;background-color:transparent;background-attachment:scroll;background-repeat:no-repeat;background-position:1px 1px;outline:0;padding:0;margin:0;outline:none;} +.fl-uploader-queue .fl-uploader-file-action:hover{border:1px solid transparent;background-color:transparent;cursor:auto;} +.fl-uploader-queue .fl-uploader-file-actions .fl-uploader-dim{border:1px solid transparent;background-color:transparent;cursor:auto;} +.fl-uploader-queue .fl-uploader-file-action-remove{background-image:url(../images/remove.png);cursor:pointer!important;} +.fl-uploader-queue .fl-uploader-file-action-remove:hover,.fl-uploader-queue tr.fl-uploader-file-state-ready:focus .fl-uploader-file-action-remove,.fl-uploader-queue tr.focus .fl-uploader-file-action-remove{border:1px solid #CCC;background-color:#FFF;} +.fl-uploader-queue .fl-uploader-file-action-remove:focus,.fl-uploader-queue .fl-uploader-file-action-remove.focus{background-color:#4F99D3;outline:none;} +.fl-uploader-queue .fl-uploader-file-state-uploaded th,.fl-uploader-queue tr.fl-uploader-file-state-uploaded th,.fl-uploader-queue tr.focus th{border-left:4px solid #74B74A;color:#5E7A5E;} +.fl-uploader-queue .fl-uploader-file-state-uploaded td{color:#5E7A5E;} +.fl-uploader-queue .fl-uploader-file-state-uploaded .fl-uploader-file-action{background-image:url(../images/tick.png);} +.fl-uploader-queue .fl-uploader-file-state-error th{border-left:4px solid #F5E392;background-color:#FCFBE6;border-bottom-width:0;} +.fl-uploader-queue .fl-uploader-file-state-error td{background-color:#FCFBE6;border-bottom-width:0;} +.fl-uploader-queue .fl-uploader-file-error td{background-color:#FCFBE6;border-left:4px solid #F5E392;background-image:url(../images/error.png);background-position:8px 2px;background-repeat:no-repeat;padding-bottom:8px;padding-left:32px;} +.fl-uploader-queue-wrapper td.fl-uploader-file-actions,.fl-uploader-queue-wrapper th.fl-uploader-file-actions{white-space:nowrap;text-align:center;} +.fl-uploader-queue td.fileStatus{white-space:nowrap;} +.fl-uploader-queue-wrapper .fl-uploader-browse-instructions{background-color:#F0F9FF;color:#666;text-align:center;border-left:1px solid #999;border-right:1px solid #999;padding:6px;} +.fl-uploader-btns{margin-top:1em;} +.fl-uploader-btns .fl-uploader-upload,.fl-uploader-btns .fl-uploader-resume,.fl-uploader-btns .fl-uploader-pause{width:8.4em;} +.fl-uploader-btns .fl-uploader-done,.fl-uploader-btns .fl-uploader-cancel{width:7.6em;} +.fl-uploader-btns button{float:right;padding:0;} +button span.fl-uploader-button-text-hidden{display:none;} +.fl-uploader-disabled,.fl-uploader-dim{cursor:auto;} +.fl-uploader-disabled{background-color:#CCC!important;border-color:#999!important;} +.fl-uploader-dim{opacity:.4;filter:alpha(opacity=40);} +.fl-uploader-hidden{display:none;} +.fl-uploader-hidden-templates{display:none;} +.fl-uploader-file-progress,.fl-uploader-total-progress-okay,.fl-uploader-total-progress-errored{position:absolute;display:none;background-attachment:scroll;background-position:bottom left;background-repeat:repeat-x;overflow:hidden;left:0;z-index:4;} +.fl-uploader-file-progress{background-image:url('../images/gradient-file-green.png');background-color:#D1D6DD;border-right:1px solid #9FCE7F;width:76%;} +.fl-uploader-file-progress .fl-uploader-file-progress-text{display:none;} +.fl-uploader-total-progress-okay,.fl-uploader-total-progress-errored{top:0;height:100%;width:76%;} +.fl-uploader-total-progress-okay{background-image:url('../images/gradient-total-green.png');border-right:1px solid #9FCE7F;background-color:#BDBFC1;} +.fl-uploader-total-progress-errored{background-image:url('../images/gradient-total-yellow.png');border-right:1px solid #E8C81C;background-color:#F6ECA0;} +.fl-uploader-flash9-container{overflow:hidden;width:1px;height:1px;} +.hideUploaderForOpera{visibility:hidden;overflow:hidden;height:0;width:0;padding:0;} \ No newline at end of file diff --git a/jscripts/infusion/components/uploader/html/Uploader.html b/jscripts/infusion/components/uploader/html/Uploader.html new file mode 100644 index 000000000..af70ce2f1 --- /dev/null +++ b/jscripts/infusion/components/uploader/html/Uploader.html @@ -0,0 +1,127 @@ + + + + + Uploader Template + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    +

    Browse to upload a file.

    + +
    +
    + + +
    + + +
    + + +
    + + + + + + + + + +
    File Upload Queue:
    File NameSize   
    +
    + + +
    +
    + + + + + + + + + + + + + + + + +
    File Name Placeholder0 KB + +
    +
    76%
    +
    +
    + +
    + Choose Browse files to add files to the queue +
    + + + +
    + + +
    + +
    +
    + +
    + + + + diff --git a/jscripts/infusion/components/uploader/images/add.png b/jscripts/infusion/components/uploader/images/add.png new file mode 100644 index 0000000000000000000000000000000000000000..6332fefea4be19eeadf211b0b202b272e8564898 GIT binary patch literal 733 zcmV<30wVp1P)9VHk(~TedF+gQSL8D5xnVSSWAVY>J9b+m>@{iq7_KE}go~11+5s4;8hc+i0Xa zI1j@EX5!S+Me6HNqKzU5YQwL;-W5$p%ZMKMeR<%zp69-~?<4?8|C8S?bklXr4v&Ov zb&06v2|-x?qB`90yn>Qi%Sh2^G4n)$ZdyvTPf9}1)_buUT7>`e2G&2VU@~Bb(o+Mz zi4)>IxlSY${Dj4k={-9RzU^W5g9|2V5RZ2ZulL9s2xQbZ@r6eP9Ra5u(s|C0Nj#&4>wTSkb?%#=9?@ z^oxDy-O@tyN{L@by(WWvQ3%CyEu8x{+#Jb4-h&K9Owi)2pgg+heWDyked|3R$$kL@A z#sp1v-r+=G4B8D6DqsDH0@7OztA7aT9qc1Py{()w`m``?Y0&gi2=ROcc-9+nU^I6< zT=e_Y=vSnG@?3Ue{BW5ONFttcE!R-R_W4O01|0-|K-YNXLo2`4Qv z`r1LxR6#yf3FB%T95gJnaKKivA~Z}S9A(ZxEDK}O3T04USJ P00000NkvXXu0mjf^IS-S literal 0 HcmV?d00001 diff --git a/jscripts/infusion/components/uploader/images/browse.png b/jscripts/infusion/components/uploader/images/browse.png new file mode 100644 index 0000000000000000000000000000000000000000..dd3927ca964a3a70e9eae11ab4061f73b86b44d2 GIT binary patch literal 5993 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 A&j0`b literal 0 HcmV?d00001 diff --git a/jscripts/infusion/components/uploader/images/gradient-file-green.png b/jscripts/infusion/components/uploader/images/gradient-file-green.png new file mode 100644 index 0000000000000000000000000000000000000000..95374c80dd08c77b9eb1339ec991868a86352805 GIT binary patch literal 1262 zcmcIk&1(}u6yKN%F<4N+i&Py}4<5{Zb(=QBCZ*l9Hqyk>1dN_K-JPUMvLEbD$+jo) zrdJPEJV-rwkW!?eLW?MZAP6EV3N2PEDm^Or2RNIgNn1ou9hk4j@AuxD-#p&6vD~@N z=&>lnFrAr^^f=u^be`x4)Ai(v@tba8Z!lAcL?UY&7rxNz>9Ntm(EH7mcbm&^emr{p zed*P=#g||2zu36<{PW$X>vx`fy1l%1^YQA9qYGE&DgJ;na>-+u=+9t=1{dTxhUqv= zhKi(^9fg{0a!9u)G3T2OqxAa@(khsMNnAFpBntpwfuSeai#=H(>!^6j7@2i(el}Oo zW-D4kXZur8#fOw^VuFBgRxJDJ2e$k{oO_D4d1Zs`u z5a4ixIKif^An*jpq*=F0R>b#RF=-TM3vuK<%h) zK29ToQ&f+>m=KSNLP3l}xd#e;m$&g7pk4H=tsAA8-E>KSf((UtlfDhqq$_+N#5Q5- zst0e{W&fXGy4KRmnRcu7Z_za#SFwpL;!(}Soto(yEZMG!NXjr#8S{=+R`|B@pZPVi zre#6Pl5f@JR_d*<+>=9F_dRjnaJrX4uJ2!ZOFKqa&>Zr1iPVsbnZH!*+=t!@ literal 0 HcmV?d00001 diff --git a/jscripts/infusion/components/uploader/images/gradient-file-grey.png b/jscripts/infusion/components/uploader/images/gradient-file-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..7da71fe54326865b3be645b185056b0ce46f6537 GIT binary patch literal 47113 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 W77TrLuebd`NUXN1-ZN0q()$zOI;DO9 literal 0 HcmV?d00001 diff --git a/jscripts/infusion/components/uploader/images/gradient-total-grey.png b/jscripts/infusion/components/uploader/images/gradient-total-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..0246ecaf3851b4cc6ce523058c815c09211cdec3 GIT binary patch literal 49062 zcmcG$1$5lblIZQk98(N2Gcz+Lu^ltS%*@P8iDPDFkC~a-j+vR6nb~7|<~jd+_uhT` z?OFKtZ6B%C>Zz)(E@`AQ^HV8AURDh01MUY12nZwzaS?^L?Yp;?9}ecNF1(rqzHOi! zze*^1HA&{!7HkQh@YtKB>vb6ARlq7!$KIvM?Ajv#}Gib274WvGQzLvR)#kT21j=rCw(^t8%MIg8Tl7GBF2t}4(4`F z=C(G(f9&cT*g882kdpox=pW_plhe2R$3QlYe-GYU7@6Gk?U+~?nVJ4!`Aw1kt&>;S z!C2qP)Km>C)Jn%Fv6>pKaWTkD$|Guhdg@-zLj^FQ?OZ(hHh7H_8| z(?8D3e{uaEhw^`=@z1#bubBUv(f`dsSe$KcARyio**U1Fx}~2by>sv<{wVspXk}-g zw^wf|EDRa#R*7%BHatXzD0#AQJm*mq4@_uf!(T2+!tby zmpuJgq}79x;t>78Gn4@g$DMubN#-&2Yg?jtR?FR4{TiH7jm3!g&sWG9{g4W#aDQS z3XNHoc1bXRt|}J7!6x7Xn@7>-&)T3Df@n(LDUe)Dr_n=cJE$iyGh2DV_s6^jI3G9+ z_66VXV^-v?Grl7c3q}7S07U~r!&13=in_VwE1mRWeK!Oos((ZPfWv8hK{o(OU(lMI zm0*(JXb6n2M?7chHkkFHeC!{=eQS_?goAqj<1y+A={G{K!l&udw;KOKPrt3<7EF&dfB$;N%*=hZOzE0hI3flqAQd0~R zk?8mMx^e`rFHf;pOdrV&WbjwWMkHA^UM>ntfbX-WAl?OIQ3Pf*EtzSxbe4chT3Yb9 z-PcaZxhPS4; zTg=mLraFk$7S~ZC`G}ql-0UwP&QV2cX+Rk$QBjq)9>R~NwyrNn{fn%W-^|)r0{chZ zP&ygIU2k${(5c-aC@!*lSv!KJ5q7w^chG z_{jSLz5v;4Sfza;g=xM5|Mr9U(~N7pQxs|;+!+af!aW!=N!g_@zHo5$2kk6$__eK3 z-uKbyE<%a;X9f&%)hhdKz9!nMZqbWwX`mG0AqeE<=tX*a=9Hwb`y0-K*nItr`0YPm z9xhJ^@_t;wOBS5;jer-hWi<&{Y(h*^f5%yXnZ_)wZ-}Xf z?tzFmP!S%v$LjVTMy_k~3Y(wMe<1C-hiHo?uKD7|$qlBc{yle>Z#?8B4y@h8BgF9C zJ3Xkm_xVzpDNyJ((q_x$r0o7^X0)j)zY229e>-?meOa`zB)$z2P}#0!hd*k>pC-H; zKp@-o6WsCxLYP3cG#Q3(E|_w2YA;&Wt7*ivuKJw(;&`@}uix0$uN!0G{k4;nXEPuV z^ih6o_(@RY7txGsm1$JfoDZU3Qm`S+K9fc%Ef=uZD9I7RJJ0%~4@u*Xk75Y(g~cIvmLFeH%!kVA zqUH>k>#?QZ`P1kz9|;uEOKYMPpnU$OYRP^bz^HgQZ$ujR#gNrZ8YW^Wiv@?>M8Yi7 zf}=?8-s=6i35(&zT&4+Ad*P8iE~Kt*SMq*TXq+N?p>^a8V;u;s%o7Fl8s0>0*uk=S zi|5odVKu1`9Fq$j66*M+rUjqUQ-S?5mqceR8!#_ggQLXhKWr@Dj7Xal@j~C4MhMhb zG+TWSTjKY`09%`WB}^gSwdgXkNgY4tNSDRko}Z=;BYa4j&+IkIvI-f#{vB?g3diQL z9V0S>267q!C$g4;N5dp0*LZ;)qCpj=y95HBjpyey{L8Ce8`H1BV@9mpy_S>G--mdS zQ&O9?bPT)%s2ek$sG_+zZR`P2?e<>O9bdnct(Wtzb^1*+UY22)*o5hu$Y4y0D9sub z1&Ho`f;O%TZDRPLnZ~EL*BkH*796-9HID#$)viA z+Om&E?X*DBlGLNV*rzP?4_xF@?)7d!DkMBdD=c#@a(3jk(AcUW*^#A4J$hJ;PXRsS z#|T)+o(wm8y-^rdw}!o*Uz~#<#ObPav7+XcgTHID-;4ZiLRV3hNfNt;jI3sJR(~w7y zDjrvO?y;5Q{EVLI9{ovaF6urELYGPx-C$7hjo}x7Ttb7EF9&9 zHSO~Yy^2Ox@1bRpyEVhb-8`0Y| zIpMJ{dVC6bdiZHeGFHyGEE4W2n7Q-Qs14r!0qvTC60Z!5c$Hwe>X=x^n5msP<$^v9 z>BTQZcS!Xi3FFafHzS0T>g zGc%r6n`{Qa<0x*98~)?!Pv$gFsIWkaqm%Ff4znMo<){T`=7veTQH;}O;a^qo^>|6` zLOM>%VUi*{i|1Nu#IswkiCY-=2amS9*bhJgH>%ly`Qi6oH#Ygz5hk0 zIK6rWnc;46nU}#>&~jQVdJ4t4=Ibwk zZ>w#-%sxZdVDLK=q+rdk2;b84HcRL`BmC4$V>&42AGY=ODExw(1F)yuJkQ39Xm51M#8V2SzB2=+h=@B2FI6lJMFquD4Bg(4KqgQ&A#1qw2r9 z1sfh8G9wAJ2QBTri)93q9yS=N;D=1wGMNaYei&E(=7Q|kP9^-B(jVI$0_F?4hUPgX zDZhLX@myf057!KFG3&-!IKrC9G4GML`c`wA9$m|wj8>44^fG2e-B-?Tv;A##D-4%} zVs6h7hrh1-HVG2o5H~1G!SBs{blpvaK6+zeLcI{WR&}FRWv~*JT#u!B%j+o1~&;Z(^38IUS}&M6!G*`<6z&+2>N9 zpwYC-_e*3vhfkj=!3<719eM@WO?wQmOr-L1tt}WmQ1F|lv@xyxo5{IHrWyG)lCCfV zm4nC(%VZ6R`IZS$^tj$ge| z*Rr4<1NLd01JXBdUSoCx;~;wXfFZAF{E_MpS4f@MzTZpY>3Jp?id8Anl;L9`)pJed z0rDMw2E(v?sv!?FUa>F&yKFgwh1pkZPPSJqp}&74K!BX0)?VOzh!C$B=ppZ8KgEGY z(M1!f4SeNYU@NOf+g`oPQX4;tKAJ{rRe!vqP{+bpd6hm>-Q(OoG*+fjSCF$oj!T;v zEDv2JbZm}*NcpG{HWdfJI1)%?oz8I7*-rOBLrNC$|CXan4*YyCg4mW57UPtdDPE{B^L}Rt9Br2=vU>D>a&~EsRAbNn}*pHvFn5vd0ZmBLtouKqS z787@zRfXa21DZ%{)PB=&hFR%xyA}aHqU5M*iV;TNj)*TbpnlmO{I!qQ5)1$HVIi2_J%11_=TdZ;ALq#>C=?y^z)zp!at<2Qfr55ZIXZ7wPM?3q= z5;jB5)FD8O=`7PU%_>+bN#dP5-ErlAlxzCBNxOnl2fQ5S-tpx1)v(Gxt-z^kaS!C0 zy_?lsHB}_`mVk`ZG)KL$LpGE8nAzI(3+u`$2fT|$tCKHmr;M^x2a+fc^}+hk3!S zICY29QASj99J!Q9WM0*q*g)~LUs=P}PS#zA+urvlYUC<(^kh_k3gQjuwxoUXzV{h_ zMbgr?ZXsy2rXiyiJAN`c4)`FSNpBr=KRO+CWjf`ACR>}cD*cI*0U4m9#awIA0Ordo$}Xoyv_)Cs)3*#4TSofBT~`1rrJp)!o>0?`;^?pPkSCpt~^;F-s*6 znK5@xeZP3Gz~e)zjvMWMpNYdaX#U41&}3)F(Z$)0(8Q493K!`zNf@dh*$(=KuIum4 z%*hSUcc$KBxsa2Cz4a)H3z?HrZjs@Fo%>*1u5Uq-D}m6yGCiCfK^r9azjfWLBz1Fc zHq~GyrGYCsY{z3sX%G^1>;`P+&2`~@~y1v*N&atxcTEg z@!Xp9IxR-o8tyU!W;6?@J=*$OB236x5f{ zGL;KJDF5)bn>ni9%KhV;q%^3yuOgebSh0bj%3cX&`rBS&=1-Wj`vg}n^kf0IDROA0 znUguAbfB>igdB4FgnX^O``J7vUP>OHIqw}X1PFB@-6@Q4=G(sRQxT-+Z+6F5Of6eV z5bw7Md%9oH(04l3nGP1#{g-n_9%y>t1Y(yz-4OxUFK8Qm1<_Y-A5)T=aZ7cY zDJqqo>pk|tkmNprKos#0Y`E;&4(92<+17uY*I+@uzUO`V;qgL&FU7jTlkPiwZ+KnE zFU@=|`_nNbhpy@-(db~}mjBcdNtg?<-6GJ23vrFvsb})j%fY*qoaHLVo^d);(*!zP z7n{Xq{R2|)li0DRP&ZpDoLJ9fsi|3^{$XG?USLJnq~K~=r*YArUDfeP+6B(P+2IZJ ziN4E5YTY^v%vBwqvgw&Z663@EyopaD2`A|vGTFa1r(A#J{?Yv7R^VSP(+#9? zn>PdLZw`$vd2=J&36aPd??*qJa@0CPc+GQYJ@N%SyG!XG7?dkJSCp5rx<$8mOtl>E zHlMyWv!uCwz#^ZRJHgg~YRzI$S8>`5o!6+7F`JZ?3@Wi$L9ejB8tK}|8wXC;G0E?? z^Pf4=S#EUVWC&HA3+%Tf)$x`10?SF}tD%2&0lYt@m1sV76nUHzEubr}RGZ7ysZ&h3 z<@nmj-F_b}gz1g zYdH}hlYcy?GxJP6>)YxwA)+X|Ku8 zvFr*Zq0sV~201JIP`1do2DAcwTCOD5e@(O6N3Pqy>mY*DAZ~IJEyF=RooJZTe2ujB z3P}vsS#CZZq)|H8*)Nef8paGK4lsUI(j<#c)25CRC&F&j^wX1?(L9T|SJC#IS$z#5 zcb3SlFI0Q=9gj2?CvgvQjUaQ`6JOR|D%zWGz3y;Ds>jCO87gt1(h>%7cnpq+l2!22Gx%eC<@|56IJI>d_ru< zN0xP92suPURzS$!C6AeYqG4y@w9ks`xKEO~uSS9G(F-dkmZ$Z7rg4RrWK7g?=5J1| ztH|+$DHu_?+pFfWCm7FP;~e$&JjJnQJeb3?=LJ9tLUcTiS83;$qPvy8$mxQk~|h<%pZiQ!O|=j8F_MU zmKO`L>y@4dZ5wTzT!eNz@BXSb1W+EoctJ_x;B{y^?s`b-1WUzCMw`gI*EpI~CS5q3 zgDRf;$X+XbLThTdF*ZL2SPg3N%1bdTTe?t4s-Z(|K&w4Fgq53FPK|vM zXren0O`*u+C${}rp001ncC5A?tdf^Z4BtyoZXFy+Yc)>w7 zWQcrx;RqTtUew4_>nJL;dO`bIY)8fB{mww03CaT8W|5~@{5t7}$R<8^)q~ja6(`?W z_oRI>4I{pf+VQdo!!7KB-y}qSc{O3KhG}+?s*|4sRZ9xpIxYnm>%YfWLS)yKKQTTW zgMY9yujiM|sTS=ZuS(e7gU5g`)M+mG=&hC zYp)pDpTS2J&{r-uzh%2yPBkHG(Q$|h*t1CO{2Jy%tV<2H^%QOkb3jL3SdnUjM!Olb zG=n6yz6O$ZsC4BMVxwMY^j0!x7zdT6*{>c<;rvd0x!F#v%yb2yw)R(_lV6mS9<_Wp z0Wmx*zbLzexw)=9PIU=fchY}BXz_x@8*XPX9OkTMI=4xS#>~pBGpFYwK?*i=GM&Lj z_MUJy`{GloTKP_H3;+JKMCBpnez*nGRrBJ<>5D2u|3i`OMDWy7(F7TzcqCA3IxKoV z-I*HKAdYgArEdNt#!IJk;djr&lShS=#vEAZ+*_8YJknR(rA{hXmK$VcD(*77wp7%o zywbum+c+a;yXU$M6i)-$Ue!Bj8+6O|@1WAraRvDLlyXspP{~g`)IkO(oDM1vJ2QOu zz~5ZovhT`is9Y!X7E__B_>C9(%{NEjYUI*9?7XtpgQ4&EwS5yo|;)L%k0pMP8ex>%{5%vtT@|2jb%YF#BK`nCn+gMKXq z1qRs;^&E4!{s>ZTw^VD=-x4B1@XS|tBWP8>QG3Mu{6ye>r+5f3IMBOR%b0Q%`RfyZbmX5*b((6wFeW+cstt_IUg7v$QHBIx+f! zZCX8WgrXYiz9BfONBvkUXT|F7IQlH*!LT*15Xqo3{DWi zf5LplES>4ioFl0|_*8wV>EC{YY<q9`!tB6BHi8xt6mT_%qfO#ONBr$* z((31sEBQ*gZ;Sj-Hk6a43k~Q#BkvB%azylXJV1LI;E}ld$vu?=mjcW}aGL>FgsC?#!2P|C<5#0kByt0wC_5o^AN((L{bH|S zX|n9jWMB1OEUMD+#vXK$Ds5q?46VA?S(H885Sux>5DF1Kuhfo@#$7frGdUvf8;=LJ z(K%wiZF}1WWoVcj6-P(3g56Wn^mO*$kB+1Vxu=}GHJ6ie>#k8_~lPQGiSM9M6Xj*pxvY2=99~a*uZQ$&a6x8P`QB3ayka0_~u# zAy@S7D*Gnc2)aRYG4B^ro!=q7_bBJvC z%V1s%*>4>RjuoACe~(kSUb-BChH2bv!7LX%>gEYvf$#Em8r}3kS|qyYfVubb@q--~ z%Yz-$t5ajQ5Kdis{*wuYBGmAc^Aji+k6Ai1Afn+v=B^9gLl|}dQi$*w+b<|aE zQqDjM)*?3y&V_-n(LfF{Uc0AqZIzqN`NGv-h;<0 znaBJPJge{p6f*2hW~Ut0)9kCK*%8T6jGo8e0VT6)HWw*THW#s<2PA7_yQ0e^tj=k} zfxy1jZU5=HAwXdF*IE>XSgYEIV%aCBP&R~FL^M3x86ih%95u74XUH@PWXcsvNyneHKLfQ<|#D-JuQf4|XW zG9vC9nN0k(U##K}G@4Lj21PB-oaR5U3&eaBk2V z3Z{R28q;Gs)0jQNXAoCPM~I!#IQEXiv1OfCSc1ja*J?Gx#pog#IENe z-|kczCt7FFE}da$iZu+Mp|#*$RXgiP6=?C~Ayk>(?e}R90Uy=Z_6+K@T)jE4y?a=? z-TnR(3c2|HC;5MiLY_{s6W))Y^u-t>=j_EtvZ@tmwp%tx!;tW>2J0(f+-o8SOPmr) zo-VooqA~r8>CY(2q>U5*(`_ME7p97Z^wzVn=uJGsy%HlLy^1#w|@MP11?Jo|g$Xj6WXoyB5faje)r5{L*wZmP2GdB6y9J-(Iz9!u;S1dLe z)$_vW$V03>=}-=WN78}9aDq2@J!vPZ>(Tv-NS~pd3r_m(D#a$dZD|staNM0vxxqF) zo#t9Hidh)!v0hd$DV=sj8vYC?Z2j4OabfxihTgXyiMX(JAKy`F^kYXW6yP8~ttUMS z95&fWaQzn;q?e@tNFjT8s+X2v#bRt2O6%6eT~s~>-sCFrRJ;^B*R!`J>_QF`z0mGl7z-s%|?5U(65aTO>8@9-B1a)tR6cUP^@gYSBg9{icGm%Sb z>j`P#M!Hrc!kg*C0Cm{c%P(souIyQ1evTN9BxRM@faq}6{%%^9lAc_Qz=O*rLbgUD z6qg~wkC{zSFlYW@G!efs4Ib%#Y{4!wRSSCxu1{4oltoZ~W51OY4wbe1l0}J#UBn5? zQTIvJV>oDd)LM6Q7UC1Lnm}affFIJ6LwPhLo<|8%KM~m#S?i@4xo`0IO}pMT;?>Qr z{s5nOHYNpAfbEF4+)AjV8UgAys=g0h@*3ypgmO!Mky9#Sw4S#{WondH+QjCxbVZMX z$T~dE^&93w8_(+|Q_$YxLi4*fQ1+p|()0)N&L|w0ig@JRW5NtMybQ#`u`=itH?43@hI}j45RyE=5z91F$=O{eV0{|DjbUkOngE>(XGgn%8D1hWltg^C@3O6(hc@F`Uh1B|D!`Q?oFC)Kmrt#|q4rRSB*d z2u^!I>1V>(nOGl}uwAAWsZw@bum9=6iY)V3^XYwZSL1RMEFsb54x~$Zx*AKXt#gx( z3wG`YN(xQq!J-EIg0Don=$U=M%ndVZyhW3xD;o9e^%fFWjl89@zpmaqscOE< zp3RPD(N9y*cuA~V65(<1Ttb4l=Q^jQ!uYs&sy=w6Jf^?qv{|A8#tc3Hn?F9^n>n?R=8C7b) znFkzDfwtli{M*-^2RTQWl0|0%vlqjSmUu`^?U^a0>6o6E*YX$@ZC(XI>h=VLQ?~zXHl3N-|)sqH7Kp;HPT^DL{h30{ouQ?xD@yKt_x8Et8 zEs$@*75$3IHWOQ%D$C4Fh z`toY2c8@zUy0S4);*RkBmoLoFCj?a8Sm!U*YZJ3R$~GakThtCxiL-HOJ-hqt4yB}x z16np@pvcen%2jo@gi<(1<2+3EvH~G6(m7wqrfAW;i9JbSbf)^mc}KpJQqv8OBMcu) zj2p)~;tkCmg>9c5pL$;7GvzZKPrT`kmoLuM;Krz+nxASy-?~(~?YZ_+o%UYcr0?AI z&KYc5a7>qwm6Pzq=o>sc^#+Y@Ke~@!c}yQOn>*v0&&AC9dCPy@Cdor1`U^X15XL1g zh?j=e5!#!d-3XKrg>1x*50Nnvt>GLXOcYU#Fvb}-#u$H6Uq7fhoth4obGYV>ZS7_> zu2hVgk5e4n#o%-(AuVL!Y3k{C`+1m)Jh+4A{FyLmHf||?$ZC18EK=Fiyb8;@^uIw7 zQ9G>K?5!%?(Qgczn5`HY45z2#w?Sf2>&ySjv|x( z2HME`|BNAG0wfHG=zzv7B*h#*2{Y(NoerD2U7}Y2PXjOt0qguz8II+2V%hk3YjZ8O zlJJC%6kW0K$j0WP-x>7+7;)|7sYd~9v$0C zl4RNibSXCwncZzA*!Mp;f$A0|m$cc;oNo|qi%yCgndP4x9}$uiTPcB zz%y+hiGYVFl-eYNHcJR=%wOX)uq?MpU(tuZE0sFf7^OU0eaI>d#T&Y1-jZgV{=D_! zM4;6Ri?^Gp(VSUpPM8zvQjkHRaL>99r0a4g+Tm}_tKa`Au74rbiEo~K;!%T*>u37< zV}rlCnTC5mm#3jr`FEO)Em>7xMak*}J4&JHqHWe}n2HL{waAr*c+_~ZJ1^F|uh7(T zi4DS@CPxF0wPv9J)6rlDHs!@SZZ8k}vwBkGat+rE#%P61jaE+3nBs|q<+gMgf4zu;3J)=rAU@`bp>}goRj4A|UD)pfZ+6NJ4`J1bmaFh0ZMZ%5@Xt8$*X*2nrYN|9mFi5X0J#y5qQsfNTD)-`9@7;h zQ?56l*05_8LFpSqcTpCYaRI@{MR$Sg0;;WbEX!!rKVY)`gXUa+WdGrDfBbwSd8}_B z?$irwvH1<({h^V6fS>8V5evxfO#h>?0Uh{<+x;PrIDsHE)pZVhZ!GRVsPK^`Bx#s}MFs@)UNsn`T)#BgDp*HAuK9;Wq&EiT0> zFO|(4SqI?Nh7{U;xz=ir%oeReSdKLog{?3v7hU-UbC%~+RMH@@wT#(`_#$&$#w7Fn zUUYqAMzFEw{xxmt3G!s!u)Bvr!_Gky0G;b-2r z2tRR8O!RI-X6xk%H(G1<-UtZ4p45vnNZI)nBsq^_y$BwrTFY&? z*z7EBU1xhAaMAIzp>XM3(@=x8+YUuC4CkkZB@%;s^fDgp@Vw=iHBcS^3w(D5Q}LX6 z3(X;Z_=zy>4ZTp*x*37Vra$kdxEr^wsO(lRQkK+)8<$HE7FGn7u5#Qmx3AAmb)C&- zaAKKS*vqky5}^tVA-_0py#O`TIL2qE7l$dw8fhgNKrLCHcL_G%L7{N+%U-1)Y;HKu&|8JJ{(2uwMtF686erwG~MZ^7F{A@QLD}ruW!J zXFxN~yk`#hcF$wMFBUb}tEam+w;Ecdq%nJWv0AIQ9}m!W18^Xd(xgYdLNv+rrZ}A0~5!Y4DG8IOfJ;-hYK;Diq3JY zdOlqoYT;PR---|cR?=;F-@se__*@~^lGiEs7h~~0rLtGVDPb)g=!Q6)#>N85< z&Pkf;fal|n@df&nfq_$%FoHg!n^6J#ezTN^)kt^m;s zUvJNEFdV(MxQ;)MSvvk5?Qs!auHE<%^@?T1S8ug)h*cEs1~g&7Bbm>WXC)wOQ#eHE z$V6)(ePJ7^t8i$*^jXMF@H%f_U9wtx8S*X~5lao}Pkm){rfEgfr(;=(^tog&FQ-Y_ z@NeC&NLK$k40%ntIE>T4nHxzFX(k#9ANzTA>i8SG5bgv^vQfx)!nFEcAZBImfbo>? z@J_aY?=Rz4#folM_~v_r9w2P&DBy`cefTK3l6v7# z2_kyS5zrpi@DAYZ_>A5_{+^F~NouU*vE+5Uqvut2VO{37tFZsw>LiA-hN7<#R1!usUFax!0NQlYi>Dc`Nr`i(#ME1I(CAFfo z>U_hYa!l8_G1#>+f(U7q3_OBXmo?&AmA$&cT406#ynWsPZ~pFBBCX#6ecCVhn=5C; z*5rd$5aw>9#~tn^#<>1Og@rS~yt2&FzVVK5aNKv!z$*%PAC8?70p#EQxmz?r(GF)m zrXG|-!fF1VP}s7aaOj}kgMi-)PSX^$)HjS^P9-$*EUdXS>pzKU;`#bP;WQ8aunfUb`+M;r;ddwvFWRqp!$8g(-58^fNfT#qZ@M zMqYo)wwhPwRGk3LQwJ5HlVdUMe6n8$omWD-7Fey(iAd(kRzr{Ku7<6fG4<(d<9ma6 zG=c8>0#!r_N%4Ve;@2U1S<4vV)%MM*6VqAlk7cT z$p6gb@Td~z2H`zX@$K?upZmz2lrs)G_S_8}@JkkCGObVY9s^`-M%`^6ZhxYIxBFAv zZ*;(i+x@8zva(q}qr0~B)^8q;$bCN>gGeaa4S5$mWrx;jI)8)wu5(#3*bl3XL}2CK z@~GVGXRIeYcL)gx{&gmm>}IH9Spv@mKpl&VkeqY9z;|Ds)z6=koKp&4dfy*=#+D}b zh0zH4!9AD4adhmNIOs-%CD{gg-aAI4xk)Y39Yll8c2W&t7 z+*P-mkkA!CccTrznKNC`5BLZai;ZPfo*nh4Je0z*I zj)hbIBAHfzKTr!#2~!qJayiraa|P)_ciSk>8&t%*t{7E++oIo)a^<&;%CImwlI$DX z{B-{tN!&NC@OMwKG}PJGdV`T~&03O;*gwKp-xMurEeXcoh@w7s+8c+Av%?)XV(e-g zb&pP}N9NP#ioZi#`9m$QYv!}j8@{TaOuh}7l1(64Mx{o3!job^7<_;Y%!~L+z`}qX zDVGvGQKfkHKc$a9UB*ye4EbG70fy72L)CKHW)nicNA4fIPE`vlivtG)_mBsA5rYg( z(4UP4VtRb@<5jZ%KlBm6ZR~sb!A5UXPHy!C?-BMcGd7q4Q%aiRhm+rdFxYmC=3U-{ z+ygVg1sX+U5Yy3?+IMPxo&W?{+2`B3r_0UwmzHeQNpT<8m(X1FpLT9eqiFnhLO+@x zp3(xQCe9(Fc3R53J8)~D6Hx@N3G+NJFCOAAdTh}_qI{u_7_E9iQLpHJ&yO5_K5fsq z(o;8(UZYRJTN4XW4~X}a#;X&*P2dY|b_^{h;Nxb82q|94V*A%w&`t%{oi2C?H-rKQ z@H6lSAuGpW>XITThBB)E9=gpj<*ERw`5Ii*%9QQ%pXt3HZIX2{JXvnm+F_oSU}mDS#>MN zQJU0(ZANq5dlz(Jq>3Uz3-JCu)l}HLNb{@2D z3!!d-aP_L_+e}8@W}*Zfe@kWc2_^p@<+1)<`7MQYsv6!;{v-64=L?BlvW6T=)gHFS z9|$%-hp;%6>nZdL9)(iDwR=A6=YQVw@UKfU<4Ic!p4m$$QEBTt@D&mG&+8!ms|6&O zV=fn_TOgqPmXz!Mk7@aG_x)Eb{6}(c(@G7oK4Ja{tu2l#K|(_wkr#~-`%NKOi1X2q z-%5O`+UPMA7SSnsyLv>Jw0?swKVCtS=1=vy@bK^eIyteF4A}6mAxx5fMmy3~R0gcs zaP8gPID&ydprg--b0MMfJcDwFue&?XqT$JHW%(`m`T3bi(z(n-C>L!0Jh2H*5fl{k zS%z?YXPsL?7w+=gIbbg4@zZ#_daCm%cWoW8)>#&3S}0FdUT~O2(lIYo)m#O9@DlT? z>nwFCN(2s>XbNU>NFRO5?GD!6PBoQEP}N?j&P*8-RVTbQv>Lvl*DL@irK{tKw<>kK z4Ts#2_5?mVJG+bw_I4|H7?39O{*p%AHocvGFZPnDLNLPBmK3A&;4K(uKAqW8!*GEA zcnAWR?}JbIyfdXGAFEE)&L{_zhUFjFUX|9#DsMN62u#5hmy$$8^G{aD#DvDXuT&QT zq4SSI0b|LwM+Z+ferMJC_a~=mx9whB*b$$e1XEvy2BL436DdacN-xl&a>`$#n69kv z>oaA2nGSa(BI}nPOm4o<`@eX24qoxk7>yor9vP+BJn7PQkjZL%Lijl7sgCa^YJIdNmcsZcw-%w zz=v7g`g`|!p)#J?)FG=ekBDiow>4Skjo8MElqE@7+U<=c^$5?g*vk$}YiIoWp-18Z zefjrJ%5$qRbCvzh(AS#PXENP+{_FJnZnoy}Ol$Iow?8eAl011Rt6Xa-+g5l-Qcgmg zqwY-RQ-LiC0S~HxzbS=miFypRghX_BE1xRK<65T^(E#KN_?cMdS$tR%r!p4;0SZjr*j$fM5O^~kA04e9e2E+#qOOy{Prjr# z)OXKo;96EdsUc{hnSk%sC)Sza?uy3SzcYA*zd`wtrPUO>VfU5yK)>H^+u)#%!!zTN z?SVQf!Qa>Nio}}AdN^}w5S+^jxM3Xh(VVC87TXwaF^HN{6J3?AJXm;Ldj6ei*PVEr zpg-&1ocGLia6QF^J@Ihx&Havf`SV7YG#Rlf?TjU;WwHF-m z2OLui1}09iU+Eua+nq5-YWQ^qm=S$Df}AdD3=_O-^oVX-LPLgn@C~B9dWc|^i6a>$fssFj~Lb*4Dc1D+LwdWN2t=)WlrFN1En z6<1(c-oSe>TKl)((1QLLG-~H>iRM3x1^jQ%chOI=6yj0bneUZsP+~* z%W_i{I>?pJLH<`ZK22q&OJ8IhxC{eYt2XOG2cA3>N4Tk5PMx!>w(f4D(6z^sz-=uVDV6fyzSI<@%>5*puIE`)& z>NXKPy7m|Yk=lrHrO_)#kC$V5;lIv5wL{ zW4ZOKiL!GRU1Q%Sq|xmkW!arMDQeRaE?18;q+i3K+Q*$)B$^1STMXdDe3Y)7PPvGf z;Jjmg0QF7`_y2)wuwOA9JV?`K&HNR3x%I<%z~}3-srjz%-QRPkjlZv?C<}?Z>w7Pr zU5udnk3Z0+)&Dgp#%%z&TAf>?F(b@b)Cj#F8E$hU9UmDN9B*f?UgdpUU^&shW!rfq zg&9*CT1ElOaKYutzzi;mV5r@r2@I~=Jq-3n_Eis{?%=1Y?L5|D@yH(t9eCQW_FFz9CgH4u-F0Z+lD|c6! zh{u9eT9DM>8;y!nElW=b3sHgzL2311Q|ooLwYf_5-DocK@yXYY9QZfClZ?wT*_U#kcn}=nhg>9IfRv;hBt0-}2pvA7l1L zz=9S~iQ#f_YdNtY?VPxwOPIg1USL|r3B>Dhxj^}$#JQEPq%0^QY3-Z!(UI9c5Nc0( zdsi|_=QD|l2T!AO2R7@^rK)r4!*nxeub6xUrE;G#VA6~(h0Tr;EsGdI6J3b6wf0av61KQzkkD(@l^<2zU+tz%H1fR$g^%W95?Wn zyyDmJ8L04A6hYhF{fiQK4dAM34&%#bm%R*;-j>V)W2E9Hk^(gH#&_E2gD1jobFm*I zrFT4b^n5BUDn=@*s;4b{8UMX`UhOR#1N20@l?#(cAuRW9hD{uUHU#t7tau1&8mvHL zU4CueX)PH;Ja#R>Ro5JJvM`InupjC*(9{Zd+OZbI9}3_Jqcp7L)K z6nsYRknUFW-F3yUnPApx z0oD>%+)&50=?>gQCoj=fldAe@NE*5ty!HhM#|)ip0q#JWANSj(Zkr7k%q}JdF-(X; zwk}UgBw%CDvt>fVOm7gAQy))LcGcy&YM>T$xacGiPjOq5H^ENMJ7hIK;byNNt4fttpT>`Xmja&3a6-*KKTn;NJ0vspb1PNxQadBkuiIM~uWS#BufF z1chB-MX5b99n~L{; zDcOscUzyGq)ODFwA61{ZEJ`_0N%{DzUYy@}4wPFWqnC_P-(82k<-Eae$OExR#b=-S z=^ZrRt?*AH5O_HfQY%e6=iZEqht$pp2`3rpyX@4V@B8>4jA!CeT+H&dDfm*aV41_^ z{@=TlPv0?VHp`Ff;Od#&rqg)G^Yhkc!W#0)#mp4*CTo9hl6IgzqFg{|?5lgH0H^0P z2efo6e*Yx#h>R8R~S|!MLo$WC2Mt8_k7HKte~* zHv&5|OcGx|yv*}@wKm~(7ul4VB91?U-&z6!SwkutQ*_Xp1zQAeV@IpD2`dM!uos4L zMuBFd)fW46RZT@_bzC_==FTo##=J}xFe3jo$v18Jhx8`kWjp4DcSipNKb2 zG#S-u(U{EPcZpYSA&e{blJ0Bq+ezCXmQ*Ucy10m&==q4wbFCBTa9mimcf!oa&6j}u zYaJS$vKw*OH(kCQRUZHR8#8+1n)AsLcywTCRg0lX*b{#EnxNF)H#yHucGaw|y_~-0 zgnZgtq;~G8sqoc_Za~~grFPYei^p6Y&0X?U)D_6CB{L{d(_&uWTVs^gH6*as4v56} z`3+_>Yr@z%5dw0+e?oVVh)hJ}?$Tybr#CoCJ;=)fC!kVh9m>w_lHx z&1nJ$IkyOz`gtUC$-I8G^P&<2UB&{^$hUHV?4D&Fr(VxL)+SdtK1rGK+&Jf`bFFLb z#WYt5#lr-CcSPw(=rzZTVC-Vez^YT=)Qd_{IX;4wo*bLoJw@+I0`vL@)_6c%-ErmY@SYE?hqH-(C&$Uq;P4oMGqZ_M4AJJ=;BiDu> z7z+-=-mD;4V4r~&KPxQ-2v(C(T#jc3*Wg{?joej6YXVN*2bZua9V8Hf!EHm6t)=cu zX7|BvfR#os!BR+|6(-)uKLP_Mzk<8f_@2Cnaz0kx+u2*_DGTs3goIBBme*%GiVWDI z>6{UI7hMxxRy*$3ux79wzvpPbaLeZr(YoPPJMeKmH8K2p8#55N$@u%^ioBDuplo*rW3%JAc} z-$&J5=DnS4*9-g7P26Bxr_;)FFTbnVh&yJA$T^0Ux=1wy-mO%*gWlf3`H+BzSX9rvzy^sH}wpLqk4RmOaK%u+{W~49s$d1^m@&kS^jnYD{ zniRz=jx?7FG$kZ46&SZ1q)$fnNx@7&Huhq6r(JYF;9cxV?W});dU6DeUG%a12Rd|B zL4^}nsgYjpUC7vVY{yN6ZszuH7)&ek3gyav4evNHh5{?up~>z)J{UBEy)M6v`SL1U z6ibyZ>-qm34s?++E9k`XDeF{kTRwv>EMSj=;OW)dpaeTKwtETei;k4QF2cWfx)$v7 zM`O_}K(qS95^|fmo7AzCaJ`N?byr*ep(C8<@<|?ZJZ8&ih593y&NjNgTyt*(%<>as z_K#JLtI;O#tQ2T8>dKh)SZ{P})nwF8%0}*To{ur9QOf8eO_j)-jAE68a2%W0<9bXf0v2WhA8%qu&=4A0oINym@_^j&5mdjGuL42UaQTk@`so zBIoC3-=R-1_Q1Q#eaM>AS||v9-5?qGOk(#*DgBUrg-KU zeKja!Pj_iI@dD69y@<2J zaDB9GCUAaJy|)eK-|A{6`DJmcg!66 zzmj0IJvPy9e^^Y$^?x|9l$co~I1LO|Imz!Fc1<8Y+!=Kh4vSBHBlw38vi=K8Z$*00 zT8eTFqy%%}#0N7tU%6v8KCo*QzN+#L8{Ip$B>?YQtK^)fUw5{p!!L4>;Q6xz{|KL4 zxJPF{Z$}yu6M?> z8|pM>{qFkk(!eBnwf+z&)3}ezdsNPux#8pjlut)?r2qofq-8(|{k-mpweRQbJV>nV zzrz@g4!tA*`1&rpuC_hqa$t!Ldbhfde;K_*)H={g?};8v?E)WeYyKF?ywpbjfZX<> zJ&%l30YQ<1i$5T$y~JbZ%zIbOcl8%Dvo5;t zHOW7o_vS`N*+@z0E^A|{>!8QxT`Y|OXZ7lxF~l7-z&>Q3wYBx%1LXP~(HsYu51uSpL*0&=w)j9c zEb~ccZ%IM!sO3O9h+on<^v=<=Bp|pEX1IGp-s+=}dzf(nK5DuU+Kbx9K=p@##TY%$ z)63!#O(q7;V^&11`RfVBQBQ;Ql=%T-5)9Pv9OfUm-eUHG5Bz|8;u7d=95g!I;xkcm zU&1#zyf4A$9YHs@G2wQFG~35-5(8kE%HNMIR|~DQ86O*UZ29=FF5uAjL9hDbL~~}q zD>r%8SlfuM(UDp;ziq%jad}Mwr8ir*6YT7UMo9lj-nzlbV^~@qq`UFmeo{VsgJTw& zg9OptAdPjnw!1aYKPTCenGq0*zUoNQPCF4344?g9a|WYN zD9y#|+eycO4hZx-E36zwoTNOmb{Joap9Sgnbx)+czud64Ex-u`Na#5Xx?Tz8Aeo%D z&97PN^U!a7j{tX_-xHEFPvE}Z@L0c=m-C=!h;YjmUtaWYWE(06`Il~HfOV?+-gE$7 zlD4eyBOlFOZ1119kc?1PpntHCbSC7EA69IWUv zQlz_hyJpcV0Tor_hvz@40&_nf{d>l0vWz_bbY*j$irnaUx>WSRj&|oNuYD%Mp%0ll z0IMNMz=Qz$#)6$6PxNo0UdIECNsFFifVG{Pwcn?3g;Pv$!mZ&_$|;WvUpio|{!FUt zK$3EFEU+;)JCY5xwy;}?S=eopZv4BTWQU1eVs$~^_|{;%R(5IaIT48s_PhS#zKJ@i zEO8t+Fp^+AE^*ow$L0jekT>&uf|T&gi^L!~{3aM`*Q&F1rjJWj?%HIkdmDDvuJTm3 zVPY(MC&;<{uHohspftI7o@0;TJ`MDtnGwe974Xo(y~f<(s83#c+ocGjgZ>kzmU2S@ zB9yEHK&d1he@|sf5f>^DdKL^>qc_WTtlq_L1l9`Dx^nAP#DX z)om?CJdh>t*XZAcLQ`z}{IBE#j!>Z|V%7D(>2mcz;J_whiAPHSPqbrv#&O#uK*y| z_zbvSAXotGG@yY_U&tG|=LR}xF$^ToBK~tZ^gHzYWG-@fc+KIFN{sOJkwdpW#t5_W z4jqn444iP|-J)8=_4zS{NpuK(v(Tuyd<>)Umt)3MS|D@J;WvEHn+1aJ=$vrD$_p}*-Sl8B{@(T13+ift2JLfSo`IajAEb8L*`_bOFziJJwlif3lB;>?#9j z-?|MgkB%oY-au%eHuXnT4>H_1+4oIVG1kU`n^1v(Y~ka8nfk7#4H#z&^RlCfW*fqF zmC2N03|?;=85qIZbmGV2yxX#QDt9lvC zs0742gO+MTc;(1J!_QAInqoJp6TeFpJ3HrlbcGa;u6)Yc*vyptW&^C{EQcedX z%BZeW$(Y$THFiv4-Gy4kxj6Z@f=Qk5vTcP`PUg9_+V?{|-yFVbuK5llzxd2GrdHR} z75Pi`Dg%hiufUBxqc%pr1fQ7p!m9IepC?4-4~BeYyGm9s+b)w# z71X}NZoYb5sR)&-9~Va#A0t0|3u3VHL++kdLJE8DU0^K#t-#j!{?BUZ|Gwm2ft^x* zh$D-oC<_C!C-pE=rn$WSq|l-07u&UGID_ut%7azwy(`cdz?9vZHJ$r+{3X3fz~G_R zP7>U4(bRXqmeuKh<#?62wPIt4P)-RZ|4i3d7XMkJYvuUA+EfcK{VPYm>OMzv598jc zBC-xBz!VO1uLNPj&?0><_f608x-sbdw<7<0D*jc|<356egwf-qI~^)4tXFjZ4b{v) zf{g#aoPGGezWE-i0IudEj=Ae(6>h5Ne%2(F=d_RSQ??}i=TwiQ<6>iD4KJ*X_roqUKsXm`u|A1s5Tf)cvak=w*<|2>9oAZSVBf_T zruE>56xfXq876a|_js+OEYybeoQ?atA>&w z&OhU3P3w!h-7K?w&6x;cn=AhhVXJawHVzRf$Ki?ts5QX~e<{i76OmaCSLU zvx(BiXGk(9n7B+^@r8<>`D=A++(GHK8%{p=uQn-G->+tgrkeF5q(?>=OQ}W`M+eTg zIBC*nB+0q=g|FS-3~Y1*=Y+C-*T0+sihjOhIbFXtW{`ZW$*BH9lZ``UTXe=u|_XRe>~67>CD2emt%WgG9vH5IZH zOeDxlZ)?es3I@zb0&;lx_n$lGXktrYp@80xo>0XSV-`_FEGClg+F&>3Oh;|MXyg~&^0c(L01N#C^c}h!3t8!r9-|J7TE}{ zWD$O+be5rPbQ=D^%abz;ECS$W+?Mf*e;9}FsJ;&RwLax>4P_ZFvVrqbIRaqyZ6_k*@{hx4(B&Swz8U;XETty)u8fRM(IFw za>UN^`g}xEEB?Nm*j>XWQ3UZ^5ss*CWw|ONnS7t;AKNd7B8hyAfoI z8&yIfRdlX#SUn-x-f&(#mjr$Q0iIhWy%NP1lNJZF>f6c__%HpH?6ASohb{cXqjzX7 z)r7m!Jq!I<788kg{e|%`bHXU^=Q$+`Q;?=ra*Eua)cT*kLYSW(l^#w}$k>l1AY2`5 z9G(Z44w^#5Ccx>@1qt6ZwBQp|+t=3CuNsb|q&qwrW5z$75bdSZQB|+*k z?+V2(a8NiEo6rBcn9yJsUssBrv{IyiA=unRB7-3O zhP5`-(E-$rv|l=L>!;vNBW!h9*yz`kby{f#iMCqYd|X-SEoCKuu8D&r?xoiXuZTQb zpqw9Jh)MO|hmg$--5EWcIueo0X?~!sU0{f1s=0Domn-ck=$2)2OJSauflBIfRQ1pD z$syr@%!=L$s8*c{7x8*0C=quHalE(P{(8a~fAQQKn0Qg;33gf2F#Jg@&4xEf8X6Y6 zE~l75lv&J~7Sj6CXI?hIeFq?iI3PASa^UFId+WRrJY0JobVl6l-yx8}wTD_a7HGPD zdc4Y+XumAlH0N$l|A4Va#*kO8Bd8EYkC$e779JB9s;#qj89W&3AZ_veZSX$Y9({Sv zu0i@a!lM7DvS7=LxQKSPa!$ZrJNWqnY&RChH6mOyAt~y*V*^HzoHiSe9jlA-d!9*4 zDl{w9sQ`Y)Iv!|bBsdc@6XTXSvS9>p;IIj*x~lYo#tPpa}Q<^PqtGV{0p7v z!7ms;W*o{SB;P5FQRdubobjU)8F4xt9dNWLy(!vjI^9U~lX1Oz|6Q75(5}}j)(+Cq zSwPIzOm3%7XXTSbHKz!IXAQ~z9+&GC>-A8ls99Ql^IodO4DYXfa^j%1UP9G;$faB|lQL|)7ZVk6i+cu?4DY=Gk$WO$j$HDb!@+IfjcwggnlR2@q z_Y)Ij)CSh@r>nwZFwsmnO`sBoFOmY(N-8PZE;V(GeJx5I%sX2*aDB$suW{S)byu1W z70R-x6^f?I=>YM@S5G=esXa*gtscyaGs%pUYpZ1P;+`+SzSRi#p0(HZ3CG(e?qHXg z?<#9LYx0m%+*JxQsnAWM?v_V@syV~&c9(R!Cyr>vTi<~~J9ECFhCL;%edD)c_wdf1 zo?!_2m0r0xWb#O)kiI>SdxRxI3cy`u-qbweFmNSZu&@L@1P?3WkI<3sWD-r^O%-GA z<9*Gmq*Jhr_Iy49ftCvPilylXDX_Q;(weIUF)<7ZM}~wkEsAF&gH_nmE-t4>^CHd+ z!GPw_q1aW`3j9R)zzedWlIOt^ z!Q$qY)_tM|9+MoZLHK|kTv_hy*@66{7&#Y2Ya75Yh=`k&WpErp?doIGs}a zo7~KU?x#e1>$HRy4QnlZif>|r@t>vFy8tNcx4#o4UddKnkwxXv8r*+0x&JenHgg$= zG6AN>aLs2?*(Hq2>op~(hA-k8y5SLJaLF_9GOZ+GubcI!0R@p1``ZZA1XHN!Y11CP zUt6X_6oVRHU|37SJN$bH zn08$->2s0O$v0(lqCH3R+6TSB^TpZR42~hK?uQ#pZv}ph+D;99UxgLrLcx6K@lu2% z>;dDgu7n&;L9+c*U!_BDNIZUh@Xr|aZ20IoF}MGFE0T{_bNSbHAxZ4n8m*UK#!~qT zo-1mkwUj+n!Ig!4T}5aSONDswu<(7@)sj^ZODQa!ww^uGJ3d{1VPFWWG7idH{5(@! zv?Y|NS!wH9N_Ad6uC63+;&5+|4oZ-Z_^eUnS&H4R zROs%&_Yw5J9Oc%G@~e!kjcEJ|$|IAlpA>ExEa7@;=jpLCL_|LHQ?jga;D@bZ$NCZD ztax=2;HqI}B@&rLN?$%tf}*|e%olI^CST_f2gm0+u3Sf79KWQzTl5%^5pt8?dn7+w z>+s`@MO1}8bj#A}4AwH}PLez!`Tm_D<}$uUCNu$-cssO=*<&hbqkWU3>R*+>_KA!x zfbMgi9i_I$CMKUfBk-;6vtGM%J?x-hwb*P!$d$r(JPp;fp5h1au>##Ke3HT+;C?u2 zMdDAE^%xaQE$}mk$xij^GdWw&J&q}>bI=t_VOPtganps|&VzttSq~Msc0o}-CH@2s z)~xj*{QJohb(Rz2Qc00W63v(<@uA}pBe;XG$Fb*i{0c}yFn+f|Y6yAa#k61I73;{n zhp5Kf+$Ch-6%`3OtD+~=e-uOG%)d_AlAGA1uv ze<4*fqDWN#IG?$ad6q1kJE)jps!DU{Vcn0b*DW?^&&bhx3nN*AS*jIwrT+0nG4(Y( zd!#JN6@lt$5fOL`4vYYpJ1vX?Q@3n@c zbzCTPg$?(mgJH2S-Z)ET;-VCI}LN>e; zWi#)55e)96u4*z@^0Tcn!Rc)^dAaz12dMWkxd=Jntl-yoHVn($AWYER)?M@y)z&QSDEL7C z=}(&e-l6LuP3%v1L(uS%y0(Y}$!8aFM)y&QG`tor8$b6i-!%R-42tjejyBeI_sqhc zHZ^>Ayz<$Y1AByd$T0K0VtT?M=Hw~=x2)B|2L(3q>+qZ-AN<#F9jrvPXG1<@<65}y z$;Wajj@h(j{?@*HQ~fzli~@&UU>G~MKW4r0Elq_YNK}9L=B?{&|HC(>)akE$a#3Gc zz`&T%_P$U=QgPuJhFqsy+pyM=3KO+yP+i+1@!s8}~v&hZ~my=Cz zV`y9_%dyH_yTki3)z7HgP2ZEhs6tFAn$oP|NyTf`efp~&`frqtvza1`(z#e4oC|~^ zW11KK6xlx!EY?A?&gUGcopeO$Cmw`DbIOf2gdDC5ZpcVlyR@*RSUm!+lLno^-(Pvh zeYu{EZavw`*kb#t&Q*Ua(`NWAkqF%ct_rkWpEgKcsi2`xD)=+~oEaeWEiK;x-~GTN z%H(O}i+GuM_)54q*&7?trO@W@1#RvgM|6VS5_bW%*oVy6`$PA3S6jqy%ff z66(GeX`@G9mtWel;TbRsh_^mBB;@#9X|XG_Ce7~qF!vFD7(|VtqPo6f35V`5vhjC^ zuwmj+N@0urRBrXv;0V=$hQ&Ur}tFv%7EV9`MhY;0A_Ps+|lB(s}NeW$wH zOJD2O8ENbZ8&8?geHrfcl^9rISg=Op0*z=A94ZiF9dwErxL z_^!C&IXBBhm0D$!WjbrZ1K(deFW#<3$R5}&kI_+TZ`(f;^tcmUUkvu4UONO(o8{^a z?00a6%6^fbzS_UiynW{C+ZFctXOWOj?YQGyMqzyvC0p+!Nz@xbpM+&$)-sy>X>HWO z)a6>Jd~k>s-cHv`>1LyZcD9FdSSsV7jx|5e0?YLW5IfC2$cJHj7mfs772MPD2Ac$O zlD3spQHoY>8TXoo>!H5c5Va*gXTm>^(~vCDeDkkH)mY;mS(y2du)u(McHD1Hh?U3E zQBz+Oq`7;*yiRshtSAME2!D}uBVHQD28B8KV>;O%d6e61(O*Hk`}P=Qbu>7y;O$(+ zB<(rj=EW+DLYGU23D+vUr6N(7HXzpUk#C!ZqY+x4E#hIcIsu0+3I6-b92jO+Rz*Pk?ucS#TtY z(!O5_H(6}VDuj3$QT@dBvZvn%OEknHKq4-kCREG!;w)}SRG$1=4dYO_s}8Te zr^yldcI>G-Yzt||fpr_)PWJfp$&Y0ElT#s1VJa2A>#H$?asJ5Wr?pzzyRmjG+>WDj z2z`}~O8k-NQ?jJckx{@SX$*aqg;fe3HuR-%JrIfPFjWN7yi0e8p?Xxd@4hoUvk+5XueaoO=!LPE0Z!8j#(V<-ncNQGUwkdh3iGkgY^)bc z(;}T`4sWhUT;tm#A`9h`jL~BC1$6P*&JDcaEz!ptWvP0-8{ji+SK<9^uPGJ5i(2|N zTEw(7>lqVdDTa;&yQLtckj+~7m3Ofi%V|{@%+60Gu}V@evGkhocv+5CYb{O_+C{GZ z5s&gV+)vf!D$i7MObJL6tw+ivx!s`F9RWg#dp-BBaWV+{!J}m&Nj*|NzW8k8*-mZ? z;#3LD>>@)EAZS&fee;5;?R%UPP5*so@r9H4uGI=x=Vxegu2BvDBZF}RWM;fD%9bI+ z6_R0`1>>sfxrM?caBFQXGwC`k8x{#SCr#g=40hKN4`( zbr_0&d_e{lOkoOJi8U4ZxpXalN)bM~8rs)q+Uns}0nT!fYY~0b6Srp*r0gBbQe%L= zr_MnLCsaM~*iVPWx?9X~FG*WJok>2x=(aqDF3*NQbHZIdJAXB;O2&!p@DqJotsUM=F9cLjho7rSA7p_*IC` zwq#`X!jffX+-vUqXXW#xvVTVBJKLex;oAk*v8v64c@9NC9CCUF*W&tIV{$s~ul&fT zR2F3++XbQ@60*R7a~o`-ea`I9|0pGncF8=O_IsU6_c4s}mx12z`Ccc>O77p^*vn#6 z*ju~cpP;TN{)x@SJ(^IqtnvHxsjZX#WQBs-pfzC)m~1G=G-=cCpYC1Pq;ercD!gRi z`&Ey{KFmvtx`%|4aU&qa$Az5EZN2iS{@JJUdX_*Rwx2wj^ee=(NoChf1#f@qr@<2) z=0+(W0o&08XQg94;IrVVt2}=g^3`i6nV-Q7CQB*NrW~K%lT(hk9GCZodNx*CS~X$s zgFR2!vovL;IyF#_3+EaZebge;$!CoI< z9*1+1%=`{raFlU&g2h4WOjgGH1S>on#fk68=s|Z45vzR$Nqgf~uRiO+rp+Q>vOj!h z_u8>I`e(I5v-e&C0!W-{%%_sFJT|OAvf+U{;X-bhZoC+)%(au&pic}uGA#Ze?aNLn za;p^?>_V9x))L>@G8}u1!=}2a&3n!#pVCI~acPOAQ2#xhS^1HZumr-MOrvLxtvb*&AO2G&DwKG1#uFYa*mXMeW zRLZ|rJrNX{w99&wKIS1w=zQH^F%rMC7sVxC3VfVZM3_kur@cX*1XuTM8BBn7i7;?F zVoe7T{@%sAo_T7oh)br-#Nab--w`QJHI{^V8@GC#?8H(lcO)rm93PuyESz9*x`$Hw zweG^Q4lWoD*NQgUY~Yu)D?M4!=#yT>Wazx`KMa|eOoO>$fAAQzx9qt}{*omdAlrPn zdrX@<{s+y!g<^Q!E$hcJta_U=@}1$)-_~|_+bM!`(4+;e+FL2tTp~*HpYOG;FA$&$ zGqo?x+VzF|qyxE!6!w7f&BWAP&Sy1qM&;2;y}N`?b5LThuP<@zOjG4Qa=6PlBlQIS z!t2N`{A{MZgHgL+<8BkiLMDQ+ZR^uss9aUN~(y{3ZJtO5HTK` zFSq!ilf*DAW!$EOQ)2b1{q=ql6Yf$W)-svqd)&l3kPwG!nBiI`wYOFhg(0mpVR4{x zzN{s)2G7;jEymX1a#4XiZ2>I^SO#Yb0^9h=^_ z3i{DcLWhO)9Un1f^%Pv=WJ|0DG|rU2(ANZX1PZ*oU72S*X(mu|wYB|8N&k~6Tr?`1 z*hh9`aGUN`tsVstWkEf4bkOf+R{q4@ulM+R0j; ziq;Vdb$7qJ%90MmeEk#rBRFyL;`>|4M7x}0vff70sD+{})1NvJ?<}L3J%YM9H?p8g zWd*wC4chK4jtDHS=nPiH!j^g!$GHeuv+NCHr5a+x+m40DFQ&&BVv_r(0jrodpBnHg zD!g(a%w~QIA_a_$R4o*&mn*N!4UX~8s$^2E0=^eF4r8DL?-gyU9flS?#bH`Q%uG>T zc(CbJSB=hUBd@W`9N+a<*R|h-7zty{0Y7b7@-RE}sTHYKIU)@W830wYsVDDqrr5ck zx*2jfeIWHuI_OI==6P|jmzs>Q2}?fm3GKNVxEL`eGFRoJBN{C+P@ZX9OhiY-QBC>2 zG-oAw1>)ddn*uIlntYBG^ISNxd3c#cBNvUgD8B$L00+nL{9<|3|sQ-Es9J7daL1(DDy@U#9>Sue=^1Pg2rZqV33>V}m zuFO;xORDfQhXjw!RedxuV3Dv}T|m8thC{W}~Q90m(7K*4DI$ z8KE>)-p`Va8~tsy7HIGcH9EPixN|^%M(9Y_M0SaMT^%^E&Q~r=wsIc)FkN-W0Um-~ zOCfEFS%8VB%OB-$L znw|MO?A_&Ei!L$fALLkC*=V0f@&Gf=Qgw3grEz#VC! z=nXBKNsGIL=nP{wMO4iE@ZTe)6wvBHioN;Gan=FQRDP7ECHN+s)auaOyuR_hlQZsz zAcN1L>3ST6(kvj1Or`sl{+p;zr)tW-7IK}_2xErBwm!qQvIc7GnT?B##6rvw0>T%Y zx@15Sbzv*+)^F$Sqgd?ObVZcm__^|vb7yaEzDg><9r5a51#CCo$c)K)QWakzXeYT* z*~2=s-{796J%&G8?mv1QqCGW6<;}*t*K$PSKnwmPR>K9&=Ul4Z@ULUZQZTpgJMWs&K{M9BqJ~Hw1g?EG z()MMKpl{D27*wXoWvli~$_QALE(YiYIZ`1`m) zogiw1_D0{zOjPoPKQk7!sM%3wv@_NF3;u}kt&=c4bho|3TIlzbZ&rXexv2Iw@s9Jc zVDc2R!kJXhr-%e5g!;OKU9PzE_#Ci~g=~{C&TQ^>!_9U+-o?x0xl&|d03+rnO`A_S zDFlMx{zA__yH}$TIf0g%aca4qyPSE={qU+3glTQt3``$Xk6{k&M?5r0)BY1({AN>_ z>sdl-fJ~rAq-~A4d(Qp8FxIc$^9~qrpu91V_=Nlph;IWxgzEL6p1#s@VW$%VZY)Db zQ+14>FIi0Rx1iUAz#7^riXL;(QtiS6<=UUtEZMKuuc=9JetpQ$J$!O=9@f!@7n9>v z$;x*hUTH4j-c;>8k?+*{Wc0vKvpxl{)qKdY+9VCPkt@w_$*&UHzB)TWtU>uKSTHSE zvT7Cd@i^qj$9tdqnf$}u%rAAk`3HpdR&$V@o*QC7WRSSVN81C-F~tRE1Nz)2ZQbD2cbKznw5? zrcp}8r|q%GADuru>Np(_H||B|rKnG#dE*3S=30phb(sG$8{w)D>#e|K5^_9n)nzoG4zE_J@HX!(fIcVa9i0ia}oSbLzaizCep-jI;_83KkTPL zon5&R3Fv9N(WqME9t*D$?m|h~-9%kj7<|t@d@xGY&NlMu#~SB+81%+gD{?om^5sMo|1xfvg=bn*FQDn;RXP(RUq;YpDsxFI9?nb$>G`ueUK8Q6fS% z%v}ll#(xEWle|lgDPV?l1u~5FaHbxWol34j=g_M|q zu5a)mRDQ^ftrxP3LEAGOQOfWu;p8ThA#)zV;i*oH^cMyW?`)7ZW z!LOxbR|c(e4J?r)8G$EJa++YQ=xhy0*TO;`@x1n=tntgQovJp(xpOvve`cHN!< zyl(d3`q|OJyL;c6l{1uTitV3?#7F4F&Fq0{Tm$>O+J1bV&ieC$TnFU)t4gE84g!_8HZ*IpyA3Y^iPr`)W zF#S}jH|e4Nl887tF1-A`s$S9m)4sU{HF1S;JeF1|x3*Gk>BZTmGwrmHY&L-?OM(Is zr5Laz0=6Ko$$}x54V%RT3ZmH7+E!rdrN&WF%A~Dz(2}pr#$6iYRCV5lUN2 z%P^5kx&Z|A!OnSbrVnQ)lQ}u_`@VDbxBq?1`9vcv=7oLlS(*HF$SXQ2YSH!nU1Z*| zvup5GLuG0o;uUZsP|uk3Iv~FI#>ZnC~^IHD9C6?M1Cwu&7?m^{k z!|fjRbvHLk_j;GN95IemH{6zXe_G->@8T-Uy~w$Z#e*-jUyZeV_w}Vkci%7RzZhe! zZeAIgI#+qn=Ga#<%Uay+y7x9YwPux7SVfn9Z=9gVnXD>x8N%?1YQl0&?jaM9@ z&Rc$Vp<-U;mt7Glcebre+0&6DJ@?JsC7FZOO+DuZTFUC=e=K^*@j266cJ8f;e+I@0J7!(Lt9iU|AR zXC=>hm7Q$NvmJe{@I=_D*|iI-Y1p-l9#i{P=Z{gPn_V@Xhx;8v_cY2y#t=p;_|eZ)A0mZ^u(I% zFDZ8|!x(WCHJips8Gmb37#p(zz`Om!Swoglly&Sq)A0Ca;N`BaD*^8B7xbS(T`TFrpY=fKGO13nlC%9z~M`L(xi#(uI%T zezb-r%@nOQAyEpE#9jFSG_~qfJt-p?GMx3XfF}sy2}Mc)hO-|`{NA+sap<9PIi=R5 z=S(XX^Du!D6XPKwTo5u*J`I{Em!NJD)5MHfci+=P^ZzAGt(x>@%%*i?k43FQNgb&t z4UCC(hCS#^t-{kOT2CL#E+7LyFiaQJ1!MpShUtR3fD8b^ zFkMg=kO3eVrVHuFqv&vC>y1rD&MKq6vf5y}^LoGGgH-5~Jsi0`Y(oVmXc#lG#rTKC`1`c} zYEgfi*59P{*QwaEbnIn1_97K~o{9~oqEA!NC&}pJWON`o^B_6%C^_>mIdgwu`p&}i z-G%9U^Hco`Q@7`*Zp}~Ln49dIo47JJaV;@^DKUOIF@7O2c5Zg;?Cj{N+0oPSk(2R} z6Y-IVK73psM*6Q~`Y$>5Gc-1E_y#U%i&ERF5rkPc`i81O`%N6&j;dQwvm8KSUWG)Z zD7QmU6I5YXRy#G|X|~<`QiD%fSZv^0eD$0lhpfB-vqnM+;oKWhc%c&_&<;C-l8XcY zkRT|!$b+Rm+NXHnkzj4F3Y&Wy{6cT1;1J2ZZnHDOVR8r}9z;T2lEy_`WJZ(2Ya>jN zAOk_2E;3tFi?0!QWEBFtm9Yr4%?|8kR<@jVl$Y%R%{&78c?q!PfTd|XupGm1b_N%d zX{6hXXQwI#xF&B+E+^i($Rh|T97To0VQbiCmDLW4aX1_l%~C9D!3c}iBOyLwk+hvz zktH23)C4uCpr9-PLzi!tyOE0|jYRpDI~=d%6G>XmJZvKs;T4Lp(o|j<6P-B7c~r*ezFk*!<8P|<7t%?87Fj2_+rF5%ZnQIXR3U%VOF0b&=1Cu4j_pLx{pZ zk==FPO24UJXcNI!c4y^|ijp&)V!C9rr9 Mt8VZLQ@>ABQ5Ze1Du>(P-jz<+F32GvN13+)L^R$?Uzw7f@nKlb&V~E_eXp~ykn_HNokp`cOw)vCS!G*@x1UBw^OMWV#|;3{ zwy+=xAp}UGRVX}iOQk%{&sFK>O&}EVS#=#=r2_N&_w%2@U~afxf6){IQSvu|{p{Hz zcWv#t6o~*rfRze(dJ0Gq#IH*Nr)2YquB-&(E5iDvUfl!SEIwp%LYrAP!yM1_*i zLrJBe^z}iBM$vWsI^yrS#o=u)OGUYgiaxJ|;Dd-qseaY?j8z2zOMq$V&_6^z@!2%PH1skcWmKEiM9z0@$`c16c-b8_wJu zyo(DU9!F(;y?jg8QxX86(MTa227mkj4i4bnyXQZaW&hYVT6gcl+ua50b%;<10vHD1 z&w!@sD@hXQIDqT^wQB-^>-x1e+P(P%aNROB4WhpvNF)$gTIwj%wjpF0^2!R3OhUxt zpr+xBEO!XpuC7&n`2vwhKq?dfMFA}fb~X!pb{4cOAQ*&FEJ6$ofz>JwJ#V$Mg?cU* z)BF2%E0bYs>JHt_OfjhRld7&W&d?g&&%6^z*PWf fw5@k6{11Ks^>}Z5^u|FY00000NkvXXu0mjfDDha` literal 0 HcmV?d00001 diff --git a/jscripts/infusion/components/uploader/images/tick.png b/jscripts/infusion/components/uploader/images/tick.png new file mode 100644 index 0000000000000000000000000000000000000000..a9925a06ab02db30c1e7ead9c701c15bc63145cb GIT binary patch literal 537 zcmV+!0_OdRP)Hs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1p
    ");var placeholder=$("");var placeholderId=fluid.allocateSimpleId(placeholder);container.append(placeholder);$("body").append(container);return placeholderId};var setupForFlash9=function(that,uploader){that.returnedOptions.uploadManager.options={flashURL:that.options.flash9URL||undefined,flashButtonPeerId:createFlash9MovieContainer()}};var createEmptyPlaceholder=function(){var placeholder=$("");fluid.allocateSimpleId(placeholder);return placeholder};var createButtonPlaceholder=function(browseButton){var placeholder=$("");var placeholderId=fluid.allocateSimpleId(placeholder);browseButton.before(placeholder);unbindSelectFiles();return placeholderId};var setupForFlash10=function(that,uploader){var browseButton=uploader.locate("browseButton");fluid.tabindex(browseButton,-1);that.isTransparent=that.options.flashButtonAlwaysVisible?false:(!$.browser.msie||that.options.transparentEvenInIE);var peerId=that.isTransparent?createButtonPlaceholder(browseButton):fluid.allocateSimpleId(browseButton);that.returnedOptions.uploadManager.options={flashURL:that.options.flash10URL||undefined,flashButtonImageURL:that.isTransparent?undefined:that.options.flashButtonImageURL,flashButtonPeerId:peerId,flashButtonHeight:that.isTransparent?browseButton.outerHeight():that.options.flashButtonHeight,flashButtonWidth:that.isTransparent?browseButton.outerWidth():that.options.flashButtonWidth,flashButtonWindowMode:that.isTransparent?SWFUpload.WINDOW_MODE.TRANSPARENT:SWFUpload.WINDOW_MODE.OPAQUE,flashButtonCursorEffect:SWFUpload.CURSOR.HAND,listeners:{afterReady:createAfterReadyHandler(that,uploader),onUploadStart:function(){uploader.uploadManager.swfUploader.setButtonDisabled(true)},afterUploadComplete:function(){uploader.uploadManager.swfUploader.setButtonDisabled(false)}}}};fluid.swfUploadSetupDecorator=function(uploader,options){var that={};fluid.mergeComponentOptions(that,"fluid.swfUploadSetupDecorator",options);that.flashVersion=swfobject.getFlashPlayerVersion().major;prepareUpstreamOptions(that,uploader);if(that.flashVersion===9){setupForFlash9(that,uploader)}else{setupForFlash10(that,uploader)}return that};fluid.defaults("fluid.swfUploadSetupDecorator",{flashButtonAlwaysVisible:true,transparentEvenInIE:false,flashButtonImageURL:"../images/browse.png",flashButtonHeight:22,flashButtonWidth:106,styles:{browseButtonOverlay:"fl-uploader-browse-overlay"}});var swfUploadOptionsMap={uploadURL:"upload_url",flashURL:"flash_url",postParams:"post_params",fileSizeLimit:"file_size_limit",fileTypes:"file_types",fileTypesDescription:"file_types_description",fileUploadLimit:"file_upload_limit",fileQueueLimit:"file_queue_limit",flashButtonPeerId:"button_placeholder_id",flashButtonImageURL:"button_image_url",flashButtonHeight:"button_height",flashButtonWidth:"button_width",flashButtonWindowMode:"button_window_mode",flashButtonCursorEffect:"button_cursor",debug:"debug"};var swfUploadEventMap={afterReady:"swfupload_loaded_handler",onFileDialog:"file_dialog_start_handler",afterFileQueued:"file_queued_handler",onQueueError:"file_queue_error_handler",afterFileDialog:"file_dialog_complete_handler",onFileStart:"upload_start_handler",onFileProgress:"upload_progress_handler",onFileError:"upload_error_handler",onFileSuccess:"upload_success_handler"};var mapNames=function(nameMap,source,target){var result=target||{};for(var key in source){var mappedKey=nameMap[key];if(mappedKey){result[mappedKey]=source[key]}}return result};var mapEvents=function(that,nameMap,target){var result=target||{};for(var eventType in that.events){var fireFn=that.events[eventType].fire;var mappedName=nameMap[eventType];if(mappedName){result[mappedName]=fireFn}}result.upload_complete_handler=function(file){that.queueManager.finishFile(file);if(that.queueManager.shouldUploadNextFile()){that.swfUploader.startUpload()}else{if(that.queueManager.queue.shouldStop){that.swfUploader.stopUpload()}that.queueManager.complete()}};return result};var browse=function(that){if(that.queue.isUploading){return }if(that.options.fileQueueLimit===1){that.swfUploader.selectFile()}else{that.swfUploader.selectFiles()}};var stopUpload=function(that){that.queue.shouldStop=true;that.events.onUploadStop.fire()};var bindEvents=function(that){var fileStatusUpdater=function(file){fluid.find(that.queue.files,function(potentialMatch){if(potentialMatch.id===file.id){potentialMatch.filestatus=file.filestatus;return true}})};that.events.afterFileQueued.addListener(function(file){that.queue.addFile(file)});that.events.onFileStart.addListener(function(file){that.queueManager.startFile();fileStatusUpdater(file)});that.events.onFileProgress.addListener(function(file,currentBytes,totalBytes){var currentBatch=that.queue.currentBatch;var byteIncrement=currentBytes-currentBatch.previousBytesUploadedForFile;currentBatch.totalBytesUploaded+=byteIncrement;currentBatch.bytesUploadedForFile+=byteIncrement;currentBatch.previousBytesUploadedForFile=currentBytes;fileStatusUpdater(file)});that.events.onFileError.addListener(function(file,error){if(error===fluid.uploader.errorConstants.UPLOAD_STOPPED){that.queue.isUploading=false}else{if(that.queue.isUploading){that.queue.currentBatch.totalBytesUploaded+=file.size;that.queue.currentBatch.numFilesErrored++}}fileStatusUpdater(file)});that.events.onFileSuccess.addListener(function(file){if(that.queue.currentBatch.bytesUploadedForFile===0){that.queue.currentBatch.totalBytesUploaded+=file.size}fileStatusUpdater(file)});that.events.afterUploadComplete.addListener(function(){that.queue.isUploading=false})};var removeFile=function(that,file){that.queue.removeFile(file);that.swfUploader.cancelUpload(file.id);that.events.afterFileRemoved.fire(file)};var setupSwfUploadManager=function(that,events){that.events=events;that.queue=fluid.fileQueue();that.queueManager=fluid.fileQueue.manager(that.queue,that.events);that.swfUploadSettings=mapNames(swfUploadOptionsMap,that.options);mapEvents(that,swfUploadEventMap,that.swfUploadSettings);that.swfUploader=new SWFUpload(that.swfUploadSettings);bindEvents(that)};fluid.swfUploadManager=function(events,options){var that={};fluid.mergeComponentOptions(that,"fluid.swfUploadManager",options);fluid.mergeListeners(events,that.options.listeners);that.browseForFiles=function(){browse(that)};that.removeFile=function(file){removeFile(that,file)};that.start=function(){that.queueManager.start();that.swfUploader.startUpload()};that.stop=function(){stopUpload(that)};setupSwfUploadManager(that,events);return that};fluid.defaults("fluid.swfUploadManager",{uploadURL:"",flashURL:"../../../lib/swfupload/flash/swfupload.swf",flashButtonPeerId:"",postParams:{},fileSizeLimit:"20480",fileTypes:"*",fileTypesDescription:null,fileUploadLimit:0,fileQueueLimit:0,debug:false})})(jQuery,fluid_1_1); \ No newline at end of file diff --git a/jscripts/infusion/components/uploader/js/Scroller.js b/jscripts/infusion/components/uploader/js/Scroller.js new file mode 100644 index 000000000..156a473eb --- /dev/null +++ b/jscripts/infusion/components/uploader/js/Scroller.js @@ -0,0 +1 @@ +fluid_1_1=fluid_1_1||{};(function($,fluid){var refreshView=function(that){var maxHeight=that.options.maxHeight;var isOverMaxHeight=(that.scrollingElm.children().eq(0).height()>maxHeight);var setHeight=(isOverMaxHeight)?maxHeight:"";that.scrollingElm.height(setHeight)};var scrollBottom=function(that){that.scrollingElm[0].scrollTop=that.scrollingElm[0].scrollHeight};var scrollTo=function(that,element){if(!element||element.length<1){return }var padTop=0;var padBottom=0;var elmPosTop=element[0].offsetTop;var elmHeight=element.height();var containerScrollTop=that.scrollingElm[0].scrollTop;var containerHeight=that.scrollingElm.height();if(that.options.padScroll){var prevElmHeight=element.prev().height();padTop=(prevElmHeight+elmHeight<=containerHeight)?prevElmHeight:0;var nextElmHeight=element.next().height();padBottom=(nextElmHeight+elmHeight<=containerHeight)?nextElmHeight:0}if((elmPosTop-padTop)(containerScrollTop+containerHeight)){elmHeight=(elmHeight6){that.scrollingElm.css("max-height",that.options.maxHeight)}};fluid.scroller=function(container,options){var that=fluid.initView("fluid.scroller",container,options);setupScroller(that);that.scrollTo=function(element){scrollTo(that,element)};that.scrollBottom=function(){scrollBottom(that)};that.refreshView=function(){if($.browser.msie&&$.browser.version<7){refreshView(that)}};that.refreshView();return that};fluid.defaults("fluid.scroller",{selectors:{wrapper:".flc-scroller"},maxHeight:180,padScroll:true})})(jQuery,fluid_1_1); \ No newline at end of file diff --git a/jscripts/infusion/components/uploader/js/Uploader.js b/jscripts/infusion/components/uploader/js/Uploader.js new file mode 100644 index 000000000..640f2b173 --- /dev/null +++ b/jscripts/infusion/components/uploader/js/Uploader.js @@ -0,0 +1 @@ +fluid_1_1=fluid_1_1||{};(function($,fluid){var rowForFile=function(that,file){return that.locate("fileQueue").find("#"+file.id)};var errorRowForFile=function(that,file){return $("#"+file.id+"_error",that.container)};var fileForRow=function(that,row){var files=that.uploadManager.queue.files;for(var i=0;i=8))){row.hide()}that.container.append(row);row.fadeIn("slow");that.scroller.scrollBottom();createProgressorFromTemplate(that,row);that.refreshView()};var prepareForUpload=function(that){var rowButtons=that.locate("fileIconBtn",that.locate("fileRows"));rowButtons.attr("disabled","disabled");rowButtons.addClass(that.options.styles.dim)};var refreshAfterUpload=function(that){var rowButtons=that.locate("fileIconBtn",that.locate("fileRows"));rowButtons.removeAttr("disabled");rowButtons.removeClass(that.options.styles.dim)};var changeRowState=function(that,row,newState){row.removeClass(that.options.styles.ready).removeClass(that.options.styles.error).addClass(newState)};var markRowAsComplete=function(that,file){var row=rowForFile(that,file);changeRowState(that,row,that.options.styles.uploaded);row.attr("title",that.options.strings.status.success);fluid.enabled(row,false);var removeRowBtn=that.locate("fileIconBtn",row);removeRowBtn.unbind("click");removeRowBtn.removeClass(that.options.styles.remove);removeRowBtn.attr("title",that.options.strings.status.success)};var renderErrorInfoRowFromTemplate=function(that,fileRow,error){var errorRow=that.errorInfoRowTemplate.clone();errorRow.attr("id",fileRow.attr("id")+"_error");var errorType=fluid.keyForValue(fluid.uploader.errorConstants,error);var errorMsg=that.options.strings.errors[errorType];that.locate("errorText",errorRow).text(errorMsg);fileRow.after(errorRow);that.scroller.scrollTo(errorRow)};var showErrorForFile=function(that,file,error){hideFileProgress(that,file);if(file.filestatus===fluid.uploader.fileStatusConstants.ERROR){var fileRowElm=rowForFile(that,file);changeRowState(that,fileRowElm,that.options.styles.error);renderErrorInfoRowFromTemplate(that,fileRowElm,error)}};var bindModelEvents=function(that){that.returnedOptions={listeners:{afterFileQueued:that.addFile,onUploadStart:that.prepareForUpload,onFileStart:that.showFileProgress,onFileProgress:that.updateFileProgress,onFileSuccess:that.markFileComplete,onFileError:that.showErrorForFile,afterFileComplete:that.hideFileProgress,afterUploadComplete:that.refreshAfterUpload}}};var addKeyboardNavigation=function(that){fluid.tabbable(that.container);that.selectableContext=fluid.selectable(that.container,{selectableSelector:that.options.selectors.fileRows,onSelect:function(itemToSelect){$(itemToSelect).addClass(that.options.styles.selected)},onUnselect:function(selectedItem){$(selectedItem).removeClass(that.options.styles.selected)}})};var prepareTemplateElements=function(that){that.rowTemplate=that.locate("rowTemplate").remove();if($.browser.msie&&($.browser.version>=8)){that.rowTemplate.removeClass(that.options.styles.hiddenTemplate)}that.errorInfoRowTemplate=that.locate("errorInfoRowTemplate").remove();that.errorInfoRowTemplate.removeClass(that.options.styles.hiddenTemplate);that.rowProgressorTemplate=that.locate("rowProgressorTemplate",that.uploadContainer).remove()};var setupFileQueue=function(that,uploadManager){that.uploadManager=uploadManager;that.scroller=fluid.scroller(that.container);prepareTemplateElements(that);addKeyboardNavigation(that);bindModelEvents(that)};fluid.fileQueueView=function(container,parentContainer,uploadManager,options){var that=fluid.initView("fluid.fileQueueView",container,options);that.uploadContainer=parentContainer;that.fileProgressors={};that.addFile=function(file){addFile(that,file)};that.removeFile=function(file){removeRowForFile(that,file)};that.prepareForUpload=function(){prepareForUpload(that)};that.refreshAfterUpload=function(){refreshAfterUpload(that)};that.showFileProgress=function(file){startFileProgress(that,file)};that.updateFileProgress=function(file,fileBytesComplete,fileTotalBytes){updateFileProgress(that,file,fileBytesComplete,fileTotalBytes)};that.markFileComplete=function(file){progressorForFile(that,file).update(100,"100%");markRowAsComplete(that,file)};that.showErrorForFile=function(file,error){showErrorForFile(that,file,error)};that.hideFileProgress=function(file){hideFileProgress(that,file)};that.refreshView=function(){that.scroller.refreshView();that.selectableContext.refresh()};setupFileQueue(that,uploadManager);return that};fluid.defaults("fluid.fileQueueView",{selectors:{fileRows:".flc-uploader-file",fileName:".flc-uploader-file-name",fileSize:".flc-uploader-file-size",fileIconBtn:".flc-uploader-file-action",errorText:".flc-uploader-file-error",rowTemplate:".flc-uploader-file-tmplt",errorInfoRowTemplate:".flc-uploader-file-error-tmplt",rowProgressorTemplate:".flc-uploader-file-progressor-tmplt"},styles:{hover:"fl-uploader-file-hover",selected:"fl-uploader-file-focus",ready:"fl-uploader-file-state-ready",uploading:"fl-uploader-file-state-uploading",uploaded:"fl-uploader-file-state-uploaded",error:"fl-uploader-file-state-error",remove:"fl-uploader-file-action-remove",dim:"fl-uploader-dim",hiddenTemplate:"fl-uploader-hidden-templates"},strings:{progress:{toUploadLabel:"To upload: %fileCount %fileLabel (%totalBytes)",singleFile:"file",pluralFiles:"files"},status:{success:"File Uploaded",error:"File Upload Error"},errors:{HTTP_ERROR:"File upload error: a network error occured or the file was rejected (reason unknown).",IO_ERROR:"File upload error: a network error occured.",UPLOAD_LIMIT_EXCEEDED:"File upload error: you have uploaded as many files as you are allowed during this session",UPLOAD_FAILED:"File upload error: the upload failed for an unknown reason.",QUEUE_LIMIT_EXCEEDED:"You have as many files in the queue as can be added at one time. Removing files from the queue may allow you to add different files.",FILE_EXCEEDS_SIZE_LIMIT:"One or more of the files that you attempted to add to the queue exceeded the limit of %fileSizeLimit.",ZERO_BYTE_FILE:"One or more of the files that you attempted to add contained no data.",INVALID_FILETYPE:"One or more files were not added to the queue because they were of the wrong type."}}})})(jQuery,fluid_1_1);(function($,fluid){var fileOrFiles=function(that,numFiles){return(numFiles===1)?that.options.strings.progress.singleFile:that.options.strings.progress.pluralFiles};var enableElement=function(that,elm){elm.removeAttr("disabled");elm.removeClass(that.options.styles.dim)};var disableElement=function(that,elm){elm.attr("disabled","disabled");elm.addClass(that.options.styles.dim)};var showElement=function(that,elm){elm.removeClass(that.options.styles.hidden)};var hideElement=function(that,elm){elm.addClass(that.options.styles.hidden)};var setTotalProgressStyle=function(that,didError){didError=didError||false;var indicator=that.totalProgress.indicator;indicator.toggleClass(that.options.styles.totalProgress,!didError);indicator.toggleClass(that.options.styles.totalProgressError,didError)};var setStateEmpty=function(that){disableElement(that,that.locate("uploadButton"));if(that.uploadManager.queue.files.length===0){that.locate("browseButton").text(that.options.strings.buttons.browse);showElement(that,that.locate("instructions"))}};var setStateDone=function(that){disableElement(that,that.locate("uploadButton"));enableElement(that,that.locate("browseButton"));hideElement(that,that.locate("pauseButton"));showElement(that,that.locate("uploadButton"))};var setStateLoaded=function(that){that.locate("browseButton").text(that.options.strings.buttons.addMore);hideElement(that,that.locate("pauseButton"));showElement(that,that.locate("uploadButton"));enableElement(that,that.locate("uploadButton"));enableElement(that,that.locate("browseButton"));hideElement(that,that.locate("instructions"));that.totalProgress.hide()};var setStateUploading=function(that){that.totalProgress.hide(false,false);setTotalProgressStyle(that);hideElement(that,that.locate("uploadButton"));disableElement(that,that.locate("browseButton"));enableElement(that,that.locate("pauseButton"));showElement(that,that.locate("pauseButton"));that.locate(that.options.focusWithEvent.afterUploadStart).focus()};var renderUploadTotalMessage=function(that){var numReadyFiles=that.uploadManager.queue.getReadyFiles().length;var bytesReadyFiles=that.uploadManager.queue.sizeOfReadyFiles();var fileLabelStr=fileOrFiles(that,numReadyFiles);var totalStateStr=fluid.stringTemplate(that.options.strings.progress.toUploadLabel,{fileCount:numReadyFiles,fileLabel:fileLabelStr,totalBytes:fluid.uploader.formatFileSize(bytesReadyFiles)});that.locate("totalFileStatusText").html(totalStateStr)};var updateTotalProgress=function(that){var batch=that.uploadManager.queue.currentBatch;var totalPercent=fluid.uploader.derivePercent(batch.totalBytesUploaded,batch.totalBytes);var numFilesInBatch=batch.files.length;var fileLabelStr=fileOrFiles(that,numFilesInBatch);var totalProgressStr=fluid.stringTemplate(that.options.strings.progress.totalProgressLabel,{curFileN:batch.fileIdx+1,totalFilesN:numFilesInBatch,fileLabel:fileLabelStr,currBytes:fluid.uploader.formatFileSize(batch.totalBytesUploaded),totalBytes:fluid.uploader.formatFileSize(batch.totalBytes)});that.totalProgress.update(totalPercent,totalProgressStr)};var updateTotalAtCompletion=function(that){var numErroredFiles=that.uploadManager.queue.getErroredFiles().length;var numTotalFiles=that.uploadManager.queue.files.length;var fileLabelStr=fileOrFiles(that,numTotalFiles);var errorStr="";if(numErroredFiles>0){var errorLabelString=(numErroredFiles===1)?that.options.strings.progress.singleError:that.options.strings.progress.pluralErrors;setTotalProgressStyle(that,true);errorStr=fluid.stringTemplate(that.options.strings.progress.numberOfErrors,{errorsN:numErroredFiles,errorLabel:errorLabelString})}var totalProgressStr=fluid.stringTemplate(that.options.strings.progress.completedLabel,{curFileN:that.uploadManager.queue.getUploadedFiles().length,totalFilesN:numTotalFiles,errorString:errorStr,fileLabel:fileLabelStr,totalCurrBytes:fluid.uploader.formatFileSize(that.uploadManager.queue.sizeOfUploadedFiles())});that.totalProgress.update(100,totalProgressStr)};var bindDOMEvents=function(that){that.locate("browseButton").click(function(evnt){that.uploadManager.browseForFiles();evnt.preventDefault()});that.locate("uploadButton").click(function(){that.uploadManager.start()});that.locate("pauseButton").click(function(){that.uploadManager.stop()})};var updateStateAfterFileDialog=function(that){if(that.uploadManager.queue.getReadyFiles().length>0){setStateLoaded(that);renderUploadTotalMessage(that);that.locate(that.options.focusWithEvent.afterFileDialog).focus()}};var updateStateAfterFileRemoval=function(that){if(that.uploadManager.queue.getReadyFiles().length===0){setStateEmpty(that)}renderUploadTotalMessage(that)};var updateStateAfterPause=function(that){};var updateStateAfterCompletion=function(that){var userPaused=that.uploadManager.queue.shouldStop;if(that.uploadManager.queue.getReadyFiles().length===0){setStateDone(that)}else{setStateLoaded(that)}updateTotalAtCompletion(that)};var bindModelEvents=function(that){that.events.afterFileDialog.addListener(function(){updateStateAfterFileDialog(that)});that.events.afterFileRemoved.addListener(function(){updateStateAfterFileRemoval(that)});that.events.onUploadStart.addListener(function(){setStateUploading(that)});that.events.onUploadStop.addListener(function(){that.locate(that.options.focusWithEvent.afterUploadStop).focus()});that.events.onFileProgress.addListener(function(){updateTotalProgress(that)});that.events.onFileSuccess.addListener(function(){updateTotalProgress(that)});that.events.onFileError.addListener(function(file,error,message){if(error===fluid.uploader.errorConstants.UPLOAD_STOPPED){updateStateAfterPause(that)}});that.events.afterUploadComplete.addListener(function(){updateStateAfterCompletion(that)})};var initUploadManager=function(that){var manager=fluid.initSubcomponent(that,"uploadManager",[that.events,fluid.COMPONENT_OPTIONS]);return that.options.demo?fluid.demoUploadManager(manager):manager};var setupUploader=function(that){that.decorators=fluid.initSubcomponents(that,"decorators",[that,fluid.COMPONENT_OPTIONS]);that.uploadManager=initUploadManager(that);that.fileQueueView=fluid.initSubcomponent(that,"fileQueueView",[that.locate("fileQueue"),that.container,that.uploadManager,fluid.COMPONENT_OPTIONS]);that.totalProgress=fluid.initSubcomponent(that,"totalProgressBar",[that.container,fluid.COMPONENT_OPTIONS]);disableElement(that,that.locate("uploadButton"));bindDOMEvents(that);bindModelEvents(that)};fluid.uploader=function(container,options){var that=fluid.initView("fluid.uploader",container,options);setupUploader(that);return that};fluid.progressiveEnhanceableUploader=function(container,enhanceable,options){enhanceable=fluid.container(enhanceable);container=fluid.container(container);if(swfobject.getFlashPlayerVersion().major<9){enhanceable.show()}else{container.show();return fluid.uploader(container,options)}};fluid.uploader.formatFileSize=function(bytes){if(typeof bytes==="number"){if(bytes===0){return"0.0 KB"}else{if(bytes>0){if(bytes<1048576){return(Math.ceil(bytes/1024*10)/10).toFixed(1)+" KB"}else{return(Math.ceil(bytes/1048576*10)/10).toFixed(1)+" MB"}}}}return""};fluid.uploader.derivePercent=function(num,total){return Math.round((num*100)/total)};fluid.defaults("fluid.uploader",{demo:false,decorators:[{type:"fluid.swfUploadSetupDecorator"},{type:"fluid.manuallyDegrade",options:{selectors:{enhanceable:".fl-uploader.fl-progEnhance-basic"}}}],uploadManager:{type:"fluid.swfUploadManager"},fileQueueView:{type:"fluid.fileQueueView"},totalProgressBar:{type:"fluid.progress",options:{selectors:{progressBar:".flc-uploader-queue-footer",displayElement:".flc-uploader-total-progress",label:".flc-uploader-total-progress-text",indicator:".flc-uploader-total-progress",ariaElement:".flc-uploader-total-progress"}}},selectors:{fileQueue:".flc-uploader-queue",browseButton:".flc-uploader-button-browse",uploadButton:".flc-uploader-button-upload",pauseButton:".flc-uploader-button-pause",totalFileStatusText:".flc-uploader-total-progress-text",instructions:".flc-uploader-browse-instructions"},focusWithEvent:{afterFileDialog:"uploadButton",afterUploadStart:"pauseButton",afterUploadStop:"uploadButton"},styles:{disabled:"fl-uploader-disabled",hidden:"fl-uploader-hidden",dim:"fl-uploader-dim",totalProgress:"fl-uploader-total-progress-okay",totalProgressError:"fl-uploader-total-progress-errored"},events:{afterReady:null,onFileDialog:null,afterFileQueued:null,afterFileRemoved:null,onQueueError:null,afterFileDialog:null,onUploadStart:null,onUploadStop:null,onFileStart:null,onFileProgress:null,onFileError:null,onFileSuccess:null,afterFileComplete:null,afterUploadComplete:null},strings:{progress:{toUploadLabel:"To upload: %fileCount %fileLabel (%totalBytes)",totalProgressLabel:"Uploading: %curFileN of %totalFilesN %fileLabel (%currBytes of %totalBytes)",completedLabel:"Uploaded: %curFileN of %totalFilesN %fileLabel (%totalCurrBytes)%errorString",numberOfErrors:", %errorsN %errorLabel",singleFile:"file",pluralFiles:"files",singleError:"error",pluralErrors:"errors"},buttons:{browse:"Browse Files",addMore:"Add More",stopUpload:"Stop Upload",cancelRemaning:"Cancel remaining Uploads",resumeUpload:"Resume Upload"}}});fluid.uploader.errorConstants={HTTP_ERROR:-200,MISSING_UPLOAD_URL:-210,IO_ERROR:-220,SECURITY_ERROR:-230,UPLOAD_LIMIT_EXCEEDED:-240,UPLOAD_FAILED:-250,SPECIFIED_FILE_ID_NOT_FOUND:-260,FILE_VALIDATION_FAILED:-270,FILE_CANCELLED:-280,UPLOAD_STOPPED:-290};fluid.uploader.fileStatusConstants={QUEUED:-1,IN_PROGRESS:-2,ERROR:-3,COMPLETE:-4,CANCELLED:-5};var renderLink=function(renderLocation,text,classes,appendBeside){var link=$(""+text+"");link.addClass(classes);if(renderLocation==="before"){appendBeside.before(link)}else{appendBeside.after(link)}return link};var toggleVisibility=function(toShow,toHide){if(window.opera){toShow.show().removeClass("hideUploaderForOpera");toHide.show().addClass("hideUploaderForOpera")}else{toShow.show();toHide.hide()}};var defaultControlRenderer=function(that){var degradeLink=renderLink(that.options.defaultRenderLocation,that.options.strings.degradeLinkText,that.options.styles.degradeLinkClass,that.enhancedContainer);degradeLink.addClass("flc-manuallyDegrade-degrade");var enhanceLink=renderLink(that.options.defaultRenderLocation,that.options.strings.enhanceLinkText,that.options.styles.enhanceLinkClass,that.degradedContainer);enhanceLink.addClass("flc-manuallyDegrade-enhance")};var fetchControls=function(that){that.degradeControl=that.locate("degradeControl");that.enhanceControl=that.locate("enhanceControl")};var setupManuallyDegrade=function(that){if(!that.degradedContainer.length){return }fetchControls(that);if(!that.degradeControl.length&&!that.enhanceControl.length){that.options.controlRenderer(that);fetchControls(that)}that.degradeControl.click(that.degrade);that.enhanceControl.click(that.enhance);that.enhanceControl.hide()};var determineContainer=function(options){var defaults=fluid.defaults("fluid.manuallyDegrade");return(options&&options.container)?options.container:defaults.container};fluid.manuallyDegrade=function(component,options){var container=determineContainer(options);var that=fluid.initView("fluid.manuallyDegrade",container,options);var isDegraded=false;that.enhancedContainer=component.container;that.degradedContainer=that.locate("enhanceable");that.degrade=function(){toggleVisibility(that.enhanceControl,that.degradeControl);toggleVisibility(that.degradedContainer,that.enhancedContainer);isDegraded=true};that.enhance=function(){toggleVisibility(that.degradeControl,that.enhanceControl);toggleVisibility(that.enhancedContainer,that.degradedContainer);isDegraded=false};that.isDegraded=function(){return isDegraded};setupManuallyDegrade(that);return that};fluid.defaults("fluid.manuallyDegrade",{container:"body",controlRenderer:defaultControlRenderer,defaultRenderLocation:"before",strings:{degradeLinkText:"Switch to the standard single-file Uploader",enhanceLinkText:"Switch to the Flash-based multi-file Uploader"},selectors:{enhanceable:".fl-ProgEnhance-basic",degradeControl:".flc-manuallyDegrade-degrade",enhanceControl:".flc-manuallyDegrade-enhance"},styles:{degradeLinkClass:"fl-uploader-manually-degrade",enhanceLinkClass:"fl-uploader-manually-enhance"}})})(jQuery,fluid_1_1); \ No newline at end of file diff --git a/jscripts/infusion/framework/core/js/DataBinding.js b/jscripts/infusion/framework/core/js/DataBinding.js new file mode 100644 index 000000000..94a515f03 --- /dev/null +++ b/jscripts/infusion/framework/core/js/DataBinding.js @@ -0,0 +1 @@ +fluid_1_1=fluid_1_1||{};(function($,fluid){fluid.VALUE={};fluid.BINDING_ROOT_KEY="fluid-binding-root";fluid.findData=function(elem,name){while(elem){var data=$.data(elem,name);if(data){return data}elem=elem.parentNode}};fluid.bindFossils=function(node,data,fossils){$.data(node,fluid.BINDING_ROOT_KEY,{data:data,fossils:fossils})};fluid.findForm=function(node){return fluid.findAncestor(node,function(element){return element.nodeName.toLowerCase()==="form"})};fluid.value=function(nodeIn,newValue){var node=fluid.unwrap(nodeIn);var multiple=false;if(node.nodeType===undefined&&node.length>1){node=node[0];multiple=true}var jNode=$(node);if("input"!==node.nodeName.toLowerCase()||!/radio|checkbox/.test(node.type)){return $(node).val(newValue)}var name=node.name;if(name===undefined){fluid.fail("Cannot acquire value from node "+fluid.dumpEl(node)+" which does not have name attribute set")}var elements;if(multiple){elements=nodeIn}else{var elements=document.getElementsByName(name);var scope=fluid.findForm(node);elements=$.grep(elements,function(element){if(element.name!==name){return false}return !scope||fluid.dom.isContainer(scope,element)})}if(newValue!==undefined){if(typeof (newValue)==="boolean"){newValue=(newValue?"true":"false")}$.each(elements,function(){this.checked=(newValue instanceof Array?$.inArray(this.value,newValue)!==-1:newValue===this.value)})}else{var checked=$.map(elements,function(element){return element.checked?element.value:null});return node.type==="radio"?checked[0]:checked}};fluid.applyChange=function(node,newValue,applier){node=fluid.unwrap(node);if(newValue===undefined){newValue=fluid.value(node)}if(node.nodeType===undefined&&node.length>0){node=node[0]}var root=fluid.findData(node,fluid.BINDING_ROOT_KEY);if(!root){fluid.fail("Bound data could not be discovered in any node above "+fluid.dumpEl(node))}var name=node.name;var fossil=root.fossils[name];if(!fossil){fluid.fail("No fossil discovered for name "+name+" in fossil record above "+fluid.dumpEl(node))}if(typeof (fossil.oldvalue)==="boolean"){newValue=newValue[0]?true:false}var EL=root.fossils[name].EL;if(applier){applier.fireChangeRequest({path:EL,value:newValue,source:node.id})}else{fluid.model.setBeanValue(root.data,EL,newValue)}};fluid.pathUtil={};var getPathSegmentImpl=function(accept,path,i){var segment=null;if(accept){segment=""}var escaped=false;var limit=path.length;for(;i=0&¤tNode.depth.fl-progEnhance-basic, .fl-ProgEnhance-basic { display: none; }")})(jQuery); \ No newline at end of file diff --git a/jscripts/infusion/framework/core/js/jquery.keyboard-a11y.js b/jscripts/infusion/framework/core/js/jquery.keyboard-a11y.js new file mode 100644 index 000000000..105f33ce4 --- /dev/null +++ b/jscripts/infusion/framework/core/js/jquery.keyboard-a11y.js @@ -0,0 +1 @@ +var fluid_1_1=fluid_1_1||{};var fluid=fluid||fluid_1_1;(function($,fluid){fluid.thatistBridge=function(name,peer){var togo=function(funcname){var segs=funcname.split(".");var move=peer;for(var i=0;i=elements.length){sc_that.activeItemIndex=0}if(sc_that.activeItemIndex<0&&sc_that.activeItemIndex!==NO_SELECTION){sc_that.activeItemIndex=elements.length-1}if(sc_that.activeItemIndex>=0){$(elements[sc_that.activeItemIndex]).focus()}};var prepareShift=function(selectionContext){unselectElement(selectionContext.selectedElement(),selectionContext);if(selectionContext.activeItemIndex===NO_SELECTION){selectionContext.activeItemIndex=-1}};var focusNextElement=function(selectionContext){prepareShift(selectionContext);++selectionContext.activeItemIndex;reifyIndex(selectionContext)};var focusPreviousElement=function(selectionContext){prepareShift(selectionContext);--selectionContext.activeItemIndex;reifyIndex(selectionContext)};var arrowKeyHandler=function(selectionContext,keyMap,userHandlers){return function(evt){if(evt.which===keyMap.next){focusNextElement(selectionContext);evt.preventDefault()}else{if(evt.which===keyMap.previous){focusPreviousElement(selectionContext);evt.preventDefault()}}}};var getKeyMapForDirection=function(direction){var keyMap;if(direction===fluid.a11y.orientation.HORIZONTAL){keyMap=LEFT_RIGHT_KEYMAP}else{if(direction===fluid.a11y.orientation.VERTICAL){keyMap=UP_DOWN_KEYMAP}}return keyMap};var tabKeyHandler=function(selectionContext){return function(evt){if(evt.which!==$.ui.keyCode.TAB){return }cleanUpWhenLeavingContainer(selectionContext);if(evt.shiftKey){selectionContext.focusIsLeavingContainer=true}}};var containerFocusHandler=function(selectionContext){return function(evt){var shouldOrig=selectionContext.options.autoSelectFirstItem;var shouldSelect=typeof (shouldOrig)==="function"?shouldOrig():shouldOrig;if(selectionContext.focusIsLeavingContainer){shouldSelect=false}if(shouldSelect&&evt.target===selectionContext.container.get(0)){if(selectionContext.activeItemIndex===NO_SELECTION){selectionContext.activeItemIndex=0}$(selectionContext.selectables[selectionContext.activeItemIndex]).focus()}return evt.stopPropagation()}};var containerBlurHandler=function(selectionContext){return function(evt){selectionContext.focusIsLeavingContainer=false;return evt.stopPropagation()}};var makeElementsSelectable=function(container,defaults,userOptions){var options=$.extend(true,{},defaults,userOptions);var keyMap=getKeyMapForDirection(options.direction);var selectableElements=options.selectableElements?options.selectableElements:container.find(options.selectableSelector);var that={container:container,activeItemIndex:NO_SELECTION,selectables:selectableElements,focusIsLeavingContainer:false,options:options};that.selectablesUpdated=function(focusedItem){if(typeof (that.options.selectablesTabindex)==="number"){that.selectables.fluid("tabindex",that.options.selectablesTabindex)}that.selectables.unbind("focus."+NAMESPACE_KEY);that.selectables.unbind("blur."+NAMESPACE_KEY);that.selectables.bind("focus."+NAMESPACE_KEY,selectableFocusHandler(that));that.selectables.bind("blur."+NAMESPACE_KEY,selectableBlurHandler(that));if(focusedItem){selectElement(focusedItem,that)}else{reifyIndex(that)}};that.refresh=function(){if(!that.options.selectableSelector){throw ("Cannot refresh selectable context which was not initialised by a selector")}that.selectables=container.find(options.selectableSelector);that.selectablesUpdated()};that.selectedElement=function(){return that.activeItemIndex<0?null:that.selectables[that.activeItemIndex]};if(keyMap){container.keydown(arrowKeyHandler(that,keyMap))}container.keydown(tabKeyHandler(that));container.focus(containerFocusHandler(that));container.blur(containerBlurHandler(that));that.selectablesUpdated();return that};fluid.selectable=function(target,options){target=$(target);var that=makeElementsSelectable(target,fluid.selectable.defaults,options);setData(target,CONTEXT_KEY,that);return that};fluid.selectable.select=function(target,toSelect){$(toSelect).focus()};fluid.selectable.selectNext=function(target){target=$(target);focusNextElement(getData(target,CONTEXT_KEY))};fluid.selectable.selectPrevious=function(target){target=$(target);focusPreviousElement(getData(target,CONTEXT_KEY))};fluid.selectable.currentSelection=function(target){target=$(target);var that=getData(target,CONTEXT_KEY);return $(that.selectedElement())};fluid.selectable.defaults={direction:fluid.a11y.orientation.VERTICAL,selectablesTabindex:-1,autoSelectFirstItem:true,rememberSelectionState:true,selectableSelector:".selectable",selectableElements:null,onSelect:null,onUnselect:null,onLeaveContainer:null};var checkForModifier=function(binding,evt){if(!binding.modifier){return true}var modifierKey=binding.modifier;var isCtrlKeyPresent=modifierKey&&evt.ctrlKey;var isAltKeyPresent=modifierKey&&evt.altKey;var isShiftKeyPresent=modifierKey&&evt.shiftKey;return isCtrlKeyPresent||isAltKeyPresent||isShiftKeyPresent};var makeActivationHandler=function(binding){return function(evt){var target=evt.target;if(!fluid.enabled(evt.target)){return }var code=evt.which?evt.which:evt.keyCode;if(code===binding.key&&binding.activateHandler&&checkForModifier(binding,evt)){var event=$.Event("fluid-activate");$(evt.target).trigger(event,[binding.activateHandler]);if(event.isDefaultPrevented()){evt.preventDefault()}}}};var makeElementsActivatable=function(elements,onActivateHandler,defaultKeys,options){var bindings=[];$(defaultKeys).each(function(index,key){bindings.push({modifier:null,key:key,activateHandler:onActivateHandler})});if(options&&options.additionalBindings){bindings=bindings.concat(options.additionalBindings)}setData(elements,ENABLEMENT_KEY,true);for(var i=0;i li { + display:block; + padding: 12px 0px 12px 12px; + text-decoration: none; + font-weight: bold; + outline: none; + border-style:solid; + border-width:1px 0px 0 0px; + overflow:auto; /* to encapsulate floating elements within */ +} +[class*=fl-list] > li:last-child { + border-bottom-width:1px; +} +[class*=fl-list] .fl-link-loading .fl-link-secondary { + display:none; +} +/* secondary link info behaviour */ +[class*=fl-list] > li .fl-link-secondary { + float:right; + margin-right:25px; + font-weight:normal; + font-size:.9em; +} + /* summary link info ehaviour */ +[class*=fl-list] > li .fl-link-summary { + display:block; + clear:right; + margin:0 25px 0 0px; + font-weight:normal; + font-size:0.8em; +} +[class*=fl-list] li .fl-icon ~ .fl-link-summary { + margin-left:30px; /* default size of fl-icon */ +} +/* icon behaviour */ +[class*=fl-list] li .fl-icon { + float:left; + margin-left:-6px; +} + +/*************************************************/ +/** + * fl-listmenu is now the way to create a link list + * The links create a hotspot over the entire list item, just like how it was by default + */ +.fl-list-menu { +} +.fl-list-menu li { + padding:0; +} +.fl-list-menu li a { + display:block; + padding: 12px 0px 12px 12px; + text-decoration: none; + font-weight: bold; + outline: none; +} + + +/*************************************************/ +/* Thumbnail and Expanded Thumbnails list features */ + +.fl-list-thumbnails > li { + margin-bottom:5px; + border-bottom-width:1px; +} +.fl-list-thumbnails > li a { + padding-top:6px; + overflow:auto; +} +/* summary behaviour */ +.fl-list-thumbnails > li a .fl-icon ~ .fl-link-summary { + margin:0 25px -12px 42px; +} +.fl-list-thumbnails:not(.fl-thumbnails-expanded):not(.fl-list-brief) > li a > .fl-icon ~ .fl-link-summary { + padding-bottom:10px; +} +/* icon behaviour */ +.fl-list-thumbnails > li a .fl-icon { + width:44px; + height:44px; + margin:-6px 10px -12px -12px; + -webkit-border-radius:0; +} +.fl-list-thumbnails.fl-thumbnails-expanded li { + margin:0; + border-bottom-width:0; +} +.fl-list-thumbnails.fl-thumbnails-expanded > li:last-child { + border-bottom-width:1px; +} +.fl-list-thumbnails.fl-thumbnails-expanded > li a { + -webkit-border-radius:0; + padding: 10px 0px 10px 10px; +} +.fl-list-thumbnails.fl-thumbnails-expanded > li a .fl-icon { + width:60px; + height:60px; + margin:-5px 5px -5px -5px; + -webkit-border-radius:0; +} +.fl-list-thumbnails.fl-thumbnails-expanded > li a .fl-icon ~ .fl-link-summary { + margin: 0px 0px -5px 60px; +} +.fl-list-thumbnails.fl-thumbnails-expanded > li a .fl-link-secondary { + padding-top:0; +} + +/*************************************************/ +/* Brief lists auto trim summary content + add ellipsis if necessary */ + +.fl-list-brief > li a { + padding-bottom:6px; +} +.fl-list-brief > li a > .fl-link-summary { + height:1.3em; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; +} +.fl-list-brief > li a > .fl-icon ~ .fl-link-summary { + margin-left:0; +} +.fl-list-brief.fl-list-thumbnails:not(.fl-thumbnails-expanded) > li a > .fl-icon { + margin:-6px 10px -6px -12px; +} +.fl-list-brief.fl-list-thumbnails:not(.fl-thumbnails-expanded) > li a > .fl-link-secondary { + padding-top:0px; +} + +/*************************************************/ +/* Thumbnails in Grid layout, changes the entire layout of the list item and contents */ +.fl-list-thumbnails.fl-grid { + overflow:auto; +} +.fl-list-thumbnails.fl-grid li { + display:inline-block; + float:none; + vertical-align:top; + width:auto; + height:auto; + margin:4px; +} +.fl-list-thumbnails.fl-grid li a { + padding:0; + margin:0; + text-indent:-5000px; + overflow:hidden; + width:44px; + height:44px; +} +.fl-list-thumbnails.fl-grid li a .fl-caption { +} +.fl-list-thumbnails.fl-grid li a .fl-icon { + display:block; + float:none; + margin:0 auto; + padding:0; + width:auto; + height:auto; + max-height:44px; + max-width:44px; +} +.fl-list-thumbnails.fl-thumbnails-expanded.fl-grid li { + margin:0px; +} +.fl-list-thumbnails.fl-thumbnails-expanded.fl-grid li a { + height:64px; + width:64px; +} +.fl-list-thumbnails.fl-thumbnails-expanded.fl-grid > li a .fl-icon { + max-height:64px; + max-width:64px; +} +/* Reiterate the properties of fl-table-cell for specificities sake */ +.fl-list-thumbnails.fl-grid li .fl-table { +} +.fl-list-thumbnails.fl-grid li .fl-table .fl-table-cell { + display:table-cell; + vertical-align:middle; +} +.fl-list-thumbnails.fl-grid .fl-grid-caption { + position:relative; + height:auto; + text-align:center; + font-size:12px; +} +.fl-thumbnails-expanded.fl-grid .fl-grid-caption { + width:64px; +} +/*************************************************/ +/* Glossy icons (and thumbnails?) */ + +/* Glossiness for 30x30 icons */ +.fl-list-glossy > li a::before { + position:absolute; + content:" "; + float:left; + width:28px; + height:28px; + margin:-4px 0 0 -5px; + -webkit-border-radius:3px; + -webkit-background-size: 30px 60px; + background-repeat:no-repeat; +} +/* Glossiness for 44x44 thumbnails */ +.fl-list-thumbnails.fl-list-glossy > li a::before { + width:42px; + height:42px; + margin:-11px; + -webkit-border-radius:0px; + -webkit-background-size: 44px 50px; +} +/* Glossiness for 60x60 thumbnails */ +.fl-list-thumbnails.fl-thumbnails-expanded.fl-list-glossy > li a::before { + width:58px; + height:58px; + margin:-4px; + -webkit-border-radius:0px; + -webkit-background-size: 60px 40px; +} + + +/*************************************************/ +/* Content Panels */ +/*************************************************/ + +[class*=fl-container]:not(.fl-navbar) { + margin:10px; + width:auto; +} + +/* Panel influence for list corners */ +[class*=fl-container] [class*=fl-list] > li { + border-right-width: 1px; + border-left-width: 1px; +} +[class*=fl-container] [class*=fl-list] > li:first-child, +[class*=fl-container] [class*=fl-list] > li:first-child a { + -webkit-border-top-left-radius: 8px; + -webkit-border-top-right-radius: 8px; +} + +[class*=fl-container] [class*=fl-list] > li:last-child, +[class*=fl-container] [class*=fl-list] > li:last-child a { + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; +} + +[class*=fl-container] [class*=fl-list]:not(.fl-thumbnails-expanded) > li a .fl-icon { + -webkit-border-bottom-left-radius: 8px; + -webkit-border-top-left-radius: 8px; +} + +[class*=fl-container] .fl-list-thumbnails:not(.fl-thumbnails-expanded) > li, +[class*=fl-container] .fl-list-thumbnails:not(.fl-thumbnails-expanded) > li a { + -webkit-border-radius: 8px; +} +/* Panel Auto Headings */ +/* The first element found becomes the "heading" */ +.fl-container-autoHeading > *:first-child { + padding:10px; + margin:0; + -webkit-border-top-left-radius: 8px; + -webkit-border-top-right-radius: 8px; +} +/* The last element found becomes the "content" - list or otherwise */ +.fl-container-autoHeading > *:last-child { + margin:0; + -webkit-border-radius:0; + -webkit-border-bottom-left-radius:8px; + -webkit-border-bottom-right-radius:8px; +} + +.fl-container-autoHeading [class*=fl-list] > li { + margin:0; +} +.fl-container-autoHeading [class*=fl-list] > li:first-child, +.fl-container-autoHeading [class*=fl-list] > li:first-child a { + -webkit-border-radius:0; +} +.fl-container-autoHeading [class*=fl-list] > li:first-child:last-child, +.fl-container-autoHeading [class*=fl-list] > li:first-child:last-child a { + -webkit-border-bottom-left-radius:8px; + -webkit-border-bottom-right-radius:8px; +} + +/* Collapsing and expanding panels */ +.fl-container-collapsable { + max-height:900px; + overflow:hidden; + -webkit-border-radius:8px; +} + +/***************************/ +.fl-table { + display:table; + border-collapse:collapse; +} +.fl-table-row { + display:table-row; +} +.fl-table-cell { + display:table-cell; + vertical-align:middle; +} + + +/************************************************************************************************/ +/* + Theme/Device specific layout adjustments + The base CSS is modelled off the iPhone WebKit environment, which is a good UI to emulate + The following CSS modifies the base slightly to be a little more device-friendly and native-ish for other Webkit environments +*/ + +.fl-theme-android { + font:normal 17px "Droid Sans", sans-serif; +} +.fl-theme-android [class*=fl-list] > li { + border-width:0px; +} +.fl-theme-android h1, +.fl-theme-android h2, +.fl-theme-android h3 { + padding:5px; + margin:0; +} +.fl-theme-android [class*=fl-container] h1, +.fl-theme-android [class*=fl-container] h2, +.fl-theme-android [class*=fl-container] h3 { + margin:0 -10px; +} +.fl-theme-android .fl-list-menu a { + font-weight:normal; +} +.fl-theme-android .fl-tabs li:not(:last-child) { + margin-right:-4px; /* for some reason, the use of Droid Sans font make the right border dissappear (on Chrome+XP). This 1px change fixes it */ +} +/* Panel influence for list corners */ +.fl-theme-android [class*=fl-container] [class*=fl-list] > li:first-child, +.fl-theme-android [class*=fl-container] [class*=fl-list] > li:first-child a { + -webkit-border-top-left-radius: 0px; + -webkit-border-top-right-radius: 0px; +} +.fl-theme-android [class*=fl-container]:not(.fl-container-autoHeading) [class*=fl-list] > li:last-child, +.fl-theme-android [class*=fl-container]:not(.fl-container-autoHeading) [class*=fl-list] > li:last-child a { + -webkit-border-bottom-left-radius: 0px; + -webkit-border-bottom-right-radius: 0px; +} +.fl-theme-android .fl-list-thumbnails.fl-thumbnails-expanded > li:last-child { + border:0; +} +.fl-theme-android .fl-container-autoHeading > :first-child { + margin-left:0; + margin-right:0; +} +.fl-theme-android .fl-container-autoHeading > :last-child { + border-width:0 1px 1px; +} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-mobile-theme-android.css b/jscripts/infusion/framework/fss/css/fss-mobile-theme-android.css new file mode 100644 index 000000000..72ff81e9a --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-mobile-theme-android.css @@ -0,0 +1,279 @@ +/***************************************/ +/* general styles */ +.fl-theme-android { + background:#222; + color:#fff; +} + +.fl-theme-android .fl-bevel-black { + text-shadow: rgba(0,0,0,0.35) 0px -1px 0px; +} +.fl-theme-android .fl-bevel-white { + text-shadow: rgba(200,200,200,0.85) 0px 1px 0px; +} + +.fl-theme-android a { + -webkit-touch-callout:none; /* prevents iphone popup menu to copy / follow / bookmark a particular link */ + -webkit-tap-highlight-color: rgba(0,0,0,0); + text-decoration:none; + color:#fff; +} + +.fl-theme-android h1, +.fl-theme-android h2, +.fl-theme-android h3 { + text-shadow: rgba(255,255,255,1) 0px 1px 0px; + color:#000; + background: #999 -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#999)) no-repeat top left; +} + +/***************************************/ +/* Navigation Bar */ + +.fl-theme-android .fl-navbar { + border-top-color:#333; + border-bottom-color:#000; + background-image: -webkit-gradient(linear, left top, left bottom, + from(#666), + color-stop(0.5, #333), + color-stop(0.50, #000), + to(#000) + ); + -webkit-box-shadow: 0px 0px 5px rgba(0,0,0,0.5); +} + +.fl-theme-android .fl-navbar { + color:#fff; +} + +.fl-theme-android .fl-navbar h1 { + color:#fff; + background:transparent; + text-shadow: rgba(0,0,0,0.5) 0px -1px 0px; +} +.fl-theme-android .fl-navbar a { + color: #fff; + text-shadow: rgba(0,0,0,0.5) 0px -1px 0px; +} +.fl-theme-android .fl-navbar .fl-button { + -webkit-border-image:none; + background-image: -webkit-gradient(linear, left top, left bottom, + from(#444), + color-stop(0.5, #666), + color-stop(0.50, #333), + to(#000) + ); +} +.fl-theme-android .fl-navbar [class*=fl-button]:active { + background-image: -webkit-gradient(linear, left top, left bottom, + from(#ffb347), + to(#ff702f) + ); +} + +.fl-theme-android .fl-navbar .fl-backButton { + /* See mobile layout for details */ + -webkit-border-image: url(../images/themes/android/navbar_back_button_insetShadow.png) 0 15 stretch; + -webkit-mask-box-image: url(../images/themes/android/backbutton_mask.png) 0 15 stretch; +} + +.fl-theme-android .fl-navbar .fl-button-inner { + -webkit-border-image: url(../images/themes/android/navbar_normal_button_insetShadow.png) 5 5 5 5 stretch; +} + +/***************************************/ +/* general purpose gel buttons */ +/* effect is applied anywhere but the navbar, which has its own button look */ +.fl-theme-android .fl-button { + text-decoration:none; + font-weight:bold; + -webkit-border-image: url(../images/themes/android/button_bg_insetShadow.png) 10 stretch; +} +.fl-theme-android .fl-button.fl-bevel-white { + color:#333333; +} +.fl-theme-android .fl-button.fl-bevel-black { + color:#FFFFFF; +} +.fl-theme-android .fl-button-white { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(240,240,240,0.25)), + to(rgba(220,220,220,0.75)), + color-stop(0.5, rgba(240,240,240,1)), + color-stop(0.50, rgba(200,200,200,0.8)) + ); +} +.fl-theme-android .fl-button-black { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(106,106,106,0.25)), + to(rgba(00,00,00,0.75)), + color-stop(0.5, rgba(130,130,130,1)), + color-stop(0.50, rgba(75,75,75,0.8)) + ); +} +.fl-theme-android .fl-button-green { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(120,190,130,0.2)), + to(rgba(50,170,60,0.75)), + color-stop(0.5, rgba(120,190,130,1)), + color-stop(0.50, rgba(0,150,10,0.8)) + ); +} +.fl-theme-android .fl-button-blue { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(149, 184, 239,0.2)), + to(rgba(35,109,229,0.75)), + color-stop(0.5, rgba(149, 184, 239,1)), + color-stop(0.50, rgba(75,148,244,0.8)) + ); +} +/***************************************/ +/* tabs 1: small general purpose content dividers */ +.fl-theme-android .fl-tabs li { + background-image: -webkit-gradient(linear, left top, left bottom, + from(#666), + color-stop(0.5, #666), + color-stop(0.50, #000), + to(#000) + ); + -webkit-border-image: url(../images/themes/android/navbar_normal_button_insetShadow.png) 5 5 5 5 stretch; + -webkit-border-left-image: none; + -webkit-background-origin: border; + -webkit-background-clip: border; +} + +.fl-theme-android .fl-tabs li a { + color:#fff; +} + +.fl-theme-android .fl-tabs li a, +.fl-theme-android .fl-tabs .fl-tabs-active a { + border-right-color:rgba(255,255,255,0.35); + border-left-color:rgba(0,0,0,1); +} + +.fl-theme-android .fl-tabs .fl-tabs-active { + background-image: -webkit-gradient(linear, left top, left bottom, + from(#ff702f), + to(#ffb347) + ); +} +.fl-theme-android .fl-tabs .fl-tabs-active a { + color:#000; + text-shadow:none; +} + +/*************************************************/ +/* Ordered lists, Unordererd lists, Thumbnail lists, Icon lists, Definition lists */ +/*************************************************/ +/* Default list system setup */ + +.fl-theme-android .fl-list a { + color:#ffb347; +} +.fl-theme-android .fl-list-menu a { + color:#fff; +} + +.fl-theme-android [class*=fl-list] > li { + color: #fff; + background:transparent -webkit-gradient(linear, left bottom, right bottom, + from(rgba(0,0,0,0)), + to(rgba(0,0,0,0)), + color-stop(0.35, rgba(255,255,255,1)), + color-stop(0.75, rgba(255,255,255,1)) + ) no-repeat bottom center; + + -webkit-background-size: 100% 1px; +} + +.fl-theme-android [class*=fl-list] > li:last-child { + background:transparent; +} + +/* secondary link info behaviour */ +.fl-theme-android [class*=fl-list] > li .fl-link-secondary { + color: #fff; +} + + /* summary link info ehaviour */ +.fl-theme-android [class*=fl-list] > li .fl-link-summary { + color:#999; +} + +.fl-theme-android [class*=fl-list]:not(.fl-list):not(.fl-grid) a { +} + +/* A simulation for a:active on the device, requires JS */ +.fl-theme-android [class*=fl-list]:not(.fl-list):not(.fl-grid) a:active, +.fl-theme-android [class*=fl-list]:not(.fl-list):not(.fl-grid) a.fl-link-hilight { + color: #000; + background: -webkit-gradient(linear, left top, left bottom, from(#ffb347), to(#ff702f)); +} + +.fl-theme-android [class*=fl-list]:not(.fl-list) a:active, +.fl-theme-android [class*=fl-list]:not(.fl-list) a:active .fl-link-secondary, +.fl-theme-android [class*=fl-list]:not(.fl-list) a:active .fl-link-summary, +.fl-theme-android [class*=fl-list]:not(.fl-list) a.fl-link-loading * { + color: #000; +} + +.fl-theme-android [class*=fl-list]:not(.fl-list) a.fl-link-loading { + color: #fff; + background: url(../images/themes/android/listmenu_loader.gif) no-repeat 97% center, + -webkit-gradient(linear, left top, left bottom, from(#ffb347), to(#ff702f)); +} + +/***************************************/ +/* Grid overrides, removes nav coloring */ +.fl-theme-android .fl-grid li { + background-color:transparent; +} +.fl-theme-android .fl-grid li, +.fl-theme-android .fl-grid li a { + background-image:none; + border:none; +} +.fl-theme-android .fl-grid li a { + background-color:#333; +} +.fl-theme-android .fl-grid li a:active, +.fl-theme-android .fl-grid .fl-link-hilight { + color: #fff; + background: -webkit-gradient(linear, left top, left bottom, from(#ffb347), to(#ff702f)); +} +.fl-theme-android .fl-grid .fl-grid-caption { + font-weight:normal; +} +/***************************************/ +/* instructional text (usually embossed too)*/ +.fl-theme-android .fl-note { + color:#999; +} + +/****************************/ +/* Collapsing and expanding panels */ +.fl-theme-android .fl-container-autoHeading > *:first-child { + color:#444; + background: -webkit-gradient(linear, left top, left bottom, from(#CCC), to(#999)); + text-shadow:rgba(0, 0, 0, 0.292969) 0px 2px 2px; +} +.fl-theme-android .fl-container-autoHeading > *:first-child:focus { + color:#000; + background: -webkit-gradient(linear, left top, left bottom, from(#FFF), to(#CCC)); +} +.fl-theme-android .fl-container-autoHeading > :last-child { + background: transparent -webkit-gradient(radial, 50% 0, 10, 50% 0%, 300, + from(rgba(100,100,100,1)), + to(rgba(0,0,0,0)) + ); + border-color:#666; + border-style:solid; +} + +/****************************/ +/* Gloss tint for glossy icons/thumbnails */ + +.fl-theme-android .fl-list-glossy > li a::before { + background-image: -webkit-gradient(radial, 50% -15%, 10, 50% -50%, 45, from(rgba(255,255,255,1)), to(rgba(255,255,255,0)), color-stop(90%, rgba(255,255,255,.65))); +} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-mobile-theme-iphone.css b/jscripts/infusion/framework/fss/css/fss-mobile-theme-iphone.css new file mode 100644 index 000000000..b1b9dc71d --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-mobile-theme-iphone.css @@ -0,0 +1,264 @@ +/***************************************/ +/* general styles */ +.fl-theme-iphone { + background: -webkit-gradient(linear, left top, right top, + from(#c5ccd3), + to(#cfd5dd), + color-stop(0.80, #c5ccd3), + color-stop(0.80, #cfd5dd)); + -webkit-background-origin: padding-box; + -webkit-background-clip: content-box; + -webkit-background-size: 10px 1px; +} + +.fl-theme-iphone .fl-bevel-black { + text-shadow: rgba(0,0,0,0.35) 0px -1px 0px; +} +.fl-theme-iphone .fl-bevel-white { + text-shadow: rgba(255,255,255,1) 0px 1px 0px; +} + +.fl-theme-iphone a { + -webkit-touch-callout:none; /* prevents iphone popup menu to copy / follow / bookmark a particular link */ + -webkit-tap-highlight-color: rgba(0,0,0,0); + text-decoration:none; + color:#000; +} + +.fl-theme-iphone h1, +.fl-theme-iphone h2, +.fl-theme-iphone h3 { + text-shadow: rgba(255,255,255,1) 0px 1px 0px; + color:#4C566C; +} + +/***************************************/ +/* Navigation Bar */ + +/*body::before {*/ +.fl-theme-iphone .fl-navbar { + border-top-color:#ccd6e2; + border-bottom-color:#000; + background-image: -webkit-gradient(linear, left top, left bottom, + from(#B0BCCD), + color-stop(0.5, #889BB3), + color-stop(0.50, #6D84A2), + to(#6D84A2) + ); +} +.fl-theme-iphone .fl-navbar { + color:#fff; +} + +.fl-theme-iphone .fl-navbar h1 { + color:#fff; + text-shadow: rgba(0,0,0,0.5) 0px -1px 0px; +} + +.fl-theme-iphone .fl-navbar a { + color: #fff; + text-shadow: rgba(0,0,0,0.5) 0px -1px 0px; +} +.fl-theme-iphone .fl-navbar .fl-button { + -webkit-border-image:none; + background-image: -webkit-gradient(linear, left top, left bottom, + from(#9aafca), + color-stop(0.5, #6d8cb3), + color-stop(0.50, #4b6b90), + to(#4b6b90) + ); +} +.fl-theme-iphone .fl-navbar [class*=fl-button]:active { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(149, 184, 239,1)), + to(rgba(35,109,229,1)), + color-stop(0.5, rgba(149, 184, 239,1)), + color-stop(0.50, rgba(75,148,244,1)) + ); +} + +.fl-theme-iphone .fl-navbar .fl-backButton { + /* See mobile layout for details */ + -webkit-border-image: url(../images/themes/iphone/navbar_back_button_insetShadow.png) 0 15 stretch; + -webkit-mask-box-image: url(../images/themes/iphone/backbutton_mask.png) 0 15 stretch; +} + +.fl-theme-iphone .fl-navbar .fl-button-inner { + -webkit-border-image: url(../images/themes/iphone/navbar_normal_button_insetShadow.png) 5 5 5 5 stretch; +} + +/***************************************/ +/* general purpose gel buttons */ +/* effect is applied anywhere but the navbar, which has its own button look */ +.fl-theme-iphone .fl-button { + text-decoration:none; + font-weight:bold; + -webkit-border-image: url(../images/themes/iphone/button_bg_insetShadow.png) 10 stretch; +} +.fl-theme-iphone .fl-button.fl-bevel-white { + color:#333333; +} +.fl-theme-iphone .fl-button.fl-bevel-black { + color:#FFFFFF; +} +.fl-theme-iphone .fl-button-white { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(240,240,240,0.25)), + to(rgba(220,220,220,0.75)), + color-stop(0.5, rgba(240,240,240,1)), + color-stop(0.50, rgba(200,200,200,0.8)) + ); +} +.fl-theme-iphone .fl-button-black { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(106,106,106,0.25)), + to(rgba(00,00,00,0.75)), + color-stop(0.5, rgba(130,130,130,1)), + color-stop(0.50, rgba(75,75,75,0.8)) + ); +} +.fl-theme-iphone .fl-button-green { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(120,190,130,0.2)), + to(rgba(50,170,60,0.75)), + color-stop(0.5, rgba(120,190,130,1)), + color-stop(0.50, rgba(0,150,10,0.8)) + ); +} +.fl-theme-iphone .fl-button-blue { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(149, 184, 239,0.2)), + to(rgba(35,109,229,0.75)), + color-stop(0.5, rgba(149, 184, 239,1)), + color-stop(0.50, rgba(75,148,244,0.8)) + ); +} +/***************************************/ +/* tabs 1: small general purpose content dividers */ + +.fl-theme-iphone .fl-tabs li { + background-image: -webkit-gradient(linear, left top, left bottom, + from(#9aafca), + color-stop(0.5, #6d8cb3), + color-stop(0.50, #4b6b90), + to(#4b6b90) + ); + -webkit-border-image: url(../images/themes/iphone/navbar_normal_button_insetShadow.png) 5 5 5 5 stretch; + -webkit-border-left-image: none; + -webkit-background-origin: border; + -webkit-background-clip: border; +} + +.fl-theme-iphone .fl-tabs li a, +.fl-theme-iphone .fl-tabs .fl-tabs-active a { + color:#fff; + border-right-color:rgba(255,255,255,0.35); + border-left-color:rgba(0,0,0,0.35); +} + +.fl-theme-iphone .fl-tabs .fl-tabs-active { + background-image: -webkit-gradient(linear, left top, left bottom, + from(rgba(149, 184, 239,1)), + to(rgba(35,109,229,1)), + color-stop(0.5, rgba(149, 184, 239,1)), + color-stop(0.50, rgba(75,148,244,1)) + ); +} + +/*************************************************/ +/* Ordered lists, Unordererd lists, Thumbnail lists, Icon lists, Definition lists */ +/*************************************************/ +/* Default list system setup */ + +.fl-theme-iphone .fl-list a { + color:#4a94f4; +} + +.fl-theme-iphone [class*=fl-list] > li { + color: #000; + border-color: rgb(169,173,176); + background-color:#fff; +} + +/* secondary link info behaviour */ +.fl-theme-iphone [class*=fl-list] > li .fl-link-secondary { + color: rgb(50, 79, 133); +} + + /* summary link info ehaviour */ +.fl-theme-iphone [class*=fl-list] > li .fl-link-summary { + color:#999; +} + +.fl-theme-iphone [class*=fl-list]:not(.fl-list):not(.fl-grid) a { + background: url(../images/themes/iphone/listmenu_arrow.png) no-repeat right 5px; +} + +/* A simulation for a:active on the device, requires JS */ +/* since .fl-list is for mixed material lists, dont include them in these effects */ +.fl-theme-iphone [class*=fl-list]:not(.fl-list):not(.fl-grid) a:active, +.fl-theme-iphone [class*=fl-list]:not(.fl-list):not(.fl-grid) a.fl-link-hilight { + color: #fff; + background: url(../images/themes/iphone/listmenu_arrow.png) no-repeat right -25px, + -webkit-gradient(linear, left top, left bottom, from(#4a94f4), to(#236de5)); +} + +.fl-theme-iphone [class*=fl-list]:not(.fl-list) a:active, +.fl-theme-iphone [class*=fl-list]:not(.fl-list) a:active .fl-link-secondary, +.fl-theme-iphone [class*=fl-list]:not(.fl-list) a:active .fl-link-summary, +.fl-theme-iphone [class*=fl-list]:not(.fl-list) a.fl-link-loading * { + color: #fff; +} + +.fl-theme-iphone [class*=fl-list]:not(.fl-list) a.fl-link-loading { + color: #fff; + background: url(../images/themes/iphone/listmenu_loader.gif) no-repeat 97% center, + -webkit-gradient(linear, left top, left bottom, from(#4a94f4), to(#236de5)); +} + +/***************************************/ +/* Grid overrides, removes nav coloring */ +.fl-theme-iphone .fl-grid li { + background-color:transparent; +} +.fl-theme-iphone .fl-grid li, +.fl-theme-iphone .fl-grid li a { + background-image:none; + border:none; +} +.fl-theme-iphone .fl-grid li a { + background-color:#333; +} +.fl-theme-iphone .fl-grid li a:active, +.fl-theme-iphone .fl-grid .fl-link-hilight { + color: #fff; + background: -webkit-gradient(linear, left top, left bottom, from(#4a94f4), to(#236de5)); +} + +/***************************************/ +/* instructional text (usually embossed too)*/ +.fl-theme-iphone .fl-note { + color:#4C566C; +} + +/****************************/ +/* Collapsing and expanding panels */ +.fl-theme-iphone .fl-container-autoHeading > *:first-child { + font-weight:normal; + color:rgba(255,255,255,1); + text-shadow: rgba(0,0,0, 1) 0px -1px 1px; + background: -webkit-gradient(linear, left top, left bottom, from(#999), to(#000)); +} +.fl-theme-iphone .fl-container-autoHeading > *:first-child:focus { + font-weight:normal; + color:rgba(0,0,0,1); + text-shadow: rgba(0,0,0, .5) 0px 1px 2px; + background: -webkit-gradient(linear, left top, left bottom, from(#FFF), to(#CCC)); +} + +/****************************/ +/* Gloss tint for glossy icons/thumbnails */ + +.fl-theme-iphone .fl-list-glossy > li a::before { + background-image: -webkit-gradient(radial, 50% -15%, 10, 50% -50%, 45, from(rgba(255,255,255,1)), to(rgba(255,255,255,0)), color-stop(90%, rgba(255,255,255,.65))); +} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-reset.css b/jscripts/infusion/framework/fss/css/fss-reset.css new file mode 100644 index 000000000..ccef1d895 --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-reset.css @@ -0,0 +1,37 @@ +table{font-size:inherit;font:100%;} +pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;} +html{color:#000;background:#FFF;} +body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;} +table{border-collapse:collapse;border-spacing:0;} +fieldset,img{border:0;} +address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;} +li{list-style:none;} +caption,th{text-align:left;} +h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;} +q:before,q:after{content:'';} +abbr,acronym{border:0;font-variant:normal;} +sup{vertical-align:text-top;} +sub{vertical-align:text-bottom;} +legend{color:#000;} +h1{font-size:138.5%;} +h2{font-size:123.1%;} +h3{font-size:108%;} +h1,h2,h3{margin:1em 0;} +h1,h2,h3,h4,h5,h6,strong{font-weight:bold;} +abbr,acronym{border-bottom:1px dotted #000;cursor:help;} +em{font-style:italic;} +blockquote,ul,ol,dl{margin:1em;} +ol,ul,dl{margin-left:2em;} +ol li{list-style:decimal outside;} +ul li{list-style:disc outside;} +dl dd{margin-left:1em;} +th,td{border:1px solid #000;padding:.5em;} +th{font-weight:bold;text-align:center;} +caption{margin-bottom:.5em;text-align:center;} +p,fieldset,table,pre{margin-bottom:1em;} +input[type=text],input[type=password],textarea{width:12.25em;*width:11.9em;} +input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;} +html{overflow:auto;font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;} +input,textarea,select{*font-size:100%;*font-family:sans-serif;} +input{*overflow:visible;*padding:0 1em;} +:focus{outline:2px solid black;} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-text.css b/jscripts/infusion/framework/fss/css/fss-text.css new file mode 100644 index 000000000..ef3f4b01f --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-text.css @@ -0,0 +1,61 @@ +.fl-font-size-70,.fl-font-size-70 body,.fl-font-size-70 input,.fl-font-size-70 select,.fl-font-size-70 textarea{font-size:.7em!important;line-height:1em!important;} +.fl-font-size-80,.fl-font-size-80 body,.fl-font-size-80 input,.fl-font-size-80 select,.fl-font-size-80 textarea{font-size:.8em!important;line-height:1.1em!important;} +.fl-font-size-90,.fl-font-size-90 body,.fl-font-size-90 input,.fl-font-size-90 select,.fl-font-size-90 textarea{font-size:.9em!important;line-height:1.2em!important;} +.fl-font-size-100,.fl-font-size-100 body,.fl-font-size-100 input,.fl-font-size-100 select,.fl-font-size-100 textarea{font-size:1em!important;line-height:1.3em!important;} +.fl-font-size-110,.fl-font-size-110 body,.fl-font-size-110 input,.fl-font-size-110 select,.fl-font-size-110 textarea{font-size:1.1em!important;line-height:1.4em!important;} +.fl-font-size-120,.fl-font-size-120 body,.fl-font-size-120 input,.fl-font-size-120 select,.fl-font-size-120 textarea{font-size:1.2em!important;line-height:1.5em!important;} +.fl-font-size-130,.fl-font-size-130 body,.fl-font-size-130 input,.fl-font-size-130 select,.fl-font-size-130 textarea{font-size:1.3em!important;line-height:1.6em!important;} +.fl-font-size-140,.fl-font-size-140 body,.fl-font-size-140 input,.fl-font-size-140 select,.fl-font-size-140 textarea{font-size:1.4em!important;line-height:1.7em!important;} +.fl-font-size-150,.fl-font-size-150 body,.fl-font-size-150 input,.fl-font-size-150 select,.fl-font-size-150 textarea{font-size:1.5em!important;line-height:1.8em!important;} +@media screen and(-webkit-min-device-pixel-ratio:0){[class~='fl-font-size-70'] input[type=submit],[class~='fl-font-size-70'] input[type=button]{padding:0 1em;} +[class~='fl-font-size-80'] input[type=submit],[class~='fl-font-size-80'] input[type=button]{font-size:.8em!important;padding:0 1em;} +[class~='fl-font-size-90'] input[type=submit],[class~='fl-font-size-90'] input[type=button]{font-size:.9em!important;padding:0 1em;} +[class~='fl-font-size-100'] input[type=submit],[class~='fl-font-size-100'] input[type=button]{font-size:1em!important;padding:0 1em;} +[class~='fl-font-size-110'] input[type=submit],input[type=submit][class~='fl-font-size-110'],[class~='fl-font-size-110'] input[type=button]{background-color:#fff;font-size:1.1em!important;padding:0 1em;} +[class~='fl-font-size-120'] input[type=submit],input[type=submit][class~='fl-font-size-120'],[class~='fl-font-size-120'] input[type=button]{background-color:#fff;font-size:1.2em!important;padding:0 1em;} +[class~='fl-font-size-130'] input[type=submit],input[type=submit][class~='fl-font-size-130'],[class~='fl-font-size-130'] input[type=button]{background-color:#fff;font-size:1.3em!important;padding:0 1em;} +[class~='fl-font-size-140'] input[type=submit],input[type=submit][class~='fl-font-size-140'],[class~='fl-font-size-140'] input[type=button]{background-color:#fff;font-size:1.4em!important;padding:0 1em;} +[class~='fl-font-size-150'] input[type=submit],input[type=submit][class~='fl-font-size-150'],[class~='fl-font-size-150'] input[type=button]{background-color:#fff;font-size:1.5em!important;padding:0 1em;} +[class~='fl-font-serif'] input[type=submit],[class~='fl-font-sans'] input[type=submit],[class~='fl-font-monospace'] input[type=submit],[class~='fl-font-arial'] input[type=submit],[class~='fl-font-verdana'] input[type=submit],[class~='fl-font-times'] input[type=submit],[class~='fl-font-courier'] input[type=submit]{background-color:#fff;padding:0 1em;} +} +.fl-font-serif,.fl-font-serif *{font-family:Georgia,Times,"Times New Roman","Book Antiqua",serif!important;} +.fl-font-sans,.fl-font-sans *{font-family:Tahoma,Verdana,Helvetica,sans-serif!important;} +.fl-font-monospace,.fl-font-monospace *{font-family:"Courier New,Courier",monospace!important;} +.fl-font-arial,.fl-font-arial *{font-family:"Arial"!important;} +.fl-font-verdana,.fl-font-verdana *{font-family:"Verdana"!important;} +.fl-font-times,.fl-font-times *{font-family:Georgia,Times,"Times New Roman",serif!important;} +.fl-font-courier,.fl-font-courier *{font-family:"Courier New",Courier,monospace!important;} +.fl-text-align-left{text-align:left;} +.fl-text-align-right{text-align:right;} +.fl-text-align-center{text-align:center;} +.fl-text-align-justify{text-align:justify;} +.fl-font-spacing-0,.fl-font-spacing-0 body,.fl-font-spacing-0 input,.fl-font-spacing-0 select,.fl-font-spacing-0 textarea{letter-spacing:0;} +.fl-font-spacing-1,.fl-font-spacing-1 body,.fl-font-spacing-1 input,.fl-font-spacing-1 select,.fl-font-spacing-1 textarea{letter-spacing:.1em;} +.fl-font-spacing-2,.fl-font-spacing-2 body,.fl-font-spacing-2 input,.fl-font-spacing-2 select,.fl-font-spacing-2 textarea{letter-spacing:.2em;} +.fl-font-spacing-3,.fl-font-spacing-3 body,.fl-font-spacing-3 input,.fl-font-spacing-3 select,.fl-font-spacing-3 textarea{letter-spacing:.3em;} +.fl-font-spacing-4,.fl-font-spacing-4 body,.fl-font-spacing-4 input,.fl-font-spacing-4 select,.fl-font-spacing-4 textarea{letter-spacing:.4em;} +.fl-font-spacing-5,.fl-font-spacing-5 body,.fl-font-spacing-5 input,.fl-font-spacing-5 select,.fl-font-spacing-5 textarea{letter-spacing:.5em;} +.fl-font-spacing-6,.fl-font-spacing-6 body,.fl-font-spacing-6 input,.fl-font-spacing-6 select,.fl-font-spacing-6 textarea{letter-spacing:.6em;} +.fl-text-aqua{color:aqua!important;} +.fl-text-black{color:black!important;} +.fl-text-blue{color:blue!important;} +.fl-text-fuchsia{color:fuchsia!important;} +.fl-text-gray{color:gray!important;} +.fl-text-green{color:green!important;} +.fl-text-lime{color:lime!important;} +.fl-text-maroon{color:maroon!important;} +.fl-text-navy{color:navy!important;} +.fl-text-olive{color:olive!important;} +.fl-text-purple{color:purple!important;} +.fl-text-red{color:red!important;} +.fl-text-silver{color:silver!important;} +.fl-text-teal{color:teal!important;} +.fl-text-white{color:white!important;} +.fl-text-yellow{color:yellow!important;} +.fl-text-underline{text-decoration:underline!important;} +.fl-text-bold{font-weight:bold!important;} +.fl-text-larger{font-size:125%!important;} +.fl-input-outline{border:2px solid;} +.fl-highlight-yellow,.fl-highlight-hover-yellow:hover,.fl-highlight-focus-yellow:focus{background-color:#FF0!important;background-image:none!important;} +.fl-highlight-green,.fl-highlight-hover-green:hover,.fl-highlight-focus-green:focus{background-color:#0F0!important;background-image:none!important;} +.fl-highlight-blue,.fl-highlight-hover-blue:hover,.fl-highlight-focus-blue:focus{background-color:#00F!important;background-image:none!important;} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-theme-coal.css b/jscripts/infusion/framework/fss/css/fss-theme-coal.css new file mode 100644 index 000000000..94cc6cf67 --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-theme-coal.css @@ -0,0 +1,57 @@ +.fl-theme-coal :focus,.fl-theme-coal .selectable{outline:.1em solid #000;} +.fl-theme-coal{color:#000!important;background-color:#ebebeb!important;border-color:#000;} +.fl-theme-coal a{color:#333!important;font-weight:bold;} +.fl-theme-coal a:hover{color:#999!important;} +.fl-theme-coal h1{color:#000;border-bottom-width:.2em;border-bottom-style:solid;} +.fl-theme-coal h2{color:#666;} +.fl-theme-coal th{border:.1em solid #fff;background-color:#dfefff!important;} +.fl-theme-coal td{border:.1em solid #999!important;} +.fl-theme-coal .fl-textfield,.fl-theme-coal .fl-textarea{background-color:#fff;border:1px solid #000;} +.fl-theme-coal .fl-icon{background-color:#666;} +.fl-theme-coal .fl-inlineEdit-edit{background-color:#ebebeb!important;border:.1em solid #fff;margin:-0.1em;} +.fl-theme-coal .fl-button-left,.fl-theme-coal .fl-button-right{color:#FFF!important;background-color:#333!important;} +.fl-theme-coal .fl-button-left{background-image:url(../images/themes/coal/buttons-med-cap.png);} +.fl-theme-coal .fl-button-right{background-image:url(../images/themes/coal/buttons-med-cap.png);} +.fl-theme-coal .fl-button-inner{background-image:url(../images/themes/coal/buttons-med-bg.png);} +.fl-theme-coal a.fl-button-left:hover,.fl-theme-coal a.fl-button-right:hover{color:#fff!important;} +.fl-theme-coal .fl-tabs{border-bottom-color:#333;} +.fl-theme-coal .fl-tabs li,.fl-theme-coal .fl-tabs li a{font-weight:bold;color:#fff!important;border-color:#333;border-bottom-color:#333;background-color:#666;text-decoration:none;} +.fl-theme-coal .fl-tabs li a:hover{background-color:#333;color:#fff!important;} +.fl-theme-coal .fl-tabs li.fl-activeTab,.fl-theme-coal .fl-tabs li.fl-activeTab:hover,.fl-theme-coal .fl-tabs li.fl-activeTab a,.fl-theme-coal .fl-tabs li.fl-activeTab a:hover,.fl-theme-coal .fl-tabs li.fl-tabs-active,.fl-theme-coal .fl-tabs li.fl-tabs-active a,.fl-theme-coal .fl-tabs li.fl-tabs-active a:hover{background-color:#ebebeb;border-bottom-color:#ebebeb;color:#000!important;} +.fl-theme-coal .fl-tabs-content{background-color:#ebebeb;color:#000;border:1px solid #999;border-top:none;} +.fl-theme-coal .fl-listmenu{border:1px solid #333;border-bottom-width:2px;background-color:#ebebeb;} +.fl-theme-coal .fl-listmenu li,.fl-theme-coal .fl-listmenu li a{font-weight:bold;background-color:#ebebeb;border-color:#333;text-decoration:none;} +.fl-theme-coal .fl-listmenu a:hover{background-color:#fff;color:#333!important;} +.fl-theme-coal .fl-listmenu .fl-activemenu,.fl-theme-coal .fl-listmenu .fl-activemenu:hover{background-color:#fff;border-bottom-color:#999;color:#d9d9d9;} +.fl-theme-coal .fl-grid{border:2px solid #000;background-color:#ccc;} +.fl-theme-coal .fl-grid li{background-color:#dfefff;border:1px solid #000;} +.fl-theme-coal .fl-grid .caption{background-color:#dfefff;color:#fff;} +.fl-theme-coal .fl-widget{background:#333 url(../images/themes/coal/widget-bg.png) repeat-x top left;border:1px solid #000;} +.fl-theme-coal .fl-widget h2{color:#fff;} +.fl-theme-coal .fl-widget .fl-icon-more{background-image:url('../images/themes/coal/icon-widget-More.png');margin-left:0;} +.fl-theme-coal .fl-widget .fl-icon-close{background-image:url('../images/themes/coal/icon-widget-Close.png');margin-right:0;} +.fl-theme-coal .fl-grabbable .fl-widget-titlebar{background-image:url('../images/themes/coal/icon-widget-gripper.png');} +.fl-theme-coal .fl-widget-titlebar{background-position:center top;background-repeat:no-repeat;} +.fl-theme-coal .fl-widget-titlebar .icon{background-position:center center;} +.fl-theme-coal .fl-widget-titlebar .fl-button-right,.fl-theme-coal .fl-widget-titlebar .fl-button-left{background-image:url(../images/themes/coal/buttons-titlebar-cap.png);text-decoration:none;} +.fl-theme-coal .fl-widget-titlebar .fl-button-inner{background-image:url(../images/themes/coal/buttons-titlebar-bg.png);padding-bottom:.3em;padding-top:0;} +.fl-theme-coal .fl-widget-options li{border-left:1px solid #ccc;} +.fl-theme-coal .fl-widget-options li a{color:#ebebeb!important;} +.fl-theme-coal .fl-widget-options li a:hover{color:#fff!important;} +.fl-theme-coal .fl-widget-options a.icon:hover{background-color:#fff;border-color:#000;} +.fl-theme-coal .fl-widget-content{background-color:#fff;} +.fl-theme-coal .fl-progress-bounds{border-color:#333;background-color:#ebebeb;} +.fl-theme-coal .fl-progress-fill{color:#fff;background-color:#999;} +.fl-theme-coal .fl-reorderer-dropMarker{background-color:#f00!important;} +.fl-theme-coal .fl-tabs-content-enhanced{background:#ebebeb url(../images/themes/coal/tabs-med-content-bg.png) repeat-x left top;border:none;} +.fl-theme-coal .fl-tabs-enhanced{background:url(../images/themes/coal/tabs-med-container-bg.png) repeat-x left bottom;border-bottom:none;margin:10px 0 0;padding:5px 0 6px;*padding:0;} +.fl-theme-coal .fl-tabs-enhanced li{background:transparent url(../images/themes/coal/tabs-med-cap.png) no-repeat left top;padding:4px 0 6px 16px;*padding-top:0;} +.fl-theme-coal .fl-tabs-enhanced li a{background:transparent url(../images/themes/coal/tabs-med-bg.png) no-repeat right top;border:none;margin:0;padding:4px 16px 6px 0;color:#ebebeb!important;*padding-bottom:4px;} +.fl-theme-coal .fl-tabs-enhanced li.fl-tabs-active{background:transparent url(../images/themes/coal/tabs-med-active-cap.png) no-repeat scroll left top;} +.fl-theme-coal .fl-tabs-enhanced li.fl-tabs-active a{background:transparent url(../images/themes/coal/tabs-med-active-bg.png) no-repeat scroll right top;color:#fff!important;} +.fl-theme-coal .fl-widget-content .fl-tabs-content-enhanced{background:#ebebeb url(../images/themes/coal/tabs-light-content-bg.png) repeat-x left top;border:none;} +.fl-theme-coal .fl-widget-content .fl-tabs-enhanced{background:url(../images/themes/coal/tabs-light-container-bg.png) repeat-x left bottom;border-bottom:none;margin:10px 0 0;padding:5px 0 6px;*padding:0;} +.fl-theme-coal .fl-widget-content .fl-tabs-enhanced li{background:transparent url(../images/themes/coal/tabs-light-cap.png) no-repeat left top;padding:4px 0 6px 16px;*padding-top:0;} +.fl-theme-coal .fl-widget-content .fl-tabs-enhanced li a{background:transparent url(../images/themes/coal/tabs-light-bg.png) no-repeat right top;border:none;margin:0;padding:4px 16px 6px 0;color:#ebebeb!important;*padding-bottom:4px;} +.fl-theme-coal .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active{background:transparent url(../images/themes/coal/tabs-light-active-cap.png) no-repeat scroll left top;} +.fl-theme-coal .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active a{background:transparent url(../images/themes/coal/tabs-light-active-bg.png) no-repeat scroll right top;color:#fff!important;} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-theme-debug.css b/jscripts/infusion/framework/fss/css/fss-theme-debug.css new file mode 100644 index 000000000..7e658bdaf --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-theme-debug.css @@ -0,0 +1,18 @@ +.fl-theme-debug{color:#fff;background-color:#000;} +.fl-theme-debug a{color:#F00;} +.outline *{outline:1px solid #f00;} +.fl-theme-debug div{background-color:#292929;} +.fl-theme-debug div div{background-color:#525252;} +.fl-theme-debug div div div{background-color:#7b7b7b;} +.fl-theme-debug div div div div{background-color:#a4a4a4;} +.fl-theme-debug div div div div div{background-color:#cdcdcd;} +.fl-theme-debug div div div div div div{background-color:#f6f6f6;} +.fl-theme-debug div div div div div div div{background-color:#fafafa;} +.fl-theme-debug div div div div div div div div{background-color:#f5f5f5;} +.fl-theme-debug div div div div div div div div div{background-color:#f0f0f0;} +.fl-theme-debug div div div div div div div div div div{background-color:#ebebeb;} +.fl-theme-debug div div div div div div div div div div div{background-color:#e6e6e6;} +.fl-theme-debug div div div div div div div div div div div div{background-color:#e1e1e1;} +.fl-theme-debug div div div div div div div div div div div div div{background-color:#dcdcdc;} +.fl-theme-debug div div div div div div div div div div div div div div{background-color:#d7d7d7;} +.fl-theme-debug div div div div div div div div div div div div div div div{background-color:#d2d2d2;} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-theme-hc.css b/jscripts/infusion/framework/fss/css/fss-theme-hc.css new file mode 100644 index 000000000..e6cc0e4da --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-theme-hc.css @@ -0,0 +1,35 @@ +.fl-theme-hc :focus,.fl-theme-hc .selectable{outline:.2em solid #F00;} +.fl-theme-hc{color:#000!important;background-color:#fff!important;} +.fl-theme-hc div,.fl-theme-hc input{color:#000;background-color:#fff;border-color:#000;} +.fl-theme-hc .fl-knockout{background:transparent!important;color:#000;} +.fl-theme-hc a{color:#000!important;font-weight:bold;background-color:#fff!important;} +.fl-theme-hc a:hover{color:#fff!important;background-color:#000!important;} +.fl-theme-hc a:hover *{color:#fff!important;background-color:#000!important;} +.fl-theme-hc h1,.fl-theme-hc h2,.fl-theme-hc h3,.fl-theme-hc h4,.fl-theme-hc h5,.fl-theme-hc h6{color:#000!important;background-color:#fff!important;border-color:#000!important;border-bottom:.1em dashed #000!important;} +.fl-theme-hc th{border:.1em solid #000;background-color:#000!important;color:#fff!important;} +.fl-theme-hc td{border:.1em solid #000;} +.fl-theme-hc .fl-inlineEdit-edit{background-color:#000!important;color:#fff!important;border:.1em solid #fff;padding:.1em;margin:-0.1em;} +.fl-theme-hc .fl-tabs{border-bottom-color:#000;} +.fl-theme-hc .fl-tabs li,.fl-theme-hc .fl-tabs li *{border-color:#000;border-bottom-color:#000;} +.fl-theme-hc .fl-tabs li{background-color:#000;} +.fl-theme-hc .fl-tabs li *{color:#fff!important;font-weight:bold;background-color:#000!important;text-decoration:none;} +.fl-theme-hc .fl-tabs li:hover,.fl-theme-hc .fl-tabs li:hover *,.fl-theme-hc .fl-tabs li a:hover{color:#000!important;background-color:#fff!important;} +.fl-theme-hc .fl-tabs li.fl-tabs-active,.fl-theme-hc .fl-tabs li.fl-tabs-active a,.fl-theme-hc .fl-tabs li.fl-tabs-active a:hover,.fl-theme-hc .fl-tabs li.fl-activeTab,.fl-theme-hc .fl-tabs li.fl-activeTab:hover,.fl-theme-hc .fl-tabs li.fl-activeTab a,.fl-theme-hc .fl-tabs li.fl-activeTab a:hover{background-color:#fff!important;border-bottom-color:#fff;color:#000!important;} +.fl-theme-hc .fl-tab-content{background-color:#fff;color:#000;} +.fl-theme-hc .fl-listmenu{border:1px solid #000;border-bottom-width:2px;background-color:#fff;} +.fl-theme-hc .fl-listmenu li,.fl-theme-hc .fl-listmenu li a{font-weight:bold;color:#000;background-color:#fff;border-color:#000;text-decoration:none;} +.fl-theme-hc .fl-listmenu a:hover{background-color:#fff;color:#fff;} +.fl-theme-hc .fl-listmenu .fl-activemenu,.fl-theme-hc .fl-listmenu .fl-activemenu:hover{background-color:#fff;border-bottom-color:#fff;color:#508cc9;} +.fl-theme-hc .fl-button-right,.fl-theme-hc .fl-button-left,.fl-theme-hc .fl-button-inner{padding:0;} +.fl-theme-hc .fl-widget{background-color:#000;border:1px solid #fff;} +.fl-theme-hc .fl-widget h2{color:#fff;} +.fl-theme-hc .fl-widget a{color:#000;} +.fl-theme-hc .fl-widget .fl-icon{color:#000;background-image:none;background-color:#fff;text-indent:0;width:auto;height:auto;margin-left:0;padding:0 2px;display:inline;font-weight:bold;} +.fl-theme-hc .fl-widget .fl-widget-titlebar{margin-bottom:3px;} +.fl-theme-hc .fl-widget .fl-widget-options{padding:0;} +.fl-theme-hc .fl-widget .fl-widget-options li{border-left:1px solid #fff;} +.fl-theme-hc .fl-widget .fl-widget-options a{color:#fff;} +.fl-theme-hc .fl-widget .fl-widget-content{background-color:#fff;} +.fl-theme-hc .fl-progress-bounds{border-color:#000;background-color:#fff;} +.fl-theme-hc .fl-progress-fill{color:#fff;background-color:#000;} +.fl-theme-hc .fl-reorderer-dropMarker{background-color:#f00!important;} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-theme-hci.css b/jscripts/infusion/framework/fss/css/fss-theme-hci.css new file mode 100644 index 000000000..7a3a93269 --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-theme-hci.css @@ -0,0 +1,35 @@ +.fl-theme-hci :focus,.fl-theme-hci .selectable{outline:.2em solid #F00;} +.fl-theme-hci{color:#fff!important;background-color:#000!important;} +.fl-theme-hci div,.fl-theme-hci input{color:#fff!important;background-color:#000!important;} +.fl-theme-hci .fl-knockout{background:transparent!important;color:#fff;} +.fl-theme-hci a{color:#fff!important;font-weight:bold;background-color:#000!important;} +.fl-theme-hci a:hover{color:#000!important;background-color:#fff!important;} +.fl-theme-hci a:hover *{color:#000!important;background-color:#fff!important;} +.fl-theme-hci h1,.fl-theme-hci h2,.fl-theme-hci h3,.fl-theme-hci h4,.fl-theme-hci h5,.fl-theme-hci h6{color:#fff;background-color:#000;border-color:#fff;border-bottom:.1em dashed #fff;} +.fl-theme-hci th{border:.1em solid #fff;background-color:#fff!important;color:#000!important;} +.fl-theme-hci td{border:.1em solid #fff;} +.fl-theme-hci .fl-inlineEdit-edit{background-color:#fff!important;color:#000!important;border:.1em solid #000;padding:.1em;margin:-0.1em;} +.fl-theme-hci .fl-tabs{border-bottom-color:#fff;} +.fl-theme-hci .fl-tabs li,.fl-theme-hci .fl-tabs li *{border-color:#fff;border-bottom-color:#fff;} +.fl-theme-hci .fl-tabs li{background-color:#fff;} +.fl-theme-hci .fl-tabs li *{color:#000!important;font-weight:bold;background-color:#fff!important;text-decoration:none;} +.fl-theme-hci .fl-tabs li:hover,.fl-theme-hci .fl-tabs li:hover *,.fl-theme-hci .fl-tabs li a:hover{color:#fff!important;background-color:#000!important;} +.fl-theme-hci .fl-tabs li.fl-tabs-active,.fl-theme-hci .fl-tabs li.fl-tabs-active a,.fl-theme-hci .fl-tabs li.fl-tabs-active a:hover,.fl-theme-hci .fl-tabs li.fl-activeTab,.fl-theme-hci .fl-tabs li.fl-activeTab:hover,.fl-theme-hci .fl-tabs li.fl-activeTab a,.fl-theme-hci .fl-tabs li.fl-activeTab a:hover{background-color:#000!important;border-bottom-color:#000;color:#fff!important;} +.fl-theme-hci .fl-tab-content{background-color:#000;border:1px solid #fff;border-top:none;color:#fff;} +.fl-theme-hci .fl-listmenu{border:1px solid #fff;border-bottom-width:2px;background-color:#000;} +.fl-theme-hci .fl-listmenu li,.fl-theme-hci .fl-listmenu li a{font-weight:bold;color:#fff;background-color:#dfefff;border-color:#fff;text-decoration:none;} +.fl-theme-hci .fl-listmenu a:hover{background-color:#5a95cf;color:#000;} +.fl-theme-hci .fl-listmenu .fl-activemenu,.fl-theme-hci .fl-listmenu .fl-activemenu:hover{background-color:#000;border-bottom-color:#000;color:#508cc9;} +.fl-theme-hci .fl-button-right,.fl-theme-hci .fl-button-left,.fl-theme-hci .fl-button-inner{padding:0;} +.fl-theme-hci .fl-widget{background-color:#fff!important;border:1px solid #000;} +.fl-theme-hci .fl-widget h2{color:#000;background-color:#fff;} +.fl-theme-hci .fl-widget a{color:#fff;} +.fl-theme-hci .fl-widget .fl-icon{color:#fff;background-image:none;background-color:#000;text-indent:0;width:auto;height:auto;margin-left:0;padding:0 2px;display:inline;font-weight:bold;} +.fl-theme-hci .fl-widget .fl-widget-titlebar{margin-bottom:3px;} +.fl-theme-hci .fl-widget .fl-widget-options{padding:0;} +.fl-theme-hci .fl-widget .fl-widget-options li{border-left:1px solid #000;} +.fl-theme-hci .fl-widget .fl-widget-options a{color:#000;} +.fl-theme-hci .fl-widget .fl-widget-content{background-color:#000;} +.fl-theme-hci .fl-progress-bounds{border-color:#fff;background-color:#000;} +.fl-theme-hci .fl-progress-fill{color:#000;background-color:#fff;} +.fl-theme-hci .fl-reorderer-dropMarker{background-color:#f00!important;} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-theme-mist.css b/jscripts/infusion/framework/fss/css/fss-theme-mist.css new file mode 100644 index 000000000..ec71b2f8d --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-theme-mist.css @@ -0,0 +1,50 @@ +.fl-theme-mist :focus,.fl-theme-mist .selectable{outline:.1em solid #F00;} +.fl-theme-mist{color:#000!important;background-color:#f4f4f4!important;border-color:#4070a1;} +.fl-theme-mist a{color:#5a95cf!important;font-weight:bold;} +.fl-theme-mist a:hover{color:#6DB5FB!important;} +.fl-theme-mist h1{color:#4070a1;border-bottom-width:.2em;border-bottom-style:solid;} +.fl-theme-mist h2{color:#5a95cf;} +.fl-theme-mist th{border:.1em solid #5a95cf;background-color:#dfefff!important;} +.fl-theme-mist td{border:.1em solid #999!important;} +.fl-theme-mist .fl-textfield,.fl-theme-mist .fl-textarea{border:1px solid #5a95cf;border-bottom-color:#5a95cf;border-right-color:#5a95cf;background-color:#dfefff;} +.fl-theme-mist .fl-icon{background-color:#5a95cf;} +.fl-theme-mist .fl-inlineEdit-edit{background-color:#dfefff!important;border:.1em solid #5a95cf;margin:-0.1em;} +.fl-theme-mist .fl-button-left{background-image:url(../images/themes/mist/buttons-med-cap.png);} +.fl-theme-mist .fl-button-right{background-image:url(../images/themes/mist/buttons-med-cap.png);} +.fl-theme-mist .fl-button-inner{background-image:url(../images/themes/mist/buttons-med-bg.png);} +.fl-theme-mist .fl-tabs{border-bottom-color:#4070a1;} +.fl-theme-mist .fl-tabs li,.fl-theme-mist .fl-tabs li a{font-weight:bold;color:#4070a1;border-color:#4070a1;border-bottom-color:#4070a1;text-decoration:none;background-color:#dfefff;} +.fl-theme-mist .fl-tabs li a:hover{background-color:#5a95cf;color:#fff!important;} +.fl-theme-mist .fl-tabs li.fl-activeTab,.fl-theme-mist .fl-tabs li.fl-activeTab:hover,.fl-theme-mist .fl-tabs li.fl-activeTab a,.fl-theme-mist .fl-tabs li.fl-activeTab a:hover .fl-theme-mist .fl-tabs li.fl-tabs-active,.fl-theme-mist .fl-tabs li.fl-tabs-active a,.fl-theme-mist .fl-tabs li.fl-tabs-active a:hover{background-color:#f4f4f4;border-bottom-color:#f4f4f4;color:#508cc9!important;} +.fl-theme-mist .fl-tabs-content{background-color:#f4f4f4;color:#000;border:1px solid #61c5ff;border-top:none;margin-top:-4px;} +.fl-theme-mist .fl-listmenu{border:1px solid #4070a1;border-bottom-width:2px;background-color:#fff;} +.fl-theme-mist .fl-listmenu li,.fl-theme-mist .fl-listmenu li a{font-weight:bold;color:#4070a1;background-color:#dfefff;border-color:#4070a1;text-decoration:none;} +.fl-theme-mist .fl-listmenu li a:hover{background-color:#5a95cf;color:#fff!important;} +.fl-theme-mist .fl-listmenu .fl-activemenu,.fl-theme-mist .fl-listmenu .fl-activemenu:hover{background-color:#fff;border-bottom-color:#fff;color:#508cc9;} +.fl-theme-mist .fl-grid{border:2px solid #4070a1;background-color:#fff;} +.fl-theme-mist .fl-grid li{background-color:#dfefff;border:1px solid #4070a1;} +.fl-theme-mist .fl-grid .caption{background-color:#dfefff;color:#5a95cf;} +.fl-theme-mist .fl-widget{background:#efefef url(../images/themes/mist/widget-bg.png) repeat-x top left;border:1px solid #CCC;} +.fl-theme-mist .fl-widget h2{color:#000;} +.fl-theme-mist .fl-widget .fl-icon-more{background-image:url('../images/themes/mist/icon-widget-More.png');margin-left:0;} +.fl-theme-mist .fl-widget .fl-icon-close{background-image:url('../images/themes/mist/icon-widget-Close.png');margin-right:0;} +.fl-theme-mist .fl-grabbable .fl-widget-titlebar{background-image:url('../images/themes/mist/icon-widget-gripper.png');} +.fl-theme-mist .fl-widget-titlebar .icon{background-position:center center;} +.fl-theme-mist .fl-widget-titlebar .fl-button-right,.fl-theme-mist .fl-widget-titlebar .fl-button-left{color:#4070a1!important;background-image:url(../images/themes/mist/buttons-titlebar-cap.png);text-decoration:none;} +.fl-theme-mist .fl-widget-titlebar .fl-button-inner{background-image:url(../images/themes/mist/buttons-titlebar-bg.png);} +.fl-theme-mist .fl-widget-options li{border-left:1px solid #ccc;} +.fl-theme-mist .fl-widget-options a.icon:hover{background-color:#5a95cf;border-color:#000;} +.fl-theme-mist .fl-widget-content{background-color:#fff;} +.fl-theme-mist .fl-progress-bounds{border-color:#999;background-color:#fff;} +.fl-theme-mist .fl-progress-fill{color:#4070a1;background-color:#000;} +.fl-theme-mist .fl-reorderer-dropMarker{background-color:#f00!important;} +.fl-theme-mist .fl-tabs-enhanced{background:url(../images/themes/mist/tabs-med-container-bg.png) repeat-x left bottom;border-bottom:none;margin:10px 1px 0;padding:5px 0 6px;*padding:2px 0;} +.fl-theme-mist .fl-tabs-enhanced li{background:transparent url(../images/themes/mist/tabs-med-cap.png) no-repeat left top;padding:4px 0 6px 16px;*padding-top:0;} +.fl-theme-mist .fl-tabs-enhanced li a{background:transparent url(../images/themes/mist/tabs-med-bg.png) no-repeat right top;border:none;margin:0;padding:4px 16px 6px 0;color:#666!important;*padding-bottom:4px;} +.fl-theme-mist .fl-tabs-enhanced li.fl-tabs-active{background:transparent url(../images/themes/mist/tabs-med-active-cap.png) no-repeat scroll left top;} +.fl-theme-mist .fl-tabs-enhanced li.fl-tabs-active a{background:transparent url(../images/themes/mist/tabs-med-active-bg.png) no-repeat scroll right top;color:#000!important;} +.fl-theme-mist .fl-widget-content .fl-tabs-enhanced{background:url(../images/themes/mist/tabs-light-container-bg.png) repeat-x left bottom;border-bottom:none;margin:10px 1px 0;padding:5px 0 6px;*padding:2px 0;} +.fl-theme-mist .fl-widget-content .fl-tabs-enhanced li{background:transparent url(../images/themes/mist/tabs-light-cap.png) no-repeat left top;padding:4px 0 6px 16px;*padding-top:0;} +.fl-theme-mist .fl-widget-content .fl-tabs-enhanced li a{background:transparent url(../images/themes/mist/tabs-light-bg.png) no-repeat right top;border:none;margin:0;padding:4px 16px 6px 0;color:#666!important;*padding-bottom:4px;} +.fl-theme-mist .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active{background:transparent url(../images/themes/mist/tabs-light-active-cap.png) no-repeat scroll left top;} +.fl-theme-mist .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active a{background:transparent url(../images/themes/mist/tabs-light-active-bg.png) no-repeat scroll right top;color:#000!important;} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-theme-rust.css b/jscripts/infusion/framework/fss/css/fss-theme-rust.css new file mode 100644 index 000000000..855b38857 --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-theme-rust.css @@ -0,0 +1,30 @@ +.fl-theme-rust :focus,.fl-theme-rust .selectable{outline:.2em solid #662e0f;} +.fl-theme-rust{color:#000!important;background-color:#F2E0B6!important;border-color:#916535;} +.fl-theme-rust .fl-knockout{background:transparent;color:#000;} +.fl-theme-rust a{color:#916535!important;} +.fl-theme-rust a:hover{color:#914E38!important;} +.fl-theme-rust h1{color:#000;border-bottom-width:1px;border-bottom-style:dotted;} +.fl-theme-rust h2{color:#453A2E;} +.fl-theme-rust th{border:.1em solid #453A2E;background-color:#917A61!important;color:#fff!important;} +.fl-theme-rust td{border:.1em solid #453A2E!important;} +.fl-theme-rust .fl-inlineEdit-edit{background-color:#dfefff;border:1px solid #5a95cf;margin:-1px;} +.fl-theme-rust .fl-wrapper-widget{border:3px solid #dfefff;background-color:#333;color:#fff;} +.fl-theme-rust .fl-wrapper-callout{border-color:#999;background-color:#dfefff;} +.fl-theme-rust .fl-tabs{border-bottom-color:#916535;} +.fl-theme-rust .fl-tabs li{background-color:#F2E0B6;} +.fl-theme-rust .fl-tabs li,.fl-theme-rust .fl-tabs li a{font-weight:bold;border-color:#916535;border-bottom-color:#916535;text-decoration:none;} +.fl-theme-rust .fl-tabs li:hover,.fl-theme-rust .fl-tabs li:hover a,.fl-theme-rust .fl-tabs li a:hover{background-color:#662e0f;color:#fff!important;} +.fl-theme-rust .fl-tabs li.fl-tabs-active,.fl-theme-rust .fl-tabs li.fl-tabs-active a,.fl-theme-rust .fl-tabs li.fl-tabs-active a:hover,.fl-theme-rust .fl-tabs li.fl-activeTab,.fl-theme-rust .fl-tabs li.fl-activeTab:hover,.fl-theme-rust .fl-tabs li.fl-activeTab a,.fl-theme-rust .fl-tabs li.fl-activeTab a:hover{background-color:#FFFBC2;border-bottom-color:#FFFBC2;color:#916535!important;} +.fl-theme-rust .fl-tab-content{background-color:#FFFBC2;color:#000;border:1px solid #916535;border-top:none;} +.fl-theme-rust .fl-listmenu{border:1px solid #4070a1;background-color:#fff;} +.fl-theme-rust .fl-listmenu li,.fl-theme-rust .fl-listmenu li a{font-weight:bold;color:#4070a1;background-color:#fff;border-color:#4070a1;text-decoration:none;} +.fl-theme-rust .fl-listmenu a:hover{background-color:#5a95cf;color:#fff;} +.fl-theme-rust .fl-listmenu .fl-activemenu,.fl-theme-rust .fl-listmenu .fl-activemenu:hover{background-color:#fff;border-bottom-color:#fff;color:#508cc9;} +.fl-theme-rust .fl-widget{background:#662e0f url(../images/themes/rust/widget-earmark.png) no-repeat top left;} +.fl-theme-rust .fl-widget h2{color:#FFFBC2;} +.fl-theme-rust .grabbable{background-image:url('../images/themes/rust/gripper.png');} +.fl-theme-rust .fl-widget-titlebar .icon{background-position:center center;} +.fl-theme-rust .fl-widget-options li{border-left:1px solid #cf923e;} +.fl-theme-rust .fl-widget-options a{color:#cf923e!important;} +.fl-theme-rust .fl-widget-options a.icon:hover{background-color:#cf923e;border-color:#fffbc2;} +.fl-theme-rust .fl-widget-content{background-color:#FFFBC2!important;} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/css/fss-theme-slate.css b/jscripts/infusion/framework/fss/css/fss-theme-slate.css new file mode 100644 index 000000000..da88d0458 --- /dev/null +++ b/jscripts/infusion/framework/fss/css/fss-theme-slate.css @@ -0,0 +1,51 @@ +.fl-theme-slate :focus,.fl-theme-slate .selectable{outline:.1em solid #000;} +.fl-theme-slate{color:#000!important;background-color:#ccc!important;border-color:#999;} +.fl-theme-slate a{color:#ebebeb!important;font-weight:bold;} +.fl-theme-slate a:hover{color:#fff!important;} +.fl-theme-slate h1{color:#999;border-bottom-width:.2em;border-bottom-style:solid;} +.fl-theme-slate h2{color:#fff;} +.fl-theme-slate th{border:.1em solid #fff;background-color:#dfefff!important;} +.fl-theme-slate td{border:.1em solid #999!important;} +.fl-theme-slate .fl-textfield,.fl-theme-slate .fl-textarea{background-color:#ccc;border:1px solid #666;} +.fl-theme-slate .fl-icon{background-color:#ebebeb;} +.fl-theme-slate .fl-inlineEdit-edit{background-color:#dfefff!important;border:.1em solid #fff;margin:-0.1em;} +.fl-theme-slate .fl-button-left,.fl-theme-slate .fl-button-right{color:#FFF!important;background-color:#999!important;} +.fl-theme-slate .fl-button-left{background-image:url(../images/themes/slate/buttons-med-cap.png);} +.fl-theme-slate .fl-button-right{background-image:url(../images/themes/slate/buttons-med-cap.png);} +.fl-theme-slate .fl-button-inner{background-image:url(../images/themes/slate/buttons-med-bg.png);} +.fl-theme-slate .fl-tabs{border-bottom-color:#999;} +.fl-theme-slate .fl-tabs li,.fl-theme-slate .fl-tabs li a{font-weight:bold;color:#999;border-color:#999;border-bottom-color:#999;background-color:#666;text-decoration:none;} +.fl-theme-slate .fl-tabs li a:hover{background-color:#999;color:#fff!important;} +.fl-theme-slate .fl-tabs li.fl-activeTab,.fl-theme-slate .fl-tabs li.fl-activeTab:hover,.fl-theme-slate .fl-tabs li.fl-activeTab a,.fl-theme-slate .fl-tabs li.fl-activeTab a:hover,.fl-theme-slate .fl-tabs li.fl-tabs-active,.fl-theme-slate .fl-tabs li.fl-tabs-active a,.fl-theme-slate .fl-tabs li.fl-tabs-active a:hover{background-color:#ebebeb;border-bottom-color:#ccc;color:#000!important;} +.fl-theme-slate .fl-tabs-content{background-color:#ebebeb;color:#000;border:1px solid #999;border-top:none;} +.fl-theme-slate .fl-listmenu{border:1px solid #999;border-bottom-width:2px;background-color:#ccc;} +.fl-theme-slate .fl-listmenu li,.fl-theme-slate .fl-listmenu li a{font-weight:bold;background-color:#999;border-color:#ccc;text-decoration:none;} +.fl-theme-slate .fl-listmenu a:hover{background-color:#ebebeb;color:#000!important;} +.fl-theme-slate .fl-listmenu .fl-activemenu,.fl-theme-slate .fl-listmenu .fl-activemenu:hover{background-color:#ccc;border-bottom-color:#ccc;color:#d9d9d9;} +.fl-theme-slate .fl-grid{border:2px solid #999;background-color:#ccc;} +.fl-theme-slate .fl-grid li{background-color:#dfefff;border:1px solid #999;} +.fl-theme-slate .fl-grid .caption{background-color:#dfefff;color:#fff;} +.fl-theme-slate .fl-widget{background:#ccc url(../images/themes/slate/widget-bg.png) repeat-x top left;border:1px solid #666;} +.fl-theme-slate .fl-widget h2{color:#ccc;} +.fl-theme-slate .fl-widget .fl-icon-more{background-image:url('../images/themes/slate/icon-widget-More.png');margin-left:0;} +.fl-theme-slate .fl-widget .fl-icon-close{background-image:url('../images/themes/slate/icon-widget-Close.png');margin-right:0;} +.fl-theme-slate .fl-grabbable .fl-widget-titlebar{background-image:url('../images/themes/slate/icon-widget-gripper.png');} +.fl-theme-slate .fl-widget-titlebar .icon{background-position:center center;} +.fl-theme-slate .fl-widget-titlebar .fl-button-right,.fl-theme-slate .fl-widget-titlebar .fl-button-left{color:#333!important;background-image:url(../images/themes/slate/buttons-titlebar-cap.png);text-decoration:none;} +.fl-theme-slate .fl-widget-titlebar .fl-button-inner{background-image:url(../images/themes/slate/buttons-titlebar-bg.png);padding-bottom:.3em;padding-top:0;} +.fl-theme-slate .fl-widget-options li{border-left:1px solid #ccc;} +.fl-theme-slate .fl-widget-options a.icon:hover{background-color:#fff;border-color:#000;} +.fl-theme-slate .fl-widget-content{background-color:#bfbfbf;} +.fl-theme-slate .fl-progress-bounds{border-color:#999;background-color:#ccc;} +.fl-theme-slate .fl-progress-fill{color:#999;background-color:#000;} +.fl-theme-slate .fl-reorderer-dropMarker{background-color:#f00!important;} +.fl-theme-slate .fl-tabs-enhanced{background:url(../images/themes/slate/tabs-light-container-bg.png) repeat-x left bottom;border-bottom:none;margin:10px 1px 0;padding:5px 0 6px;*padding:0;} +.fl-theme-slate .fl-tabs-enhanced li{background:transparent url(../images/themes/slate/tabs-light-cap.png) no-repeat left top;padding:4px 0 6px 16px;*padding-top:0;} +.fl-theme-slate .fl-tabs-enhanced li a{background:transparent url(../images/themes/slate/tabs-light-bg.png) no-repeat right top;border:none;margin:0;padding:4px 16px 6px 0;color:#666!important;*padding-bottom:4px;} +.fl-theme-slate .fl-tabs-enhanced li.fl-tabs-active{background:transparent url(../images/themes/slate/tabs-light-active-cap.png) no-repeat scroll left top;} +.fl-theme-slate .fl-tabs-enhanced li.fl-tabs-active a{background:transparent url(../images/themes/slate/tabs-light-active-bg.png) no-repeat scroll right top;color:#000!important;} +.fl-theme-slate .fl-widget-content .fl-tabs-enhanced{background:url(../images/themes/slate/tabs-med-container-bg.png) repeat-x left bottom;border-bottom:none;margin:10px 1px 0;padding:5px 0 6px;*padding:0;} +.fl-theme-slate .fl-widget-content .fl-tabs-enhanced li{background:transparent url(../images/themes/slate/tabs-med-cap.png) no-repeat left top;padding:4px 0 6px 16px;*padding-top:0;} +.fl-theme-slate .fl-widget-content .fl-tabs-enhanced li a{background:transparent url(../images/themes/slate/tabs-med-bg.png) no-repeat right top;border:none;margin:0;padding:4px 16px 6px 0;color:#666!important;*padding-bottom:4px;} +.fl-theme-slate .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active{background:transparent url(../images/themes/slate/tabs-med-active-cap.png) no-repeat scroll left top;} +.fl-theme-slate .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active a{background:transparent url(../images/themes/slate/tabs-med-active-bg.png) no-repeat scroll right top;color:#000!important;} \ No newline at end of file diff --git a/jscripts/infusion/framework/fss/images/exclamation.png b/jscripts/infusion/framework/fss/images/exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..056f680ad288a1f126e2c0d4beef406cda8ffe8a GIT binary patch literal 622 zcmV-!0+IcRP)$M$+k2uA} z%||2QCbimx_EPSZIEIJ=LKQ+mAaUypP^sd;33vuxfg6ti2P9C4!~r1zh4c^ush}|% z$Diz2NeMKqDzVbfyE{AoSei&Fx2DVf*nn$m+|ck}j^}+Mxg;|4e}MJB9L%>ahQwFvbA!oE?(8aJ7nSBcB5e zi9#M}L>$58GurZ+s%G+`zYmV%B!od9(rGP!`fPa(eI_+D3=a-e6^PKxFI=hm>v7`wx;^@B;vZ|IW2@bC*^K@wDEc12+2v?RFcw z-7bWZ3z6&s$U{xhiYw?l))9P&932FCLoe1Ad;M47M?H|p5Wd?F5@gX!5CsP^hOrof zxPcnfl7Oec)3`lbuq;1%r&8j8BDx8>aX(=^N7PiHW{p`3HK-*)0A*298s`hDDZvj& z@twwu+oTQ2&1)v1w1Ha6SiEIgdPNrZ#W=f literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/_common/exclamation.png b/jscripts/infusion/framework/fss/images/themes/_common/exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..056f680ad288a1f126e2c0d4beef406cda8ffe8a GIT binary patch literal 622 zcmV-!0+IcRP)$M$+k2uA} z%||2QCbimx_EPSZIEIJ=LKQ+mAaUypP^sd;33vuxfg6ti2P9C4!~r1zh4c^ush}|% z$Diz2NeMKqDzVbfyE{AoSei&Fx2DVf*nn$m+|ck}j^}+Mxg;|4e}MJB9L%>ahQwFvbA!oE?(8aJ7nSBcB5e zi9#M}L>$58GurZ+s%G+`zYmV%B!od9(rGP!`fPa(eI_+D3=a-e6^PKxFI=hm>v7`wx;^@B;vZ|IW2@bC*^K@wDEc12+2v?RFcw z-7bWZ3z6&s$U{xhiYw?l))9P&932FCLoe1Ad;M47M?H|p5Wd?F5@gX!5CsP^hOrof zxPcnfl7Oec)3`lbuq;1%r&8j8BDx8>aX(=^N7PiHW{p`3HK-*)0A*298s`hDDZvj& z@twwu+oTQ2&1)v1w1Ha6SiEIgdPNrZ#W=%X8O-xS>N=;0uEIgTN15~8v>EaktaVzN$KMRjX18Yb9V(vdG z8XxACG_GI5U9&KtLGbKm9v_~JgbQ2@ED{XoxP^}%j@y(5)XCuK>gTe~DWM4f9h)uQ literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/_common/gripper.png b/jscripts/infusion/framework/fss/images/themes/_common/gripper.png new file mode 100644 index 0000000000000000000000000000000000000000..5e7cc70cf714d34e3dd305bcdb997b17e83bfd16 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^5f literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/android/backbutton_mask.png b/jscripts/infusion/framework/fss/images/themes/android/backbutton_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..5191bd0f22711e86ee0080303722c97ab5b58ba3 GIT binary patch literal 429 zcmV;e0aE^nP)w2tB=)dsJYX)BsdEBz%sXsRO7mSZ)>t$_!SU zk5&IW&W2imjQ-x~{vEkg2~gn|)}UW8oTvtiVI0VPSAYQk XkcA!xF!kQC00000NkvXXu0mjf4<@`y literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/android/button_bg_insetShadow.png b/jscripts/infusion/framework/fss/images/themes/android/button_bg_insetShadow.png new file mode 100644 index 0000000000000000000000000000000000000000..1662147b9ec06999d2fbd2940b8b4b67f152dbff GIT binary patch literal 1262 zcmV>@Ou8|VG@_W)nAXrJCNy!OX>?(tjjbjwY_J;>S+D?+SZUg{nqbmR zT@;K&69fyT2r{&#P&$-hVBUlOGXb26JgPIg@h1O#b6@9o@BPmG&KW_c*V7iI2x@>1 zLS|?mq}^itkB|@g7CIM=M7)~~6jU|qui6WslTeexQLeSwY$VGP<>uy+APAdMeLf$B z!x5UEo+hu?OG``3pQHKRK)}CY0YYBBg?#sB*BelO29IvH+i7$;9b1C7Dgww5 z#>U29^?Lp2>bskS{9vza0_8=Spc1Gm8jY5Bbad?L>+3rdkH=|rY=Z90%pL)&D+zX2 zJhtXUzj$0_*;z(4($v)SaZOFlINJK4QNZF4bHrE#J776f-P_x1Z)SDP;$H7rvd#Q11ly&^?}`Zj#qlhw9#l}sI|5As3b}EFmaO7P_X-wa5$Xb+}zwLilS@e#w3Np5swCa zhppy3Z7dcgbhHDyj5aT&uXx!ee5D=low7|vZmVbT(^*CXg+d|R!SL6p2R3t{zf)Fi z`GdjWH_R|)soZ>!!44T-3@8l`58p(6p3-0Z__)kOA`vnu64&=J*sRlOiCwi` zm;rDI_j)lFi;+g7fdnIiMgA_p3D%A#k!~t%#`uxKAWe$*QKm~OLqq>j`5*kRf+K%& zEFl9NA=NJP7<@Mtizi*&ZnrBLIt7i#B`Oa`Bwx$BD~P;#Jn33WbhIgL=ZrLb*8DVo2( z$ZcH=o`Bl?Scxpl`w_UGM?IhOu{1#0Mn2nQ+lxy<3Wmbx`una7YD7_wIIKUlu(%wM z6_pUaKB}mwsN&r3A09i;wXrR>X;6(czpxYm>z$;xDM(4Kes}qn!Jr%=jgH_1+3>tB zOn4UkuGM@@+2J)4R>$%j7u#T41qFrKY#WT%z#2;qeC~x8>EijXlC$a?4!x-r=#!;@ zA4&ObLj1g{)9EfuO-+sX{r>PywrMh%q~hXYCqiHYGMZXjQ4!K)6U7oM?_avmeRBOB z7}Y=WYIK}A{PNKWy(oU67!2yd{rTFtxw%^Y2VpcCcXYl;agtZFu&|J_b8<*UtO$aYt*vFc zRHoEQun}wpE5SENz{U{4ClCZd5v;Zqv=OYd3Q3XYNGl1-_zhWck=f+l&0Q2S7Y@jv z-`V+dZ$c^M#922^%w+|a7bJiC!usW%9eRIvd!0*^0v>3HE>R$S3TmM0kBuc~kO6uB zvkQjQnNS5EpakBy63!lkN$QgKz$6)grsPF4Nd}-Ld6TuI0=@!ceNd1Rq`|~LzQ^i< zjf6ml&pblqrpXqU*$gWL2HjvOkypc^e=d3PDRr34SVSN3xcpe>l@ zn1Iaj>rqsYZsFOz;aQhUp;KT;f}x~Bf{$yo)QTFJt)=(hCQ7h3C|s9#)aER?NmWHl z2%AA>9J`E^wNE*om~duFm_A!;vJBrBNxzStPKh&C7s!0zIdY&TkLE^JdBad3y;e?o^kSVP#JLNJWoixv%au8sa zdubzoSPg7W&?KzJ0G*?7Xo98qvz@?5Vg&;s!H#y(rh-=AnXPSbmJyxl8`J9RHB*OvYy57CSgaP~uRz5H!KVEn zsou>0w6~YSGWr@EA%hvxWkjFfw>%8`qq~?#w<4RsNf7n zae+>>=%@u(|H7cobgk99aHthOaH8TpU?1&D{9L=5R|+0RH(s%4-NbX0w0h12%D=l^ xk-S#+t=Aq_G( z3m$=_lGL4Xwq;OtoD36q3Vwn2;3vdIcFO2yKrvQ`Ep z98sF+D|Kma6HRJh2aiEUshFJFiWz0_5Y&~enJB6K3~qt*O1-DSKGQJt9LKeJp3g-h zkpi{$q)9y)hK-Gl*G8k!ySBFG-P+osQ?Q}w3JL|9ID4K*&1PA( zT5a#{?!HVU5=(x+e_1)#wK0$+iDfbw)^4}+FxbZZ<#;^q*UEsS4~Vl)r^7rRPd=B+ zEzZx+^TlGZ5DW%IO$;amL0~46iP>zn-<3+`S|AWOlTN4iR#sLdO$;cI3+8gUB)wi= z1lO?wDHe+rH#awBO$?|7>h(HvyWRV!DA8;-udc7Jw-y!_%9JVrRO>OE5HEx#6B3sA$d3e0000c{F0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzF-b&0RCwCVlCev}KorLB&Pp)iV5LPt zAr5g?9K<47BF_19i)P6tf?1So!Qfy>sF8BE*V2^qd+iAsDyf?v{BrN!``+Cn9IvX1 zan2cZ4DP@o=&+RrAO{cdC6wU0;TG{VpyUVm)D3NL1Wv&dy!S1bX>$hp;2lU5#>>+< z-@&p6PQVrRZ!HSIBZ$DXqARr!Dl7`dA=Wv01wJDh1;t6-To=+)J?>EGc_nMsH^``g zt}UC~xP?3uv5MD^vaV0CKk6ShY;EdY{-vTQb~k7+e;Q_GS$3M-S(cG!E?n3BilS&= zzsld#O_HSTIL=u3zMtlKUMZz|C|rVrnvtIO1!^~Gni@e6B=lNY7>3W5WsPjxJ~vIX tXBfsd@Qc_Z5V`D=@Lr6$SN=iyWK|%lj z|M&OzdU|?&eSLU%cy)Dkc6N4jbaZ@teE9hIy}iA8d3l3_gR!x({{H?J78Yb=WDgGy zjEsyhFfgyLuc)Y~>FMdKtE>I}{rdX)T3T9;j*f3{Zzd)tjg5_WcX!9f$NBmBprD|H zgoHXeI(vJ2=jZ1g9UbH2`T6ynCa>1cXxOD z`}>1~gRifz{{H^`{r$(s$8T?Mjg5^NZpAVH004?fL_t(|+Ema*62brwM9~IGhcf}s;t7My}$xK65qp>-+?3x~8|Xzm?hVVn{S{xK`~pl?h8x3~X4 k{n;PX1$g8V7e4_80H~D~YjfhBJOBUy07*qoM6N<$f=lne(f|Me literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-bg.png b/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..08adba59cf1504ea8d701175c1edb2418bd484e8 GIT binary patch literal 453 zcmV;$0XqJPP)i{NK{l*LqkJSQc^)dL1JQJN=iyuSy}7r z>*VC*Z*OmLadB;JZFF>Wb8~ZSY;1vnfuEnBZfW>gML=9UUFz<>jEDpk!oZ zx3{-2Ffh^4(GL#~aBy&Wd3l6{gz4$&udlBb78WKZCYYF*IyyRab#>R**IHUyYinz+ zuC8)&a;T`Nj*gD!=jWrNqt(^bW%fp;0001(NklWE89m{E8 z3|vkUBY0X)31il@oC?Ox+&{R}ZVq#)tWo1fAN}QaWMn*W>e12BgoK2nqoXh|Frc8I zT3T8rCMIiZYpAHG>FMd^<>hW}Znw9$WMpKnuC6*dI&yMyd3kxSudmhB)ejF3=jZ2` zn3xt87Ut&Wj*gDk*Vi2#9d&hee)?o%0001nNkl)=A9sh3ySqE= ze^vCG6=zsUCvCPGj$>f<>_*6J-98t7;6gtI3P(Q) z3iVwMT$p7*p%*3v-wS{XhaxC!CJ9hz&CB4z#{-4NqXI5m`P$U dytD8VU;sgq77nZG9~=Mx002ovPDHLkV1gS+w8a1b literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-bg.png b/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..497932827c3ddf871d8ba385ca78fe5bff933f71 GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^2|z5s!3-qtrF5MJQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jit+<|LR`JPyyD~IQ&Li5Vq%h#l0rj6Lqb9#A|iNrcme|h ztE#G8TwMJ8{M_B$U0q$HqoZSEW22&?5)%`{!ouR>;*yh-0|EjP5)y)ff~>8r#l*xs zJUlcsG{VEfjf{+ZeSNdDvweJg+}zxXi;Lyu<>PWsDS@o^ba4!+xV7ctb;c$K29}F^ zS%jW^Qgm#(Rp09*_*-g5zvF3@r7bGI*yb+m)(6UQ>5LuQi{P|<#qS6%=K3`7#WtCuf;imbr U^vm?GK&u%%UHx3vIVCg!03g_NnE(I) literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-cap.png b/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-cap.png new file mode 100644 index 0000000000000000000000000000000000000000..20acddcf3b248541347ca56c7eeea17b3c575cba GIT binary patch literal 311 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8oB*E?S1&KGn3$N*(9rn!_@tzyl$4aJswy5Hp1{Dsh=>S3 zKfjQW5LZ`M7Z(?IclYS%=-AlU@bK`sxVW&eu*Af~7!;edbuD=RBsUtbRo z4+8@OH8r)IoE#Y$nS_J{A0MB{$jFkC65ltr)1e{ZQ=A z*Z=h@D=n7YNlNq)H^`{9Yg>?ebRFlEUDmEK9o3Ej*UNf3`Dd!ESe4e*!JqA<&~oQM zj0M-D!xMz3q)Hiy&#F9Wr+4GUFaK)o%jdNZFxYi*OxW~er!dem22WQ%mvv4FO#mK< BXX^j} literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/coal-icons.psd b/jscripts/infusion/framework/fss/images/themes/coal/coal-icons.psd new file mode 100644 index 0000000000000000000000000000000000000000..795b4148a893a198d5b056030a22784c66f4bf5e GIT binary patch literal 443048 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 b@edgPfbSnL^1#RgBM*!`F!JCMOTzyT{cw=m literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/icon-menu-Delete.png b/jscripts/infusion/framework/fss/images/themes/coal/icon-menu-Delete.png new file mode 100644 index 0000000000000000000000000000000000000000..85e307089c1d72811eab20e6fc34960ab6161e6c GIT binary patch literal 3770 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 WK{J<4kFE4rAfPC#CQ|`23-}+9dw=}^ literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Close.png b/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Close.png new file mode 100644 index 0000000000000000000000000000000000000000..1d8bab37d35e06bc34fc50ea40b6c81f2bbac90b GIT binary patch literal 460 zcmV;-0WkdJGHNl8phOmT5> zLPA0zAt4zV8DU{zXlQ6YK0Z%RPe@2eD=RBYOG_dmB5P}FU0q#0Jv}@;JQ^ArcXxME zQc@}^DijnHiUrY|00021Nkl);@C;I>2rM`gc zVQ-{=gGW=e>*=VsXz>-NqW5W$@0od0Fc*`UIW?#%Yi$?_5rqe9b00J_L~$5JVN68o zAJmjm%JFsrH|8sLQizUQ35eQdnm_8so` zb=cbh^78a(38#_4Q3oPWJcrkBW-&@bIv+ zvokRtgOt+$`TO~DK9UNj*hmmv8k@EuB)pvGBV1}&TedMOiWC4a&q$V@sW~} zii?X23k%cM)-EnC?(Xi^(9kFeEKEyFOGrqtwzh6=ZjNH~`vr9W z3{MxwkcwMA=Z=dt83?pKl%CtbHuVB)_4~cAfA60zEOub~jQ?|XOQ}qbn%a8!b?(%2 z@=F#UNfcM#K1=Dw7M@>EJ!3TXCkw3g4Pq5!(^Fm~tu7)kF|4PZUH#Gp<4og4J5OG3 zd*^H_!lolO7$ literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-More.png b/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-More.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2263d9fb3aa464829150e41333d100c55a2a5 GIT binary patch literal 405 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8X#qYVu3lbVAt530@$pGXNii`oJUl!uE-qD7RXca?Oi4*` zcXxMnb@lV}3k?ko3=9kp508k52nYyBPEHOB3-j>s2nq^{jEoEp4z8@M6cZCOGBS#a zit_RCk(ZY*E-p?@O?7i~v$nR*&dwGP5YW)jaCCI^_4O?%D2R=XZE0yqNJ!Ycd2?%P zYi@3CQBhG-Q&V(wbYEZJ&!&}4Ko?H+ba4!+xaD)^xLA_|PwPV-jYX3(d1RL)=fD47 z#5ddG{-bAe>IEgWubETb4`Lu)P(wjdA!f@9=tt%e~4|4@{ZTUr<`_xsqi= zpJLqg0=Ja84Qs;Zd@h%;dmFfyH&r(Be$CxoA`^VrI%Xz$%zEI^bgV6j??lXz1IO1U wyPQxeR#EF>{Q0SVR{`Tn2e*I_dqy^f=W0ANY{a5B0v*WU>FVdQ&MBb@0BmKJApigX literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-ShowSettings.png b/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-ShowSettings.png new file mode 100644 index 0000000000000000000000000000000000000000..7f43e1d4d7fc9bfe05af9a994385fd91a47cb956 GIT binary patch literal 321 zcmeAS@N?(olHy`uVBq!ia0vp^Za^%+!3-qlsQ-)sQY`6?zK#qG>ra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrH1%i3a$Dxc>O@y+n7}f3OJG^f3&c};>a2hcDD3aKJb2;B_ Qpko<4UHx3vIVCg!0RIAouK)l5 literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-gripper.png b/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-gripper.png new file mode 100644 index 0000000000000000000000000000000000000000..ce26271911fb066165b4da1ddcff5a995fc495fc GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^MnEjU!3-pmTkq@xQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jikt#`LR{nH<6~oET3cJw)6-*OVxps?@9ej>1)ApT>Eaktacj=$ z=)Z!1N2J_q)|d_4em2CVI0aQRuk` zk?W;TVysupiC8ax6PaGTB0|0FNDTGDD^cn#9FgdqHbi7VLUatMh@RdH#7M6?5Cc8` rA-rC2_XrHwUC=fGEfZkFUw{Dsp-7X>V5rRh00000NkvXXu0mjfZV;bx literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-cap.png b/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-cap.png new file mode 100644 index 0000000000000000000000000000000000000000..3099399582536e61f34fa230217c75519cee300f GIT binary patch literal 347 zcmV-h0i^zkP)f6cEw;9{qN1Xem6fTfshXOa zWMpJfQBi?`fr^TXZf+( zsxwWQiqhqJXXsL%=(s#W+vOIfF3-?+S%;R(H!NH}VdSz8GnZdjy1c@`r3fpRcj&qt xLgBIrJ(q15yIjKDWp~7i{1niduoZfsXsqIWMpJgQc||Iwl+34fq{XQm6eK$ij0hm zl9H05qM~NY1N<2TegX^t?5q=mW^-V*00000NkvXXu0mjf*XoUU literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-container-bg.png b/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-container-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..1fe4dd0721ecad0a13814ee7e396c14c56c5ccde GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRm!3-pi)b-i{DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MM42SA+G=b|5sL4c6D`~Idi77v$KDNl8-o%9!^b_lX0iblGkCiCxvX|6H_V+Po~-c6-fv9gt)$b{rd6a$NTo}D=aKrw{G3MdGl`Gyy@)heCEuV zWsEvWKxHbPE{-7;x27CyWMp7qIlLk1%zyFSj!nLzcRf-zo#qj=najY;Aeh47+4^0| Q8K|DY)78&qol`;+00$sD7XSbN literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-bg.png b/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..742fe1384afe3211bb9d40c1f422b9e93977ac95 GIT binary patch literal 343 zcmeAS@N?(olHy`uVBq!ia0vp^Z9pu+!3-ohPM1yqQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JirNEwLR@=$d!3z~U0q$BoSb&=-tFt_n~;zY8yg!Q9-f+- z8W|axoSf|D<`xhJIG;o%V&7#J597abk#?d|R9>6wy}5*!?yn3%Y9>Cz=jmV||c z85V6sHiA2Gjrp{jV>-O?(Xi}wr!g-Wy-2m zt1e%@ti@`p3UpGMr;B4q#jP_3TNw{AFfeaWkbY-&XYT*^k9*ZvR@BYn)SAnXFUBbM zg{dZzH6obJ!kF{JLeU4OOb$%UYhZrMV9v()PKS9)4|9$Ln}!CH%}mxC#{3&nMIL;) m#J^$p3ip8S1%eOt4=}`ZO!T`rzcvDSr z1<%~X^wgl##FWaylc_d9MePATA+Ei>y-rR}&d$!RuCBXx@AmceO-M-a_V$j8i%U*U z_Vo0OjEr=1a|;g-_weut4i1iujZI8UjE;^@O-)TnNeK-N4GawQ_xE4Abm@{MOAHMS zZES4Z-QB~&!m6vQTUuJSZQC|w%9P8OFZcBH6crV1+_*6_Gjr9dRm#fB+S=O2#>R8z z%yDsXx%uw@bD)!=JzX3_DsIIbJj!^8fq`YigLh$f=Kg*DxYjp@Yvl@czs(0~c%`@kA<7?ry?&Ikxq__A92Yn6_fd cA65y5m>40k*pqDQfYva0y85}Sb4q9e0Jm9u;{X5v literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-bg.png b/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..9dfff0937be2459c63d48aeb1dfaaa8b1b7a243d GIT binary patch literal 343 zcmV-d0jU0oP)tgNh^otVq#)xX=!$Lb}TF`mzS5Fot;llPjGN>goK2Rjg3`R zRi&k+fq{XNl9EwTQK_k^NJvPUnwmd9KW=Vr)6>%y78ZARccP-AHa0e7WMnNZEsBbY zOG`_(wzi3hiQ)L=oB#j-Ur9tkRCwB4!Bql+02BbhwLmejKoARTy#M|D-Zw*Al|nT! zetja;$xPrh7kEqsE?a@)M&OeP)C+-oA#fvju?}Tr+7DfT% ffI+}8_zN%qpqLU4neNu100000NkvXXu0mjfTJ3{E literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-container-bg.png b/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-container-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..112a5b7e49a231417c41fa24036d1d4b54d5c213 GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRm!3-pi)b-i{DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MM42SA+E1qzgAXOc6D{_?d^4Tc6M@dI(Aev6)3Cd>Eakt zaVsf7f^`a0n#q9!9c&UaE-)~2OC=<@GBYr;F(@%GeB85ZCL2&OgQu&X%Q~loCIE|S BE3*Iq literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-content-bg.png b/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-content-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..6feba9c6c031b3fd0b4ee47bc232605a8ea68040 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^Oh7Eb!3-pytKNAHq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c6-fv9gt)$b{rd6a$NTo}D=aKrw{G3MdGl`Gyy@)heCEuV zWsEvWKxHbPE{-7;x27CyWMp7qIlLk1%zyFSj!nLzcRf-zo#qj=najY;Aeh47+4^0| Q8K|DY)78&qol`;+00$sD7XSbN literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/coal/widget-bg.png b/jscripts/infusion/framework/fss/images/themes/coal/widget-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..4579055e3e404337e3d85b28c99280954b0e40d8 GIT binary patch literal 427 zcmeAS@N?(olHy`uVBq!ia0vp^EI{1A!3-p&`7D}%lw^r(L`iUdT1k0gQ7VIDN`6wR zf@f}GdTLN=VoGJ<$y6JlqICg2A+GW9@jgC29v&Xf&dxSAHum=RdU|@6mX>B_X2!A;o)InVbRgiQBhGbF)@jWiOI>yNl8g@ zadGD6=4okZ78VvJCMFIJ4vvnFAt52|?(X{f`gV49etv$Tp`od%sh*ymE-o&HhK6o# zZnn0z2?+_2k&#YLPQk&!*4EZuUS8hb-mb2$R#sLiDJiC=rnZWDt!o{@ZY% RZ6nZm44$rjF6*2UngEG2j_?2g literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/iphone/backbutton_mask.png b/jscripts/infusion/framework/fss/images/themes/iphone/backbutton_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..5191bd0f22711e86ee0080303722c97ab5b58ba3 GIT binary patch literal 429 zcmV;e0aE^nP)w2tB=)dsJYX)BsdEBz%sXsRO7mSZ)>t$_!SU zk5&IW&W2imjQ-x~{vEkg2~gn|)}UW8oTvtiVI0VPSAYQk XkcA!xF!kQC00000NkvXXu0mjf4<@`y literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/iphone/button_bg_insetShadow.png b/jscripts/infusion/framework/fss/images/themes/iphone/button_bg_insetShadow.png new file mode 100644 index 0000000000000000000000000000000000000000..1662147b9ec06999d2fbd2940b8b4b67f152dbff GIT binary patch literal 1262 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 xk-S#+t=Aq_G( z3m$=_lGL4Xwq;OtoD36q3Vwn2;3vdIcFO2yKrvQ`Ep z98sF+D|Kma6HRJh2aiEUshFJFiWz0_5Y&~enJB6K3~qt*O1-DSKGQJt9LKeJp3g-h zkpi{$q)9y)hK-Gl*G8k!ySBFG-P+osQ?Q}w3JL|9ID4K*&1PA( zT5a#{?!HVU5=(x+e_1)#wK0$+iDfbw)^4}+FxbZZ<#;^q*UEsS4~Vl)r^7rRPd=B+ zEzZx+^TlGZ5DW%IO$;amL0~46iP>zn-<3+`S|AWOlTN4iR#sLdO$;cI3+8gUB)wi= z1lO?wDHe+rH#awBO$?|7>h(HvyWRV!DA8;-udc7Jw-y!_%9JVrRO>OE5HEx#6B3sA$d3e0000c{F0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzF-b&0RCwCVlCev}KorLB&Pp)iV5LPt zAr5g?9K<47BF_19i)P6tf?1So!Qfy>sF8BE*V2^qd+iAsDyf?v{BrN!``+Cn9IvX1 zan2cZ4DP@o=&+RrAO{cdC6wU0;TG{VpyUVm)D3NL1Wv&dy!S1bX>$hp;2lU5#>>+< z-@&p6PQVrRZ!HSIBZ$DXqARr!Dl7`dA=Wv01wJDh1;t6-To=+)J?>EGc_nMsH^``g zt}UC~xP?3uv5MD^vaV0CKk6ShY;EdY{-vTQb~k7+e;Q_GS$3M-S(cG!E?n3BilS&= zzsld#O_HSTIL=u3zMtlKUMZz|C|rVrnvtIO1!^~Gni@e6B=lNY7>3W5WsPjxJ~vIX tXBfsdk>FK$-xyQ%H z)YR0<%F5;C<)EOT*4EaxwzjLQtIW*I-rnBe;Nb7?@6OK7ot>SKkdUXRr?)V z`1sP&(wAq~{{R30s!2paRCwCV*Vh4pPyj>GP*g;UDCnbuz4z|^dr5HzawfQ+3B2Yl zqQDk4jGFgJLL;n$FytU#k}Ah0A>mr)SS++SC~{01^4=2Y@qjL%@lbLT3K0j8h-|-T z6H$H0M8YLnM4}8T5xmeL5~t`A@z)^Y?E>gLQeVKJahfUueHQP@xfmD&606y*oL>$F yH+wkMICY!1@sIO^R8RMD^X|`F`X8I000RKMtez(PmM+8q0000|3( literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-cap.png b/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-cap.png new file mode 100644 index 0000000000000000000000000000000000000000..42e72865f6c001cbf27f6bcc7fa120952c5d0f5a GIT binary patch literal 451 zcmV;!0X+VRP)71MgRZ*^78WJgwv^ z;^OS=?C|jL=;-M6_4VfF=J)sa`T6+9>awYAI3%crNOrlzL0wzlQv<=)=j>FMdw(b3@G;J?4W zs;a81tE;!Sx6aPa%F4>Xz`)1H$DN&>o12^0*Voh2)6C4wxw*O6*x2vy?~ss?h=_=w zprF>)*7*4N)YR0kV4o!b005s!L_t(|+EmfU0)j9UMA0D>6{U!RhHCG<*Z==52_PhA zcb9J-khA@0>M=zIW)OaQOxEFC?*6g96Ui>6?Voh30$6X^-~bwF@n9s1&I5520cK-K z0IJpnxCXKSaa0pvO;iCEw}t>;+7!Tld4vbhoyG%{-{0T)`T6?#`uzO--QC^&{r%kB+~ecp+uPgz{{Do7 zgtfJ`>FMe1?d{&)-kO@4>+9>v%F5T**TciZ_V)IOh={AJtG~a$$H&LOz`)4J$nWp( z`1tsckdUOLq~+!1prD}6&d$%z&&*8ze+00hy6;}lVhpky%ToJ0S0iAWE(uY&m- zIOB{o~`~+uPfOgoOV7 z{_XAUwY9bB>FLA6!^+Canwpx<&d%%W>)zhpq@<*vprEm_vB=2CrlzL&`1sY;)z{b8 z<>lpwh={7Hs@B%l%*@R3@$u8s)4aUA$H&L3tE-TZkj2Ht@9*!xz`(-7!uIy|)YR0! zzrVP+xY*d(MrjO`0001+NkldoTAtmxP+U-QUds zIoTnq#T4o3&TBDZvX%^KP-5*-xnD^;UTNJLa9Bzm4v^3`5BC5X4~Yp;w3Y>UDop{t zjwFB^Hw190BEacK1c-c90B>SZ9`Jdzcz_b_bxyi=s)wthB0_bS<+_tmw{LjI6U mHy1y!nesHbjl&`H7hnK@jS>EO-ZQ%Z0000i_@$`1ttX;NboJ{oC8yfPjG6+1dK~`tR@Wx3{;^(b1fooWQ`ql$4aezrTx% zi~Rijo}Qkwv$L+QuJ-o!;o;%T%*@l%)02~vgww8 z@$v5N?%?3y|NsB^`1t+({oC8yfPjG6+1dK~`tR@Wx3{;kv9YtWv!e;raRb>FMeG{QSqq$CQ+m zesEgs00017NklIc5qKgb6tPi65xoBi`An7>h%Ekxi19HjNf-!| T>X`UF00000NkvXXu0mjf@pz^( literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/icon-menu-Delete.png b/jscripts/infusion/framework/fss/images/themes/mist/icon-menu-Delete.png new file mode 100644 index 0000000000000000000000000000000000000000..85e307089c1d72811eab20e6fc34960ab6161e6c GIT binary patch literal 3770 zcma)9g;Uh+6aVspbg6`h_<|taDGhgY$I&3o5t5>m;L$1Fa7aiu$kE+hl6Q22fKo^E zT8{XEak?1pQo$rItf#0LO?NKrvX>k&!+Q(VkP%gjNd{Rnt23I-nm zfZ+LmiUFi&z5oDxbvrOvL&MhD-T9-fvkR>v7)l4qU=y1wKsy7-2E!_0!eB{+_z&}kOlO)0Wurq9${df1K?KFvrz!5>Vb|iQi2))mk{9A3=3ufuzUcs0eX5* zASewWlRME9`^#2Ay2JX&R9cN#BZIK4zY#8nD~_HX=WFIMMM^d@LGypi)1^4Oywa$6 z{rO2&Pe%YCJC6LZ+tWMGG2)7`F~MjAu_?!XJNCmH3yY2Wt?^P9DF9e=_nW+D=c;`P z6UBx(-siqO!m@o&ko^fAX;VWYRR?76E$W@R{Kt)KPHf}C!q(>IyiyOy$b3l8_a541 z+M##%;mS|!7Ja_jw$9+sYvM1Db-CI#a;B31YCIk<$O5$;BY#^@@Ni2r{ia9JqEVlf zY(w*zYqU&y)G2TNn{ZG(OYhsM2dniJ_8Uxz6#<~iUIN1>j-$xL67xctiB#XcV;v0u zC(X{CznF0`VYY#5;~o#kl6UgiZvmK%V!SH=yq96%gbdY6_TvJ8OtwF3l{EEP=L_}@ z>{p#nP@M#~W`aQ=hMq1EDG1*R_R7tSr7Qr%95Px(!)nI$ONxfSL(?`S!G)N!OQ(TY z+=b-Y95<_zxy2t3OL_qJxjEfp7=}fJK7BMUWm@nhU8e%pvq%Qo!7wt-H_CAW3Org7 z5Lz7tniFxie}qGg<(lF|2LRrH+mIG{o<#o|P4QoZb>PM#p_f5&xdand?mX!+6xkUQ zZa-g&M+xTaOqAM^EXGKH`?))IU*PcdeBkEn>jqW7W5-Ld9Ik#-Mp)YSvP!oMvpM|S za=h9D>zTi72c9672Z+}EwX}l1ioV*sLJ{pN2L7juctUuXL7H7mZxV`>e=;^bz3O>k z!OIh>ufR?}`z+=;7pYGexJQWLSvZ)NsWJ8+#p)lBsb^DiQ%+j+d+fy-{0cAW69yNp z9BOcc6WM9m27))?o86myo79_hCr0>i3n|whm)h&hT0{0qR9mE5{9D-8sl3uUdFfh9 zMLCeC0x^c5@*IR#rDkZ+bN)~1!lQu{ipu%rxk=M6Z7FThc9pyCO;4ASUaO@{=uHjp z$?vA@VxD;r5`~Zl_J={4sWM*Ryns@9yr@i7DWIB;r0R!o3-KJJ?xc~VIqT@NB3QqW z#|_~pDJ3x`QL}PEic1PgmP-h9PIZPMF~#q-eJkX2__Q~Q@qQ(j>Xg7W4z#JYp~a#_ zp(W5FXNcqvKZCn)Y=dr)_v_*EKttc@K? zl2elPOBG5rOXUw*1uG5<;RUU-%?3CA#M1prOvX&|(3r6&@Ez?1?WIEeLJC!Jk@E`9 z5{G>1oMsu1cBjIl)U3Amuq+kl!ey`4o$FV=Tz8N=$Lqp#c|v{cT0#bFB;nW#v0edJ zXE7SCe&kb~kx zx|V8V0ZW0Xil|Ee=*Vd9=xmxmhB(h0?@q=}#$3iqy_un>Av>Z7(O|7uvuRjhaD-@a z3N{or$k*2~G^v#_o0KBTXH7QB(J&D_4rOZzqtnLr3+o9*OV1H5eIAd z?aof%m~Wd$cKZ=B1&0Rfg-Axbgz3MW;+|SS3n0 z?Zp;_n)sU}`#5b3MrQQk`idWvUkP)vRtP$MXj{2GJPlokuM=$eP^NMXn7DZ5jCGv_ z7g7{+K4TY2mQo2*dzbh}nv}JOBI@nQbC0*t_+DK!UC=jNlY*0B^I&7v*J>vUtg7De zzvEg2ycl}<^4YfFM1F{}9tVy7c59ivaE+L&7ZQJi{0Uo#E_Rt%Lj;1=aYnuLP%ovz zua(hT)xrOL=TJar9frdJfe!vwu{7JN;`}6SpLKexDGRll> z4n%+(^V-E4TC58P$3{gZAwM8zkZXwG(bQ6%`P`+`YOulh6DOYoJJ?zJv!o0(wCq~RQ{+gkg5{GET%(5NG6Ziky zb`L-H;2;tt()pIzw@&XOmEMf7b)>Ch1bT9gRg<{Fijp=S|xOu^Y>8QL#y~XrQ z>QH(Cyc9YkX>^a8KSq7E@9@YzokYo_v4aE0@8iy6NWf2G(qe`~h(Zps-bn}xuS>e! z?cElF$F0U0vncO#?}sZWDY*PDXAb7ZCQ~PKQchA5+8w{--|crCP*o!Py82$X4SMqN zyg$>uIqdX6dC(l^t&pg1exd!!{9t=keXc!Gc#}Ytknn(onR zYkz%ncWrqMiM~G1&C8uzoXO10TtLlT-dvoaPp%%fo6FOU?bVa>KSzImuWhazpB!FX zouO715C0ro{1gnm} z>+2gB7#JEF8W|ZG8ylOLn7n`g-qh68%*@Q(+}y&#!qU>x%F4>x+SLf?(XiNK7IQ9`Lle0_cW{QSOt{R)G@{Qdm{0s;a91A~Hsf`fxYLPA1AL&L(t!o$NOA|fIq zBcr0CzJ2=^9UUDL6B8R78y6QBA0MBPkdT;|n3R;1oSdAJl9HO5`p9j1dU{4i#^VdZ z;qa`itnc5yXJ=>U^qprEj@u&Ai0xVX5aq@=X8w5+VGyu7@k zqN1|0vZ|`8y1Kfirlz*G_UF%^b#-+J1fssazM-L^v9YnKsj0cSxuvD0wY9aat*yPi zy`!U}v$M0StE;=ayQin8x3{;iudlzqe_&u>aBy&FXlQtNcw}T`baZrVY;1gdd}3l^ za&mHNYHE6V`q!^tGcz-@v$Jz^bMy1_3kwU2i;GAk5`{u7EiElCFR!eutgf!Et*x!E zuWxK@Y;JCDZEbCDZ}05v?C$RF?d|RF?;ji-{Qmv>@bK{H=;-+P_|Km|fB*hHIXO8! zJv}=+J3l|axVS*0(U+H(S65fp*Vi{UH@CO9cXxO9_xF$gFt$O1;c;%5-L=%TAKm{) zfVL9O$FmMdQk0Q~crF^NFA#T0k)|)#JGRC&WGukD9v*T?%3p5bDZpqM6nS(KlHwD0 z?9yYjO$Y5$hXEkfSq}eh-U56m7M#8osK%m5ViqoC3^d405x`PIW95NnNCX7{GQb7~ zA|&%TdqUa=)b}h^f);(DIQbLJ*X6afg|P@{-6hoYq48FQs)tL`%5wZNWHf=n6cgzD WK{J<4kFE4rAfPC#CQ|`23-}+9dw=}^ literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/icon-options-ListOrGrid.png b/jscripts/infusion/framework/fss/images/themes/mist/icon-options-ListOrGrid.png new file mode 100644 index 0000000000000000000000000000000000000000..b39267f184002359fc1fd52911c7a7f86d27388f GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=C2Ka=yHZ(Ns-@o72*Vp{#mmfeu zrjj7P;QtIyw;Ol?c`lwVjv*Ddk`kPldomgtwYVf6Ff3wN#J5UnHm3yFiu^+tzOb%U z=8k2EWiAz2@LNzw=|aK5G^RGDSF;UFY8@Y+VB#=fSfs<+zsj&q7ib=Xr>mdKI;Vst E09y@0iU0rr literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Close.png b/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Close.png new file mode 100644 index 0000000000000000000000000000000000000000..347e288a15802049778367741a613ded9ffb330a GIT binary patch literal 520 zcmV+j0{8uiP)s|NsBv+5^W z`^w77x3{;=;OpYz;^5%m=jZ3;<>lk*?Sjquv9YnQudl4$`_JO-hlhuK&i%N!xYz3R z?DF>R^!SjFkbKSk`1ts`y1HS-|DT_q`T6<%{r%I^)7;$L%jEOV&(F=x&7GZ{!^6Yk z@AmHQ?!Lah%gf8b!NKb4>g4hGxw*NvwzjpkwczLKyu7>?`6)91007KML_t(|+B}cv z7J@JgMkCPDQY|uM2#A7v@4d(Szml!z$UP_dZt^REl7MM$6uBCc4x(yKZ9?fpgj~?2 zlHke@oG42Q6g8lrkx-~^s~G1)jEDIhivb*!b8dA9gRXVtJdPpo48!PKaBV0+Ztt=GHW$o{|JPWPS8e(Fm93NQev(i4PbFxc||0000< KMNUMnLSTY#F*6PT literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Less.png b/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Less.png new file mode 100644 index 0000000000000000000000000000000000000000..fdd1a3d521031a2f287088c58ea7506d261acb2c GIT binary patch literal 467 zcmV;^0WAKBP)s|NsBt;o)J$|KsE1=H}-2 z_xI`P>Gk#X@9*#O^78EL?Ee1#-rnB){QUd-``_Q+_V)JZ=;-k9@X5)^>+9>_;Naro z;;=jXb*y3^Cs+}zxUhlih^pOBD{`T6j&2@X6%!xZwAu)$p<4`@Foo!NI|~xw*Esw(9EY!^6Yw?(Vwc`Lo;dwY9a& z%gesLzP{h`-wdD500028Nkl<3_#IH5Jam*b`WJ1aNl?T|GP|>ku%fY zv`;Pch%_wUw+zWM(6UXt7NVx`g(%cWo3yk@Ed+@E8anGpz;rd$$bmPF4qz$+hL1L$ z2cbJ;h&#^p4ad8|5OGTxecKX z0;Ln&B*<)8#p707Ei=gvcG4!F&+{gA{$H?W71Mf&>s|NsB@_xIu9;q~?PFMe0?Cjp&-u(Rh{{H^&@9+Ej``_Q+$;rv~_V(!L=)3+}x0mkk8N0<>lr0`1ps1hn=0By1Kgk{r&m*`M9{a z&CSiRv9YSy^T*=xwzjs#-|xA(xoXJ%meTdX!NKb4>hA9D!^6X^+xDy4^~=l4$>j5~ z+3>u)ytTEpzP`S7pGb)S006B?L_t(|+Dy+^4udcd1keqHP!ijiUPI_5z4!ZHD>hOP zPg>1*zt%8e2sS;>#DsTXW9xQ?P{x8UP$l`oq9^!Lg#gJZ3{oF~u`+}Kdw?!ZM_d;i zYc_Iz!25#nP2mntjI}R(EKSqye6e9g-9q51%vw=)JoPra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrH1%i3a$Dxc>O@y+n7}f3OJG^f3&c};>a2hcDD3aKJb2;B_ Qpko<4UHx3vIVCg!0RIAouK)l5 literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-gripper.png b/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-gripper.png new file mode 100644 index 0000000000000000000000000000000000000000..c0b6b974c6c9cbad23d584db72b317ba650fa1df GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^MnEjU!3-pmTkq@xQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JilhU4LR|m-`}hC<|6jj;{rU6f@87?_fB)|4>PkyXlaY~` zSbruEsLamO#WAGf)}He_c^M3N4m((W-2VT_N3N2I%n_?5@LW#XH0i8V>YDry;@^#~ s`Dn!EsNK|Yow6~_>19IqN#O*BUH0r(&zik82Aag+>FVdQ&MBb@05jiCrvLx| literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/mist-icons.psd b/jscripts/infusion/framework/fss/images/themes/mist/mist-icons.psd new file mode 100644 index 0000000000000000000000000000000000000000..8c8472ee0779e3bfb8cd301993e7dc692ccdcffc GIT binary patch literal 439723 zcmeD^2VfINwj;S4;{t||K!P9;AOUM7chhXk1qT}(+mw(hOKV#}Eh`Bdl1otPg@hy~ zz4v~X`qTf$NZ^gtE-tWfolZor|M`D@9Oze z%P>N>vaVidatXXHFUOc;oMYh%a&`H+Il1}90%P86osr{;^jx7nmz!fKDlr&K3`IKW zn7SYpuI35WmXd~wDycxYn?JS9>vfmt_3iELIqkVQE}>O#C@wD6b4I<B*sQnPYJ|^c`lm z-jKuTgE_;4^8hO)CV@A5T|$$~WnUn^dwrYB>+-a@+&WWZo^FNBX?3-G=IO%|he{9S z!&jKS`~tvE@fwU0`;*?KX=U39et`v`S=j%XFEi4<(Iut%HRGE3aasV03S03PzQfTy~cL z6yg`;&eKN%D@YcKt}&VF1y_sB&M)vZRF~^&Doq83;(~%X#vFq(8%4TkzdW#bU0viC zm)noSp^wz=Jl@JR&?KcMu0omoq^D`Eo7Xq+9+ywB@Re(T2}12$YK?VuB{fcu*X*?L zH5Ch>Iyp9*wWQcs$(0x9RaE73RTY&6LwP}Yer35Sm#fOlE#izu+N{)%Dlkl6s-(hY z@gd(y*;*mn$_lQi$dqR)&gUvH+hS8yK~bf_WGFIL7IB6mIa|e}y|P^@*gy`O?TR#5 z>!D;HA&XwcEV6k(My-{bqA2uegKXqaRYT>LYPZn_>NX1=9x*?Es*HN22o!U|XD9_r z%oc2P3oIxUdFwpA3L2_b_+z%-L>m~pV%~~J`_8|;jd!ZVYHkT+vGGL#gS5=JZRmA889h5BIhTXoZK>zK(>lj}9$}cK08d&>^9=!}PrdM%E zKF8=4J$e~qNiQ_TN^*-By`o32+?dlVpV2Fd{a0R$>18O%W&Kwa>6IU2df^BjlZVlh zhXpaFS6)eO0i##+=*5{Tjm4F@Wn5oFLBrAK^Gb~QCHc%=j-os?#F$=SFEe=$lv`YsS6*(2#ds9ZE4ReJ+E*0ml^4(wuw<(M2Ilc1P_+b?3RS{cy z8F5O5>GM(3=ef!l)2j$vZWz6yN3W{b(kmCfcVPNq^yHy|i#ffR|6vsMLxUm4^n&#n zn162++m|us^kU;t(QIG2F{T&zvoU|{D7LS>7}E><*_b?x9=$5^i_5v9{PKz_aXhN5 zsKQiLQCwVTC@3tiDlV4Sg4wObg^Y%hd|Y3M>GM(3=PL?gOfT?{V)Tk)`znkvz3>wi z*1n?GzKUW@uL3sS8%_Tx=&EAde}TTi^us91!?Kvu%gFpUqDZgunA3~-_eRlPt}w-z zUa&|tYhO`pUllQ?7p!;9{12l@ugaLy3zmYH);MS1!w&m^F5=1%PY(+gr&rXv3?YMTnR0UjrT@T-zX@H zF}>J&U$`Ma)W$aoDq>5oT=@QrjYmb1UX`(=7Yxo9vh}f}*}e?LRh6cS+#=|&=nfl2 z@ZouJc|lcGetDs(+?YR%{>w=Bz=827Ha;J{_GKuGIlb6=uu<&44COJV7wi|n+E*0Y zmnpXNf+=Q>jYmb1UKKH?7yI5Jn)0wR#`H3lu<@v9(yJ==^kVWbdh*c7#hhMjy{{;? zFQXyG^nxu!*?dYA+m|uM^eQgl*!uI)qZe%LU@V4B+l;VhC~ox`?ePsGtl!JV52Hx0 zs@T#C){kQLauoZoGA`!y+Pj;*?q}V4r2!t7CZM zy)gL&;~Q*!o9M|ygNe~=U)s;tP!VH#<(1?!dpU~jt1_1KDnNf5_Pu!&^$kN+Z0TiS z>(58kf5XVdoLYxZXoxYrz`vLEU(xHojK_YgsA(rh6*56?IVHEZGA};3iV*XLlq?aM)^vYxUeDwOSB4fUoL>3NpDl{?Du^+?V0?r5M@5fbTtQ)eMQ(0IUQuku zH}YY8gRNH?MS2y+m|jIC9Md3Ne?E%z zDvu?-z}W`&^JV@3QKXkC=JaCrR}{yixQZCl3;Yk+_(l}lS7prUmCMHGqeriT!m7%$ zLN33eT(Xx7%JVDBP4G2XUTzU*G!El?hukIud`tvg7_+~kNUx&U(hI)-V(aNevHvP6 ztK{Izp^B>fShTO)l3Z}1XZn2f+83@Y8e@8~{iLGVzDzNv7yG_4n)HH|U}M_8VEscT z52Gj#D`QKqynLn~M$;b~)WevzFQ6COk3Nd>u$YT6y_m5aJz-dEh&8cF3~X{aiu5wZ zo?iQh^Ts^AU~MUB{2>$3}H7tSu4T|665V=&A%7-t)DXB+Zn10O>oL*Z;-dLyKT zrT{Gg8c9C17hrd28Nl#!xeBh(SOF|zFqY>-zLgb4uzidXW-2NxjTL3Es1GHkiemk8 zxybts6;%sTK}wc@oOJMBvqD&byo%Kk;tcyBNBLgn=O?X57Wl zH6#oSDKXcQJGg2?Ik)%(#o8Ye*OvQewtk3|&LQz>pF%?qcW~5(b8p zm~j_F*N`wUq{NK77`ldpfgvSk+{Mr}Bn%8GG26}b_*`N&@73{6>-?$hQx`5EMddYhao-a>GRY$N)xe(=VqE+YG%lU~ z(w~Lv34JPlG`)?7-&Hi_!NL2(Xs$of`xN>MKN|Yfj7)l;3b*2+rD?a(n?*$5(gHDd z_)Vk`jRd*nnrnj)BgAOPD)^PtGV!Z?>PE9gdh5>_|ybV9|ui?Z;seNoO&rFnL={m7hu{_@gzxv79}73M^{#u9Z?DPo+A{9cn0Y04cXS>!egk z^qui-i5B^>cb!u)F09TeErQv>H+p&Z(&kev5U+ksBpSX6FPPzjJE^2>0FJWqP8ZZP z1lF#^v^2X0vzNEJe9fgIjsZ^Mz3^Ss;ETXV`Y0mV>VwL_2Udg5?I3sGxuv$dgw-|9 zrfAh}6y~?o=N(dp{Z)|Enw(w*yJWQ5UCm~DlK?`j7CwVqZm;zqw%|xh#Ja- zlppeFOcnh0<_Fp$!}uZF)5i0(_I8_-UrN8G^vGq<91o*8B8Q0*@DRMJc^Q7NP4q4$ z4r7|l9)1ZhT2tl9Cb&-^%ZP{YM1X5(v+zNt)9O*vv%%suKpZWn$>r8bD4-A2@+~q- z=zY1@CdJeHM33EO;pMkDOjS+-je}gq!kI*GYAVFM#(#&PR>9?S%OG+P zfD&DTtrd_1%>wSyDjP9(_y`nWa6j7T;e~46i7ke=0)Q-W=ycM1XA>K-5EpThIiww~ zt)vyO;Dx(Zz=aLuv^$6u(zMpiONFG0g&+iTtZ}sJAQ%eX67abY-ZVLQAHo|MhxZ}8 zX-$qd%IvmQ8N(Uk%P*3_@MMyeZ!!DqUbswI!wcRB$(PGub@EZot@Hy)1#+{*{L8C@ z5%36E;&LLcQ@k!WG-nU5WVlp2FkmleTC>XwjMxi43Rt5pEcsx0ko*m+ujWA{)i8K4 z9r(>mnxXy8A$H)oR?>`EChj8R9$g54>0KT?^hT`0jYPWqh~y`R+8J zLm1y>0B9I|m!{x52S_<9@Lz0rj_Pgw@Eqky#&a$~?oURBtV>$ko97aT$8#A9o>R&? z3rIjz3gL*UwEHWT(6_Xs(vV5Jy)NyNTtKEUX@`pEFr?jR1?S1qE>>I@F7Lv*E==Ar zt{WNhZYJ6{VO)o%^)R?@f`aSR@(yhd)cb4@qTLm%zBgRoqjp6<5^y@aFq43LT>{QT z+dYgI(IOrOFOFC6Vn_n+DLxFBiQ#-0E)y9ajtF}V(MJX{0v za1LDW8I2J6tUrw~AEKE&-0Si%?IWN=m^{n^&@klTIE6gyn-61S5r_B6$YuR;A!?9} z3->-3-VbyL<3b!@9|jkWRdAu)E2|}8Y&bByKMv=>@cx)_;K=BYaXdDR1Jj4cf!PCc zV66J$a2^cri^F-4^~EEC2UG9p&4cK6F$^h~1tZTs;6pE+i`y$RAsRaO*|%j(XD(SE zyx05S)EjzpU)u1vZ%n`37c1#UrCVrLMAHl}(T@&D`IGVAUgy8WH+u8msNwNnMlb%8 zHNici`}LB1Vf`+Xd?RCgIuU((!gN1$Asa@&o7pGl#Y*ah>3qGUUKsB&sW&osFCoG> zAUe?wgZEOyJg#CR>!6M*e;0bM(DK$JAmn>M{o7bCyVrZ!_^W^{VX_XVM25j{V_B{H|Qo{}GuXx*iA#n4?0=I~ng4{5KBoL;R;fF9^ned!PSS03E{k4;Ny5D@ z3E^wCUR;QvVQ^vJ{cs&MxEC!S98_WFSBW z;y$y_>r#Dj6jffr3MecJ@rx`4FHuPsvM5-8x;OgM2)u-HWu);^Sg+WNm*{9+h?nSB zYD_NeeYu2RX^k{qq9YlKk&YacQwq5h#!E~tjS#tnUm1=xUJ9Ef2$xHgcNC*$^lMor zm-fC~Y8&~yq(KiVg4*BbUcAJO-co2z!=R(dV2^@K}+khDd3*4msdZ6+ZOr&nmGF4wI*c9kyaH5fln zfi>9R%Vx91Wo!z_|ujy)N*#cA$t|uV8RlJ@}(}>rlcx`n!VFXr-b#_}F zR=j=zu7AJAhbuY7)x!08YixWwT)zX?)9gM6XpM2W#$L9AH^Yp2JmPGcm$!gr63@YP zn$Xl>g6ny(+)-Mq;<{OJ?S-Y0AT2Su+-vD#LAsfiSvo^;aglBj-)`r<-Z}NK+?QFf z>P#+&+w6puGQ@Yrr^pz@tqzb}U??sqm}AVr56k7F-$*&p03p&=O!DI~lxCXouZCcu zheI&et*|=SHo(oLAz1S@ggkmKArn3f!KU3zNX9vYJo38Yea0idw0XVm61~2?y*@;zKbJpEPuXfJ_Vt;g1?L$Il#NoVm1Yw%iH+AV>k zk_?hf4km|@!^uqe64yuy$y`!OD#_7gDQP0dk`uv?s*N~cuB@G$PW-7!XYwd{k~|BmW&MM^LH^Qq>) zn*V8j)5dAjwAtE2wMS_6+9GY4cCofedxExAd#ZMw_8jfS+N-seZ?D zr@ol_aq5qwMva;}s(92fqimzrkGgu)gQH#^_35Zz)5fG7nPy5mKFybQaoSyJo6|l> z`*HN>(MOCf9ldI_cl5=h?-~94=ubxfl0G&)C%q=!nto>bb?J|!zm@(&MoPx?jIxXq zGuCBXnems5*E7D$OvyYV)0An>JR|eE%*Qj|%iNYVHp`H;G|Q27QP#a#f6w}QOwyPm z##D~6jyY${ZDXDv^V!(Av4@W}jkSzDd+hCFUmW{Ic4GF7?3(OTvMcM~tr-Zy$fj_&<+-cl>V$9ePmNLHt1%9CZIdZyogWgozVM zC-4(COn6|zKPUWlugI6EidGMnL|Lc(WLykP;m_ynRx$%$}5BY9l_QbgpEfY6P z{PV;QCut`gIjLdNX_Ic7^y;K-haPrl^`Y)VuQ~MjL%%z0{9&bsopRXahdq7Re-4<+Uk0rp}nUZ0cE4ADH^D!&470 zI=toZ%MX9<@U7D(Ppg}D`n3C|eKlc=$Q*=o;vf^neWU>oK-Yy^{g9ay>X=W$owPQj=b*3*N@U3RdAH;s2h%Y zb9Tb);@QsGx6XcVPTHKZIcw(JH|Nuw@j10QXXQMa^MihxewDsc|B}9k%jfLe?c7I( zF@~cJXBnO_{Lgr#(PF&L_)hNV+^XC&b05p?&O0iP&$}`2{rs`{b@}J#KbOC&ps+wF zxWC}Ph0_X8F1)_*y`t=*`l5@9UM@~3E-yZ__{rj5OA1Q7C4Vm2GI!40)pPHi`}MpT z^Z0qU&--lt)cNN5x6JQapj&YAf}0m~Eu6B@yzthA|0zAZ)LMFH=~rd5%1$Z!L)j1I zT)9yGaQV-s64UQYn=9ffsw*~ByjD4;a#`iIm7i2it!k~huWD;`e)Vb9n-?W4TD+)p z(R($A)>vxpso8pT;nBZ4`h~@5iyIeTyZAq~N7f3pPtZKehg^$0Qz8f6R5qeA!@VIKAPe#<7hjHQw8}y{WwElBSQA&0OYN_T2J} z<;O3-XZg=7DpqV-(X~>)@^>r$aqJ<-+Kzo}Rnn^EtL|L2?YPS0t~lLOa&p4SD^9-mbq74>?`aK zIFcPFJ05qAajtg0;5y9J;d;}3l>0n)*Qq6^UUBMHVX<(hN8>rc^O!dql&M#JGkxd! zK3%h5&2?*jXJ$L$))BUG^`n$5<-ExNJ zjOH_*JM-`}&pGpp_0{X|_NV$+`~UAO?yOB`{dD$`yX=w6b(e3r{HH5Uy5jFw=3RNyRjF6`uIjqF_Ugy3 znRd;k*X+Eu?b?4{SAN|C*H60sg6n_2!Ft16H3)ymQxG&bzwqZn*n}dkXKl=iW*8UUHxIKHq)+ z{loEpc;k;1e|+rzIrrc4!1xC)e2_fod+?h-o&2YF|GfCm&;6z7FZVw*{h{k09{ccy zM>LPDePrvSZI5<6cI;zsK7RD$&pk2siHHB1^Vhqcobu##PmO!(lBZLjKIiGcGwYt& zw%NV;yJuUU{p`7up8M$emCwKP!Z9zr_Tu6fU;bOw-=2S|?4@U3p8xWbe=qs_yN!r{KgY+&U^Few-&zj?AsM@|LvWccmDCu zCI5W$-DU5-|K9QMb-izW|LYIzA9R1X=EEHyt^YXT;|>4H{MVJAO#0;3f6w~&16>7O zPkmbP>8t-~{Le?9Sw8#rbI<2Hzc}a1(O+Kq)#R`4`P%sPlmD&y?;GD7_stjIy1(7= z-Fe?j79YtPOa_6?Br6$S>E<}iRHDt&#ARs$|AN7h)W8dxg;2``aeZs3FdlL6 za`6d?Ny#axqhLXDb)<%9=^Udsi*pl ziP@dE{Hgr#Y2zB6ZO+XT&U(|7G<{R!)=#@Fp8WB*{`u@9DlTiXRz7!|x8R_+mi>qS z-(Svo{%zl9KUU4S{PuGny5fa*KL6>ChhO~X7u%M%tU2$>J0JPmyI*cEtX|RDe*RT= zJ^IpnU;Ug#v|6ZbJgrkwVnRNx!W6@pc&NdtQ^zJ4{hirZgJ&Dw+?qSB`BUMnO{T_i z7EfOH^hB&c(h>R3y#!QT5W?x7WOW zSCVVe&NqI}el+{?4W0mbui3rx-G5v^YJF1KBWKiK@Yo%X-G2M%m;dj>zu&v~NzI`b z{rJNJKV7%*h5$*}`Os%a%^ds0RSw(vj`$n%F8So=0J&}F3yv4(yg0FP>WHN|Hw1DMnCiA!X2IMP{^1IzkeVAX5X)u^nCvG2e$Zv3)|1IKm7cO|6cszg;>~K zfj547>eaT158X6v{rxYkuljO*t7+`itg}D-TS4nF?~e_ThU~+RwLClL<9E;g>Dtmw z$4#z1^Vp*H_x{iJ{au@$I%MIS=g^-=j_KL>%$Mg#f&PVipSgG3*KhV*G@btZ`I4U; z0g^Xe4$qjrY*+mQRhRr^j$id|@pqn-5C1dq`x#3rCRBY;{aZnRT$_3B1Aln@tVhS6 zf9UObok=I1GWX&;jytTg=cKOtz8?o~9UwQK`MCa}6+M^q_@%+}{P^6C?0^5h=cG^PY~K(dZvti(etgx3M+HcAfRqM^aHgE@st+gkT=4HNC;xEt zlc^89aHf9il{+fW{5n9sp8P|tD{;+(8~>QI;H{!L-IEvpch>K20r(9)=AO@=TD|1^ zhaWSg{5Ea%oHx!q>c7)ZOjz-s329FX)9i=;>C(ns_g<3NlUzL`e$%46FZ=tHzx?>G zAL_PWl<}{-dd^Cz+kWr7iN8#zzq(zw#rS-u&tIO*PYJP1{s+bj`F) z4V!AFU9JM$eh0)|F$*H^IJ%*sX8OXwTLV0y%wlQa?IERpXGp34Ii%F@4k`6}LrVR| zkWzmjj#5pb;&U1NYOo8*AZ4)FtP7lhd7=ZewGFQE!+#IGazl!-1EkOq6KN!Qn4wt0 zRJbIXoVq%4BwQquB+VEQ*BA{2Y7T+*&GDL$X3fOl^(mT}QX1&8GzRKvItkJU8Y8{N ze(>V+ZFH<0pS>N}G%x&tewpHdhSGEq);e4`GDpjzemroEpSu8Nln0`L9(VyGaH@l* z#hoh<8ZTe;6=EVx$xem?#sdc?51WW{+;iNt> z*@Y(6O8u$g76ImFI}g!R0zn#iX9;{H$>yo-Fk8IxHZY{l>ouwKSOUA3tl^LGnNgyl zw1r(xEZ^mzIIJXN~wC;$9w<%wq&Z6ON{bS9uIYpehCmBUn!e#sLQHF4$+H z&g_BG>&-YL6U6pJuV8k<1OX1*uMK zqt=1Li&ZRDMdXxK7*XSF!KJO^;Y_i#nVnAFE^cKab(5ochZ{DHv905AcMchOnL+4s z-U8eH1bHo?#qJ930))k9)8ZGwHZ-nQ*hgpuPj|XdZX~p4>Ve$L^as|j9hm(Zqx+tr zJ4!t1VEThny#S;z{UJ=VKt^Hu!#<)vpcc;b2iA}5nSO-f`M|_8b_vqK^arLtD27bf z_`|+E{(u84On+eg+JV`xF}m*=y35J~giL>6`UBG+$o?{af&L;)e_;K{p6N#zo)1hs zGyQ>LfcZZ#|A+m|{{g%;nf}0hJAn&W-_CFlD;{9_1JfVEyc{G4M>c=4Z_i)g*aI7X zh?U%A`LaHl@qj{mVDlI1&oclu%zrXWvp{xX{*(KN|70={i|G$c#^J@oxV1CWA6VZJyS{_rmf==b9w21;1JfUv{;=QZ52;Ll01U7`j^Tju zK_lcSja!1M>EKQSJNDG$)zQ~Y7$4{ZE_jX&%+;}5uJ1)INM zwrA{YPlj8DTc$ry46yMBHvX`mjX#WL`U7Bq^>GXbj0a-J15AHl`a{@P7VP`Y{bK)% zbf!Nr{ekIEj0a-M1GM)Pf7tv5o4;W57yHfpMFzBLHh;lv&)C_X47UuoOn;ylVDlGj z{$f9yzrdYznf}1~IM&B89*7+eF#Un)4{ZLT0(Ugu&-8~Zrau4%Sl_{Lz<3~bJizn^ zrav(KVSm&g#xVVX=?_eQVmuI29-zIa_{01knEwOwf7oyQAI3tfX7d-!_KcnF$#BbX z%k&3|0p|a}{2%tS`HO6(KL7?;AIEUOcp!E>!1M>EKd||W{c-+c9Md0|{=oDn#se|s z0or?tKWzSj&0nzji~VE%qNu#4F5?z#*NGQ6JiHe!@OummLP)&RXSZVr=lDfUb+zSY z%j#Ca<#Spad9T;zZ1pT{KE(pD>hy^K85>G#a@k!%x!uR3^C?83nU@_FT`lmuQxQKV zEWUxaDq_Q*ZuFYHa_(3aMd39Lb1PqO_O>-uu55xRv8LI}JUopl^Mnd&cDe9vNgBRt zzb`l*QjIIKx0}~`%BQBuFOPX(5u<|;li4FzEeGdqP*Lol}QA(+m!U+R}h?y}IvyHyy7MI=13s|m% z{3K!{4q_&)geRCgAz2uUrJB9AW}DsST^o_35ObqLN$boO8FlF)=&~g>D`l`E-xW5e z)zwaMjqf{lsn6^7dFy<3ugz_zm0ZWKU)Vw{9n=V< z2@)CP)H1okqiStVzS4=!!6TK4Jj$rFM(lhpTYVGj^OLl_$ooJAHwAt)SAS$0!e@r`0foZ+D9Y#=|S;yJH4FbXc261 zg-uq07rD1Ce~4)3mXZlHE&aiSa3%zxi=Kct_zUI0FWgPg26`|7mf(aY5TK80J_Z=h zVW0&TyW`zQ&D?~s>q1MWEXU5>NQlCn;?SH zyuqr|QR+Cl=)2$()q=AZtlX|nhh^J+PQ=_KIMaxUxQHE2f#^sTbYXV*(-8wr=ky4W zCf)*4gx;^US@9muR=l^^o2dqc)x~?8v!z3dr}wL!tMMHvPtfN*?iSU(#g6%_sOxES zU>S?x)X*}Z#ltlod2ohsmF$qj2SBj`DR%Q(UTCs8ctlItTh3LLYw!G()}y6Ekns!& zBOQoBIOF@`4XjCsD^k9e@)$G+WLFDjs|~sWi3xNT&}kqO%(vQF1z6N#o}sssTPnp} z!x|wd`bilmQp=p)s>9(z*)*}Yk`}E4MzEqfNNSye4nr(mVx<&P4Wu>`E0CHeP9h-} zo^V3K1Wdieq0Hsb{babO(gWvx<_Q-v^-$J>ZujngYorhE|Z!~@t^78zPK zJ3|bXPG515%#*tfB7m|yPb=GKvFjpKHr-+NczK~}Wn;U|YiYv{9R&bzW|dt{HH1I}S`mbInwx`3 z>p*(SOk8ev)?tDr&`DvaLeYGLhzDy#J}LtCXd_y^p2VLl<6r9C6tG}}SHK3>;A7l~ zXqA<0uV&04Hlph>J4J3M;r;G(1nQo|-s%jth(6jHS=!?9s_R}4y%-cvwT?x?I;Y%1 zu}<=_T<}`L;Zy^FRtw$rMUXCu_U;ZUn$W{kR{5+RE2_k~aB9W=2(D1ApnVgB^uUK5 zKD&-+o{>S44l=yQU9EO3{YW@NrGttW2t56iSb)bOI0+Q0gxSQWjvDAG5L3#>n)x*- zahA}>4ii`ET9ay$y)kc-x}i47(c=_#FJLd&QxiLo68YDglw)AL-4`z>L7fMEc=xOaY!z5W)T{@Eq7<|q6tJLt zm0*i z<;IOvZrq4(8Hg28#x}E?*HNrSODZT|L#DhECR1L9QHoNHK6#!5 z>#fmKGp`RVV^3=I3ZrMg(dbuCNRHnuVqT_KsRAa`O9>mCmMrF}&^!-ZvzVfyZ%p*1 z(MLA2BU>NC(dcRQ%J!^Ak7ZVYMz6LH5CReD7^^b6>HLCIG$L`t8WYqwJ#+*KjEW}N zAcqWSoMgR&;WJ2MUD31~*Xg98gAV(`tyq$H#BQ8vzY!)UbxrP)uoV<03RqB>C}4X{ znAji8_^ec{Y_P5hSg@`N*x>3Kxq(6>kIonyt?~#LCVlkPL?vXRH#`6+p?U-xApm<` z9&8vnBLr(4p4C92pWvipvm}x7aHOB)NI(ge2)Zwfg7i8xWCpMd=#$n9z!wa zHwhw#yXu!h%&Z3ixOKGT~!$+6~4Q6~s4#gPE7a?+pd$_oj3}&_U&1v_{JY;YaC$dr9| z!JsIPn8`_|jHs24zBH4lDkIFY5u3<<kG&G|w`G|7T!-KYP(&y=G(OFT4pPRTBDD6ufISE4eSU_{CI6FpOrSV5oOdn10PJIYMvtwC9wk4(6G3w14EgP~_(d7=NYo-*S zvty&z(>ILd=SfFX`skPXnjKpzREPPyAq0v@!(!y2cUokBL+FDW5~tZnKk{2EVX{J^ zgsD8~=rF$+TWxO-4)&KYRs6zO3S*61WB z_He&ep&pwb%>x~z&?8oSM*KzRC?!l?15<>jOI=;T4CJSkuo|aZLJoL!)&!Hr#3Pc8 zGEn>uPT3Yo!A2Qmmh2l7Z2pw?dw?i5f4Xmfqo5c)kUPp!1x)TJm9V`gOoph+U^5X?(5r)}U@_H!Hr=IuWDAqt z1u9aZsE7yisd4Ib815%}ZMxa%wVCZUv!@!?6$pM&3$*Y`Umd7ygk+zGr*nrg4N?Kn z#CLdog8bn(wZ--+4+CmNAWh{1Uja>G`st_yL|C-GLBMeiIOSq6q#s&D`UXMzB7uH_ zAYF?!!lyh#{02c9huaJ}49CMD9%_&85PoQgc0G)Kz2c+cLlBMkkK}KF)bU6A$YTIW z1PF%74{xBMhNMbqFvMpt#5^=K&4U2JXE4M(FjS7_px9M=3&7{Uh#Py~-U1GcAKBgl z$8G-RkTruD;c>zH;|Py`&?7vEeLQ9k;zu#p}ov|IX- zo@(H1WPL!c@Dq`FTj>=qYl%wO;0zurwUpmzGEezFKBZ?PJ+gknMtayt56F!@vKj>& z>1h?`-Bcga<2RGT`jDQA{XXXH)J|*Bbxva=JrwhDA5L$iAGr@#!q`Yp5;zo$+!;(Z z(j&T-D0wzG1j9ypRA>p_^gq(`xHQu9B&5fYo~MH&J(~f7qc#}gNDqeMNDrpL5gvR7 zL(D@Q=~0dj$)iQek)Gb(QwB0v?t8j`(yAdc+5D zjmMxzeA315L&Omuosrgx`SwKqK7{%9Sb(*p;b7+51GosE>r;)~G2b4PM1!2-rb*d| z513U0VSAUNWKA&&T9D317G8XW1tXE4M(#1Ws( z0Fg(DaKs1il_NenxJ_5B1fm%6fw3ReI)@RynRD#}em+k>wq~Mw)Tb2QT79GdDtQ29 zLTHypeQ<3RLJp%^?_orX*Z8*Lmt08Tqo!d3IA+RzJOhOq`0R#yPSG!^;^8@ZK&i5B zt=P}0FW=h;UsBOr8eLU3wZkhyAPR=GLVmM!7@@x}srs^|2R90%^f{2{HIy)UUPB2R z+%yoacqE74MqJAnjswx*w{+=xe06&4Ifvf`a*^QItaSKwl?Ee}4!=PlTtssCg{^aR zn1(t0Qb+9G4!@$z*rz;xsS6bn72Rs(g}`FD>bNvKiqdt!RmTIjQ19BIc>I2F75>5N zKsoZYD9fAHHntHmO-D2fzrJeLNi7e&gRiYTB>1zXYYMh}ba~pA(wwudyZwrPZ^l)( zl)I%gb=2Z#nThypQ-@dHzCh`!K|>N9DLOrx;h}Y;-_W3VNF3eqSBZR!7+FrB*s&KQO)mcDeFey=GXEy*V{? z-n`Tly%b@ycvDjl01&Iq>k{CK-lpg=Ki$lp0KCKKSpZ(J08`bQ1#_!lcDFV7oT0Q8 ze2W?Od;=I31#l@7Mf%1zo`%4-Gmx~NStt(uDwb8bY*A>Ze z^#U(O>vDr2m&Y3pGr?|nyv65*UGT&xkIpEkUj+=W%Pk1KA}s}~4vhq8lv70MX2{9S zm5G<4uZ68Kc>!NUOe7OBL`>k(YCF76C=F6yJ|!+l(c@d!@~|lqlx4^X)=2`P&t-@z zi_F-HT6HaESn)uveYsCCd(5q7!KQ{UhW+lnb+!5ORun~4S1hj?JTBI#IZXL`E&NL?7%q z8>;vcSGmBOSF7uWJKfPe@1!zhe#FwSWy@u!=$D&e>s-03$jL3@a*K62g@t)s0Tmgt zxR6>XJP#Ogt%g<%yY3Dx^GK2^lr7e{3O?CWAyX`if>p?*iLs|pzxA;pBFZnQ2L&Ce zN;lBd<#0i`sSWyZ3y9JXL4wS*$Oij^F7Y{f{KLrcFL?(Jh$5!Ql7_tLfq}N$%U_j91x;}h#t@cZIIaP zq5ZEOHae|Bf`T?-w$uKPl04Wr^$k3>aNZh1r?fAqNKC*aY~w(+ExGQvttbeda0bzv z+H7{Kz&k|+N!E~{Wnd1W!r;1GK))<#l=5T<+eDvse8vIdpqe8uro&Q8J;>lJW_@Cc-1+1cF5{J3XLrIcT1o zLi?132t27yWROY?auv$M@I=N)Rgp;vCQn7VL!0J<7hgIYuvqp_lGkfLAWq6#WL$Z0rjAT5y2 z?Fbwam>8HG&~Q6-<8%k>4h^Jm;{x*oCvYj;C~h>D!DVr~xE}5X?nW+wE8|RDC0EVW za2D=t?p*GC?qcp1?l$fY?k?^g?s@Jr?hEcK?gwrgx1GC=yPhm0Q^@hN$O+_kWH!km zC1mcDxG5>*)oK{qLo%w-4W29D#Bxl}HVOXo7l@iWPBF@)o67Z!FEW`v4BwFyd1v7o6LQh@Y9SjhY=z_-KU z0onKz4L6YELDN%$L@+!>+f}bNJM{w@or5qr2R1k(nwyX_wLcYord}2^A$?CD(a_XK zFf;Z27@5J&AcZnzh!=;L9<83R&eM- z-18nuE3@8ql9Yd#l|)~Fj;1LDN-qORZ63jEh~lMB<4E6!X{1*hMiI=Su=YnsBl=Lg zRzOeLO!voj!or&#QJ}+2nawQ?XHaGfnJW6mwN7sv4@MYeyoee}9W3Q3Krf8@Zr8(= zi_C%Uf`|Nlu-!VWDcD~r6f1)%r3`X+LEU*$18Wtof$}`CrMnAv`QEMkMN#F0kzkk? zfY+x2B`3cKM;!HyAvj2XFjc_HfT2Put~ufp60~=E=$`G+oW%kPC6&3tWr2|ir&m^; z!ET|vE6d)#L?_dm1EB6Ry_xCFFnQFwG4y2{FuhsqeVE=H8V^yag7P$ttO+u`Sv^Ie zLRLtjm4`k-LlYT{>CN&S1k;;&rZ+RaIjVZ|f(6teGUON;-)=O^QZ<4DABJ=T15{gL##ASXwSVAydUA;RPRWbauSDB zEj{?YZ{J9s`VqWq8ocf~5TbYOMUr>zREn7~l+0tOckLK_TZMNmM^g;NyB2n#rx;}3 zwTczOn0GDZBiR{QVk#QQ&d5rba*-A0U5oyRaEzdBBBKAv4fC!g&wy8}(owh9j=d0% z2TIqy;$1r%ylc0LbqN2J;iVno=NP5jdNIjhy=$S@fJ5a;D|P597$(|H?^?>U)XxGY z{izz$%g+`O+}qFAGIT%N0_JBMHt$L8)!=-=(A|ZZpDoUoswX@FFwm4IBw>EGyX8Gj zXTF%9EqaRY1AewfP8<;ferKMxd)(7D4;^id`T5MzmO0u|7i^^qL2wFyPG`d>9pDBl z4P>ia6C~w8>DC?cOXx4d-Eg!;=YHmBiw;Lov>l4?F!ga^ zj<&nwOw1exn8QH$mz}CPaON-|f3M0MZG)fgGDlm*7mrE}nd&k@uU+P73ra4!=`u%K z=4i`4P!s4;pBzK2T#HsD;)+VIQbT9=PlSH9Ce!9Rb+~Y9Uu` z_JJDvKn*{18`KAC`=O()=+C9}w3Yor`}VXQHS#@ek0*;y{EEI?O9%BK$}DZ)j7~|U z6nE$r4_wE#9WJvVJWb_kJC%CcPNkl z30fsCrb$%$N*`F;88T1XeZ$jsOYfC&k#mEdw(a7=&#ql%BS*5V7V`0F9$=eer<+TwjZ6NICVi zEvMdoEK$Yj%}zeN54w?&JEVy9=WnBYN^6YS6=j02~*+)N?D7gE%OMp zzO@#wQ`!t2!KBXWmhX+~+U!PMU0pO!{0Y`o0Sneu0UKOhkulVD#rr0~*$18>2H%!q za;e+mrQB05SW1t@=fq?1m>61ak?M+kZl~)wC(o0L@6GF!D;h0!U4+`EJIo%~VX$gt zW4q03X=`@5?6jd&3srVC*$@Inq+&6!eR*>*X&rEs%*Ew)XB{S3A~-NH=?h1!75S(P zV6CVx4%w5fX!m-8hBbl*8v*h;DYLgagDs+uwnmn=c)aRHRS!KDJWV~`7YXZ}atp;Q z=cOGERG~?3zK{WpliE56@u4^E z#&tSL&~(^kS7@xzAiE`n2p_Dd-*1G;NnMk>5LIvl-(3LQ!u)>Q!;TwNn|bNf=490g(0m;6u(nWzdAsz*@i0Xt6+l7#mb2i0|wkjS7D zAjk^K{=;1^oLW)Ag{uxYsj>?pJus!iXV($UGcrhg+%~+&U9EO3{YW@NrK=WX$3@o( z6slaL-6lSD)Id+cD=HuBwYEFHBAobg1Fr4<%rn&7=>2^NecIJxgS>EO4=vu~ULIIB z+{1x^d@Yu?#^G5F6qpGSRPQ8-m@eXx91$u4lc7pfwF^t5p}GE@FQMTp>%~$q6k~Qt z5IO9*_C<{OqJ#t>2CP3rfLXy&`5+Cd$I{j>rW zte^r0IWLtJPNiWB(z{O96vGWYh>KBeAm@m+XaBQp|9268dTfC6(lqQbRtHJN3Gdzr6|CWp*$H%h(~B`h=iJEyH60|9kej{38+Yg zq9QnQ0~OKfFhuKLO~A&;-@i^H;-8+F#0`Go*9Rj{9-=1|F!Iy_V^FxYu$MxV-^$J> z$~!D{zuu#U${MD{fZFl&Q=;VUrNu}ZfGp+Z5G6Yv$RYBTHV~kx<@-#+1QBV#m1dY3;em&B!~mxSIND9tQu;|fvE95@p!cDknD)#gyC=Tu2;0mP z)ID)F;xzPCuz08V701Ns2mtiJh;c3U#39`7gXGY`HB!MzJYiV((R+^t1#23d8oYO#Jz_eKy9Hph%cbco zb^-2_;iLz`JZ%mu+$@5VK5OxLsE~$xl&m3bAlbot%~rG5j49zv0UqFmCYyuqJ6QIX z^G@%(7tc{)7gF_q3>LeHVmtHi>72hNQFe!1fgmIalJ$$%kX{OJLm_-c6 ziT8UFqjfiKnAoMl=;q_rLsg*l7SRBImcaQ}IDyidkW~!{K0v4>R0yen1BA4vL6FXEAuz4LeJ35$|Tt(v;tciC( zG$IfHyEDTt_asz;sD@no8kddgP%L5dIu!HRd+jH#7K-_rU;|aag1wvqHn;{VA}Xpq zvSPj_SXTusSXTvXaCP0sd*o;clfLAKO2|Z2m{2`}jflVyMxP@h`|rHYjP-<^c;9yf z!;|2YLk+#U4@MCXB8Pu?XDnEAGmrX?{PD~HnMH^y)DW&`7lp{7;q5GXuOY@Wb`g9U zL(EMM#TYCZA##W{(hz(a$6GNsWvHPgBLr)JA=U^(<@f~$x8wVr*U44b;V{cX+d0@_g| zwS`I(YMm!uEfEuw(V-_Ik4j;`Li*_O`|`S6DpZGgT_ObX!GYJHOBY`E>zXyVxl5Xj z^dqZ!N|?-xN|?%9uWWrVs@mQjoQx~4Y^ubF#+LG03{9&YlAmnY+?Zm{tTY)@ipLU{ zhnMHrWEh5xZPq1`;AKv4S#Uyc(35IOBYJ8KCJEirgd#W>*M!i}r*TN#bmUoY%85gD z`fCKX!Gd;YwtSdQe=AEDzI3zrk}@Zjw%tsUN=2B=iKPmObRf^7H|i#Tw>b=53RnWi~qMSunx{gL=7JXzl_;j<=YctzzW>2-< z*>#`-wZya3XJ%*y?x9V)s@{_~kR?3#&@o}Q%rwm2Q4qSU-D8^uJ7^4hv zPF?g4fXq|}&~(GSB;_u!^C3(<4QMNcV$2w@<`E)?W1A?o3$7$%fh%XF-m3~VM1R5E z6e5S*2~W~{pj-;Xz|$CFZgMEbV95xPL#z?-4m_Q_BS3RihCpp786k2w6KaI_1L1nr z(;}{wp@{2~HjB7ch9X80f*4hXVhrYjG0JctTpy1u3ov?ugjozlT=UQdFh&kr#Fhnb z)(Y(f-&7fjxJC%#8bibuh9XAsX~ZbzCWm4SmWU8J#2SgXJ}w~UrVK?~BLr)JA>tZB zlwEewcVRZcHW&Uqqta7WF``1B>w7e%8RJLzXv%40@@(qS6a!!Y4u#)?2#|jh0B`7< zgNMpbC(r|KAGhK=hsIPiI!J}GYXbBbn;={d!grI@Am#2<}(k0}nL%qxQj;GF&Y-83|6@DZ5mB6bgj=_5B;MgA?gT`X|JiX|X+$y+y z?gg|~64&b>pL8-0{3w+kX|3Rwh8~t~vOvk{u)lM-2hT|gjPE@Uo(bx&uw_gmlB5H6 zcA_3Upo8yu51w4*xZu}Nlip(`%!3CB75p^Y(K!1*>k*yZ_nIl;OrV8Oa7V1uh`Z|7FgInWp(P^(OU4jugrU;%=HPZH z9z3~9OFY6?+C0XO@G&R%s_(_ohu~M**3F0(!ll3AF3Roi18@7>X7j`q*HM9EzShXkVc}4%%DF zP_+0Ef;Jk4XtQA`+I9Fe+I5(l9EvemB0}U4Yowu1kKHL+e9BO?_z;3Mzz}PMVSm0H z)4_9R%g31{Jk%nDJkWD!uTV>7K#mv_9?}?^8^ox>{%SB4azh`R@K8%3=&^$_$`IsiiR2ho6MhM~>L&O$_B1Z9P#3<$_hhhwtj1V~#J$LYF^vl8Al%a@ggkTLY z#2P_J?U=!?aR=UPG=1M5J1a4MgpZ!I{r;l4^gV*3Cz8hw@J-~SUyq$c>ZKGKL(%9U z6Y6ca2R(L9q;we>9y?jv=d4oUx0fG@1{VKje+&Z*0|zJu`~iQU6V7fp*%iY9;{(kVk*nyE6yp^OiyNdGSicpBxI71NkS$GnIvSAkV!%& z37I5hl8{M4CJC7&+`l9t?32LO0b=VQF$^5Q7}$-~{dW3ycEZ`c)BlVAmrgjlfARn1 z|EUws?w=HT!Z-eJI^pd8#{aqh^G-OsKlgv)|D+Sn?oa&h``_<`v-^GjoBlUD;p~3X z|BC;WPB^z7x*w=lxImpX`LQ`$_*p{)al@?0(4qNBp!;>&hB&lr~6OugtPl}zt`{WgtOc0 zxBKm#aCY1MR=>3q&Tgy!c>nR8aCRT>Z}K;F!r9&Auk+V+!r5Ktuk=@T!r5KvU*KQR z31{~Le}TWC6VC1eKj-H<;q2!8v;4C<;q0E}Kiq$KC!F1f`zQG)b;8*_$$yalpiVfu z5AtXEvpV7I&hn4;kM4xCd$d2XbzaE5^c83Vg91HkqSX8R_y zy`zB#*v?Q42m9B~P^>p#ZJM=d)~4CMmrN2eNysE2lY~qXGD*lJA(Mprt0ZJI00;8u zFMD@}0oK2>{+;#j`)mKsS~qLmtaY>2&Big9BxI71NkS$GnIvSAkV!%&37I5hl8{Nl z?f{zsU}L}x0|zn&m=t9CAkzn#KFIVzCJC7&WRj3cLM92BBxI71NkS$GnIvSAkV!%& z37I6^eUo{C(i-fab?VJIO9W zT;~P83=r2Y(nB;$cH)g@=aL=*&LtDKo*Vcg5Vv(Afje@|If2gu+G_~WlEZe=>z#)| zRXffOd=}7jz|~Xq>It~|oL+qnSHIG$UsYG3*w7PlE?P20%6p2cOsvh1v^GCNZF&e< zf;R-}seA)?wU)?wZI~a|y zeMdmEUD$7N1; z_?7-aF7RjoG6=vA6i@-@GI|X#j3dN6z(L51100xp{|BvEI&>=7$s{WN@IDm`A8jW{ zdhNu!Kff*z|M}fu5@^Yc@2?7IzsFwWzAFNn`yt#4;g3+`%bik#d zN1Y#ZelS9E$RRnjF(^Wy2~uXT=ipO9j&GB<2b-^)Zban|R6|T5&J^LGaV5T!{APm- zp_zc83n$WKpzF7Xge|D|;OZdfI$-(AI>xpxj1nV-D;On43?;CGQyw^|h5e`4gDOq6 z@M}zyPU8Kfi^P${b3Q*8M)~4CKPM50d;Zyh{{%GW!__zR>KiEGtiY$VH{Cm7s3vml zHejtceno5j)y70Hbi1&R9^#Om|NE}*>G?y~4}LtibZzl(?b=Gu?yheC|GNI?|FP>w z|4&^%`L}g#^Kb9k?*F;#Xa6ryEKw$SCPA5x2_PNM0gZVBDtzPXx4|UzYL@3L)n|8F(x} zu9WV+p?BNGyT1k=4eZUUmkAk%bs%Z4LS5m`Mepo*_X^Rb*tX-ncDpU*HGpVH#@WC` zmq1rM;H8KUNT<}92X&3p$s6HR{Py^JHo~d+1-{-0C;f$utP!(4ju;T!@DdCgd z4HpxvEqBXuTGWS`H85zK!2fO6x54wfuJ2UVfovJbR>5E`0<;DsivYBmLDxl08LSP~ z>_DkeGIfGMH<-TuGQssF8zvpazhrFlesB(X6m;mzK7TE6FSQ3gl3?V@R|9v0MG(B- zPVcvi_m2^h@ee9}F^7HVZUWYtqg8}ccD1V@b{;EQ1TlCOhe~%)h7Df;K_3wg^S{Xg`*S`}&ur~ZVdZ@kO z|E-5w95kf%Mi2e<{|X`49sXZ>sO91Rxrf>wG^CbC5B>FThmcwyJ@nVVO%YNXq=)|c zf9gS7WWaX~Pr=@OKcKtZj}Fh?8G;=`?W(?HUSIH`=D!>1nNGzvChu!wa%Qm%*J4p) zkhQe~udQK613o4=+e;7QdI*ZjYwGIFUN1uMx~ao!B-*5-bovUH#pmFi-Ui;|@(C8+ zqnkHRr>}B3y^Xw=UWa0iQ{B^>Mz6q|9Zlv|o!;bf*_+n7d3;ufy>o=8y&` zx0U#a9iW0-Di_UC%b+wDyc zn?0UEp~`Cntsb33E<}4zeosE6HHmnp7^dLDp2A4r;-2D2 z;JH0>BZ24l%#Q?K*t0MaxU8ov64=yZiU1BI2a+R!=LRCxBye2dxCp6r6Lb?IfVpHY zQr=uDml`Q`8kZIcoX(|30%vlWk-*tpb_8%wK~Dh%8<8nO)G6vIiUcm{DTxH0*E25? zctOvCNZ`_*(n#R)p7KcGik^xH;6O?sB@%dkKx7E@>jX{+oDde1ZjNZ<@EBN8}^%ZdaZ$Bl~s4rBzxOmbD!37i->k)~FIxiU`V?=Y~56Zty~ zT**~N%A}gBjs&jZY9fIxoFx+YZ0_tx;B&ciBZ1H7&W{AXn7cR<_y+EVNZ=c}8zX^l z;ckfpzKy#r68H}8j!59UxVs{O@8Rx=1b&`-J`(sd?z2eXFSsuvfxqIuiUj_F`ymo| z8@D|Ycqi8r37o(sL;wdg0SyJ`shMqZU~(kzA%Te$oUcmF?cjDq0P8Y!qEJ$&4x|S} z5muniM9XPuCTegXEg;IlLUn2lC)QgH){WDNO{z$hS~p#H1kGCw4#WrIDY#gbn%qW2 zK2d|=(=Cx`Vc>*-h*$&d-GkDU+)8f!|MtEGx{jkvvnAO;GRzFKhdF143A17zY}lRH zSCXGI3v>L)i@dzCfn@^c?2UD8Uq_b2l>`Y4d(bWnoS8Gb_`un-10;}_65=p3IXNML zi9?pG#*a8*LwJSQ@iU6wvZQ+@OS-zfUsZQ?cU4zacUN0U3`Mxsz1{UyRagE0U;nGB zY31Vee|G*^cD=RGT996EZ(Sn0UT_MIcb)1oLA!<31y7{5@#3(sw&2NB@%mMTtGs%I z^4AoukzW5`;a2JO&cf%V*Z-#QH`420D*V0l`u{9^MRwhNhwI7hHbJD_Q}D-`C*i_< zh5NjEgzH0vA?fu83lB=KKUDak^!ooT9FtxjDU3+3A1|DdULP$?$ga1ywy%+1|6IFI zKm2pG|G##R@7wt69ZttbWc74>qT`d&>!0g5AiI8}^UeEZ*FlE9^2W2d^B(8FdgXbB zcJ6ju+4a`TS}&7ce|PJ*?+vhc!{|>+LGuaecY3pQ)d|T>*ANDPHT{P`tIP zlu6%(I==-fbw56R^joj9%((%-wnE*$4(k0j{9O+fyx$>A%IU_TijBT3UNz9)v!M^B z57oEmH$ib;UE0)BDYag)XeqI6I;r_Wv{7G0lir3_s=E#CRCAkXsk+;k4$W<%%<67K zsny+vP}kkY3Zc7=bwzg@O0R!=)uw(}g#4I7&(qv}zxM9Ob$5wksjiujvHC92J~elt zglg_W7uDQ_HmSJ_%~W$2tEu`fS;y6P$#Sl~OICIDU9!0QcLTa_2Vb!1B;Nxbc~^$A zP+!v4OLUsFWr?DLvYxo1VurH$+G2lkRi#qA!}BZj7At)v@3&_wU)$YTx*az1;w!sK z#3{mT=yecgZLLdD-(pe)MX#d&-i$ko@bv7166;VDZF~QxJs`S(S9QCz>UJQaRj&@J zE-WgvLjK_Yv|_r_dKHs8Nw=^pX|M!OK5BG_2A$OMk{nomFoh@f2UHx)@R^ruTIQaa z>QLf{tsEHqE^X>QZe2=~lVDuI@nqwPFY0{tgP%UU`~;Ec9qd0JM{m`5R$ zB~@(RA!YLpfz3k+LZ(kocpa_$6!2JrZ=-A;^oa3Fhf-|OMiV%#qm>_pc(bGy%BK=e z>uBXUEsj=pnrE{NK!L}yAbAeSf;65Rfj6?W$umZXT-3&vBY)AL<_9kVL5VzTq=O5K z*%B$0FUdjWOL7tGl3c`!ekTtphL#e$WK4li9#aWk7*r@y66Is5FsRwq%cV*kfvGLz zFMY6x>cS+*QbF`&ZCfk;eOgtw&CiL@b}0X){pRN*#HM>{Uh-u{^O7$_r;)l#TY2%9 z6)niX%y6Bi!NyKKV|q;^al+wFLfwIyow@V*$W4#|nOj$pT)Js*OGk6TA}`2}Wm4I( zOfNe^9+K#d%k;eOV}t%Mk+G~*I}2)mYpF1)EI$dR%psVm`rkqKh zRLiRBC)M+)2B&3C4UX4tQa_%a9X0J4Qh=TT{q|t_N>*SHx|aQtz)I$$K&`^RT*`UN z({i3fcV3=~^Dq!nY?{GWVYNJ%AikI9;=$#qcu>{vG>oXirs75=I1Otl!P@06Bw6V+ z3+98Isg*5jCpxL+l$<8-b`DNkA?36c0;h%c3|)A`hmovtD+EsCu>`NMQdi`%R#phC z5^q+wT&)mTC5~2hn&*=YK!HiJAbAeSg5=pF3zCSLUMem8LYlQm~)mbD&@j3^DJb&4M(fakObj<3JkHIFLDL z9O(Qsj)9grI&hg+aQPWvdN`O*BFO0 z2b}5BN1%plAA$11AT*oO#7+pFmj>zZBFW2Rgcy(<>z{Fy5Y6&qB5+!g6v`JTMC+#O zUxzWtW1vk*9#B(~2h?=^1Jsn{0p+JRBv~Sz=|V#bNNpNIlPZeWz(s3c`>lc_dQ7rV%m}z zh&CPz5|Ig4@lQBu+Bs8lf(|c|qdYzcO3y)#VuUap(y(Vbauh>_+3~r^Q4AV}&9jw_ z{H4q!ab;uZk`xymIW9|-<$AO!IbyMU(4cb7WJK4*apxSh&uTW*GJ3LSQv0%jmc9w&QmF|j0( zAV@Ti-VU?WrMJTbc?0^MrB3ufMkWRh9#nz!2&AkesN z9ZbJa=@z0JkVhwtHXjwph85I`j9?v&aL+6W;J~%UD<&D+<}J;PU|yxk2D_JEFr8Plx9H7a^K1ABpp&RWlON zf(*>sxOPV*dwTV_334=Z>%1_{4Cf_kW;icaGsCqihhpLc2_W|-iq1r#XWC3m(U&O4 z2FeUuB}5luYM6Em?QVls%q71@rXE>6Z}~(ZnMfg4l)gPJH)-0_l0ZnTl0XUuN6SP> zV3pWTqqoaFr=sIjK661<8xRI1)@v)mX_YgdX#(kqRwna(2G~m#9n&WnuYmJwTAWnLjHp5jfE!Xeki5wsZ&<-2K;r-CQLwW= z6VyH23L@(8AE2fRAE2fL52&fa2dIkRQ^|PXRTQtoizF`(%A#vINZ#%@&>Jl}L1y(71}n)8ctX58J0!T|&SD&F4~F?0y5991#wBIBD@Y zr_z!BgGi&0#V@#0^Jek4+bn*_L7T5Bsu#;EezZFP+jo%v{6d zFJ0s!Ibni2D0gy+J}%z2!Yna`M&jeLS^Q$Wo!O&wDkRZoo5fFdas?TUR7Xl6F~NSz z2IXFO!soZAcLtBtJ_eH;tAkY$Q zRhx}Z2tOP%o#w?@L_FOhE1D0>hiJNnB+eHfn#hV4WMF1E&%l}Cyd2F8=Y?rzI4@B% z!+Eiq8P1Eb+MA+N}EMvM-o#5*r`;F48cHGPgYq zwN4q zJ50&TZ_g8ZwAt}`8n>tM$8+5IWiigW3Z{pCtYdm+h>PJ5W`qNthtL7%&G0P!<}HFO zNWmz`1m*SnOi=SFGW9~@LG#FT0_XLX`14AZ1VM4J+3}I(QXCt3;E*ID8~bPhHl;M1 z9dEPay-;_AWCK}mv*Tk9CRn4n)<5F_92z!V{}5X;48fsM(-<6qno1==4NV%-s!qM} z9S&5xkx@M0{QAecD;022#>>NSc%~dO-e$+gRS}hni-9&JctA}RK0r+g9#B()2UJDy zY2wa=C{+}%!;2&@50av5IY{1S$4A31aq^dF;=$5wv*T@c{0E{ADsXu&#YK$cWh2L5 z$J6+!h>T~)3#p%Zv*X)rc0A;u&5pO(@jacT*2^1ip*eWVfn=|M&5nPwX2(ZuD?vt* z0Ly%)$0s(;E9YvPP}*-^J?Ftm?soBYmpq#En|xHcH~Iv54lV6D?{J8>%@j~6STMn| zqItU^jt-t%-Z*c>#o_v{^LAWTbS`QUthuaht86(o*^=iLOgWQGc~ZNiW@e4pRQXh= z^1=CJ=(YfoC-}H_uJDxr$rF1PBv0sBki1L`kvf+{NKJyrA{|^HbGeTW-VjX~CV|Xt zuDs2a=WOn!La3b=<`9@nW=8fIGAdt+w9BabC&V(Hf&8T_zZA_I`&rR^kUm6H4zRiM z>R}h8Y1WYw+`yUPyj;x;SCO1<+6B34k}IDeSH)u~_{R#su~?mW97TFuo?#UIZ1Gk} zk5wwW=w^$fsmzc(Ybs@3f)jL(^bKmyuh-3rpsQqPkylkR0C`O%15if^!801A%`4CX zJDKFoM{ACx7qgXRxsWZQ7!6fcbgH3Lfpx>l)-{!IozK+y#34(?U8<)}$6`t}ui9sv zF71Mp_}JR@C>gDr?qsxXHBz7j?lDQBkJc>lE!Y$0Q`xe}4mY%Af#eB3H1EjNkdMNa z1(IJNvLJb}mIcWZc^t`>2ZP4bn(; zjFZws$);8U7jze!MITu(#j!EUqEC_rHjO^LG(c*&sWbr8P{9MvRKp`sLluued0`)# zU}@qN1g|n>ba;{G<$*a2NRE&N(55S3zGJM=aIO{5=Fum=<%^JpDa`|_qIt~{##~IK zWfjS5;3CD#V{~|?JQQye>C;PjjI^ng2h>!`18ORi05zp}Kvfj4lhOgn1Fs@^9bTk) zqeS{7&BkWZ>kSu)HXh8Y&7{wsNuNZIVni>LNFl4R=yt>k2}ALEtRCWN&l3XN z;PYI%ix}hMTaS*N{MnN%cpWw4ET<%iPT{dYAs$*O(8Eci!zw*Qir`hES%;_ck^X}& z2?xy!i4XEr`j0wA=Qd}++2-^(-S}LC-`%)EuiE5!%(hLQx5@K1dH&X}5?Lt)kLe`FqE@>dLS3o4-8w*@Bx=Y0)*5M;rK zE*g@crA?kEszZFp$gruL>^6D6zeou_K5R_EkW!jpyJbc5Izm=7uhwNntL!%ohY7qZ z_tE8Y(HVQ_@;T`8`R9z?bH=A!V2qO#TSZI^NJCErkcN&5AbCQM+W_j`@}x8nadq>@PKejR9Ll!fmp|E^1Mx+x5@LM0p7ajmoTP9iW($iA{@8D3&G5A zo`W;Pc|n>P&P&tGa9*TlhVybYGn|*J>EMD~waN2>XN@04wv4D$cG0mGN6UR%;^`FW zaawjG-BmScljqssAnjS==1e$qJ{VdaEwGbKo~H&J**7@ zKy}j_RX!wAoxpkBCC;OgMM2PAZ1Q|$XvMLS2N+43vbp;fXj5rm&!D$w(0ftX2+ano zj7^^RU;PqYdh-S6paYq1A2SZmp-o73IPMI#j{(a2mE$XdLTe+fnu;Yr4b2+TD!&4% z9S1U!2b^C4Q^7?kFOSjTnes?^n>?SM_At_>6c4B=#RF-)S+ZA7%9VcL-k*h)~nAW7VLZXcatG5`=j5n@FCvp&l%N|YXZpifEBymro`lUnB2fNWTmeopcBoYosx80g_8|PULwYo zYBiFl^DIbS7G^>66dodVnl#hIW4^g`aDmF@zBxESG#*ktmD}`rn?BE_gV?j@3Cjv= zUxJ!r)8`}cnao4Si`C3x$|uCkvwS{{agYQv<<$qn&z}xxemwcDCyhd zHI(!?UO!2X(~>|)tdhVX2wlrWQn0{6CMol5)Kz)VX+}_mO*4urtac<75`{7TeCEq1 zjaM3uQay7jwo-!A@Q4zeXPVQRtP;VsTpfB8tWG&NCCfy>0?U}>$wx>);v}+X%%?J0 zkpOOJvI5DIdT7P5by8un0?E^P79=msvLJa;7Ds}fs9GRd72dEkc|DEWBj!`lIP%U7 zX&gvH8n;>UeNd;FCV=rbcgp~5C@z51F}+Qa=U#4)IWi=6GQH+gBJLjFRN_OCCtnl+tXDyv>nUYIvY4K^lbK23<@; zBzbx4jbt+$ha~_uCHZ{ESE1oto5XC2e2PtCpbSHkh7eVr^)D8N3n`u%a4FV5c^D4Q zl!M@HhJ0KVQ7J(fXhY+M%#o?W2dJq`0@Re?0aX#aPDTe-2=Khd7gD?qFUokM4EY4L z#-1*3o{(%4fBJ=484_$TFMWXPs)$~E2m@+n@1Xk&5tj&Ufyt9e?QnVuxe9(slx9V zXnnWtZs8K`-QUpO{Y~xN-`3v!9qrxU)!lV}OLw=>th?*JU3a(86xjt zjh~0_zAHo7KS*D;Fq^(?>pN&!s*Ww*P_+5+raUOn~>*t~kBN7yK@J|x*(db`cX_YUE<`S_xR9f^WOa?=^vAuFCY z0R)*B+ShV$8ab=n380m>NiFkcK*nz8F_CQ^KPL1R9QT!U;FrR7Tp{R)p#{gBK}w*h ziEQ)u7vCJo(_y|*bz0*}>1#`o@NH}$+dO`o#}D0}tvetkUi!zSfP_jTXkPrMLJLkH z2oveMd66@1Oy#JPUrys4QwTR8bpvAa_-!74=IkMn;1w^UR=YD)5e^3xlVERoLSqtW+Br&ScMfr|t$5BuSnauB@D zzTf(|c|qx|G8PcoeOflt!cb*nO_ElHkJc#v!s zkQ0z9ajqk$DBhSHDNUkF!a4VXZ7I*=7tgh~dHd9A3{tBj+0=zg<5|zq+hN+a6dh)S zOK*E=(u5wQx8E7G%@VyMO}p9*dH4NV?8kL?ZQefkJ;19ukJIMui&+K9^DHdz*(OV{*fN6ikcrZ2qjZwM z8A_8a6_|IUhtKSB1!c)VlM>im?#hGLgk$qiGfwW0u_G^^A1|L9&)AD+?8L(mC#k>+ zZwQdQAmwL=%#a$%3(+h{o;R`}c>x+C(d}$y`8(@;{<-h5c4~?ELYP zDQqEHDgRTU`S^v<@5K4y-2hq9f(*Hw*R@2-v+4edBgsIbrj%xn z?T_T6C8$dnW@BLhsQDBb3j?&ub6n`1N)tOF56nx0ba+u5$YX>U5Z?RBiyF^BQbqFt zHXYA=VL~*VYyC406ry3%_0K$1Nan~;*&|T%DKaK`w5lR`#@>Hb8lhJzLkx^~W#oS?&tBWz5F_-`O%*OvQwyNY*QU+(M21`aZo!A^w&^MnRhXp#z7 zhE~R1p+R`=3Kinqm8G$om&L9uCs*hrEWWPX-Pd#5!1_`@*FpBS%%FRujT?*G=#lr% zjZP0*h= z^B2xvIv;mF>)g}&&eq>=eRpf&JuUCKq|j7oE-WluQuvL+Zx(*L@H>UyEvzc6DO_2& zx^Qh_W8uFSzEJpL;eQmqUie1g&ca=Vy9@Ug?k_x0_;%r$!qLL3h1Uva3$?;zVYW~& z%oQ4Lle^Hp#C^N_TkgBuKXCu8`+gTY62h@@K6bfti}M-hUgv?<-)p_B@V3IGg|`=e ztMHD(zbU-aZE+X4Z*yDSzi@AHUj>W}Bl`QNt?$K!$Y7FwlXh^GA5m z{~cYW^;=4%{_OI!;%tNP>T+70o1Lqj)nI|EO2z(xN~w{+dCV$tLKV5>+ z1&w;~RmFkZSNC*vwr(nR^_9He*Y+2?${X30RRjG!*O!W&MDF%i22h6mA}aafYkDf> zPovIa*ZPgcE>ue{ez;WWFXLzA(uPWL<87o4++JPmD|O+WI_tNTH}%tS>Ebn8iYS2B zpWNKjU+gdUbbFUc@up3i`mmJIN4vhf*&jgfxv?B?tdvLt#R?W>cGDlZHRw|5_Wprl zSM1iRE<9LwvA-0%b5mC*opUzWSCK(L1AXkmO%)70riT4q|EXf%7WQL9xgQU*nf*i$ zx=R#(RZnGG2mw(+gD7tdx)I#E3BB4@>h2FNTvgr-it)OHVP041>FvQ(;)%O%C}Bi5 z1|_d6b{AR09>D~+ZXUSx4t9%8(%N#x>l(6!7jNhrfFeN0m^O9uK=-Py^5$;R6>|T2 zEb{}!%_!2k_=CmXVs{BUsn{6$R88((*-c)W@{wYtyk^S)YtjFCJ!W`K&wy893Pf zpmYhPbi73M#zx_)a#z=yp01t>WqvS2skn{2!D{jP^0wZt(z;?9BZJ+0ti*;p zdcnKk++x=NBnGw%gOAA!u}`3|qOP9XFtiMLA?se#;>vQ$}DA&>1B@?f#clyE#EAzwe(Q-&-8P2xLdzJH4+ z;ch5{rLQk6_m#nMv9XU`_w;c5n_>y|mcDh6|C=kt-YxU9{Bh4s=97{KnKiO4 z6GY)lBbXr33$Q|>Ke4HEW69ry>;cFHZ`(2by%qwKu2Ep6=)&2yh=?_B6{W0(9+3Oj z+|j$GL{u4I&&d{r%xD$o#(2#)1CwC;cLng#N0hyVTmT zj1Xo}Bf3>43;U-_okC*}?`UCDsZ#3RSR%3N#k6re8d+R!XS7FD$^>Y&cn>r4Hh zeKHi@Z6+beUrhJaU3jMOu7_lfV~C&b%@EmmIDSU(l*}IS*^>+pq#~IsV!CYT*^Jd3 z0>I$pZ7L3Q_2aT99K1pIuX^jK zUos%@a4AGDxlzCpyQQa-be=VPC9!vj7|7008=#Cpo!C}NrAn?YeY%XNB%*g6Bpx(^ zTLRG^o#5zqk8uN=j!gs^wwo$tFqN;!LF}O~H+8Qo_HWs6GuHMJDyPdC{RcF2bI+%) z9w1cZu^|?hfo*ubn8|qs-Dfnu7QL4A-BAX*OWW4U{EPXZ7M=X!D<;^CYs?E zC<%RJg$S&h8;ZTXp422TNde;8a#~@$1yu%EdaErsl0pT>YaLbKWr<*%mZ1U{lEAGW zd!HJ;Gzsmrtz^;HC0$!#mhTYodki4n2w!R%v|j+0&*}UNZsdJVOy}#~ChP%8o2`GScuqRevggEGQRQQc ziT2mbU_PNIzV2;8O_a2meLh#Jp7YP+&j}8bv^l@Lf26kVwi#FNEgVu8>HX8&#@D5_K1ONf(A?vgj)9 z3W+qYVzB*B{te_2E)uNdbM8mppYhBxtMVPkC>FDhRbc1CU z*@X08!-RAwk2WFw;z&qe)Ul$lq;=8qB@Yzn}-h8!=pH>lV} zTcP>Jezup-1RpM!K1G*mn)l0+zJerIn*U0&ZL!G$$=;oTkA&>MQMgaM9B8tQhN30o z2F}AqE7lm+aixbUNBuW0b#V zajpMr`J(0R%U2?p;87U}VO8HoqS0Q_R#?=wtYZZl4{oh3dH32EEh)5<08jQ&53414 z=By$KIcmp$eQ7h@^tG7f0uyo#m?PM-MtR)(1rqtl&DfY#X1g?r5Td@x07hx)+HO|& z>rPa(w`?l&j4Yk-$k5V?i4*icAK zs6MuIP|@r?P#`9l&E)M#^mgcZI?!Tay9MK=&@u!ku!|8Jhb(z?*9Hjo%5ArnI?1lI z>r0=6*PTdIY<=k}5n9D|SJF7^iDpo-U#jog?lm{FBxEZ7Gcx84*qex+qYJd5#D5U( zrJIezZ6Tg|1t4K9Nfcq3jUt5m!orwMeyA+Hi|%)D7&kZKS0Iv(?=p1T7S8XBFN%9Hi|$_6tPi+ zNZ3bo--l5I|A2cNMX*r>KD>}Uicqmp1RF&NPhVx{)xi+7dl5(=QSht~#V31uCNPq~ zWf1qarv=;7f`bEzKr1$iV50~&ieRG%VHRa%v*(+BFM?g{$lD71DSft)l+QgiLm z_;0?W@ktD!VS+1ltA?F7hA(QYVogtKCfC?-I0O0CFb34Fg~c z$o%hX&<00r7{G=B=;o0y)1JQKDw1g*gaKBh3j@&9bCcZzPP}ud{t~lp18)*M$`+INgq0mAL$IlBRUg9At;% zNW+8dAk!iT*%9U6zU+*=rV$hRYw)r&XT|DkZ@B7)zS|tfdAsAdF8-4LwL4ADrNMv8 zoewxGosT)2alHet^u7(R=3egn7hGA1JIjM|AtLLycGUDA@Y3(goqvb_E_dE5{70_; zd;IrjDD_9qyU-6({t8@uuk$C)pT_l4}Xb_9M^M!@p_ugXl?=ddG? zeFR<){_k+!hZSA7{=W}vI$QtWi*J8~75-10KX%@OZ+|TOM>K-Vam_1l*Z=HuZV|uc zBQWR=y4&#?!zcZ>JKeX=jvha98&TSbzR;v%WlkS3g?bxV0*cfbltI;xd^tGwk`huy~j?Og} zG`wGz&|m(Kx6&WA!?X9zRp**#57!9rg~keNj0tFJYh8|2$Ue4}3$ z;LFj`duJzRn?{d%b^qlix6#P*nf%e@)YQXgr>4$6d~6Con(>*NavrHqEyOo^cLDzW z@#MYdC(bubzCfC}yU}#;aHDqV*3X_k{n@QYYPCa$aG_pnDt^CSbIJD_{y4ktsg2j1 zT|Q#(smA!ipKtx^zux-u#(2}oXPfq(s*k%x=ZU%TmXpu!JyjcTKKbkq?im{&bAI4G zOIwyPB4wXEJ2%?$Y_a(4+-TG3r<$HTTOVx{ojr}wx14_J$+M%Q->DbhvUShs=;@~( zzvt}uS?BS=xxu*?=Gr#7b8}ffkDY3qUU>8~Tep7ZXybI#iSIT&cB+0FEj`sZ-E!i) z8-7_*i;tUPe2kUp&oA&H&j9mKPo*Nl?j{LJ{FD}$a znu?FqM_TsmrQiP-zBljLd)GZj$B#O95!eg$_P@QXUeEG5_01zwN1G3S^Hejw%^h`y z>PHvi7rndyU%s*b-a`|In)ZKVaCUI^h1n}MxwEtBKaJU@|MU3N;pWGmnQfjW-xlH< z{jvaG9)0HC0}}_Do_Uni{jJ%S>3@3U#o1RDy!gmJP0ucv^?qGKfB8S&N`D;q>WlZi zTz$Fu#jhS1oEe&v)GmYra+31hCnU<&j?(uy?FHOF*VBh0^ z_cXABGk4Cs_4xPi{Nfky{QmK|nYr-Cboh04rupb2Um5z`AEbe484a+K$f{KEb!i!O89TjNub}s}0t+<1>a&@U1#n-Hy)~K9X+} zgA?2F8N(;|Ha>GLk9>Qi`L#0>6V;aSGsj07M+cAAkG6~)KQlg4og00n z+VacIPo90)-RE9%_Q}n^tX9X5jn$uazwNfv$BvCpRu_(+IDBRja(=K;Z!A1NdFJqm z@j3-I-SyN%?p}A{Q+HLXC(b?9c%adA?!;8J`Ru{7Gho;AbL1ODH`-|4bl%<3*bDro zYW3jEQ*#f@HO(A6Uu`~dYFk@WHjnPDPJ&{pwdU&HQPMk9SD)Ka z-%}^m?b=x#sye%7QQiI;h-jkPwEvY@sUPkf9~yUlh*HnLS_9!sj5oddBfr#aeaGyc zSyI=|o#%$mIXhA6hbL>8?1^(tCx7Udnwi}(vuB2sdSd6akO`(}SlcPB)!;%r7-PyJLFKG^y)5J5LUsbiPAM9jl!jJUMZ) z=~%qfgFBB89d{l?sV9F?J3e@P;&{`~clvdmpV@JK&v{bUy*pnWdeylXrJjGLHZnLe zG1BzR^M0wR=^ayhrbwx~b{-vqY{j^q_}0jW(~(la$f%WXMR{fC{0 zhYmacfNA={)00OAk5rE|Km7wt)1m5u(YuFcXLd~PnQZyiy`x{~!KS;X8`Gq0^VnURo-E#91R?$Gp%>2|96UIAaKVcY{frRO zboIp4!t=8YcV==&ZEvk*vR0dXa_asm5a;fpUDF2!4@@0s-ZiwF5GQJ_x6IY2Cm*Wq zsWnfYpMA1^e|@Uj@{=z=_VVoh!Tr_+y^WU# zU#`EraPKoazVyg(!a!dD!Tj%P%kkm+zSj8s;OFa~Z~5AN!^a6v9&nq_ey2jYxN_gC zb8d5e`~>AU9ci z+VPmj#rL@GsL#ctQ*QI*p@54IO+qmwZFG;h?!%OgH@og3pN$Wlcbi8eY&_bag`J|u z#jeY^*lnJOaPfpI6k|;6HqZPlVB(*7Plw7V2fHrgV7KW=z`;jYO@0~1zR+5q4cPbD zY4QloMaI3Z%edEVIu>y6F|iP1UbpFmfO&uH6=J;Wx{P<-re^})ea0)qSl4wK>$*+f z3t0DiVj;%4F3-87x9Y6=T6zIB^^5b*8OULnS|uFKfgZTd;T zwm>E|`AsUdFW#PBX4`o2y?B zxc2K+l$>@c*G>ss>o&g<;o4US;Y_;4{9!}3Sm17szGl?+xgSjk`|!y6_EGelm)Sgno0=8eI=Qk|NrzA_ex!5&Hz zgWdi7`1td?N5dHG@R5WV?8)8FJ-7R0BnCU|9J;_U*ki-P@IwYM*kR{b5QBZWKKz#5 z$1YF|_T;`@yY`)o#9)V=gJBHzmvh6;5mgL!->xG^cI|sDjKK~&FNZPMUknc)RK;MQ zJ2WzKi2TE1u){BfG1wpD`vs4|{?x``X+YM-VDm&{v&3LezqI$^;eEr+dtW-8G6s8k z*T^)D!A_6tI-Mm3`-|6T^yal+B*$QP)x8*OeOGb}c5F9|-V7(jV8e1Od?Ss)j_vki zu&9tla3nFE?sEY-2;xX8<-F`F&rC2ma z7K1&v*N^7x8rQ{O$6wm-MRVL=HjT4rjx+{4ykOVCPX>@bbN z4r^nuK^exrvKZ_z<6d11Hd2T&Z#)J&%y?H9gB@n9tBb)#3Ng-&$6$y3@G8cDF>X8t z8!5#2HXef=W^AjA!45O7)yH6G_Asuk>te8T2VeGMu)jPw$GBD+gC$%$D{!ri!MkTW!qEHL8&qtm{l&;212nr(lOe5QBA{ zi690$S$E&kn7BYO*fX<@#_X9$4AynV!x$_a&(4@C20Pn0bEYwSGK|3j8pdGp0HdlH z?DVOTkyGR!7K3%qg)!I~zF+Vd>^U2QwK3S-J7TlMU?*o9B%s>dn3+r$gLNjq?e1~k zGMORz>dX+xf|=ylE6zvQflaLo+~m+}&80yblw=#{dVk7w$B3}<$x%3?UO z$m?rvOJk0NaCT2Rv;X4W?_MNf*DtukDYm@~zu_)>Rx5?u})WX@?X?<=(Db=o36bg9Fbs4Y4j>q~8C$=KtFTCT#!!0BK1?K~#9!%+7}ufFKYA(c>^7V9u%k zG8LQH{cj2wabV4Ts;Vg1@x+l}Lv`7|4FfkviVfp$^K==|7OSkZp#1i`u{Pk6;A*F0B=b|K~#9!Y{*F#gD?yP(Y9q76ES2C zB*6WT32czU)8ADAK>O(G!`8#^-omG_UqheS{n6jEl}0pSFflU0BL0QiOo3}CB0SpWb407*qo IM6N<$f`=TXApigX literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-bg.png b/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..ca91cc1c0c75405928956b5a68eea2d2abfe7386 GIT binary patch literal 336 zcmV-W0k8gvP)mJF_T%H@=;-MC`}^VH;rRIY>gww5?(Y5l{r>*`|NsBy=H}$& z^%*@Q$+1cso>8-7;v$M0PsHmBlneXrK(b3VPqodW;)xyHU zl$4Y+&MAKY003x7L_t(|+RWC60fImPz`#66vmo7qf*sv|LD$GlaSSGN6nc7-a5@A! zm+VFBlBAR}6N$`1CG%z@bH!#PlKGO!oM~i^6f$>enHxKq2Wy!RTbT=cnJ1~tkJyHl i4Z()oMz<0E0t^5_&5%WRFs~s10000l_~?(FRB;o;%x>gwp|=;Pz#_V)Jp`1t<*{{8*^|NsBy=H}Vi z*_oM{l$4ah!or@Op7HVV@9*!-%*^EEFMcbP+t82003c0L_t(|+FZ|D0zzOIhQa3#p+qHzB1wq-Z_usxn;i^=i`H@n_i0_R z!`sURVek-CvEmyjrn{oMDHe=Zr1*J?@vN8=MV~5$Oz}S{nyaF|D4wC>U~n{W2EJkQ bYyu1bOFt0EfR(X(00000NkvXXu0mjfxFNOB literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-container-bg.png b/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-container-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..eda424ee5caab715174e7b15d9f0f003a0df48fd GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRm!3-pi)b-i{DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MWO*dA+G=b|Nr{+YvR%WfByXW^5x6-@85s?_+hd7=@Os< zB~KT}kcwML2@`$N#?P|Jv;KyypMh?DpyjH;MoN0BA`>K~#9!%+7@sfG`jQ(F>Z3;BNhw z389JI|E7Qu2iCNYs)_~M@8crau=bq34FlIliVfpQVL0001YNklAfnrIzgvLlcz<1VF@?ihbrrgYqClH1@GnF}R}Cp9 zR}GfT&4G?;ap=w`NEr;uO>l@w${i4XOrNfO1sDJ+ WYzbDzn$Fn(0000gwv_=_>_V)JR;NbZ9_~GH*`|NsBy=H|@I z%&o1h?d|QjxVZ80@zvGUsHmvt=jXGtv)|v}nVFfCl$4&Hp2EVy(b3Vz$H$|iquJTn zGSjRCtA?^0001DNkljp< zFkheL3hwvj6f69_T<{Ju!E{vI1I2JqRA0|0yO5h_&nbIbq$002ovPDHLkV1g)rz1RQ% literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-container-bg.png b/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-container-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..888fb8682fa3236cce0094ed4041236271455da7 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRm!3-pi)b-i{DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MM42SA+BG(eEItIYvR%WfByXW{{8!pA3u~W@*V?a6+B%W zLn>}1B}lMNVNx?WaG;4RLUP7M24-cc0|vqh2N;ykFf8ipZ8id`W$<+Mb6Mw<&;$VM Cdo~CF literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/mist/widget-bg.png b/jscripts/infusion/framework/fss/images/themes/mist/widget-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..a7beb3d3e189e1199845d4bddbd915d3184e856b GIT binary patch literal 224 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 literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/rust/icon-menu-Delete.png b/jscripts/infusion/framework/fss/images/themes/rust/icon-menu-Delete.png new file mode 100644 index 0000000000000000000000000000000000000000..b1ec1caf3f5fcd9fe089b83e721aa712fe92f32e GIT binary patch literal 674 zcmV;T0$u%yP)_j%6up7Y#up7SWCY1&pYR6d>_ z4e@0cILg2jI26$oS<_zb9hxy!OQ0Iy235cS8ITj5S{-nK63|62R024FFZLzdl`EE; z4sCFcVm%Fhh#mwj52hBk8l`gP$i})D*7^Sfc!#I6(N{l>gNbbE;puko5NvVL+pfG% zMQ=`|BSDw?83A6yHv6vzl5=5qbM4sZul&ASAJYdkRateS&GVSzjkq3xa}R8VQ(HXu zB*mgN02d2;n`=khRrXN-%jEH^<2oID6ftkDQI+DkF8`hGM(r6)Np5Hjz{1Rf$(}|n z*1w}ZaQ6A!o=fjit$!*AXV{*E1I|ZSPDp^F&2ns}L}Yc7`0(rg^{#J~v*w&Oan$8o z(;9&1tAPd(LJhXvpUkY|&V+N?psfV~IabA*0^feH>Dst{?0Q`9pyP#|^=~Nljm(7y z#!?+iB{O<*vqzWy;9unJmH=KHRRqqvSnc!O{=4m~or8pP+6F)9y%*mu9A?i*!!1j} z{4`%^sCFnuU#IW#_Mmnfwqe5gnNHt;)1l;Fd^f_D*0w3UOt=fxlENC`0cq*Nq-;Q~ ziGK+e!3;>?F65T=f#k-nT&lFH=m`nxl%}iH;O`%-Wc(Ij0Cz6gc@iq=9RL6T07*qo IM6N<$f|&C>7XSbN literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/rust/icon-options-ListOrGrid.png b/jscripts/infusion/framework/fss/images/themes/rust/icon-options-ListOrGrid.png new file mode 100644 index 0000000000000000000000000000000000000000..b39267f184002359fc1fd52911c7a7f86d27388f GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=C2Ka=yHZ(Ns-@o72*Vp{#mmfeu zrjj7P;QtIyw;Ol?c`lwVjv*Ddk`kPldomgtwYVf6Ff3wN#J5UnHm3yFiu^+tzOb%U z=8k2EWiAz2@LNzw=|aK5G^RGDSF;UFY8@Y+VB#=fSfs<+zsj&q7ib=Xr>mdKI;Vst E09y@0iU0rr literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Close.png b/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Close.png new file mode 100644 index 0000000000000000000000000000000000000000..9c12532a9faad0d17508ee5208e5105a1a6cd4b4 GIT binary patch literal 689 zcmV;i0#5yjP)P000;W1^@s654Bdt00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!LrFwIRCwBA z{Qv(y1FZoQ10BGqB_shBY=Y_f0x$=_9Ptl`|3fhdFhb=Rp?r||znK?5{3qHHs5mDO z^8hg`5dQ$;FF^bqDi4z51!8U>{tLvPpz^;-v4k0jg)Dp{EZ8}@We%>IdlATg1jNsw z@{&NTWE+yG&(6v9WzXWNyMX+kJbv9ZSCwQC?T_L!=%QQK(T93c`g5(+9=PI zBKJ2>AKn34avCW1lGKO?#li1W+gD%u_2c_5kE8-Guhf!spqLj3gV-Poet!S}(6EO`H!OJqlLWCrVo)D^f~x&bYMg+4!U4o$Kr9Q46BDPXbOn$PKp4aZ zi2>#0fLI)wq!~$#6Ogzt5NrBoRYiKIl^TN#pPUnV4kVd7WwlX2UcE638%NZZ86BUX z`R6w@858XjkR|-UWa7{p#slQH}E&mTYa>^+Q-iW!np7aKIVq=A?Zh(AM9 z&|9bykem$2w?OP000;W1^@s654Bdt00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzR7pfZRCwBA z{Qv(y1FZoQ10BGqB_q@l7Hop)`T{UHE+FQG(yVC8{sHk1ApQcxUxE1N%!?oX5p4++ zumQ0M5UT>Q0uT!$S@aKT@G~I31H=!3_%~FXXiFGDmZ+QBs%G>rS;@uA&yUL`mk#b& zGBq!v9?1MiYMg+IfxkCTAAY;Mtz_lDzkjg0@a~0^&wv(e0y2N$@&%!Y2WfnFYTN3g zyB1EqjV}4=?W-SiD^u4%gX{|>mb?Js^Xn%z&c1c#=o@&91AVr*F>mkNXODJ+JVc2l z|DmbpF%X|vSeLcv!|N9yKEs|R)2{&IVik~i4T#?Z@gGX^$v>zo@4k8ZaNEWy&C?&> zxO8=GUu_pq>>SjmzX;_MMr=hrG@l>?b|4mnCSx{eN_q>#pOJ$Jm`@l;%_r#b^A>72 s6Et}K;EM->J{c__M=cpTmH-4803<4kChc6j4*&oF07*qoM6N<$f?*W9ZvX%Q literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Settings.png b/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Settings.png new file mode 100644 index 0000000000000000000000000000000000000000..1dbedbbcece6f46e62965e91c993e650abbe4052 GIT binary patch literal 555 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 literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-cap.png b/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-cap.png new file mode 100644 index 0000000000000000000000000000000000000000..af127031cffa9bbcb6d9698e801b26f1a1c13408 GIT binary patch literal 597 zcmV-b0;>IqP)>Ma<01$jHdT!os$;w!FN&qN1X& zudmC?%f`mWv$M0x%F3LaoR*fB!^6XxnwqJpsl>#@s;a7xkdV#I&7-5Exw*MbO-=v* z|FpEU($dnNo}QbVn~RH!#l^+u=H|VFMdRvagvhTA!bvQc_Z^ ztgO(`(9h4$US3}7>+A6F@WH{sx3{i%2(ed5kfe1+yYAdw*$y28!yHqtipHR<$gPE@}TwGjsc6MK1Uw?mpczAe#fPi{>dUA4d+1c6L+}zO6&~0sPrlzJyNJw2>U0GRK zTU%Sez`%!xhfPgQdwYAQr>D)$&EVkRmX?-PRaN8TV9+4ByiSY6vMkhD)?VLsqX3zm1j$6(04Yhaps&x5U+P&?1>7 vPoev!35{iV)5cC7=p^%OJ2G-$`wK7tVqG;dPAP0V00000NkvXXu0mjf2}I6l literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-cap.png b/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-cap.png new file mode 100644 index 0000000000000000000000000000000000000000..f17f7f6574b86cb4ccf222a7ead40da5e565d83a GIT binary patch literal 412 zcmV;N0b~A&P)QjRGI()0Ch=3K~#9!T*27_LO>V>&`)T}sK$~ur5!D3)&BmE z4v8Va!4 z)}X*kvqfQv@y8_Ka_{GFdJ;xocAK&qgxFv6BbV^65?}z)Aq@+Gciqnb0000d3kxo#l=oePKt_(Sy@>*IXSJZtwKUVLqkK}-rk6ah}YNG*x1-jO-*fWZBI{6 zTU%RbXlP4IOUK8@w6wHxa&oJyt7Tjfdix{aNKcw%)H z3IS3|-wm<##^rUvqy(ju?^e!;^JX++N&#c6ybAh=FVFIIK+3goTE@v8{v=ilmj`Zs zK`Dh~H>ej%F=o=>oI9WTwpVPHPB+Rq5JH@BBXX!$X%s0T=>fICA%xbvA=hCUAZz#v ZFaWdq4wkeg;&A`~002ovPDHLkV1k<%%2i#=5$?`uh6hvIXgRhd3jBmG^w((GCDeX@#4jik&yub0qyPWdU|@3 zCr^%wii(YmO-)TLEG#rMG|bP>FDWTmvSdk6P|(`7YwhgprX5k+4Rk=hr;B4q#jThV zH-(xUcw8>BrARjMJm5OD?8X0jC7*uRNK5^lnJ!#Otn92SzyG`|BKh>ww~30&BcHtD zc9(K_a>U8=p_xtGI)MqImgeH&zYYA(PHtAbT~;c(!^!b<`R~XNdEZYxnE&#-eaHlj c)eOuGDRLZ&%AX43fmSnky85}Sb4q9e0AbF4xc~qF literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-More.png b/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-More.png new file mode 100644 index 0000000000000000000000000000000000000000..7eaf5195e00e771b7870ae645bbf95d45bb1b829 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8t^l79*TlrcU3Fy1M%M`ihE*xVX4y z&z{A{$0sBtl$Dj0mX^lE#5g!OxVpNAhlj_;#)gE17#bSt>+6Swh3V?*1_cGJTD8i+ zz+lFV88$XHd3kxk!NDaZB_19g(b3WO@86#?WlB_3l&`OEQBhGxN5{g23*Fq@+S=N3 za&n4`i$yzKxPh)J@^o=dHGb?QdgBtK|Ofd*ICO z`m!u~zOcDjxVUdKP`*9%KN=H#GK!z#h>Xfr2sHe1VlpzREvu6{1-oD!M<%UXvb literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-gripper.png b/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-gripper.png new file mode 100644 index 0000000000000000000000000000000000000000..d944f442f7896ccf15c1f2481d2a03312830db66 GIT binary patch literal 248 zcmeAS@N?(olHy`uVBq!ia0vp^MnEjU!3-pmTkq@xQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JiaY{*LR{nG;t~@R8yg$T%F62M>XMR@>g(&1lau4)<0~pE z5)u+hOG~S&s%mO#($dm$a&p?*+M1i2Jv}{V&YbD$>YACE84?m=X=(X%!_TWgGu=F0 z978H@%{hIXx50qnfP?xMk*b^Z!4U`NJXzi;wbyNR;Ol)mG*spE)Oy{NpBI-getz~V s`HIKOov|$iYuDv(d||@&Ry2X(h?m&k6Gtz<0h-9*>FVdQ&MBb@0I(}xzW@LL literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/slate/slate-icons.psd b/jscripts/infusion/framework/fss/images/themes/slate/slate-icons.psd new file mode 100644 index 0000000000000000000000000000000000000000..874d7551be9f1d3418bb2a70d3b4bb027a8ac347 GIT binary patch literal 394447 zcmeD^2VfM%+H;p4LTCX|QBMRx1#a)sBPAW831AY66)$(2&A1zu*;1VUkBDQxhZZ=Snh)8T73o!D4@ z?i1p%$56j9=y(4|lcz0ib2t}@lBG#(ar>r>{`Rr8qb+XNl+p9@>x8oCb0yb5o{f8p+TgDQ%0Ae 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 o|JTG!(Rgwp|=)}at$jHde z&CT1}+tt<8#>U3v!tUFKw( zx15}u$H&L*?d|91=h4y8q@<*sot^sn`oqJ+`}_O5ySwG(<*=}@^z`(=z`(Dsuff5= z#l^*%nwsC=-^C#g6#xJLgh@m}RCwC#&e;KiFcbv9MIMMz@gQ=lc%S|EXSRyXhf~1- zfALAg4(8d|N#)(;`RPN$OZD~saXW=ZrDa+5I);Xcz0H!)nCY>R>9MiaV zQjd*7kByZc8&f?tHhS#{EcBZ0jIG|cY2nnarE>#HxuN!Vq*1^m3O@k`01}LN+c=LQ QHUIzs07*qoM6N<$g24LC4*&oF literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-cap.png b/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-cap.png new file mode 100644 index 0000000000000000000000000000000000000000..a150b586ce822e0fdb23fe58980c64659143bf72 GIT binary patch literal 415 zcmV;Q0bu@#P)gwp|=-b=d)z#JA z-QC#O*yiTu$jHdc%gfTz($LV*;^N}X&CSNf#^mJW;NalI#KgkF!tUoh$;rvb$H&Ub%KQ8K^z`)X?CkUN^S-{m!^6Y7yStyCpV!yd{{H^y>FNFb{h^_u z&(F`))YPP;r1kan_xJbb=jY$w-`UyOzrVknoSfm|;raRbo12@sxVR$%I(+~D0FX&U zK~#9!oXABIgFp~K(T0rRB#__|cenc=Ez?y~bc!W!_dmcOb$K#6s@qR2kB6D2M*dA8 zOQ)I|`-l0;7qO;>5wSAr;0SpUc5v9-CODk6LvRH5fZ*_$M{u~pB{=wv6CBc)1P8Mi z0l2J0BM4N*GEF7~p7XkDd;;g)roD~{yt8C${ee*rm_0uM1^@+F5CC>VFFXJM002ov JPDHLkV1iA_gwwF`1rE2vYDBg=;-Lz z*Vo6#$JEr+;NalF!NI=1zQn}D+S=OA&d$op%H7@F(b3W3;^OS=?DzNg>FMd@GA8AD{L;Jo` zOkSmgolK}@LM0QHGGQ+hwlblS2@9F9kqIlAu$Bok8zwfUKl);BgwwF`1rE2vgqjO*VosX znVHno)X~w=+S=O6%F4mP!N?4!w?i`H&%geatic2ufR663M_1)z{*t$?CzZc%TuYq=Fljt lGFW6Vnr~QUe#%FH0RY9v4HnEGZDSr z1<%~X^wgl##FWaylc_c!9RWTeuK)l4fA#9six)3u&Ybz<$B&;sf4+VD_Vw%6J7&Fa z2dYu=ba4!+xRsP3!8(O0&E&v=4yGG7B_aYCwV8#b94;R^!6A^qppwe4_nfkM0Z=`I Mr>mdKI;Vst087|C#sB~S literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-bg.png b/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..0d87fcc9ab512556fa7f9a02f6a40b17a8d35cc6 GIT binary patch literal 405 zcmV;G0c!qgvSA#Ky+P&CSiy($d}C-P_ySW z&ged`#>U3T z$jH^z)!5kB=H}+z-QB#rywK3l&CSik#Kh9l(%akH;^N}q;Naxsw=jYVa)RdHz|NsBW%F3>;uED{FM?L_44xa-{0T+`}_I%`M$os*4EZs+o9+H005#%L_t(|+KkB65`s_|Md5u=I>jOc z!S3$%{a;GY4EQ@<`Q5Dr{DDbU`p|Rl7WLLP5^{i~PdD=XSjzzM`j9NcekB96N2zxW z;%Wv=kE~{3eSp;kQ5Fdw(o-UQFoh95%%(~BFop)7lQ7XSbN literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-bg.png b/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..1d35340209edb18e13382ca46dd81d5ac9455249 GIT binary patch literal 374 zcmV-+0g3*JP)gwwF`1riMyq%q$#Kgqt z=;+$o+Sk|D%F4>!-QB^#!OqUk)YR0+$H&pp(U6dkudlD*;Naro;_U3~$jHd)>FKbr zu)x5;y}iB5%gdjipT54nt$BHQ_4W0}#>U~{;i#ynrKP3Z+}x3ok>lgz z>+9>LrlyjTl3qTu`2YX_h)G02RCwC#);AKuKokVgfz=`j&&oSI!rlLV{D7Ra1KtU` zYLb!uesUar?Tc25IJ#HWoLl7>&vJCvrqC2)e<}x8d1V}rA&YOd0>~wVuGgF;a*A23 zC}FXp6^j*ZSgdHtVnr#76|GsU$g^0{g2jqgwwF`1riMyq%q$=;-Lg z#KhX#+Sk|DkdTnn)YQ(-&fVSJ(b3V$%F3^=ugAy7!NI}c;Na}+?Be3$z`(%E%gepJ zy^)cTpP!%O`{m{3|NsB)?(XL1=JoaU($dnUrKQHk#^>kf z-rnA#}_TK;h<9ws|#DRNw);hp{MJMv!Hc;%#_F6jzx^gZKtIU9Cb89(R2J)TU zJQgzpeVt}a`T^VV*FHl*Hj<&>UR;KPK5B+Sa1R&?p7a|cv;^t9r;<_0t^6i{SW@2ijuwn0000DSr z1<%~X^wgl##FWaylc_d9MJfS4A+Bf6oO$)?)r%J|e*E~cXV0FwbLXBuefsCmpRZrP ze*5m?b?-dLbd~_UdPkLF{I*FQi24l3ZogDSjd612fDa+NJs=Su&Xm0YbB&7 iI5B5LNk}p>@i2I?Fsg}eJKP4;$KdJe=d#Wzp$P!VK|@~v literal 0 HcmV?d00001 diff --git a/jscripts/infusion/framework/fss/images/themes/slate/widget-bg.png b/jscripts/infusion/framework/fss/images/themes/slate/widget-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..1f60b14cfe5049e2e80b59c24d91e02dc8f15801 GIT binary patch literal 663 zcmV;I0%-k-P))r>CcKa&l#5WoT$uCueVfPjFwxVVpx zk9&K2larHqd3lbGj+>jCV`F1tVq$D;Y;<&VU|?WoW@eh2ns0A!Yinz^wzkK|$Bc}O zn3$N#%F3msrHP4&UteEwadEY^wPa*uoSdAOmzQ&Mb8T&Hk&%&oeSHjZr=$P?0PIOb zK~#9!B+$clMF9{6(TZ*J#kOtRwv+$=fYiFZ2Q}`~}N42u^rDi+0qI2QY2N&JdsaVW;biue+bVnSSt6Y(jk#Hy$k zTcSp+i8}EtCPl4i5KW>{q(rmW7A>Mpw2F4IBRa&pxD%aXPjrbPQKHC(=oS5~7Iv9pwk@l=Jz0BVK zF$u|Xqct9;fGuld%l7jgq=X1a41W9l+WGS5&z}GQsKJ{<006c4`Stbn_n)6XfB*jN zaCA<5v)Z*!dElbwOl(~Ka@j{F;}E4hP)jlvQKWEoUjsN`k@#EL8Uz~Lk zlRj-Q{iyVx=J_$@8B!izt4+mUukOLQ|MqgZOgDaP@c1gz?fX&l&XkHX1k&l#sgXtt zO`$YMPTjC|_vlVFns?kvte2-zCZ*If7W-B5 zMRp+Vf0U_1g9qKCl&bLW&7|cgPpwn8u;eckbkCztp}_zYjY_FFphnkM;Z_%|)a8Pd}NSll@wbN+c=LAyf zDKxtDWILormp;6oUoX__9=#vH$oNQrQjccRy=ych@g1yBUEPmUqi8ivKZYK6R+`1(Zso ztrFh~qfG4r>FCWdZ1N@){;O&<6!(WN9m**2W1os#U!z|S@Ft|xodweDxLS7{f2RZS zd4EFucF3O82`RP5Kw7BUDx>o&C^GcdZ8|#%DH1F9`+iSANiKA5C%^0KKQw4mT(X8||34 z`RLPF$j2K#^uRt+DvdVPXnNp{>Kwnyp}=8NYPK}D-FS4vt>#nOj5(A;fm5W^yd|OA zZalie6+lbYCXG2X4$b>00|!Z|P4_5iv9dmFI{03w(T|UhC{v$Z;5aF@PmP8xW|B5- z6CFATboeTY0((lSRdo5P5HP-s8%d{}+s(ZV(Zv2QeHQXf9x$V#nS z#fN4~LiaMVG~PdIR8bF{sPL!JifzUrCX+^ilcv-~1O59Npbzz~%QVqu>>f4Y36N50 zG_y^NqUw48VRm?G+hL{9-^QZ?DS%}uwboWo*)4n9c=Y~t-{0T$kOIg|sY~S;_ON5# z(l*198BOXF17KN7rO}AVPXO{$YRYC*>nsV~c9s3$(3nY;_5dM8}WnNl-!)@ZE1u$xtZ4`BPk ze>LSbdR?&;KzAv1t2Im6&l!75uh*+s3ZRRWy48+(g}t|8DS#eR>h!he)>myvxBy9% zsdo`TUn%u*UTV*^zo91pdrGNc^#S_X+CSciGF9UP*nv`e7{q_|kn8oT!xX?i3jdJ1 z8CB~p)NvzyG6k@kl=}J2bkUtsm{-v#nF82-O3lz{Xw!unFHGY4P5}0rQfJrruQpmG znF2VDQmbe&vG)I`felOn9JcVUsL|PwJJ|esy^<+_vlRYe;Yd`Xd50So<^wojN?lZ^ zmj~Fm(TU}x0FIebXLrl5cnQ@gQiX%vQJtp%5GJmWI;+w9PFOw#fbeu)>Y@=UJ_UgJ!hbrh z#z?F46aZcp{<9kWC!ace0PsYqgCF1C-cknl$M%jL03;Ru(}|T&0bnYnK8o^H*D9X^ zz`Rl)*Z4p3*yRcUm?`}E6ab1+>V!!XoeKa`>i@dDTrN*d%BKLZIHkU->&X-V7ArMz z#2)}KQ~1*;08}pg9S#7fqf`JuA#V-=07$6-K&b$r@CN`$sQ@6Q0)UhX0F(*J607fuUpUWQi QLjV8(07*qoM6N<$g78|=0;--i){var lump=tagstack[i];if(lump.rsfID!==undefined){return lump}}return t.rootlump}function newLump(){var togo=fluid.XMLLump(lumpindex,nestingdepth);if(debugMode){togo.line=parser.getLineNumber();togo.column=parser.getColumnNumber()}t.lumps[lumpindex]=togo;++lumpindex;return togo}function addLump(mmap,ID,lump){var list=mmap[ID];if(!list){list=[];mmap[ID]=list}list[list.length]=lump}function checkContribute(ID,lump){if(ID.indexOf("scr=contribute-")!==-1){var scr=ID.substring("scr=contribute-".length);addLump(t.collectmap,scr,lump)}}var parseUri=function(source){var o=parseUri.options,value=o.parser[o.strictMode?"strict":"loose"].exec(source);for(var i=0,uri={};i<14;i++){uri[o.key[i]]=value[i]||""}uri[o.q.name]={};uri[o.key[12]].replace(o.q.parser,function($0,$1,$2){if($1){uri[o.q.name][$1]=$2}});return uri};parseUri.options={strictMode:false,key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}};function rewriteUrl(url){var po=parseUri(url);if(po.protocol||url.charAt(0)==="/"){return url}else{return baseURL+url}}fluid.debugLump=function(lump){var togo=lump.text;togo+=" at ";togo+="lump line "+lump.line+" column "+lump.column+" index "+lump.lumpindex;togo+=parent.href===null?"":" in file "+parent.href;return togo};function debugLump(lump){return"<"+lump.tagname+">"}function hasCssClass(clazz,totest){if(!totest){return false}return(" "+totest+" ").indexOf(" "+clazz+" ")!==-1}function matchNode(term,headlump){if(term.predList){for(var i=0;i0){if(cut.tree[nextterm-1].child&&cutstat[nextterm-1]!==headlump.nestingdepth-1){continue}}var isMatch=matchNode(term,headlump);if(isMatch){cutstat[cutstat.length]=headlump.nestingdepth;if(cutstat.length===cut.tree.length){if(togo!==undefined){fluid.fail("Cutpoint specification error - node "+debugLump(headlump)+" has already matched with rsf:id of "+togo)}if(cut.id===undefined||cut.id===null){fluid.fail("Error in cutpoints list - entry at position "+i+" does not have an id set")}togo=cut.id}}}}}return togo}function tagEndCut(){if(cutpoints){for(var i=0;i0&&cutstat[cutstat.length-1]===nestingdepth){cutstat.length--}}}}function processTagStart(isempty,text){++nestingdepth;if(justended){justended=false;var backlump=newLump();backlump.nestingdepth--}if(t.firstdocumentindex===-1){t.firstdocumentindex=lumpindex}var headlump=newLump();var stacktop=tagstack[tagstack.length-1];headlump.uplump=stacktop;var tagname=parser.getName();headlump.tagname=tagname;var attrs=headlump.attributemap=parser.m_attributes;var ID=attrs[fluid.ID_ATTRIBUTE];if(ID===undefined){ID=tagStartCut(headlump)}for(var attrname in attrs){var attrval=attrs[attrname];if(/href|src|codebase|action/.test(attrname)){attrval=rewriteUrl(attrval);attrs[attrname]=attrval}else{if(ID===undefined&&/for|headers/.test(attrname)){ID=attrs[fluid.ID_ATTRIBUTE]="scr=null"}}}if(ID){if(ID.charCodeAt(0)===126){ID=ID.substring(1);headlump.elide=true}checkContribute(ID,headlump);headlump.rsfID=ID;var downreg=findTopContainer();if(!downreg.downmap){downreg.downmap={}}addLump(downreg.downmap,ID,headlump);addLump(t.globalmap,ID,headlump);var colpos=ID.indexOf(":");if(colpos!==-1){var prefix=ID.substring(0,colpos);if(!stacktop.finallump){stacktop.finallump={}}stacktop.finallump[prefix]=headlump}}headlump.text="<"+tagname+fluid.dumpAttributes(attrs)+">";tagstack[tagstack.length]=headlump;if(isempty){processTagEnd()}}function processTagEnd(){tagEndCut();var endlump=newLump();--nestingdepth;endlump.text="";var oldtop=tagstack[tagstack.length-1];oldtop.close_tag=t.lumps[lumpindex-1];tagstack.length--;justended=true}function processDefaultTag(){if(defstart!==-1){if(t.firstdocumentindex===-1){t.firstdocumentindex=lumpindex}var text=parser.getContent().substr(defstart,defend-defstart);justended=false;var newlump=newLump();newlump.text=text;defstart=-1}}fluid.ID_ATTRIBUTE="rsf:id";fluid.getPrefix=function(id){var colpos=id.indexOf(":");return colpos===-1?id:id.substring(0,colpos)};fluid.SplitID=function(id){var that={};var colpos=id.indexOf(":");if(colpos===-1){that.prefix=id}else{that.prefix=id.substring(0,colpos);that.suffix=id.substring(colpos+1)}return that};fluid.XMLLump=function(lumpindex,nestingdepth){return{nestingdepth:nestingdepth,lumpindex:lumpindex,parent:t}};fluid.XMLViewTemplate=function(){return{globalmap:{},collectmap:{},lumps:[],firstdocumentindex:-1}};fluid.fetchResources=function(resourceSpecs,callback){var resourceCallback=function(thisSpec){return{success:function(response){thisSpec.resourceText=response;thisSpec.resourceKey=thisSpec.href;thisSpec.queued=false;fluid.fetchResources(resourceSpecs,callback)},error:function(response,textStatus,errorThrown){thisSpec.fetchError={status:response.status,textStatus:textStatus,errorThrown:errorThrown}}}};var complete=true;for(var key in resourceSpecs){var resourceSpec=resourceSpecs[key];if(resourceSpec.href&&!resourceSpec.resourceText){if(!resourceSpec.queued){var thisCallback=resourceCallback(resourceSpec);$.ajax({url:resourceSpec.href,success:thisCallback.success,error:thisCallback.error});resourceSpec.queued=true}complete=false}else{if(resourceSpec.nodeId&&!resourceSpec.resourceText){var node=document.getElementById(resourceSpec.nodeId);resourceSpec.resourceText=fluid.dom.getElementText(node);resourceSpec.resourceKey=resourceSpec.nodeId}}}if(complete){if($.browser.mozilla){setTimeout(function(){callback(resourceSpecs)},1)}else{callback(resourceSpecs)}}};fluid.XMLEncode=function(text){return text.replace(/&/g,"&").replace(//g,">")};fluid.dumpAttributes=function(attrcopy){var togo="";for(var attrname in attrcopy){var attrvalue=attrcopy[attrname];if(attrvalue!==null&&attrvalue!==undefined){togo+=" "+attrname+'="'+attrvalue+'"'}}return togo};fluid.aggregateMMap=function(target,source){for(var key in source){var targhas=target[key];if(!targhas){target[key]=[]}target[key]=target[key].concat(source[key])}};var unUnicode=/(\\u[\dabcdef]{4}|\\x[\dabcdef]{2})/g;fluid.unescapeProperties=function(string){string=string.replace(unUnicode,function(match){var code=match.substring(2);var parsed=parseInt(code,16);return String.fromCharCode(parsed)});var pos=0;while(true){var backpos=string.indexOf("\\",pos);if(backpos===-1){break}if(backpos===string.length-1){return[string.substring(0,string.length-1),true]}var replace=string.charAt(backpos+1);if(replace==="n"){replace="\n"}if(replace==="r"){replace="\r"}if(replace==="t"){replace="\t"}string=string.substring(0,backpos)+replace+string.substring(backpos+2);pos=backpos+1}return[string,false]};var breakPos=/[^\\][\s:=]/;fluid.parseJavaProperties=function(text){var togo={};text=text.replace(/\r\n/g,"\n");text=text.replace(/\r/g,"\n");lines=text.split("\n");var contin,key,valueComp,valueRaw,valueEsc;for(var i=0;i",idpos);parser=new XMLP(template.substring(brackpos+1))}else{parser=new XMLP(template)}parseloop:while(true){var iEvent=parser.next();switch(iEvent){case XMLP._ELM_B:processDefaultTag();processTagStart(false,"");break;case XMLP._ELM_E:processDefaultTag();processTagEnd();break;case XMLP._ELM_EMP:processDefaultTag();processTagStart(true,"");break;case XMLP._PI:case XMLP._DTD:defstart=-1;continue;case XMLP._TEXT:case XMLP._ENTITY:case XMLP._CDATA:case XMLP._COMMENT:if(defstart===-1){defstart=parser.m_cB}defend=parser.m_cE;break;case XMLP._ERROR:fluid.setLogging(true);var message="Error parsing template: "+parser.m_cAlt+" at line "+parser.getLineNumber();fluid.log(message);fluid.log("Just read: "+parser.m_xml.substring(parser.m_iP-30,parser.m_iP));fluid.log("Still to read: "+parser.m_xml.substring(parser.m_iP,parser.m_iP+30));fluid.fail(message);break parseloop;case XMLP._NONE:break parseloop}}return t};var chars="(?:[\\w\u0128-\uFFFF*_-]|\\\\.)";var quickChild=new RegExp("^>\\s*("+chars+"+)");var quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)");var selSeg=new RegExp("^s*([#.]?)("+chars+"*)");var quickClass=new RegExp("([#.]?)("+chars+"+)","g");var childSeg=new RegExp("\\s*(>)?\\s*","g");var whiteSpace=new RegExp("^\\w*$");fluid.parseSelector=function(selstring){var togo=[];selstring=$.trim(selstring);quickClass.lastIndex=0;var lastIndex=0;while(true){var atNode=[];while(true){var segMatch=quickClass.exec(selstring);if(!segMatch||segMatch.index!==lastIndex){break}var thisNode={};var text=segMatch[2];if(segMatch[1]===""){thisNode.tag=text}else{if(segMatch[1]==="#"){thisNode.id=text}else{if(segMatch[1]==="."){thisNode.clazz=text}}}atNode[atNode.length]=thisNode;lastIndex=quickClass.lastIndex}childSeg.lastIndex=lastIndex;var fullAtNode={predList:atNode};var childMatch=childSeg.exec(selstring);if(!childMatch||childMatch.index!==lastIndex){var remainder=selstring.substring(lastIndex);fluid.fail("Error in selector string - can not match child selector expression at "+remainder)}if(childMatch[1]===">"){fullAtNode.child=true}togo[togo.length]=fullAtNode;if(childSeg.lastIndex>=selstring.length){break}lastIndex=childSeg.lastIndex;quickClass.lastIndex=childSeg.lastIndex}return togo}})(jQuery,fluid_1_1); \ No newline at end of file diff --git a/jscripts/infusion/framework/renderer/js/fluidRenderer.js b/jscripts/infusion/framework/renderer/js/fluidRenderer.js new file mode 100644 index 000000000..053605592 --- /dev/null +++ b/jscripts/infusion/framework/renderer/js/fluidRenderer.js @@ -0,0 +1 @@ +fluid_1_1=fluid_1_1||{};(function($,fluid){function debugPosition(component){return"as child of "+(component.parent.fullID?"component with full ID "+component.parent.fullID:"root")}function computeFullID(component){var togo="";var move=component;if(component.children===undefined){togo=component.ID+(component.localID!==undefined?component.localID:"");move=component.parent}while(move.parent){var parent=move.parent;if(move.fullID!==undefined){togo=move.fullID+togo;return togo}if(move.noID===undefined){var ID=move.ID;if(ID===undefined){fluid.fail("Error in component tree - component found with no ID "+debugPosition(parent)+": please check structure")}var colpos=ID.indexOf(":");var prefix=colpos===-1?ID:ID.substring(0,colpos);togo=prefix+":"+(move.localID===undefined?"":move.localID)+":"+togo}move=parent}return togo}function isBoundPrimitive(value){return fluid.isPrimitive(value)||value instanceof Array&&(value.length===0||typeof (value[0])==="string")}function processChild(value,key){if(isBoundPrimitive(value)){return{componentType:"UIBound",value:value,ID:key}}else{var unzip=unzipComponent(value);if(unzip.ID){return{ID:key,componentType:"UIContainer",children:[unzip]}}else{unzip.ID=key;return unzip}}}function fixChildren(children){if(!(children instanceof Array)){var togo=[];for(var key in children){var value=children[key];if(value instanceof Array){for(var i=0;i"}function dumpTillLump(lumps,start,limit){for(;startbasedepth+(closeparent?0:1)){fluid.log("Error in component tree - leaf component found to contain further components - at "+lump.toString())}else{break}}++renderindex}if(!closeparent&&(renderindex==lumps.length||!lumps[renderindex].rsfID)){--renderindex}dumpTillLump(lumps,start,renderindex);return renderindex}var trc={};function openTag(){if(!trc.iselide){out+="<"+trc.uselump.tagname}}function closeTag(){if(!trc.iselide){out+=""}}function renderUnchanged(){dumpTillLump(trc.uselump.parent.lumps,trc.uselump.lumpindex+1,trc.close.lumpindex+(trc.iselide?0:1))}function replaceAttributes(){if(!trc.iselide){out+=fluid.dumpAttributes(trc.attrcopy)}dumpTemplateBody()}function replaceAttributesOpen(){if(trc.iselide){replaceAttributes()}else{out+=fluid.dumpAttributes(trc.attrcopy);out+=">";trc.nextpos=trc.endopen.lumpindex}}function dumpTemplateBody(){if(trc.endopen.lumpindex===trc.close.lumpindex){if(!trc.iselide){out+="/>"}}else{if(!trc.iselide){out+=">"}dumpTillLump(trc.uselump.parent.lumps,trc.endopen.lumpindex,trc.close.lumpindex+(trc.iselide?0:1))}}function rewriteLeaf(value){if(isValue(value)){replaceBody(value)}else{replaceAttributes()}}function rewriteLeafOpen(value){if(trc.iselide){rewriteLeaf(trc.value)}else{if(isValue(value)){replaceBody(value)}else{replaceAttributesOpen()}}}function replaceBody(value){out+=fluid.dumpAttributes(trc.attrcopy);if(!trc.iselide){out+=">"}out+=fluid.XMLEncode(value.toString());closeTag()}function isValue(value){return value!==null&&value!==undefined&&!isPlaceholder(value)}function isPlaceholder(value){return false}function rewriteURL(template,URL){return URL}function dumpHiddenField(todump){out+='\n"}function applyAutoBind(torender,finalID){var tagname=trc.uselump.tagname;var applier=renderOptions.applier;function applyFunc(){fluid.applyChange(fluid.byId(finalID),undefined,applier)}if(renderOptions.autoBind&&/input|select|textarea/.test(tagname)&&!renderedbindings[finalID]){var decorators=[{jQuery:["change",applyFunc]}];if($.browser.msie&&tagname==="input"&&/radio|checkbox/.test(trc.attrcopy.type)){decorators.push({jQuery:["click",applyFunc]})}outDecoratorsImpl(torender,decorators,trc.attrcopy,finalID)}}function dumpBoundFields(torender,parent){if(torender){var holder=parent?parent:torender;if(directFossils&&holder.submittingname&&holder.valuebinding){directFossils[holder.submittingname]={name:holder.submittingname,EL:holder.valuebinding,oldvalue:holder.value};applyAutoBind(torender,torender.fullID)}if(torender.fossilizedbinding){dumpHiddenField(torender.fossilizedbinding)}if(torender.fossilizedshaper){dumpHiddenField(torender.fossilizedshaper)}}}function dumpSelectionBindings(uiselect){if(!renderedbindings[uiselect.selection.fullID]){renderedbindings[uiselect.selection.fullID]=true;dumpBoundFields(uiselect.selection);dumpBoundFields(uiselect.optionlist);dumpBoundFields(uiselect.optionnames)}}fluid.NULL_STRING="\u25a9null\u25a9";var LINK_ATTRIBUTES={a:"href",link:"href",img:"src",frame:"src",script:"src",style:"src",input:"src",embed:"src",form:"action",applet:"codebase",object:"codebase"};function isSelectedValue(torender,value){var selection=torender.selection;return selection.value&&typeof (selection.value)!=="string"&&typeof (selection.value.length)==="number"?$.inArray(value,selection.value,value)!==-1:selection.value===value}function getRelativeComponent(component,relativeID){component=component.parent;if(relativeID.indexOf("..::")===0){relativeID=relativeID.substring(4);component=component.parent}return component.childmap[relativeID]}function explodeDecorators(decorators){var togo=[];if(decorators.type){togo[0]=decorators}else{for(var key in decorators){if(key==="$"){key="jQuery"}var value=decorators[key];var decorator={type:key};if(key==="jQuery"){decorator.func=value[0];decorator.args=value.slice(1)}else{if(key==="addClass"||key==="removeClass"){decorator.classes=value}else{if(key==="attrs"){decorator.attributes=value}else{if(key==="identify"){decorator.key=value}}}}togo[togo.length]=decorator}}return togo}function outDecoratorsImpl(torender,decorators,attrcopy,finalID){renderOptions.idMap=renderOptions.idMap||{};for(var i=0;i";var values=torender.optionlist.value;var names=torender.optionnames===null||torender.optionnames===undefined||!torender.optionnames.value?values:torender.optionnames.value;if(!names||!names.length){fluid.fail("Error in component tree - UISelect component with fullID "+torender.fullID+" does not have optionnames set")}for(var i=0;i';out+=fluid.XMLEncode(names[i]);out+="\n"}closeTag()}else{dumpTemplateBody()}dumpSelectionBindings(torender)}else{if(componentType==="UILink"){var attrname=LINK_ATTRIBUTES[tagname];if(attrname){var target=torender.target.value;if(!isValue(target)){target=attrcopy[attname]}else{target=rewriteURL(trc.uselump.parent,target)}attrcopy[attrname]=target}var value=torender.linktext.value;if(!isValue(value)){replaceAttributesOpen()}else{rewriteLeaf(value)}}else{if(torender.markup!==undefined){var rendered=torender.markup;if(rendered===null){out+=fluid.dumpAttributes(attrcopy);out+=">";renderUnchanged()}else{if(!trc.iselide){out+=fluid.dumpAttributes(attrcopy);out+=">"}out+=rendered;closeTag()}}else{}}}}}function adjustForID(attrcopy,component,late,forceID){if(!late){delete attrcopy["rsf:id"]}if(forceID!==undefined){attrcopy.id=forceID}else{if(attrcopy.id||late){attrcopy.id=component.fullID}}var count=1;var baseid=attrcopy.id;while(renderOptions.document.getElementById(attrcopy.id)){attrcopy.id=baseid+"-"+(count++)}return attrcopy.id}function rewriteIDRelation(context){var attrname;var attrval=trc.attrcopy["for"];if(attrval!==undefined){attrname="for"}else{attrval=trc.attrcopy.headers;if(attrval!==undefined){attrname="headers"}}if(!attrname){return }var tagname=trc.uselump.tagname;if(attrname==="for"&&tagname!=="label"){return }if(attrname==="headers"&&tagname!=="td"&&tagname!=="th"){return }var rewritten=rewritemap[getRewriteKey(trc.uselump.parent,context,attrval)];if(rewritten!==undefined){trc.attrcopy[attrname]=rewritten}}function renderComment(message){out+=("")}function renderDebugMessage(message){out+='';out+=message;out+="
    "}function reportPath(branch){var path=branch.fullID;return !path?"component tree root":"full path "+path}function renderComponentSystem(context,torendero,lump){var lumpindex=lump.lumpindex;var lumps=lump.parent.lumps;var nextpos=-1;var outerendopen=lumps[lumpindex+1];var outerclose=lump.close_tag;nextpos=outerclose.lumpindex+1;var payloadlist=lump.downmap?lump.downmap["payload-component"]:null;var payload=payloadlist?payloadlist[0]:null;var iselide=lump.rsfID.charCodeAt(0)===126;var endopen=outerendopen;var close=outerclose;var uselump=lump;var attrcopy={};$.extend(true,attrcopy,(payload===null?lump:payload).attributemap);trc.attrcopy=attrcopy;trc.uselump=uselump;trc.endopen=endopen;trc.close=close;trc.nextpos=nextpos;trc.iselide=iselide;rewriteIDRelation(context);if(torendero===null){if(lump.rsfID.indexOf("scr=")===(iselide?1:0)){var scrname=lump.rsfID.substring(4+(iselide?1:0));if(scrname==="ignore"){nextpos=trc.close.lumpindex+1}else{openTag();replaceAttributesOpen();nextpos=trc.endopen.lumpindex}}}else{if(payload){trc.endopen=lumps[payload.lumpindex+1];trc.close=payload.close_tag;trc.uselump=payload;dumpTillLump(lumps,lumpindex,payload.lumpindex);lumpindex=payload.lumpindex}adjustForID(attrcopy,torendero);openTag();renderComponent(torendero);if(payload!==null){if(trc.nextpos===nextpos){dumpTillLump(lumps,trc.close.lumpindex+1,outerclose.lumpindex+1)}}nextpos=trc.nextpos}return nextpos}function renderContainer(child,targetlump){var t2=targetlump.parent;var firstchild=t2.lumps[targetlump.lumpindex+1];if(child.children!==undefined){dumpBranchHead(child,targetlump)}else{renderComponentSystem(child.parent,child,targetlump)}renderRecurse(child,targetlump,firstchild)}function fetchComponent(basecontainer,id,lump){if(id.indexOf("msg=")===0){var key=id.substring(4);return{componentType:"UIMessage",messagekey:key}}while(basecontainer){var togo=basecontainer.childmap[id];if(togo){return togo}basecontainer=basecontainer.parent}return null}function fetchComponents(basecontainer,id){var togo;while(basecontainer){togo=basecontainer.childmap[id];if(togo){break}basecontainer=basecontainer.parent}return togo}function findChild(sourcescope,child){var split=fluid.SplitID(child.ID);var headlumps=sourcescope.downmap[child.ID];if(headlumps===null){headlumps=sourcescope.downmap[split.prefix+":"]}return headlumps===null?null:headlumps[0]}function renderRecurse(basecontainer,parentlump,baselump){var renderindex=baselump.lumpindex;var basedepth=parentlump.nestingdepth;var t1=parentlump.parent;if(debugMode){var rendered={}}while(true){renderindex=dumpScan(t1.lumps,renderindex,basedepth,!parentlump.elide,false);if(renderindex===t1.lumps.length){break}var lump=t1.lumps[renderindex];var id=lump.rsfID;if(lump.nestingdepth=targetlump.nestingdepth;var newbase=child.children?child:basecontainer;if(wasopentag){renderRecurse(newbase,targetlump,t1.lumps[renderend]);renderend=targetlump.close_tag.lumpindex+1}if(i!==children.length-1){if(renderend")}if(options.model){fluid.bindFossils(node,options.model,fossils)}if($.browser.msie){$(node).html(rendered)}else{node.innerHTML=rendered}processDecoratorQueue();if(lastId){var element=fluid.byId(lastId);if(element){$(element).focus()}}return templates};function findNodeValue(rootNode){var node=fluid.dom.iterateDom(rootNode,function(node){return node.nodeType===8||node.nodeType===4?"stop":null},true);var value=node.nodeValue;if(value.indexOf("[CDATA[")===0){return value.substring(6,value.length-2)}else{return value}}fluid.extractTemplate=function(node,armouring){if(!armouring){return node.innerHTML}else{return findNodeValue(node)}};fluid.selfRender=function(node,tree,options){options=options||{};node=fluid.unwrap(node);var resourceSpec={base:{resourceText:fluid.extractTemplate(node,options.armouring),href:".",resourceKey:".",cutpoints:options.cutpoints}};var templates=fluid.parseTemplates(resourceSpec,["base"],options);return fluid.reRender(templates,node,tree,options)}})(jQuery,fluid_1_1); \ No newline at end of file diff --git a/jscripts/infusion/jquery.autoHeight.js b/jscripts/infusion/jquery.autoHeight.js new file mode 100644 index 000000000..01e039954 --- /dev/null +++ b/jscripts/infusion/jquery.autoHeight.js @@ -0,0 +1,34 @@ +function doIframe(){ + o = document.getElementsByTagName('iframe'); + for(i=0;i]+)/g;var attrStartRegex=/\s*([\w:]+)/gm;var attrValRegex=/\"([^\"]*)\"\s*/gm;var attrValIERegex=/([^\>\s]+)\s*/gm;var closeRegex=/\s*<\//g;XMLP.prototype._parseElement=function(iB){var iE,iDE,iRet;var iType,strN,iLast;iDE=iE=this.m_xml.indexOf(">",iB);if(iE==-1){return this._setErr(XMLP.ERR_CLOSE_ELM)}if(this.m_xml.charAt(iB)=="/"){iType=XMLP._ELM_E;iB++}else{iType=XMLP._ELM_B}if(this.m_xml.charAt(iE-1)=="/"){if(iType==XMLP._ELM_E){return this._setErr(XMLP.ERR_ELM_EMPTY)}iType=XMLP._ELM_EMP;iDE--}nameRegex.lastIndex=iB;var nameMatch=nameRegex.exec(this.m_xml);if(!nameMatch){return this._setErr(XMLP.ERR_ELM_NAME)}strN=nameMatch[1].toLowerCase();if("li"===strN&&iType!==XMLP._ELM_E&&this.m_stack.length>0&&this.m_stack[this.m_stack.length-1]==="li"&&!this.m_emitSynthetic){this.m_name="li";this.m_emitSynthetic=true;return XMLP._ELM_E}this.m_attributes={};this.m_cAlt="";if(nameRegex.lastIndex",iB);if(iE==-1){return this._setErr(XMLP.ERR_CLOSE_CDATA)}this._setContent(XMLP._CONT_XML,iB,iE);this.m_iP=iE+3;return XMLP._CDATA};XMLP.prototype._parseComment=function(iB){var iE=this.m_xml.indexOf("-->",iB);if(iE==-1){return this._setErr(XMLP.ERR_CLOSE_COMMENT)}this._setContent(XMLP._CONT_XML,iB-4,iE+3);this.m_iP=iE+3;return XMLP._COMMENT};XMLP.prototype._parseDTD=function(iB){var iE,strClose,iInt,iLast;iE=this.m_xml.indexOf(">",iB);if(iE==-1){return this._setErr(XMLP.ERR_CLOSE_DTD)}iInt=this.m_xml.indexOf("[",iB);strClose=((iInt!=-1)&&(iInt":">";while(true){if(iE==iLast){return this._setErr(XMLP.ERR_INFINITELOOP)}iLast=iE;iE=this.m_xml.indexOf(strClose,iB);if(iE==-1){return this._setErr(XMLP.ERR_CLOSE_DTD)}if(this.m_xml.substring(iE-1,iE+2)!="]]>"){break}}this.m_iP=iE+strClose.length;return XMLP._DTD};XMLP.prototype._parsePI=function(iB){var iE,iTB,iTE,iCB,iCE;iE=this.m_xml.indexOf("?>",iB);if(iE==-1){return this._setErr(XMLP.ERR_CLOSE_PI)}iTB=SAXStrings.indexOfNonWhitespace(this.m_xml,iB,iE);if(iTB==-1){return this._setErr(XMLP.ERR_PI_TARGET)}iTE=SAXStrings.indexOfWhitespace(this.m_xml,iTB,iE);if(iTE==-1){iTE=iE}iCB=SAXStrings.indexOfNonWhitespace(this.m_xml,iTE,iE);if(iCB==-1){iCB=iE}iCE=SAXStrings.lastIndexOfNonWhitespace(this.m_xml,iCB,iE);if(iCE==-1){iCE=iE-1}this.m_name=this.m_xml.substring(iTB,iTE);this._setContent(XMLP._CONT_XML,iCB,iCE+1);this.m_iP=iE+2;return XMLP._PI};XMLP.prototype._parseText=function(iB){var iE=this.m_xml.indexOf("<",iB);if(iE==-1){iE=this.m_xml.length}this._setContent(XMLP._CONT_XML,iB,iE);this.m_iP=iE;return XMLP._TEXT};XMLP.prototype._setContent=function(iSrc){var args=arguments;if(XMLP._CONT_XML==iSrc){this.m_cAlt=null;this.m_cB=args[1];this.m_cE=args[2]}else{this.m_cAlt=args[1];this.m_cB=0;this.m_cE=args[1].length}this.m_cSrc=iSrc};XMLP.prototype._setErr=function(iErr){var strErr=XMLP._errs[iErr];this.m_cAlt=strErr;this.m_cB=0;this.m_cE=strErr.length;this.m_cSrc=XMLP._CONT_ALT;return XMLP._ERROR};SAXStrings={};SAXStrings.WHITESPACE=" \t\n\r";SAXStrings.QUOTES="\"'";SAXStrings.getColumnNumber=function(strD,iP){if(!strD){return -1}iP=iP||strD.length;var arrD=strD.substring(0,iP).split("\n");arrD.length--;var iLinePos=arrD.join("\n").length;return iP-iLinePos};SAXStrings.getLineNumber=function(strD,iP){if(!strD){return -1}iP=iP||strD.length;return strD.substring(0,iP).split("\n").length};SAXStrings.indexOfNonWhitespace=function(strD,iB,iE){if(!strD){return -1}iB=iB||0;iE=iE||strD.length;for(var i=iB;i=iB;i--){if(SAXStrings.WHITESPACE.indexOf(strD.charAt(i))==-1){return i}}return -1};SAXStrings.replace=function(strD,iB,iE,strF,strR){if(!strD){return""}iB=iB||0;iE=iE||strD.length;return strD.substring(iB,iE).split(strF).join(strR)};function __unescapeString(str){return str.replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/"/g,'"').replace(/'/g,"'")}function __escapeString(str){var escAmpRegEx=/&/g;var escLtRegEx=//g;var quotRegEx=/"/g;var aposRegEx=/'/g;str=str.replace(escAmpRegEx,"&");str=str.replace(escLtRegEx,"<");str=str.replace(escGtRegEx,">");str=str.replace(quotRegEx,""");str=str.replace(aposRegEx,"'");return str}; \ No newline at end of file diff --git a/jscripts/infusion/lib/jquery/core/js/jquery.js b/jscripts/infusion/lib/jquery/core/js/jquery.js new file mode 100644 index 000000000..0de899df7 --- /dev/null +++ b/jscripts/infusion/lib/jquery/core/js/jquery.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var window=this,undefined,_jQuery=window.jQuery,_$=window.$,jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context)},quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,isSimple=/^.[^:#\[\.,]*$/;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;this.context=selector;return this}if(typeof selector==="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1]){selector=jQuery.clean([match[1]],context)}else{var elem=document.getElementById(match[3]);if(elem&&elem.id!=match[3]){return jQuery().find(selector)}var ret=jQuery(elem||[]);ret.context=document;ret.selector=selector;return ret}}else{return jQuery(context).find(selector)}}else{if(jQuery.isFunction(selector)){return jQuery(document).ready(selector)}}if(selector.selector&&selector.context){this.selector=selector.selector;this.context=selector.context}return this.setArray(jQuery.isArray(selector)?selector:jQuery.makeArray(selector))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(num){return num===undefined?Array.prototype.slice.call(this):this[num]},pushStack:function(elems,name,selector){var ret=jQuery(elems);ret.prevObject=this;ret.context=this.context;if(name==="find"){ret.selector=this.selector+(this.selector?" ":"")+selector}else{if(name){ret.selector=this.selector+"."+name+"("+selector+")"}}return ret},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this},each:function(callback,args){return jQuery.each(this,callback,args)},index:function(elem){return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this)},attr:function(name,value,type){var options=name;if(typeof name==="string"){if(value===undefined){return this[0]&&jQuery[type||"attr"](this[0],name)}else{options={};options[name]=value}}return this.each(function(i){for(name in options){jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name))}})},css:function(key,value){if((key=="width"||key=="height")&&parseFloat(value)<0){value=undefined}return this.attr(key,value,"curCSS")},text:function(text){if(typeof text!=="object"&&text!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text))}var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8){ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this])}})});return ret},wrapAll:function(html){if(this[0]){var wrap=jQuery(html,this[0].ownerDocument).clone();if(this[0].parentNode){wrap.insertBefore(this[0])}wrap.map(function(){var elem=this;while(elem.firstChild){elem=elem.firstChild}return elem}).append(this)}return this},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html)})},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html)})},append:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType==1){this.appendChild(elem)}})},prepend:function(){return this.domManip(arguments,true,function(elem){if(this.nodeType==1){this.insertBefore(elem,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this)})},after:function(){return this.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this.nextSibling)})},end:function(){return this.prevObject||jQuery([])},push:[].push,sort:[].sort,splice:[].splice,find:function(selector){if(this.length===1){var ret=this.pushStack([],"find",selector);ret.length=0;jQuery.find(selector,this[0],ret);return ret}else{return this.pushStack(jQuery.unique(jQuery.map(this,function(elem){return jQuery.find(selector,elem)})),"find",selector)}},clone:function(events){var ret=this.map(function(){if(!jQuery.support.noCloneEvent&&!jQuery.isXMLDoc(this)){var html=this.outerHTML;if(!html){var div=this.ownerDocument.createElement("div");div.appendChild(this.cloneNode(true));html=div.innerHTML}return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(events===true){var orig=this.find("*").andSelf(),i=0;ret.find("*").andSelf().each(function(){if(this.nodeName!==orig[i].nodeName){return }var events=jQuery.data(orig[i],"events");for(var type in events){for(var handler in events[type]){jQuery.event.add(this,type,events[type][handler],events[type][handler].data)}}i++})}return ret},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i)})||jQuery.multiFilter(selector,jQuery.grep(this,function(elem){return elem.nodeType===1})),"filter",selector)},closest:function(selector){var pos=jQuery.expr.match.POS.test(selector)?jQuery(selector):null,closer=0;return this.map(function(){var cur=this;while(cur&&cur.ownerDocument){if(pos?pos.index(cur)>-1:jQuery(cur).is(selector)){jQuery.data(cur,"closest",closer);return cur}cur=cur.parentNode;closer++}})},not:function(selector){if(typeof selector==="string"){if(isSimple.test(selector)){return this.pushStack(jQuery.multiFilter(selector,this,true),"not",selector)}else{selector=jQuery.multiFilter(selector,this)}}var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector})},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector==="string"?jQuery(selector):jQuery.makeArray(selector))))},is:function(selector){return !!selector&&jQuery.multiFilter(selector,this).length>0},hasClass:function(selector){return !!selector&&this.is("."+selector)},val:function(value){if(value===undefined){var elem=this[0];if(elem){if(jQuery.nodeName(elem,"option")){return(elem.attributes.value||{}).specified?elem.value:elem.text}if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0){return null}for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0)}else{if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0)});if(!values.length){this.selectedIndex=-1}}else{this.value=value}}})},html:function(value){return value===undefined?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(value)},replaceWith:function(value){return this.after(value).remove()},eq:function(i){return this.slice(i,+i+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(args,table,callback){if(this[0]){var fragment=(this[0].ownerDocument||this[0]).createDocumentFragment(),scripts=jQuery.clean(args,(this[0].ownerDocument||this[0]),fragment),first=fragment.firstChild;if(first){for(var i=0,l=this.length;i1||i>0?fragment.cloneNode(true):fragment)}}if(scripts){jQuery.each(scripts,evalScript)}}return this;function root(elem,cur){return table&&jQuery.nodeName(elem,"table")&&jQuery.nodeName(cur,"tr")?(elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody"))):elem}}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src){jQuery.ajax({url:elem.src,async:false,dataType:"script"})}else{jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"")}if(elem.parentNode){elem.parentNode.removeChild(elem)}}function now(){return +new Date}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(typeof target==="boolean"){deep=target;target=arguments[1]||{};i=2}if(typeof target!=="object"&&!jQuery.isFunction(target)){target={}}if(length==i){target=this;--i}for(;i-1}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name]}callback.call(elem);for(var name in options){elem.style[name]=old[name]}},css:function(elem,name,force,extra){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;if(extra==="border"){return }jQuery.each(which,function(){if(!extra){val-=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0}if(extra==="margin"){val+=parseFloat(jQuery.curCSS(elem,"margin"+this,true))||0}else{val-=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0}})}if(elem.offsetWidth!==0){getWH()}else{jQuery.swap(elem,props,getWH)}return Math.max(0,Math.round(val))}return jQuery.curCSS(elem,name,force)},curCSS:function(elem,name,force){var ret,style=elem.style;if(name=="opacity"&&!jQuery.support.opacity){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret}if(name.match(/float/i)){name=styleFloat}if(!force&&style&&style[name]){ret=style[name]}else{if(defaultView.getComputedStyle){if(name.match(/float/i)){name="float"}name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle){ret=computedStyle.getPropertyValue(name)}if(name=="opacity"&&ret==""){ret="1"}}else{if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase()});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft}}}}return ret},clean:function(elems,context,fragment){context=context||document;if(typeof context.createElement==="undefined"){context=context.ownerDocument||context[0]&&context[0].ownerDocument||document}if(!fragment&&elems.length===1&&typeof elems[0]==="string"){var match=/^<(\w+)\s*\/?>$/.exec(elems[0]);if(match){return[context.createElement(match[1])]}}var ret=[],scripts=[],div=context.createElement("div");jQuery.each(elems,function(i,elem){if(typeof elem==="number"){elem+=""}if(!elem){return }if(typeof elem==="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">"});var tags=elem.replace(/^\s+/,"").substring(0,10).toLowerCase();var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
    "]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||!jQuery.support.htmlSerialize&&[1,"div
    ","
    "]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--){div=div.lastChild}if(!jQuery.support.tbody){var hasBody=/"&&!hasBody?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j){if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length){tbody[j].parentNode.removeChild(tbody[j])}}}if(!jQuery.support.leadingWhitespace&&/^\s/.test(elem)){div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild)}elem=jQuery.makeArray(div.childNodes)}if(elem.nodeType){ret.push(elem)}else{ret=jQuery.merge(ret,elem)}});if(fragment){for(var i=0;ret[i];i++){if(jQuery.nodeName(ret[i],"script")&&(!ret[i].type||ret[i].type.toLowerCase()==="text/javascript")){scripts.push(ret[i].parentNode?ret[i].parentNode.removeChild(ret[i]):ret[i])}else{if(ret[i].nodeType===1){ret.splice.apply(ret,[i+1,0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))))}fragment.appendChild(ret[i])}}return scripts}return ret},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8){return undefined}var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&elem.parentNode){elem.parentNode.selectedIndex}if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode){throw"type property can't be changed"}elem[name]=value}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name)){return elem.getAttributeNode(name).nodeValue}if(name=="tabIndex"){var attributeNode=elem.getAttributeNode("tabIndex");return attributeNode&&attributeNode.specified?attributeNode.value:elem.nodeName.match(/(button|input|object|select|textarea)/i)?0:elem.nodeName.match(/^(a|area)$/i)&&elem.href?0:undefined}return elem[name]}if(!jQuery.support.style&¬xml&&name=="style"){return jQuery.attr(elem.style,"cssText",value)}if(set){elem.setAttribute(name,""+value)}var attr=!jQuery.support.hrefNormalized&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr}if(!jQuery.support.opacity&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+""=="NaN"?"":"alpha(opacity="+value*100+")")}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase()});if(set){elem[name]=value}return elem[name]},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"")},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||typeof array==="string"||jQuery.isFunction(array)||array.setInterval){ret[0]=array}else{while(i){ret[--i]=array[i]}}}return ret},inArray:function(elem,array){for(var i=0,length=array.length;i0?this.clone(true):this).get();jQuery.fn[original].apply(jQuery(insert[i]),elems);ret=ret.concat(elems)}return this.pushStack(ret,name,selector)}});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1){this.removeAttribute(name)}},addClass:function(classNames){jQuery.className.add(this,classNames)},removeClass:function(classNames){jQuery.className.remove(this,classNames)},toggleClass:function(classNames,state){if(typeof state!=="boolean"){state=!jQuery.className.has(this,classNames)}jQuery.className[state?"add":"remove"](this,classNames)},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).length){jQuery("*",this).add([this]).each(function(){jQuery.event.remove(this);jQuery.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){jQuery(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments)}});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0}var expando="jQuery"+now(),uuid=0,windowData={};jQuery.extend({cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id){id=elem[expando]=++uuid}if(name&&!jQuery.cache[id]){jQuery.cache[id]={}}if(data!==undefined){jQuery.cache[id][name]=data}return name?jQuery.cache[id][name]:id},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id]){break}if(!name){jQuery.removeData(elem)}}}else{try{delete elem[expando]}catch(e){if(elem.removeAttribute){elem.removeAttribute(expando)}}delete jQuery.cache[id]}},queue:function(elem,type,data){if(elem){type=(type||"fx")+"queue";var q=jQuery.data(elem,type);if(!q||jQuery.isArray(data)){q=jQuery.data(elem,type,jQuery.makeArray(data))}else{if(data){q.push(data)}}}return q},dequeue:function(elem,type){var queue=jQuery.queue(elem,type),fn=queue.shift();if(!type||type==="fx"){fn=queue[0]}if(fn!==undefined){fn.call(elem)}}});jQuery.fn.extend({data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length){data=jQuery.data(this[0],key)}return data===undefined&&parts[1]?this.data(parts[0]):data}else{return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value)})}},removeData:function(key){return this.each(function(){jQuery.removeData(this,key)})},queue:function(type,data){if(typeof type!=="string"){data=type;type="fx"}if(data===undefined){return jQuery.queue(this[0],type)}return this.each(function(){var queue=jQuery.queue(this,type,data);if(type=="fx"&&queue.length==1){queue[0].call(this)}})},dequeue:function(type){return this.each(function(){jQuery.dequeue(this,type)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var chunker=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,done=0,toString=Object.prototype.toString;var Sizzle=function(selector,context,results,seed){results=results||[];context=context||document;if(context.nodeType!==1&&context.nodeType!==9){return[]}if(!selector||typeof selector!=="string"){return results}var parts=[],m,set,checkSet,check,mode,extra,prune=true;chunker.lastIndex=0;while((m=chunker.exec(selector))!==null){parts.push(m[1]);if(m[2]){extra=RegExp.rightContext;break}}if(parts.length>1&&origPOS.exec(selector)){if(parts.length===2&&Expr.relative[parts[0]]){set=posProcess(parts[0]+parts[1],context)}else{set=Expr.relative[parts[0]]?[context]:Sizzle(parts.shift(),context);while(parts.length){selector=parts.shift();if(Expr.relative[selector]){selector+=parts.shift()}set=posProcess(selector,set)}}}else{var ret=seed?{expr:parts.pop(),set:makeArray(seed)}:Sizzle.find(parts.pop(),parts.length===1&&context.parentNode?context.parentNode:context,isXML(context));set=Sizzle.filter(ret.expr,ret.set);if(parts.length>0){checkSet=makeArray(set)}else{prune=false}while(parts.length){var cur=parts.pop(),pop=cur;if(!Expr.relative[cur]){cur=""}else{pop=parts.pop()}if(pop==null){pop=context}Expr.relative[cur](checkSet,pop,isXML(context))}}if(!checkSet){checkSet=set}if(!checkSet){throw"Syntax error, unrecognized expression: "+(cur||selector)}if(toString.call(checkSet)==="[object Array]"){if(!prune){results.push.apply(results,checkSet)}else{if(context.nodeType===1){for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&(checkSet[i]===true||checkSet[i].nodeType===1&&contains(context,checkSet[i]))){results.push(set[i])}}}else{for(var i=0;checkSet[i]!=null;i++){if(checkSet[i]&&checkSet[i].nodeType===1){results.push(set[i])}}}}}else{makeArray(checkSet,results)}if(extra){Sizzle(extra,context,results,seed);if(sortOrder){hasDuplicate=false;results.sort(sortOrder);if(hasDuplicate){for(var i=1;i":function(checkSet,part,isXML){var isPartStr=typeof part==="string";if(isPartStr&&!/\W/.test(part)){part=isXML?part:part.toUpperCase();for(var i=0,l=checkSet.length;i=0)){if(!inplace){result.push(elem)}}else{if(inplace){curLoop[i]=false}}}}return false},ID:function(match){return match[1].replace(/\\/g,"")},TAG:function(match,curLoop){for(var i=0;curLoop[i]===false;i++){}return curLoop[i]&&isXML(curLoop[i])?match[1]:match[1].toUpperCase()},CHILD:function(match){if(match[1]=="nth"){var test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(match[2]=="even"&&"2n"||match[2]=="odd"&&"2n+1"||!/\D/.test(match[2])&&"0n+"+match[2]||match[2]);match[2]=(test[1]+(test[2]||1))-0;match[3]=test[3]-0}match[0]=done++;return match},ATTR:function(match,curLoop,inplace,result,not,isXML){var name=match[1].replace(/\\/g,"");if(!isXML&&Expr.attrMap[name]){match[1]=Expr.attrMap[name]}if(match[2]==="~="){match[4]=" "+match[4]+" "}return match},PSEUDO:function(match,curLoop,inplace,result,not){if(match[1]==="not"){if(match[3].match(chunker).length>1||/^\w/.test(match[3])){match[3]=Sizzle(match[3],null,null,curLoop)}else{var ret=Sizzle.filter(match[3],curLoop,inplace,true^not);if(!inplace){result.push.apply(result,ret)}return false}}else{if(Expr.match.POS.test(match[0])||Expr.match.CHILD.test(match[0])){return true}}return match},POS:function(match){match.unshift(true);return match}},filters:{enabled:function(elem){return elem.disabled===false&&elem.type!=="hidden"},disabled:function(elem){return elem.disabled===true},checked:function(elem){return elem.checked===true},selected:function(elem){elem.parentNode.selectedIndex;return elem.selected===true},parent:function(elem){return !!elem.firstChild},empty:function(elem){return !elem.firstChild},has:function(elem,i,match){return !!Sizzle(match[3],elem).length},header:function(elem){return/h\d/i.test(elem.nodeName)},text:function(elem){return"text"===elem.type},radio:function(elem){return"radio"===elem.type},checkbox:function(elem){return"checkbox"===elem.type},file:function(elem){return"file"===elem.type},password:function(elem){return"password"===elem.type},submit:function(elem){return"submit"===elem.type},image:function(elem){return"image"===elem.type},reset:function(elem){return"reset"===elem.type},button:function(elem){return"button"===elem.type||elem.nodeName.toUpperCase()==="BUTTON"},input:function(elem){return/input|select|textarea|button/i.test(elem.nodeName)}},setFilters:{first:function(elem,i){return i===0},last:function(elem,i,match,array){return i===array.length-1},even:function(elem,i){return i%2===0},odd:function(elem,i){return i%2===1},lt:function(elem,i,match){return imatch[3]-0},nth:function(elem,i,match){return match[3]-0==i},eq:function(elem,i,match){return match[3]-0==i}},filter:{PSEUDO:function(elem,match,i,array){var name=match[1],filter=Expr.filters[name];if(filter){return filter(elem,i,match,array)}else{if(name==="contains"){return(elem.textContent||elem.innerText||"").indexOf(match[3])>=0}else{if(name==="not"){var not=match[3];for(var i=0,l=not.length;i=0)}}},ID:function(elem,match){return elem.nodeType===1&&elem.getAttribute("id")===match},TAG:function(elem,match){return(match==="*"&&elem.nodeType===1)||elem.nodeName===match},CLASS:function(elem,match){return(" "+(elem.className||elem.getAttribute("class"))+" ").indexOf(match)>-1},ATTR:function(elem,match){var name=match[1],result=Expr.attrHandle[name]?Expr.attrHandle[name](elem):elem[name]!=null?elem[name]:elem.getAttribute(name),value=result+"",type=match[2],check=match[4];return result==null?type==="!=":type==="="?value===check:type==="*="?value.indexOf(check)>=0:type==="~="?(" "+value+" ").indexOf(check)>=0:!check?value&&result!==false:type==="!="?value!=check:type==="^="?value.indexOf(check)===0:type==="$="?value.substr(value.length-check.length)===check:type==="|="?value===check||value.substr(0,check.length+1)===check+"-":false},POS:function(elem,match,i,array){var name=match[2],filter=Expr.setFilters[name];if(filter){return filter(elem,i,match,array)}}}};var origPOS=Expr.match.POS;for(var type in Expr.match){Expr.match[type]=RegExp(Expr.match[type].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var makeArray=function(array,results){array=Array.prototype.slice.call(array);if(results){results.push.apply(results,array);return results}return array};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(e){makeArray=function(array,results){var ret=results||[];if(toString.call(array)==="[object Array]"){Array.prototype.push.apply(ret,array)}else{if(typeof array.length==="number"){for(var i=0,l=array.length;i";var root=document.documentElement;root.insertBefore(form,root.firstChild);if(!!document.getElementById(id)){Expr.find.ID=function(match,context,isXML){if(typeof context.getElementById!=="undefined"&&!isXML){var m=context.getElementById(match[1]);return m?m.id===match[1]||typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id").nodeValue===match[1]?[m]:undefined:[]}};Expr.filter.ID=function(elem,match){var node=typeof elem.getAttributeNode!=="undefined"&&elem.getAttributeNode("id");return elem.nodeType===1&&node&&node.nodeValue===match}}root.removeChild(form)})();(function(){var div=document.createElement("div");div.appendChild(document.createComment(""));if(div.getElementsByTagName("*").length>0){Expr.find.TAG=function(match,context){var results=context.getElementsByTagName(match[1]);if(match[1]==="*"){var tmp=[];for(var i=0;results[i];i++){if(results[i].nodeType===1){tmp.push(results[i])}}results=tmp}return results}}div.innerHTML="
    ";if(div.firstChild&&typeof div.firstChild.getAttribute!=="undefined"&&div.firstChild.getAttribute("href")!=="#"){Expr.attrHandle.href=function(elem){return elem.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var oldSizzle=Sizzle,div=document.createElement("div");div.innerHTML="

    ";if(div.querySelectorAll&&div.querySelectorAll(".TEST").length===0){return }Sizzle=function(query,context,extra,seed){context=context||document;if(!seed&&context.nodeType===9&&!isXML(context)){try{return makeArray(context.querySelectorAll(query),extra)}catch(e){}}return oldSizzle(query,context,extra,seed)};Sizzle.find=oldSizzle.find;Sizzle.filter=oldSizzle.filter;Sizzle.selectors=oldSizzle.selectors;Sizzle.matches=oldSizzle.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var div=document.createElement("div");div.innerHTML="
    ";if(div.getElementsByClassName("e").length===0){return }div.lastChild.className="e";if(div.getElementsByClassName("e").length===1){return }Expr.order.splice(1,0,"CLASS");Expr.find.CLASS=function(match,context,isXML){if(typeof context.getElementsByClassName!=="undefined"&&!isXML){return context.getElementsByClassName(match[1])}}})()}function dirNodeCheck(dir,cur,doneName,checkSet,nodeCheck,isXML){var sibDir=dir=="previousSibling"&&!isXML;for(var i=0,l=checkSet.length;i0){match=elem;break}}}elem=elem[dir]}checkSet[i]=match}}}var contains=document.compareDocumentPosition?function(a,b){return a.compareDocumentPosition(b)&16}:function(a,b){return a!==b&&(a.contains?a.contains(b):true)};var isXML=function(elem){return elem.nodeType===9&&elem.documentElement.nodeName!=="HTML"||!!elem.ownerDocument&&isXML(elem.ownerDocument)};var posProcess=function(selector,context){var tmpSet=[],later="",match,root=context.nodeType?[context]:context;while((match=Expr.match.PSEUDO.exec(selector))){later+=match[0];selector=selector.replace(Expr.match.PSEUDO,"")}selector=Expr.relative[selector]?selector+"*":selector;for(var i=0,l=root.length;i0||elem.offsetHeight>0};Sizzle.selectors.filters.animated=function(elem){return jQuery.grep(jQuery.timers,function(fn){return elem===fn.elem}).length};jQuery.multiFilter=function(expr,elems,not){if(not){expr=":not("+expr+")"}return Sizzle.matches(expr,elems)};jQuery.dir=function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1){matched.push(cur)}cur=cur[dir]}return matched};jQuery.nth=function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir]){if(cur.nodeType==1&&++num==result){break}}return cur};jQuery.sibling=function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem){r.push(n)}}return r};return ;window.Sizzle=Sizzle})();jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8){return }if(elem.setInterval&&elem!=window){elem=window}if(!handler.guid){handler.guid=this.guid++}if(data!==undefined){var fn=handler;handler=this.proxy(fn);handler.data=data}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){return typeof jQuery!=="undefined"&&!jQuery.event.triggered?jQuery.event.handle.apply(arguments.callee.elem,arguments):undefined});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var namespaces=type.split(".");type=namespaces.shift();handler.type=namespaces.slice().sort().join(".");var handlers=events[type];if(jQuery.event.specialAll[type]){jQuery.event.specialAll[type].setup.call(elem,data,namespaces)}if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem,data,namespaces)===false){if(elem.addEventListener){elem.addEventListener(type,handle,false)}else{if(elem.attachEvent){elem.attachEvent("on"+type,handle)}}}}handlers[handler.guid]=handler;jQuery.event.global[type]=true});elem=null},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8){return }var events=jQuery.data(elem,"events"),ret,index;if(events){if(types===undefined||(typeof types==="string"&&types.charAt(0)==".")){for(var type in events){this.remove(elem,type+(types||""))}}else{if(types.type){handler=types.handler;types=types.type}jQuery.each(types.split(/\s+/),function(index,type){var namespaces=type.split(".");type=namespaces.shift();var namespace=RegExp("(^|\\.)"+namespaces.slice().sort().join(".*\\.")+"(\\.|$)");if(events[type]){if(handler){delete events[type][handler.guid]}else{for(var handle in events[type]){if(namespace.test(events[type][handle].type)){delete events[type][handle]}}}if(jQuery.event.specialAll[type]){jQuery.event.specialAll[type].teardown.call(elem,namespaces)}for(ret in events[type]){break}if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem,namespaces)===false){if(elem.removeEventListener){elem.removeEventListener(type,jQuery.data(elem,"handle"),false)}else{if(elem.detachEvent){elem.detachEvent("on"+type,jQuery.data(elem,"handle"))}}}ret=null;delete events[type]}}})}for(ret in events){break}if(!ret){var handle=jQuery.data(elem,"handle");if(handle){handle.elem=null}jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle")}}},trigger:function(event,data,elem,bubbling){var type=event.type||event;if(!bubbling){event=typeof event==="object"?event[expando]?event:jQuery.extend(jQuery.Event(type),event):jQuery.Event(type);if(type.indexOf("!")>=0){event.type=type=type.slice(0,-1);event.exclusive=true}if(!elem){event.stopPropagation();if(this.global[type]){jQuery.each(jQuery.cache,function(){if(this.events&&this.events[type]){jQuery.event.trigger(event,data,this.handle.elem)}})}}if(!elem||elem.nodeType==3||elem.nodeType==8){return undefined}event.result=undefined;event.target=elem;data=jQuery.makeArray(data);data.unshift(event)}event.currentTarget=elem;var handle=jQuery.data(elem,"handle");if(handle){handle.apply(elem,data)}if((!elem[type]||(jQuery.nodeName(elem,"a")&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false){event.result=false}if(!bubbling&&elem[type]&&!event.isDefaultPrevented()&&!(jQuery.nodeName(elem,"a")&&type=="click")){this.triggered=true;try{elem[type]()}catch(e){}}this.triggered=false;if(!event.isPropagationStopped()){var parent=elem.parentNode||elem.ownerDocument;if(parent){jQuery.event.trigger(event,data,parent,true)}}},handle:function(event){var all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);event.currentTarget=this;var namespaces=event.type.split(".");event.type=namespaces.shift();all=!namespaces.length&&!event.exclusive;var namespace=RegExp("(^|\\.)"+namespaces.slice().sort().join(".*\\.")+"(\\.|$)");handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||namespace.test(handler.type)){event.handler=handler;event.data=handler.data;var ret=handler.apply(this,arguments);if(ret!==undefined){event.result=ret;if(ret===false){event.preventDefault();event.stopPropagation()}}if(event.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(event){if(event[expando]){return event}var originalEvent=event;event=jQuery.Event(originalEvent);for(var i=this.props.length,prop;i;){prop=this.props[--i];event[prop]=originalEvent[prop]}if(!event.target){event.target=event.srcElement||document}if(event.target.nodeType==3){event.target=event.target.parentNode}if(!event.relatedTarget&&event.fromElement){event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement}if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0)}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode)){event.which=event.charCode||event.keyCode}if(!event.metaKey&&event.ctrlKey){event.metaKey=event.ctrlKey}if(!event.which&&event.button){event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)))}return event},proxy:function(fn,proxy){proxy=proxy||function(){return fn.apply(this,arguments)};proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy},special:{ready:{setup:bindReady,teardown:function(){}}},specialAll:{live:{setup:function(selector,namespaces){jQuery.event.add(this,namespaces[0],liveHandler)},teardown:function(namespaces){if(namespaces.length){var remove=0,name=RegExp("(^|\\.)"+namespaces[0]+"(\\.|$)");jQuery.each((jQuery.data(this,"events").live||{}),function(){if(name.test(this.type)){remove++}});if(remove<1){jQuery.event.remove(this,namespaces[0],liveHandler)}}}}}};jQuery.Event=function(src){if(!this.preventDefault){return new jQuery.Event(src)}if(src&&src.type){this.originalEvent=src;this.type=src.type}else{this.type=src}this.timeStamp=now();this[expando]=true};function returnFalse(){return false}function returnTrue(){return true}jQuery.Event.prototype={preventDefault:function(){this.isDefaultPrevented=returnTrue;var e=this.originalEvent;if(!e){return }if(e.preventDefault){e.preventDefault()}e.returnValue=false},stopPropagation:function(){this.isPropagationStopped=returnTrue;var e=this.originalEvent;if(!e){return }if(e.stopPropagation){e.stopPropagation()}e.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=returnTrue;this.stopPropagation()},isDefaultPrevented:returnFalse,isPropagationStopped:returnFalse,isImmediatePropagationStopped:returnFalse};var withinElement=function(event){var parent=event.relatedTarget;while(parent&&parent!=this){try{parent=parent.parentNode}catch(e){parent=this}}if(parent!=this){event.type=event.data;jQuery.event.handle.apply(this,arguments)}};jQuery.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(orig,fix){jQuery.event.special[fix]={setup:function(){jQuery.event.add(this,orig,withinElement,fix)},teardown:function(){jQuery.event.remove(this,orig,withinElement)}}});jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data)})},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments)});return this.each(function(){jQuery.event.add(this,type,one,fn&&data)})},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn)})},trigger:function(type,data){return this.each(function(){jQuery.event.trigger(type,data,this)})},triggerHandler:function(type,data){if(this[0]){var event=jQuery.Event(type);event.preventDefault();event.stopPropagation();jQuery.event.trigger(event,data,this[0]);return event.result}},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off)}var type="GET";if(params){if(jQuery.isFunction(params)){callback=params;params=null}else{if(typeof params==="object"){params=jQuery.param(params);type="POST"}}}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified"){self.html(selector?jQuery("
    ").append(res.responseText.replace(//g,"")).find(selector):res.responseText)}if(callback){self.each(callback,[res.responseText,status,res])}}});return this},serialize:function(){return jQuery.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?jQuery.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val,i){return{name:elem.name,value:val}}):{name:elem.name,value:val}}).get()}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f)}});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type})},getScript:function(url,callback){return jQuery.get(url,null,callback,"script")},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json")},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={}}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type})},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!=="string"){s.data=jQuery.param(s.data)}if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre)){s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?"}}else{if(!s.data||!s.data.match(jsre)){s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?"}}s.dataType="json"}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data){s.data=(s.data+"").replace(jsre,"="+jsonp+"$1")}s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp]}catch(e){}if(head){head.removeChild(script)}}}if(s.dataType=="script"&&s.cache==null){s.cache=false}if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"")}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null}if(s.global&&!jQuery.active++){jQuery.event.trigger("ajaxStart")}var parts=/^(\w+:)?\/\/([^\/?#]+)/.exec(s.url);if(s.dataType=="script"&&type=="GET"&&parts&&(parts[1]&&parts[1]!=location.protocol||parts[2]!=location.host)){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset){script.charset=s.scriptCharset}if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();script.onload=script.onreadystatechange=null;head.removeChild(script)}}}head.appendChild(script);return undefined}var requestDone=false;var xhr=s.xhr();if(s.username){xhr.open(type,s.url,s.async,s.username,s.password)}else{xhr.open(type,s.url,s.async)}try{if(s.data){xhr.setRequestHeader("Content-Type",s.contentType)}if(s.ifModified){xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default)}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){if(s.global&&!--jQuery.active){jQuery.event.trigger("ajaxStop")}xhr.abort();return false}if(s.global){jQuery.event.trigger("ajaxSend",[xhr,s])}var onreadystatechange=function(isTimeout){if(xhr.readyState==0){if(ival){clearInterval(ival);ival=null;if(s.global&&!--jQuery.active){jQuery.event.trigger("ajaxStop")}}}else{if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null}status=isTimeout=="timeout"?"timeout":!jQuery.httpSuccess(xhr)?"error":s.ifModified&&jQuery.httpNotModified(xhr,s.url)?"notmodified":"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s)}catch(e){status="parsererror"}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified")}catch(e){}if(s.ifModified&&modRes){jQuery.lastModified[s.url]=modRes}if(!jsonp){success()}}else{jQuery.handleError(s,xhr,status)}complete();if(isTimeout){xhr.abort()}if(s.async){xhr=null}}}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0){setTimeout(function(){if(xhr&&!requestDone){onreadystatechange("timeout")}},s.timeout)}}try{xhr.send(s.data)}catch(e){jQuery.handleError(s,xhr,null,e)}if(!s.async){onreadystatechange()}function success(){if(s.success){s.success(data,status)}if(s.global){jQuery.event.trigger("ajaxSuccess",[xhr,s])}}function complete(){if(s.complete){s.complete(xhr,status)}if(s.global){jQuery.event.trigger("ajaxComplete",[xhr,s])}if(s.global&&!--jQuery.active){jQuery.event.trigger("ajaxStop")}}return xhr},handleError:function(s,xhr,status,e){if(s.error){s.error(xhr,status,e)}if(s.global){jQuery.event.trigger("ajaxError",[xhr,s,e])}},active:0,httpSuccess:function(xhr){try{return !xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223}catch(e){}return false},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]}catch(e){}return false},httpData:function(xhr,type,s){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror"){throw"parsererror"}if(s&&s.dataFilter){data=s.dataFilter(data,type)}if(typeof data==="string"){if(type=="script"){jQuery.globalEval(data)}if(type=="json"){data=window.eval("("+data+")")}}return data},param:function(a){var s=[];function add(key,value){s[s.length]=encodeURIComponent(key)+"="+encodeURIComponent(value)}if(jQuery.isArray(a)||a.jquery){jQuery.each(a,function(){add(this.name,this.value)})}else{for(var j in a){if(jQuery.isArray(a[j])){jQuery.each(a[j],function(){add(j,this)})}else{add(j,jQuery.isFunction(a[j])?a[j]():a[j])}}}return s.join("&").replace(/%20/g,"+")}});var elemdisplay={},timerId,fxAttrs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function genFx(type,num){var obj={};jQuery.each(fxAttrs.concat.apply([],fxAttrs.slice(0,num)),function(){obj[this]=type});return obj}jQuery.fn.extend({show:function(speed,callback){if(speed){return this.animate(genFx("show",3),speed,callback)}else{for(var i=0,l=this.length;i").appendTo("body");display=elem.css("display");if(display==="none"){display="block"}elem.remove();elemdisplay[tagName]=display}jQuery.data(this[i],"olddisplay",display)}}for(var i=0,l=this.length;i=0;i--){if(timers[i].elem==this){if(gotoEnd){timers[i](true)}timers.splice(i,1)}}});if(!gotoEnd){this.dequeue()}return this}});jQuery.each({slideDown:genFx("show",1),slideUp:genFx("hide",1),slideToggle:genFx("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(name,props){jQuery.fn[name]=function(speed,callback){return this.animate(props,speed,callback)}});jQuery.extend({speed:function(speed,easing,fn){var opt=typeof speed==="object"?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&!jQuery.isFunction(easing)&&easing};opt.duration=jQuery.fx.off?0:typeof opt.duration==="number"?opt.duration:jQuery.fx.speeds[opt.duration]||jQuery.fx.speeds._default;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false){jQuery(this).dequeue()}if(jQuery.isFunction(opt.old)){opt.old.call(this)}};return opt},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum}},timers:[],fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig){options.orig={}}}});jQuery.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(force){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;var self=this;function t(gotoEnd){return self.step(gotoEnd)}t.elem=this.elem;if(t()&&jQuery.timers.push(t)&&!timerId){timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim){if(this.options.curAnim[i]!==true){done=false}}if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){jQuery(this.elem).hide()}if(this.options.hide||this.options.show){for(var p in this.options.curAnim){jQuery.attr(this.elem.style,p,this.options.orig[p])}}this.options.complete.call(this.elem)}return false}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now)},_default:function(fx){if(fx.elem.style&&fx.elem.style[fx.prop]!=null){fx.elem.style[fx.prop]=fx.now+fx.unit}else{fx.elem[fx.prop]=fx.now}}}});if(document.documentElement.getBoundingClientRect){jQuery.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return jQuery.offset.bodyOffset(this[0])}var box=this[0].getBoundingClientRect(),doc=this[0].ownerDocument,body=doc.body,docElem=doc.documentElement,clientTop=docElem.clientTop||body.clientTop||0,clientLeft=docElem.clientLeft||body.clientLeft||0,top=box.top+(self.pageYOffset||jQuery.boxModel&&docElem.scrollTop||body.scrollTop)-clientTop,left=box.left+(self.pageXOffset||jQuery.boxModel&&docElem.scrollLeft||body.scrollLeft)-clientLeft;return{top:top,left:left}}}else{jQuery.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return jQuery.offset.bodyOffset(this[0])}jQuery.offset.initialized||jQuery.offset.initialize();var elem=this[0],offsetParent=elem.offsetParent,prevOffsetParent=elem,doc=elem.ownerDocument,computedStyle,docElem=doc.documentElement,body=doc.body,defaultView=doc.defaultView,prevComputedStyle=defaultView.getComputedStyle(elem,null),top=elem.offsetTop,left=elem.offsetLeft;while((elem=elem.parentNode)&&elem!==body&&elem!==docElem){computedStyle=defaultView.getComputedStyle(elem,null);top-=elem.scrollTop,left-=elem.scrollLeft;if(elem===offsetParent){top+=elem.offsetTop,left+=elem.offsetLeft;if(jQuery.offset.doesNotAddBorder&&!(jQuery.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(elem.tagName))){top+=parseInt(computedStyle.borderTopWidth,10)||0,left+=parseInt(computedStyle.borderLeftWidth,10)||0}prevOffsetParent=offsetParent,offsetParent=elem.offsetParent}if(jQuery.offset.subtractsBorderForOverflowNotVisible&&computedStyle.overflow!=="visible"){top+=parseInt(computedStyle.borderTopWidth,10)||0,left+=parseInt(computedStyle.borderLeftWidth,10)||0}prevComputedStyle=computedStyle}if(prevComputedStyle.position==="relative"||prevComputedStyle.position==="static"){top+=body.offsetTop,left+=body.offsetLeft}if(prevComputedStyle.position==="fixed"){top+=Math.max(docElem.scrollTop,body.scrollTop),left+=Math.max(docElem.scrollLeft,body.scrollLeft)}return{top:top,left:left}}}jQuery.offset={initialize:function(){if(this.initialized){return }var body=document.body,container=document.createElement("div"),innerDiv,checkDiv,table,td,rules,prop,bodyMarginTop=body.style.marginTop,html='
    ';rules={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(prop in rules){container.style[prop]=rules[prop]}container.innerHTML=html;body.insertBefore(container,body.firstChild);innerDiv=container.firstChild,checkDiv=innerDiv.firstChild,td=innerDiv.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(checkDiv.offsetTop!==5);this.doesAddBorderForTableAndCells=(td.offsetTop===5);innerDiv.style.overflow="hidden",innerDiv.style.position="relative";this.subtractsBorderForOverflowNotVisible=(checkDiv.offsetTop===-5);body.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(body.offsetTop===0);body.style.marginTop=bodyMarginTop;body.removeChild(container);this.initialized=true},bodyOffset:function(body){jQuery.offset.initialized||jQuery.offset.initialize();var top=body.offsetTop,left=body.offsetLeft;if(jQuery.offset.doesNotIncludeMarginInBodyOffset){top+=parseInt(jQuery.curCSS(body,"marginTop",true),10)||0,left+=parseInt(jQuery.curCSS(body,"marginLeft",true),10)||0}return{top:top,left:left}}};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,"marginTop");offset.left-=num(this,"marginLeft");parentOffset.top+=num(offsetParent,"borderTopWidth");parentOffset.left+=num(offsetParent,"borderLeftWidth");results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left}}return results},offsetParent:function(){var offsetParent=this[0].offsetParent||document.body;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,"position")=="static")){offsetParent=offsetParent.offsetParent}return jQuery(offsetParent)}});jQuery.each(["Left","Top"],function(i,name){var method="scroll"+name;jQuery.fn[method]=function(val){if(!this[0]){return null}return val!==undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val}):this[0]==window||this[0]==document?self[i?"pageYOffset":"pageXOffset"]||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method]}});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom",lower=name.toLowerCase();jQuery.fn["inner"+name]=function(){return this[0]?jQuery.css(this[0],lower,false,"padding"):null};jQuery.fn["outer"+name]=function(margin){return this[0]?jQuery.css(this[0],lower,false,margin?"margin":"border"):null};var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(document.documentElement["client"+name],document.body["scroll"+name],document.documentElement["scroll"+name],document.body["offset"+name],document.documentElement["offset"+name]):size===undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,typeof size==="string"?size:size+"px")}})})(); \ No newline at end of file diff --git a/jscripts/infusion/lib/jquery/plugins/bgiframe/js/jquery.bgiframe.js b/jscripts/infusion/lib/jquery/plugins/bgiframe/js/jquery.bgiframe.js new file mode 100644 index 000000000..b3f29699a --- /dev/null +++ b/jscripts/infusion/lib/jquery/plugins/bgiframe/js/jquery.bgiframe.js @@ -0,0 +1 @@ +(function($){$.fn.bgIframe=$.fn.bgiframe=function(s){if($.browser.msie&&parseInt($.browser.version)<=6){s=$.extend({top:"auto",left:"auto",width:"auto",height:"auto",opacity:true,src:"javascript:false;"},s||{});var prop=function(n){return n&&n.constructor==Number?n+"px":n},html=''; + ifr = document.getElementById('iframe'); + doc = ifr.contentWindow.document; + + // Force absolute CSS urls + css = [ed.baseURI.toAbsolute("themes/" + ed.settings.theme + "/skins/" + ed.settings.skin + "/content.css")]; + css = css.concat(tinymce.explode(ed.settings.content_css) || []); + tinymce.each(css, function(u) { + cssHTML += ''; + }); + + // Write content into iframe + doc.open(); + doc.write('' + cssHTML + ''); + doc.close(); + + doc.designMode = 'on'; + this.resize(); + + window.setTimeout(function() { + ifr.contentWindow.focus(); + }, 10); + }, + + insert : function() { + var h = document.getElementById('iframe').contentWindow.document.body.innerHTML; + + tinyMCEPopup.editor.execCommand('mceInsertClipboardContent', false, {content : h, wordContent : true}); + tinyMCEPopup.close(); + }, + + resize : function() { + var vp = tinyMCEPopup.dom.getViewPort(window), el; + + el = document.getElementById('iframe'); + + if (el) { + el.style.width = (vp.w - 20) + 'px'; + el.style.height = (vp.h - 90) + 'px'; + } + } +}; + +tinyMCEPopup.onInit.add(PasteWordDialog.init, PasteWordDialog); diff --git a/jscripts/tiny_mce/plugins/paste/langs/en_dlg.js b/jscripts/tiny_mce/plugins/paste/langs/en_dlg.js new file mode 100644 index 000000000..eeac77896 --- /dev/null +++ b/jscripts/tiny_mce/plugins/paste/langs/en_dlg.js @@ -0,0 +1,5 @@ +tinyMCE.addI18n('en.paste_dlg',{ +text_title:"Use CTRL+V on your keyboard to paste the text into the window.", +text_linebreaks:"Keep linebreaks", +word_title:"Use CTRL+V on your keyboard to paste the text into the window." +}); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/paste/pastetext.htm b/jscripts/tiny_mce/plugins/paste/pastetext.htm new file mode 100644 index 000000000..b65594547 --- /dev/null +++ b/jscripts/tiny_mce/plugins/paste/pastetext.htm @@ -0,0 +1,27 @@ + + + {#paste.paste_text_desc} + + + + +
    +
    {#paste.paste_text_desc}
    + +
    + +
    + +
    + +
    {#paste_dlg.text_title}
    + + + +
    + + +
    +
    + + \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/paste/pasteword.htm b/jscripts/tiny_mce/plugins/paste/pasteword.htm new file mode 100644 index 000000000..0f6bb4121 --- /dev/null +++ b/jscripts/tiny_mce/plugins/paste/pasteword.htm @@ -0,0 +1,21 @@ + + + {#paste.paste_word_desc} + + + + +
    +
    {#paste.paste_word_desc}
    + +
    {#paste_dlg.word_title}
    + +
    + +
    + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/preview/editor_plugin.js b/jscripts/tiny_mce/plugins/preview/editor_plugin.js new file mode 100644 index 000000000..507909c5f --- /dev/null +++ b/jscripts/tiny_mce/plugins/preview/editor_plugin.js @@ -0,0 +1 @@ +(function(){tinymce.create("tinymce.plugins.Preview",{init:function(a,b){var d=this,c=tinymce.explode(a.settings.content_css);d.editor=a;tinymce.each(c,function(f,e){c[e]=a.documentBaseURI.toAbsolute(f)});a.addCommand("mcePreview",function(){a.windowManager.open({file:a.getParam("plugin_preview_pageurl",b+"/preview.html"),width:parseInt(a.getParam("plugin_preview_width","550")),height:parseInt(a.getParam("plugin_preview_height","600")),resizable:"yes",scrollbars:"yes",popup_css:c?c.join(","):a.baseURI.toAbsolute("themes/"+a.settings.theme+"/skins/"+a.settings.skin+"/content.css"),inline:a.getParam("plugin_preview_inline",1)},{base:a.documentBaseURI.getURI()})});a.addButton("preview",{title:"preview.preview_desc",cmd:"mcePreview"})},getInfo:function(){return{longname:"Preview",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/preview",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("preview",tinymce.plugins.Preview)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/preview/editor_plugin_src.js b/jscripts/tiny_mce/plugins/preview/editor_plugin_src.js new file mode 100644 index 000000000..80f00f0d9 --- /dev/null +++ b/jscripts/tiny_mce/plugins/preview/editor_plugin_src.js @@ -0,0 +1,53 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + tinymce.create('tinymce.plugins.Preview', { + init : function(ed, url) { + var t = this, css = tinymce.explode(ed.settings.content_css); + + t.editor = ed; + + // Force absolute CSS urls + tinymce.each(css, function(u, k) { + css[k] = ed.documentBaseURI.toAbsolute(u); + }); + + ed.addCommand('mcePreview', function() { + ed.windowManager.open({ + file : ed.getParam("plugin_preview_pageurl", url + "/preview.html"), + width : parseInt(ed.getParam("plugin_preview_width", "550")), + height : parseInt(ed.getParam("plugin_preview_height", "600")), + resizable : "yes", + scrollbars : "yes", + popup_css : css ? css.join(',') : ed.baseURI.toAbsolute("themes/" + ed.settings.theme + "/skins/" + ed.settings.skin + "/content.css"), + inline : ed.getParam("plugin_preview_inline", 1) + }, { + base : ed.documentBaseURI.getURI() + }); + }); + + ed.addButton('preview', {title : 'preview.preview_desc', cmd : 'mcePreview'}); + }, + + getInfo : function() { + return { + longname : 'Preview', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/preview', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('preview', tinymce.plugins.Preview); +})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/preview/example.html b/jscripts/tiny_mce/plugins/preview/example.html new file mode 100644 index 000000000..b2c3d90ce --- /dev/null +++ b/jscripts/tiny_mce/plugins/preview/example.html @@ -0,0 +1,28 @@ + + + + + +Example of a custom preview page + + + +Editor contents:
    +
    + +
    + + + diff --git a/jscripts/tiny_mce/plugins/preview/jscripts/embed.js b/jscripts/tiny_mce/plugins/preview/jscripts/embed.js new file mode 100644 index 000000000..f8dc81052 --- /dev/null +++ b/jscripts/tiny_mce/plugins/preview/jscripts/embed.js @@ -0,0 +1,73 @@ +/** + * This script contains embed functions for common plugins. This scripts are complety free to use for any purpose. + */ + +function writeFlash(p) { + writeEmbed( + 'D27CDB6E-AE6D-11cf-96B8-444553540000', + 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0', + 'application/x-shockwave-flash', + p + ); +} + +function writeShockWave(p) { + writeEmbed( + '166B1BCA-3F9C-11CF-8075-444553540000', + 'http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0', + 'application/x-director', + p + ); +} + +function writeQuickTime(p) { + writeEmbed( + '02BF25D5-8C17-4B23-BC80-D3488ABDDC6B', + 'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0', + 'video/quicktime', + p + ); +} + +function writeRealMedia(p) { + writeEmbed( + 'CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA', + 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0', + 'audio/x-pn-realaudio-plugin', + p + ); +} + +function writeWindowsMedia(p) { + p.url = p.src; + writeEmbed( + '6BF52A52-394A-11D3-B153-00C04F79FAA6', + 'http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701', + 'application/x-mplayer2', + p + ); +} + +function writeEmbed(cls, cb, mt, p) { + var h = '', n; + + h += ''; + + h += ' + + + + + +{#preview.preview_desc} + + + + + diff --git a/jscripts/tiny_mce/plugins/print/editor_plugin.js b/jscripts/tiny_mce/plugins/print/editor_plugin.js new file mode 100644 index 000000000..b5b3a55ed --- /dev/null +++ b/jscripts/tiny_mce/plugins/print/editor_plugin.js @@ -0,0 +1 @@ +(function(){tinymce.create("tinymce.plugins.Print",{init:function(a,b){a.addCommand("mcePrint",function(){a.getWin().print()});a.addButton("print",{title:"print.print_desc",cmd:"mcePrint"})},getInfo:function(){return{longname:"Print",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/print",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("print",tinymce.plugins.Print)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/print/editor_plugin_src.js b/jscripts/tiny_mce/plugins/print/editor_plugin_src.js new file mode 100644 index 000000000..3933fe656 --- /dev/null +++ b/jscripts/tiny_mce/plugins/print/editor_plugin_src.js @@ -0,0 +1,34 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + tinymce.create('tinymce.plugins.Print', { + init : function(ed, url) { + ed.addCommand('mcePrint', function() { + ed.getWin().print(); + }); + + ed.addButton('print', {title : 'print.print_desc', cmd : 'mcePrint'}); + }, + + getInfo : function() { + return { + longname : 'Print', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/print', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('print', tinymce.plugins.Print); +})(); diff --git a/jscripts/tiny_mce/plugins/safari/blank.htm b/jscripts/tiny_mce/plugins/safari/blank.htm new file mode 100644 index 000000000..266808ce2 --- /dev/null +++ b/jscripts/tiny_mce/plugins/safari/blank.htm @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/safari/editor_plugin.js b/jscripts/tiny_mce/plugins/safari/editor_plugin.js new file mode 100644 index 000000000..794477c95 --- /dev/null +++ b/jscripts/tiny_mce/plugins/safari/editor_plugin.js @@ -0,0 +1 @@ +(function(){var a=tinymce.dom.Event,c=tinymce.grep,d=tinymce.each,b=tinymce.inArray;function e(j,i,h){var g,k;g=j.createTreeWalker(i,NodeFilter.SHOW_ALL,null,false);while(k=g.nextNode()){if(h){if(!h(k)){return false}}if(k.nodeType==3&&k.nodeValue&&/[^\s\u00a0]+/.test(k.nodeValue)){return false}if(k.nodeType==1&&/^(HR|IMG|TABLE)$/.test(k.nodeName)){return false}}return true}tinymce.create("tinymce.plugins.Safari",{init:function(f){var g=this,h;if(!tinymce.isWebKit){return}g.editor=f;g.webKitFontSizes=["x-small","small","medium","large","x-large","xx-large","-webkit-xxx-large"];g.namedFontSizes=["xx-small","x-small","small","medium","large","x-large","xx-large"];f.addCommand("CreateLink",function(k,j){var m=f.selection.getNode(),l=f.dom,i;if(m&&(/^(left|right)$/i.test(l.getStyle(m,"float",1))||/^(left|right)$/i.test(l.getAttrib(m,"align")))){i=l.create("a",{href:j},m.cloneNode());m.parentNode.replaceChild(i,m);f.selection.select(i)}else{f.getDoc().execCommand("CreateLink",false,j)}});f.onKeyUp.add(function(j,o){var l,i,m,p,k;if(o.keyCode==46||o.keyCode==8){i=j.getBody();l=i.innerHTML;k=j.selection;if(i.childNodes.length==1&&!/<(img|hr)/.test(l)&&tinymce.trim(l.replace(/<[^>]+>/g,"")).length==0){j.setContent('


    ',{format:"raw"});p=i.firstChild;m=k.getRng();m.setStart(p,0);m.setEnd(p,0);k.setRng(m)}}});f.addCommand("FormatBlock",function(j,i){var l=f.dom,k=l.getParent(f.selection.getNode(),l.isBlock);if(k){l.replace(l.create(i),k,1)}else{f.getDoc().execCommand("FormatBlock",false,i)}});f.addCommand("mceInsertContent",function(j,i){f.getDoc().execCommand("InsertText",false,"mce_marker");f.getBody().innerHTML=f.getBody().innerHTML.replace(/mce_marker/g,f.dom.processHTML(i)+'XX');f.selection.select(f.dom.get("_mce_tmp"));f.getDoc().execCommand("Delete",false," ")});f.onKeyPress.add(function(o,p){var q,v,r,l,j,k,i,u,m,t,s;if(p.keyCode==13){i=o.selection;q=i.getNode();if(p.shiftKey||o.settings.force_br_newlines&&q.nodeName!="LI"){g._insertBR(o);a.cancel(p)}if(v=h.getParent(q,"LI")){r=h.getParent(v,"OL,UL");u=o.getDoc();s=h.create("p");h.add(s,"br",{mce_bogus:"1"});if(e(u,v)){if(k=h.getParent(r.parentNode,"LI,OL,UL")){return}k=h.getParent(r,"p,h1,h2,h3,h4,h5,h6,div")||r;l=u.createRange();l.setStartBefore(k);l.setEndBefore(v);j=u.createRange();j.setStartAfter(v);j.setEndAfter(k);m=l.cloneContents();t=j.cloneContents();if(!e(u,t)){h.insertAfter(t,k)}h.insertAfter(s,k);if(!e(u,m)){h.insertAfter(m,k)}h.remove(k);k=s.firstChild;l=u.createRange();l.setStartBefore(k);l.setEndBefore(k);i.setRng(l);return a.cancel(p)}}}});f.onExecCommand.add(function(i,k){var j,m,n,l;if(k=="InsertUnorderedList"||k=="InsertOrderedList"){j=i.selection;m=i.dom;if(n=m.getParent(j.getNode(),function(o){return/^(H[1-6]|P|ADDRESS|PRE)$/.test(o.nodeName)})){l=j.getBookmark();m.remove(n,1);j.moveToBookmark(l)}}});f.onClick.add(function(i,j){j=j.target;if(j.nodeName=="IMG"){g.selElm=j;i.selection.select(j)}else{g.selElm=null}});f.onInit.add(function(){g._fixWebKitSpans()});f.onSetContent.add(function(){h=f.dom;d(["strong","b","em","u","strike","sub","sup","a"],function(i){d(c(h.select(i)).reverse(),function(l){var k=l.nodeName.toLowerCase(),j;if(k=="a"){if(l.name){h.replace(h.create("img",{mce_name:"a",name:l.name,"class":"mceItemAnchor"}),l)}return}switch(k){case"b":case"strong":if(k=="b"){k="strong"}j="font-weight: bold;";break;case"em":j="font-style: italic;";break;case"u":j="text-decoration: underline;";break;case"sub":j="vertical-align: sub;";break;case"sup":j="vertical-align: super;";break;case"strike":j="text-decoration: line-through;";break}h.replace(h.create("span",{mce_name:k,style:j,"class":"Apple-style-span"}),l,1)})})});f.onPreProcess.add(function(i,j){h=i.dom;d(c(j.node.getElementsByTagName("span")).reverse(),function(m){var k,l;if(j.get){if(h.hasClass(m,"Apple-style-span")){l=m.style.backgroundColor;switch(h.getAttrib(m,"mce_name")){case"font":if(!i.settings.convert_fonts_to_spans){h.setAttrib(m,"style","")}break;case"strong":case"em":case"sub":case"sup":h.setAttrib(m,"style","");break;case"strike":case"u":if(!i.settings.inline_styles){h.setAttrib(m,"style","")}else{h.setAttrib(m,"mce_name","")}break;default:if(!i.settings.inline_styles){h.setAttrib(m,"style","")}}if(l){m.style.backgroundColor=l}}}if(h.hasClass(m,"mceItemRemoved")){h.remove(m,1)}})});f.onPostProcess.add(function(i,j){j.content=j.content.replace(/
    <\/(h[1-6]|div|p|address|pre)>/g,"");j.content=j.content.replace(/ id=\"undefined\"/g,"")})},getInfo:function(){return{longname:"Safari compatibility",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/safari",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_fixWebKitSpans:function(){var g=this,f=g.editor;a.add(f.getDoc(),"DOMNodeInserted",function(h){h=h.target;if(h&&h.nodeType==1){g._fixAppleSpan(h)}})},_fixAppleSpan:function(l){var g=this.editor,m=g.dom,i=this.webKitFontSizes,f=this.namedFontSizes,j=g.settings,h,k;if(m.getAttrib(l,"mce_fixed")){return}if(l.nodeName=="SPAN"&&l.className=="Apple-style-span"){h=l.style;if(!j.convert_fonts_to_spans){if(h.fontSize){m.setAttrib(l,"mce_name","font");m.setAttrib(l,"size",b(i,h.fontSize)+1)}if(h.fontFamily){m.setAttrib(l,"mce_name","font");m.setAttrib(l,"face",h.fontFamily)}if(h.color){m.setAttrib(l,"mce_name","font");m.setAttrib(l,"color",m.toHex(h.color))}if(h.backgroundColor){m.setAttrib(l,"mce_name","font");m.setStyle(l,"background-color",h.backgroundColor)}}else{if(h.fontSize){m.setStyle(l,"fontSize",f[b(i,h.fontSize)])}}if(h.fontWeight=="bold"){m.setAttrib(l,"mce_name","strong")}if(h.fontStyle=="italic"){m.setAttrib(l,"mce_name","em")}if(h.textDecoration=="underline"){m.setAttrib(l,"mce_name","u")}if(h.textDecoration=="line-through"){m.setAttrib(l,"mce_name","strike")}if(h.verticalAlign=="super"){m.setAttrib(l,"mce_name","sup")}if(h.verticalAlign=="sub"){m.setAttrib(l,"mce_name","sub")}m.setAttrib(l,"mce_fixed","1")}},_insertBR:function(f){var j=f.dom,h=f.selection,i=h.getRng(),g;i.insertNode(g=j.create("br"));i.setStartAfter(g);i.setEndAfter(g);h.setRng(i);if(h.getSel().focusNode==g.previousSibling){h.select(j.insertAfter(j.doc.createTextNode("\u00a0"),g));h.collapse(1)}f.getWin().scrollTo(0,j.getPos(h.getRng().startContainer).y)}});tinymce.PluginManager.add("safari",tinymce.plugins.Safari)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/safari/editor_plugin_src.js b/jscripts/tiny_mce/plugins/safari/editor_plugin_src.js new file mode 100644 index 000000000..6667b7c79 --- /dev/null +++ b/jscripts/tiny_mce/plugins/safari/editor_plugin_src.js @@ -0,0 +1,438 @@ +/** + * $Id: editor_plugin_src.js 264 2007-04-26 20:53:09Z spocke $ + * + * @author Moxiecode + * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. + */ + +(function() { + var Event = tinymce.dom.Event, grep = tinymce.grep, each = tinymce.each, inArray = tinymce.inArray; + + function isEmpty(d, e, f) { + var w, n; + + w = d.createTreeWalker(e, NodeFilter.SHOW_ALL, null, false); + while (n = w.nextNode()) { + // Filter func + if (f) { + if (!f(n)) + return false; + } + + // Non whitespace text node + if (n.nodeType == 3 && n.nodeValue && /[^\s\u00a0]+/.test(n.nodeValue)) + return false; + + // Is non text element byt still content + if (n.nodeType == 1 && /^(HR|IMG|TABLE)$/.test(n.nodeName)) + return false; + } + + return true; + }; + + tinymce.create('tinymce.plugins.Safari', { + init : function(ed) { + var t = this, dom; + + // Ignore on non webkit + if (!tinymce.isWebKit) + return; + + t.editor = ed; + t.webKitFontSizes = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', '-webkit-xxx-large']; + t.namedFontSizes = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large']; + + // Safari CreateLink command will not work correctly on images that is aligned + ed.addCommand('CreateLink', function(u, v) { + var n = ed.selection.getNode(), dom = ed.dom, a; + + if (n && (/^(left|right)$/i.test(dom.getStyle(n, 'float', 1)) || /^(left|right)$/i.test(dom.getAttrib(n, 'align')))) { + a = dom.create('a', {href : v}, n.cloneNode()); + n.parentNode.replaceChild(a, n); + ed.selection.select(a); + } else + ed.getDoc().execCommand("CreateLink", false, v); + }); + +/* + // WebKit generates spans out of thin air this patch used to remove them but it will also remove styles we want so it's disabled for now + ed.onPaste.add(function(ed, e) { + function removeStyles(e) { + e = e.target; + + if (e.nodeType == 1) { + e.style.cssText = ''; + + each(ed.dom.select('*', e), function(e) { + e.style.cssText = ''; + }); + } + }; + + Event.add(ed.getDoc(), 'DOMNodeInserted', removeStyles); + + window.setTimeout(function() { + Event.remove(ed.getDoc(), 'DOMNodeInserted', removeStyles); + }, 0); + }); +*/ + ed.onKeyUp.add(function(ed, e) { + var h, b, r, n, s; + + // If backspace or delete key + if (e.keyCode == 46 || e.keyCode == 8) { + b = ed.getBody(); + h = b.innerHTML; + s = ed.selection; + + // If there is no text content or images or hr elements then remove everything + if (b.childNodes.length == 1 && !/<(img|hr)/.test(h) && tinymce.trim(h.replace(/<[^>]+>/g, '')).length == 0) { + // Inject paragrah and bogus br + ed.setContent('


    ', {format : 'raw'}); + + // Move caret before bogus br + n = b.firstChild; + r = s.getRng(); + r.setStart(n, 0); + r.setEnd(n, 0); + s.setRng(r); + } + } + }); + + // Workaround for FormatBlock bug, http://bugs.webkit.org/show_bug.cgi?id=16004 + ed.addCommand('FormatBlock', function(u, v) { + var dom = ed.dom, e = dom.getParent(ed.selection.getNode(), dom.isBlock); + + if (e) + dom.replace(dom.create(v), e, 1); + else + ed.getDoc().execCommand("FormatBlock", false, v); + }); + + // Workaround for InsertHTML bug, http://bugs.webkit.org/show_bug.cgi?id=16382 + ed.addCommand('mceInsertContent', function(u, v) { + ed.getDoc().execCommand("InsertText", false, 'mce_marker'); + ed.getBody().innerHTML = ed.getBody().innerHTML.replace(/mce_marker/g, ed.dom.processHTML(v) + 'XX'); + ed.selection.select(ed.dom.get('_mce_tmp')); + ed.getDoc().execCommand("Delete", false, ' '); + }); + + /* ed.onKeyDown.add(function(ed, e) { + // Ctrl+A select all will fail on WebKit since if you paste the contents you selected it will produce a odd div wrapper + if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) { + ed.selection.select(ed.getBody(), 1); + return Event.cancel(e); + } + });*/ + + ed.onKeyPress.add(function(ed, e) { + var se, li, lic, r1, r2, n, sel, doc, be, af, pa; + + if (e.keyCode == 13) { + sel = ed.selection; + se = sel.getNode(); + + // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973 + if (e.shiftKey || ed.settings.force_br_newlines && se.nodeName != 'LI') { + t._insertBR(ed); + Event.cancel(e); + } + + // Workaround for DIV elements produced by Safari + if (li = dom.getParent(se, 'LI')) { + lic = dom.getParent(li, 'OL,UL'); + doc = ed.getDoc(); + + pa = dom.create('p'); + dom.add(pa, 'br', {mce_bogus : "1"}); + + if (isEmpty(doc, li)) { + // If list in list then use browser default behavior + if (n = dom.getParent(lic.parentNode, 'LI,OL,UL')) + return; + + n = dom.getParent(lic, 'p,h1,h2,h3,h4,h5,h6,div') || lic; + + // Create range from the start of block element to the list item + r1 = doc.createRange(); + r1.setStartBefore(n); + r1.setEndBefore(li); + + // Create range after the list to the end of block element + r2 = doc.createRange(); + r2.setStartAfter(li); + r2.setEndAfter(n); + + be = r1.cloneContents(); + af = r2.cloneContents(); + + if (!isEmpty(doc, af)) + dom.insertAfter(af, n); + + dom.insertAfter(pa, n); + + if (!isEmpty(doc, be)) + dom.insertAfter(be, n); + + dom.remove(n); + + n = pa.firstChild; + r1 = doc.createRange(); + r1.setStartBefore(n); + r1.setEndBefore(n); + sel.setRng(r1); + + return Event.cancel(e); + } + } + } + }); + + // Safari doesn't place lists outside block elements + ed.onExecCommand.add(function(ed, cmd) { + var sel, dom, bl, bm; + + if (cmd == 'InsertUnorderedList' || cmd == 'InsertOrderedList') { + sel = ed.selection; + dom = ed.dom; + + if (bl = dom.getParent(sel.getNode(), function(n) {return /^(H[1-6]|P|ADDRESS|PRE)$/.test(n.nodeName);})) { + bm = sel.getBookmark(); + dom.remove(bl, 1); + sel.moveToBookmark(bm); + } + } + }); + + // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 + ed.onClick.add(function(ed, e) { + e = e.target; + + if (e.nodeName == 'IMG') { + t.selElm = e; + ed.selection.select(e); + } else + t.selElm = null; + }); + + ed.onInit.add(function() { + t._fixWebKitSpans(); + }); + + ed.onSetContent.add(function() { + dom = ed.dom; + + // Convert strong,b,em,u,strike to spans + each(['strong','b','em','u','strike','sub','sup','a'], function(v) { + each(grep(dom.select(v)).reverse(), function(n) { + var nn = n.nodeName.toLowerCase(), st; + + // Convert anchors into images + if (nn == 'a') { + if (n.name) + dom.replace(dom.create('img', {mce_name : 'a', name : n.name, 'class' : 'mceItemAnchor'}), n); + + return; + } + + switch (nn) { + case 'b': + case 'strong': + if (nn == 'b') + nn = 'strong'; + + st = 'font-weight: bold;'; + break; + + case 'em': + st = 'font-style: italic;'; + break; + + case 'u': + st = 'text-decoration: underline;'; + break; + + case 'sub': + st = 'vertical-align: sub;'; + break; + + case 'sup': + st = 'vertical-align: super;'; + break; + + case 'strike': + st = 'text-decoration: line-through;'; + break; + } + + dom.replace(dom.create('span', {mce_name : nn, style : st, 'class' : 'Apple-style-span'}), n, 1); + }); + }); + }); + + ed.onPreProcess.add(function(ed, o) { + dom = ed.dom; + + each(grep(o.node.getElementsByTagName('span')).reverse(), function(n) { + var v, bg; + + if (o.get) { + if (dom.hasClass(n, 'Apple-style-span')) { + bg = n.style.backgroundColor; + + switch (dom.getAttrib(n, 'mce_name')) { + case 'font': + if (!ed.settings.convert_fonts_to_spans) + dom.setAttrib(n, 'style', ''); + break; + + case 'strong': + case 'em': + case 'sub': + case 'sup': + dom.setAttrib(n, 'style', ''); + break; + + case 'strike': + case 'u': + if (!ed.settings.inline_styles) + dom.setAttrib(n, 'style', ''); + else + dom.setAttrib(n, 'mce_name', ''); + + break; + + default: + if (!ed.settings.inline_styles) + dom.setAttrib(n, 'style', ''); + } + + + if (bg) + n.style.backgroundColor = bg; + } + } + + if (dom.hasClass(n, 'mceItemRemoved')) + dom.remove(n, 1); + }); + }); + + ed.onPostProcess.add(function(ed, o) { + // Safari adds BR at end of all block elements + o.content = o.content.replace(/
    <\/(h[1-6]|div|p|address|pre)>/g, ''); + + // Safari adds id="undefined" to HR elements + o.content = o.content.replace(/ id=\"undefined\"/g, ''); + }); + }, + + getInfo : function() { + return { + longname : 'Safari compatibility', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/safari', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + }, + + // Internal methods + + _fixWebKitSpans : function() { + var t = this, ed = t.editor; + + // Use mutator events on new WebKit + Event.add(ed.getDoc(), 'DOMNodeInserted', function(e) { + e = e.target; + + if (e && e.nodeType == 1) + t._fixAppleSpan(e); + }); + }, + + _fixAppleSpan : function(e) { + var ed = this.editor, dom = ed.dom, fz = this.webKitFontSizes, fzn = this.namedFontSizes, s = ed.settings, st, p; + + if (dom.getAttrib(e, 'mce_fixed')) + return; + + // Handle Apple style spans + if (e.nodeName == 'SPAN' && e.className == 'Apple-style-span') { + st = e.style; + + if (!s.convert_fonts_to_spans) { + if (st.fontSize) { + dom.setAttrib(e, 'mce_name', 'font'); + dom.setAttrib(e, 'size', inArray(fz, st.fontSize) + 1); + } + + if (st.fontFamily) { + dom.setAttrib(e, 'mce_name', 'font'); + dom.setAttrib(e, 'face', st.fontFamily); + } + + if (st.color) { + dom.setAttrib(e, 'mce_name', 'font'); + dom.setAttrib(e, 'color', dom.toHex(st.color)); + } + + if (st.backgroundColor) { + dom.setAttrib(e, 'mce_name', 'font'); + dom.setStyle(e, 'background-color', st.backgroundColor); + } + } else { + if (st.fontSize) + dom.setStyle(e, 'fontSize', fzn[inArray(fz, st.fontSize)]); + } + + if (st.fontWeight == 'bold') + dom.setAttrib(e, 'mce_name', 'strong'); + + if (st.fontStyle == 'italic') + dom.setAttrib(e, 'mce_name', 'em'); + + if (st.textDecoration == 'underline') + dom.setAttrib(e, 'mce_name', 'u'); + + if (st.textDecoration == 'line-through') + dom.setAttrib(e, 'mce_name', 'strike'); + + if (st.verticalAlign == 'super') + dom.setAttrib(e, 'mce_name', 'sup'); + + if (st.verticalAlign == 'sub') + dom.setAttrib(e, 'mce_name', 'sub'); + + dom.setAttrib(e, 'mce_fixed', '1'); + } + }, + + _insertBR : function(ed) { + var dom = ed.dom, s = ed.selection, r = s.getRng(), br; + + // Insert BR element + r.insertNode(br = dom.create('br')); + + // Place caret after BR + r.setStartAfter(br); + r.setEndAfter(br); + s.setRng(r); + + // Could not place caret after BR then insert an nbsp entity and move the caret + if (s.getSel().focusNode == br.previousSibling) { + s.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br)); + s.collapse(1); + } + + // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117 + ed.getWin().scrollTo(0, dom.getPos(s.getRng().startContainer).y); + } + }); + + // Register plugin + tinymce.PluginManager.add('safari', tinymce.plugins.Safari); +})(); + diff --git a/jscripts/tiny_mce/plugins/save/editor_plugin.js b/jscripts/tiny_mce/plugins/save/editor_plugin.js new file mode 100644 index 000000000..8e9399667 --- /dev/null +++ b/jscripts/tiny_mce/plugins/save/editor_plugin.js @@ -0,0 +1 @@ +(function(){tinymce.create("tinymce.plugins.Save",{init:function(a,b){var c=this;c.editor=a;a.addCommand("mceSave",c._save,c);a.addCommand("mceCancel",c._cancel,c);a.addButton("save",{title:"save.save_desc",cmd:"mceSave"});a.addButton("cancel",{title:"save.cancel_desc",cmd:"mceCancel"});a.onNodeChange.add(c._nodeChange,c);a.addShortcut("ctrl+s",a.getLang("save.save_desc"),"mceSave")},getInfo:function(){return{longname:"Save",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/save",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_nodeChange:function(b,a,c){var b=this.editor;if(b.getParam("save_enablewhendirty")){a.setDisabled("save",!b.isDirty());a.setDisabled("cancel",!b.isDirty())}},_save:function(){var c=this.editor,a,e,d,b;a=tinymce.DOM.get(c.id).form||tinymce.DOM.getParent(c.id,"form");if(c.getParam("save_enablewhendirty")&&!c.isDirty()){return}tinyMCE.triggerSave();if(e=c.getParam("save_onsavecallback")){if(c.execCallback("save_onsavecallback",c)){c.startContent=tinymce.trim(c.getContent({format:"raw"}));c.nodeChanged()}return}if(a){c.isNotDirty=true;if(a.onsubmit==null||a.onsubmit()!=false){a.submit()}c.nodeChanged()}else{c.windowManager.alert("Error: No form element found.")}},_cancel:function(){var a=this.editor,c,b=tinymce.trim(a.startContent);if(c=a.getParam("save_oncancelcallback")){a.execCallback("save_oncancelcallback",a);return}a.setContent(b);a.undoManager.clear();a.nodeChanged()}});tinymce.PluginManager.add("save",tinymce.plugins.Save)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/save/editor_plugin_src.js b/jscripts/tiny_mce/plugins/save/editor_plugin_src.js new file mode 100644 index 000000000..f5a3de8f5 --- /dev/null +++ b/jscripts/tiny_mce/plugins/save/editor_plugin_src.js @@ -0,0 +1,101 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + tinymce.create('tinymce.plugins.Save', { + init : function(ed, url) { + var t = this; + + t.editor = ed; + + // Register commands + ed.addCommand('mceSave', t._save, t); + ed.addCommand('mceCancel', t._cancel, t); + + // Register buttons + ed.addButton('save', {title : 'save.save_desc', cmd : 'mceSave'}); + ed.addButton('cancel', {title : 'save.cancel_desc', cmd : 'mceCancel'}); + + ed.onNodeChange.add(t._nodeChange, t); + ed.addShortcut('ctrl+s', ed.getLang('save.save_desc'), 'mceSave'); + }, + + getInfo : function() { + return { + longname : 'Save', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/save', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + }, + + // Private methods + + _nodeChange : function(ed, cm, n) { + var ed = this.editor; + + if (ed.getParam('save_enablewhendirty')) { + cm.setDisabled('save', !ed.isDirty()); + cm.setDisabled('cancel', !ed.isDirty()); + } + }, + + // Private methods + + _save : function() { + var ed = this.editor, formObj, os, i, elementId; + + formObj = tinymce.DOM.get(ed.id).form || tinymce.DOM.getParent(ed.id, 'form'); + + if (ed.getParam("save_enablewhendirty") && !ed.isDirty()) + return; + + tinyMCE.triggerSave(); + + // Use callback instead + if (os = ed.getParam("save_onsavecallback")) { + if (ed.execCallback('save_onsavecallback', ed)) { + ed.startContent = tinymce.trim(ed.getContent({format : 'raw'})); + ed.nodeChanged(); + } + + return; + } + + if (formObj) { + ed.isNotDirty = true; + + if (formObj.onsubmit == null || formObj.onsubmit() != false) + formObj.submit(); + + ed.nodeChanged(); + } else + ed.windowManager.alert("Error: No form element found."); + }, + + _cancel : function() { + var ed = this.editor, os, h = tinymce.trim(ed.startContent); + + // Use callback instead + if (os = ed.getParam("save_oncancelcallback")) { + ed.execCallback('save_oncancelcallback', ed); + return; + } + + ed.setContent(h); + ed.undoManager.clear(); + ed.nodeChanged(); + } + }); + + // Register plugin + tinymce.PluginManager.add('save', tinymce.plugins.Save); +})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/searchreplace/css/searchreplace.css b/jscripts/tiny_mce/plugins/searchreplace/css/searchreplace.css new file mode 100644 index 000000000..ecdf58c7b --- /dev/null +++ b/jscripts/tiny_mce/plugins/searchreplace/css/searchreplace.css @@ -0,0 +1,6 @@ +.panel_wrapper {height:85px;} +.panel_wrapper div.current {height:85px;} + +/* IE */ +* html .panel_wrapper {height:100px;} +* html .panel_wrapper div.current {height:100px;} diff --git a/jscripts/tiny_mce/plugins/searchreplace/editor_plugin.js b/jscripts/tiny_mce/plugins/searchreplace/editor_plugin.js new file mode 100644 index 000000000..cd9c985b7 --- /dev/null +++ b/jscripts/tiny_mce/plugins/searchreplace/editor_plugin.js @@ -0,0 +1 @@ +(function(){tinymce.create("tinymce.plugins.SearchReplacePlugin",{init:function(a,c){function b(d){a.windowManager.open({file:c+"/searchreplace.htm",width:420+parseInt(a.getLang("searchreplace.delta_width",0)),height:170+parseInt(a.getLang("searchreplace.delta_height",0)),inline:1,auto_focus:0},{mode:d,search_string:a.selection.getContent({format:"text"}),plugin_url:c})}a.addCommand("mceSearch",function(){b("search")});a.addCommand("mceReplace",function(){b("replace")});a.addButton("search",{title:"searchreplace.search_desc",cmd:"mceSearch"});a.addButton("replace",{title:"searchreplace.replace_desc",cmd:"mceReplace"});a.addShortcut("ctrl+f","searchreplace.search_desc","mceSearch")},getInfo:function(){return{longname:"Search/Replace",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/searchreplace",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("searchreplace",tinymce.plugins.SearchReplacePlugin)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/searchreplace/editor_plugin_src.js b/jscripts/tiny_mce/plugins/searchreplace/editor_plugin_src.js new file mode 100644 index 000000000..1433a06a4 --- /dev/null +++ b/jscripts/tiny_mce/plugins/searchreplace/editor_plugin_src.js @@ -0,0 +1,57 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + tinymce.create('tinymce.plugins.SearchReplacePlugin', { + init : function(ed, url) { + function open(m) { + ed.windowManager.open({ + file : url + '/searchreplace.htm', + width : 420 + parseInt(ed.getLang('searchreplace.delta_width', 0)), + height : 170 + parseInt(ed.getLang('searchreplace.delta_height', 0)), + inline : 1, + auto_focus : 0 + }, { + mode : m, + search_string : ed.selection.getContent({format : 'text'}), + plugin_url : url + }); + }; + + // Register commands + ed.addCommand('mceSearch', function() { + open('search'); + }); + + ed.addCommand('mceReplace', function() { + open('replace'); + }); + + // Register buttons + ed.addButton('search', {title : 'searchreplace.search_desc', cmd : 'mceSearch'}); + ed.addButton('replace', {title : 'searchreplace.replace_desc', cmd : 'mceReplace'}); + + ed.addShortcut('ctrl+f', 'searchreplace.search_desc', 'mceSearch'); + }, + + getInfo : function() { + return { + longname : 'Search/Replace', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/searchreplace', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('searchreplace', tinymce.plugins.SearchReplacePlugin); +})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/searchreplace/js/searchreplace.js b/jscripts/tiny_mce/plugins/searchreplace/js/searchreplace.js new file mode 100644 index 000000000..c0a624329 --- /dev/null +++ b/jscripts/tiny_mce/plugins/searchreplace/js/searchreplace.js @@ -0,0 +1,130 @@ +tinyMCEPopup.requireLangPack(); + +var SearchReplaceDialog = { + init : function(ed) { + var f = document.forms[0], m = tinyMCEPopup.getWindowArg("mode"); + + this.switchMode(m); + + f[m + '_panel_searchstring'].value = tinyMCEPopup.getWindowArg("search_string"); + + // Focus input field + f[m + '_panel_searchstring'].focus(); + }, + + switchMode : function(m) { + var f, lm = this.lastMode; + + if (lm != m) { + f = document.forms[0]; + + if (lm) { + f[m + '_panel_searchstring'].value = f[lm + '_panel_searchstring'].value; + f[m + '_panel_backwardsu'].checked = f[lm + '_panel_backwardsu'].checked; + f[m + '_panel_backwardsd'].checked = f[lm + '_panel_backwardsd'].checked; + f[m + '_panel_casesensitivebox'].checked = f[lm + '_panel_casesensitivebox'].checked; + } + + mcTabs.displayTab(m + '_tab', m + '_panel'); + document.getElementById("replaceBtn").style.display = (m == "replace") ? "inline" : "none"; + document.getElementById("replaceAllBtn").style.display = (m == "replace") ? "inline" : "none"; + this.lastMode = m; + } + }, + + searchNext : function(a) { + var ed = tinyMCEPopup.editor, se = ed.selection, r = se.getRng(), f, m = this.lastMode, s, b, fl = 0, w = ed.getWin(), wm = ed.windowManager, fo = 0; + + // Get input + f = document.forms[0]; + s = f[m + '_panel_searchstring'].value; + b = f[m + '_panel_backwardsu'].checked; + ca = f[m + '_panel_casesensitivebox'].checked; + rs = f['replace_panel_replacestring'].value; + + if (s == '') + return; + + function fix() { + // Correct Firefox graphics glitches + r = se.getRng().cloneRange(); + ed.getDoc().execCommand('SelectAll', false, null); + se.setRng(r); + }; + + function replace() { + if (tinymce.isIE) + ed.selection.getRng().duplicate().pasteHTML(rs); // Needs to be duplicated due to selection bug in IE + else + ed.getDoc().execCommand('InsertHTML', false, rs); + }; + + // IE flags + if (ca) + fl = fl | 4; + + switch (a) { + case 'all': + // Move caret to beginning of text + ed.execCommand('SelectAll'); + ed.selection.collapse(true); + + if (tinymce.isIE) { + while (r.findText(s, b ? -1 : 1, fl)) { + r.scrollIntoView(); + r.select(); + replace(); + fo = 1; + + if (b) { + r.moveEnd("character", -(rs.length)); // Otherwise will loop forever + } + } + + tinyMCEPopup.storeSelection(); + } else { + while (w.find(s, ca, b, false, false, false, false)) { + replace(); + fo = 1; + } + } + + if (fo) + tinyMCEPopup.alert(ed.getLang('searchreplace_dlg.allreplaced')); + else + tinyMCEPopup.alert(ed.getLang('searchreplace_dlg.notfound')); + + return; + + case 'current': + if (!ed.selection.isCollapsed()) + replace(); + + break; + } + + se.collapse(b); + r = se.getRng(); + + // Whats the point + if (!s) + return; + + if (tinymce.isIE) { + if (r.findText(s, b ? -1 : 1, fl)) { + r.scrollIntoView(); + r.select(); + } else + tinyMCEPopup.alert(ed.getLang('searchreplace_dlg.notfound')); + + tinyMCEPopup.storeSelection(); + } else { + if (!w.find(s, ca, b, false, false, false, false)) + tinyMCEPopup.alert(ed.getLang('searchreplace_dlg.notfound')); + else + fix(); + } + } +}; + +tinyMCEPopup.onInit.add(SearchReplaceDialog.init, SearchReplaceDialog); diff --git a/jscripts/tiny_mce/plugins/searchreplace/langs/en_dlg.js b/jscripts/tiny_mce/plugins/searchreplace/langs/en_dlg.js new file mode 100644 index 000000000..370959afa --- /dev/null +++ b/jscripts/tiny_mce/plugins/searchreplace/langs/en_dlg.js @@ -0,0 +1,16 @@ +tinyMCE.addI18n('en.searchreplace_dlg',{ +searchnext_desc:"Find again", +notfound:"The search has been completed. The search string could not be found.", +search_title:"Find", +replace_title:"Find/Replace", +allreplaced:"All occurrences of the search string were replaced.", +findwhat:"Find what", +replacewith:"Replace with", +direction:"Direction", +up:"Up", +down:"Down", +mcase:"Match case", +findnext:"Find next", +replace:"Replace", +replaceall:"Replace all" +}); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/searchreplace/searchreplace.htm b/jscripts/tiny_mce/plugins/searchreplace/searchreplace.htm new file mode 100644 index 000000000..d0424cfc9 --- /dev/null +++ b/jscripts/tiny_mce/plugins/searchreplace/searchreplace.htm @@ -0,0 +1,99 @@ + + + + {#searchreplace_dlg.replace_title} + + + + + + + +
    + + +
    +
    + + + + + + + + + + + +
    + + + + + + + + +
    +
    + + + + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + +
    + + + + + + + + +
    +
    + + + + + +
    +
    +
    + +
    + +
    + + + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/spellchecker/css/content.css b/jscripts/tiny_mce/plugins/spellchecker/css/content.css new file mode 100644 index 000000000..24efa0217 --- /dev/null +++ b/jscripts/tiny_mce/plugins/spellchecker/css/content.css @@ -0,0 +1 @@ +.mceItemHiddenSpellWord {background:url(../img/wline.gif) repeat-x bottom left; cursor:default;} diff --git a/jscripts/tiny_mce/plugins/spellchecker/editor_plugin.js b/jscripts/tiny_mce/plugins/spellchecker/editor_plugin.js new file mode 100644 index 000000000..377e4e8a0 --- /dev/null +++ b/jscripts/tiny_mce/plugins/spellchecker/editor_plugin.js @@ -0,0 +1 @@ +(function(){var a=tinymce.util.JSONRequest,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.SpellcheckerPlugin",{getInfo:function(){return{longname:"Spellchecker",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker",version:tinymce.majorVersion+"."+tinymce.minorVersion}},init:function(e,f){var g=this,d;g.url=f;g.editor=e;e.addCommand("mceSpellCheck",function(){if(!g.active){e.setProgressState(1);g._sendRPC("checkWords",[g.selectedLang,g._getWords()],function(h){if(h.length>0){g.active=1;g._markWords(h);e.setProgressState(0);e.nodeChanged()}else{e.setProgressState(0);e.windowManager.alert("spellchecker.no_mpell")}})}else{g._done()}});e.onInit.add(function(){if(e.settings.content_css!==false){e.dom.loadCSS(f+"/css/content.css")}});e.onClick.add(g._showMenu,g);e.onContextMenu.add(g._showMenu,g);e.onBeforeGetContent.add(function(){if(g.active){g._removeWords()}});e.onNodeChange.add(function(i,h){h.setActive("spellchecker",g.active)});e.onSetContent.add(function(){g._done()});e.onBeforeGetContent.add(function(){g._done()});e.onBeforeExecCommand.add(function(h,i){if(i=="mceFullScreen"){g._done()}});g.languages={};c(e.getParam("spellchecker_languages","+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv","hash"),function(i,h){if(h.indexOf("+")===0){h=h.substring(1);g.selectedLang=i}g.languages[h]=i})},createControl:function(h,d){var f=this,g,e=f.editor;if(h=="spellchecker"){g=d.createSplitButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f});g.onRenderMenu.add(function(j,i){i.add({title:"spellchecker.langs","class":"mceMenuItemTitle"}).setDisabled(1);c(f.languages,function(n,m){var p={icon:1},l;p.onclick=function(){l.setSelected(1);f.selectedItem.setSelected(0);f.selectedItem=l;f.selectedLang=n};p.title=m;l=i.add(p);l.setSelected(n==f.selectedLang);if(n==f.selectedLang){f.selectedItem=l}})});return g}},_walk:function(i,g){var h=this.editor.getDoc(),e;if(h.createTreeWalker){e=h.createTreeWalker(i,NodeFilter.SHOW_TEXT,null,false);while((i=e.nextNode())!=null){g.call(this,i)}}else{tinymce.walk(i,g,"childNodes")}},_getSeparators:function(){var e="",d,f=this.editor.getParam("spellchecker_word_separator_chars",'\\s!"#$%&()*+,-./:;<=>?@[]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');for(d=0;d$1$2');q=q.replace(g,'$1$2');j.replace(j.create("span",{"class":"mceItemHidden"},q),r)}}});l.moveToBookmark(m)},_showMenu:function(g,i){var h=this,g=h.editor,d=h._menu,k,j=g.dom,f=j.getViewPort(g.getWin());if(!d){k=b.getPos(g.getContentAreaContainer());d=g.controlManager.createDropMenu("spellcheckermenu",{offset_x:k.x,offset_y:k.y,"class":"mceNoIcons"});h._menu=d}if(j.hasClass(i.target,"mceItemHiddenSpellWord")){d.removeAll();d.add({title:"spellchecker.wait","class":"mceMenuItemTitle"}).setDisabled(1);h._sendRPC("getSuggestions",[h.selectedLang,j.decode(i.target.innerHTML)],function(e){d.removeAll();if(e.length>0){d.add({title:"spellchecker.sug","class":"mceMenuItemTitle"}).setDisabled(1);c(e,function(l){d.add({title:l,onclick:function(){j.replace(g.getDoc().createTextNode(l),i.target);h._checkDone()}})});d.addSeparator()}else{d.add({title:"spellchecker.no_sug","class":"mceMenuItemTitle"}).setDisabled(1)}d.add({title:"spellchecker.ignore_word",onclick:function(){j.remove(i.target,1);h._checkDone()}});d.add({title:"spellchecker.ignore_words",onclick:function(){h._removeWords(j.decode(i.target.innerHTML));h._checkDone()}});d.update()});g.selection.select(i.target);k=j.getPos(i.target);d.showMenu(k.x,k.y+i.target.offsetHeight-f.y);return tinymce.dom.Event.cancel(i)}else{d.hideMenu()}},_checkDone:function(){var e=this,d=e.editor,g=d.dom,f;c(g.select("span"),function(h){if(h&&g.hasClass(h,"mceItemHiddenSpellWord")){f=true;return false}});if(!f){e._done()}},_done:function(){var d=this,e=d.active;if(d.active){d.active=0;d._removeWords();if(d._menu){d._menu.hideMenu()}if(e){d.editor.nodeChanged()}}},_sendRPC:function(e,h,d){var g=this,f=g.editor.getParam("spellchecker_rpc_url","{backend}");if(f=="{backend}"){g.editor.setProgressState(0);alert("Please specify: spellchecker_rpc_url");return}a.sendRPC({url:f,method:e,params:h,success:d,error:function(j,i){g.editor.setProgressState(0);g.editor.windowManager.alert(j.errstr||("Error response: "+i.responseText))}})}});tinymce.PluginManager.add("spellchecker",tinymce.plugins.SpellcheckerPlugin)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js b/jscripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js new file mode 100644 index 000000000..0b3d3b600 --- /dev/null +++ b/jscripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js @@ -0,0 +1,341 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM; + + tinymce.create('tinymce.plugins.SpellcheckerPlugin', { + getInfo : function() { + return { + longname : 'Spellchecker', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + }, + + init : function(ed, url) { + var t = this, cm; + + t.url = url; + t.editor = ed; + + // Register commands + ed.addCommand('mceSpellCheck', function() { + if (!t.active) { + ed.setProgressState(1); + t._sendRPC('checkWords', [t.selectedLang, t._getWords()], function(r) { + if (r.length > 0) { + t.active = 1; + t._markWords(r); + ed.setProgressState(0); + ed.nodeChanged(); + } else { + ed.setProgressState(0); + ed.windowManager.alert('spellchecker.no_mpell'); + } + }); + } else + t._done(); + }); + + ed.onInit.add(function() { + if (ed.settings.content_css !== false) + ed.dom.loadCSS(url + '/css/content.css'); + }); + + ed.onClick.add(t._showMenu, t); + ed.onContextMenu.add(t._showMenu, t); + ed.onBeforeGetContent.add(function() { + if (t.active) + t._removeWords(); + }); + + ed.onNodeChange.add(function(ed, cm) { + cm.setActive('spellchecker', t.active); + }); + + ed.onSetContent.add(function() { + t._done(); + }); + + ed.onBeforeGetContent.add(function() { + t._done(); + }); + + ed.onBeforeExecCommand.add(function(ed, cmd) { + if (cmd == 'mceFullScreen') + t._done(); + }); + + // Find selected language + t.languages = {}; + each(ed.getParam('spellchecker_languages', '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv', 'hash'), function(v, k) { + if (k.indexOf('+') === 0) { + k = k.substring(1); + t.selectedLang = v; + } + + t.languages[k] = v; + }); + }, + + createControl : function(n, cm) { + var t = this, c, ed = t.editor; + + if (n == 'spellchecker') { + c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t}); + + c.onRenderMenu.add(function(c, m) { + m.add({title : 'spellchecker.langs', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + each(t.languages, function(v, k) { + var o = {icon : 1}, mi; + + o.onclick = function() { + mi.setSelected(1); + t.selectedItem.setSelected(0); + t.selectedItem = mi; + t.selectedLang = v; + }; + + o.title = k; + mi = m.add(o); + mi.setSelected(v == t.selectedLang); + + if (v == t.selectedLang) + t.selectedItem = mi; + }) + }); + + return c; + } + }, + + // Internal functions + + _walk : function(n, f) { + var d = this.editor.getDoc(), w; + + if (d.createTreeWalker) { + w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false); + + while ((n = w.nextNode()) != null) + f.call(this, n); + } else + tinymce.walk(n, f, 'childNodes'); + }, + + _getSeparators : function() { + var re = '', i, str = this.editor.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c'); + + // Build word separator regexp + for (i=0; i$1$2'); + v = v.replace(r3, '$1$2'); + + dom.replace(dom.create('span', {'class' : 'mceItemHidden'}, v), n); + } + } + }); + + se.moveToBookmark(b); + }, + + _showMenu : function(ed, e) { + var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin()); + + if (!m) { + p1 = DOM.getPos(ed.getContentAreaContainer()); + //p2 = DOM.getPos(ed.getContainer()); + + m = ed.controlManager.createDropMenu('spellcheckermenu', { + offset_x : p1.x, + offset_y : p1.y, + 'class' : 'mceNoIcons' + }); + + t._menu = m; + } + + if (dom.hasClass(e.target, 'mceItemHiddenSpellWord')) { + m.removeAll(); + m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + + t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(e.target.innerHTML)], function(r) { + m.removeAll(); + + if (r.length > 0) { + m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + each(r, function(v) { + m.add({title : v, onclick : function() { + dom.replace(ed.getDoc().createTextNode(v), e.target); + t._checkDone(); + }}); + }); + + m.addSeparator(); + } else + m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1); + + m.add({ + title : 'spellchecker.ignore_word', + onclick : function() { + dom.remove(e.target, 1); + t._checkDone(); + } + }); + + m.add({ + title : 'spellchecker.ignore_words', + onclick : function() { + t._removeWords(dom.decode(e.target.innerHTML)); + t._checkDone(); + } + }); + + m.update(); + }); + + ed.selection.select(e.target); + p1 = dom.getPos(e.target); + m.showMenu(p1.x, p1.y + e.target.offsetHeight - vp.y); + + return tinymce.dom.Event.cancel(e); + } else + m.hideMenu(); + }, + + _checkDone : function() { + var t = this, ed = t.editor, dom = ed.dom, o; + + each(dom.select('span'), function(n) { + if (n && dom.hasClass(n, 'mceItemHiddenSpellWord')) { + o = true; + return false; + } + }); + + if (!o) + t._done(); + }, + + _done : function() { + var t = this, la = t.active; + + if (t.active) { + t.active = 0; + t._removeWords(); + + if (t._menu) + t._menu.hideMenu(); + + if (la) + t.editor.nodeChanged(); + } + }, + + _sendRPC : function(m, p, cb) { + var t = this, url = t.editor.getParam("spellchecker_rpc_url", "{backend}"); + + if (url == '{backend}') { + t.editor.setProgressState(0); + alert('Please specify: spellchecker_rpc_url'); + return; + } + + JSONRequest.sendRPC({ + url : url, + method : m, + params : p, + success : cb, + error : function(e, x) { + t.editor.setProgressState(0); + t.editor.windowManager.alert(e.errstr || ('Error response: ' + x.responseText)); + } + }); + } + }); + + // Register plugin + tinymce.PluginManager.add('spellchecker', tinymce.plugins.SpellcheckerPlugin); +})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/spellchecker/img/wline.gif b/jscripts/tiny_mce/plugins/spellchecker/img/wline.gif new file mode 100644 index 0000000000000000000000000000000000000000..7d0a4dbca03cc13177a359a5f175dda819fdf464 GIT binary patch literal 46 ycmZ?wbhEHbWMN=tXkcXcqowu#|9{1wEQ|~cj0`#qKmd|qU}ANVOOs?}um%7FLkRf* literal 0 HcmV?d00001 diff --git a/jscripts/tiny_mce/plugins/style/css/props.css b/jscripts/tiny_mce/plugins/style/css/props.css new file mode 100644 index 000000000..eb1f26496 --- /dev/null +++ b/jscripts/tiny_mce/plugins/style/css/props.css @@ -0,0 +1,13 @@ +#text_font {width:250px;} +#text_size {width:70px;} +.mceAddSelectValue {background:#DDD;} +select, #block_text_indent, #box_width, #box_height, #box_padding_top, #box_padding_right, #box_padding_bottom, #box_padding_left {width:70px;} +#box_margin_top, #box_margin_right, #box_margin_bottom, #box_margin_left, #positioning_width, #positioning_height, #positioning_zindex {width:70px;} +#positioning_placement_top, #positioning_placement_right, #positioning_placement_bottom, #positioning_placement_left {width:70px;} +#positioning_clip_top, #positioning_clip_right, #positioning_clip_bottom, #positioning_clip_left {width:70px;} +.panel_wrapper div.current {padding-top:10px;height:230px;} +.delim {border-left:1px solid gray;} +.tdelim {border-bottom:1px solid gray;} +#block_display {width:145px;} +#list_type {width:115px;} +.disabled {background:#EEE;} diff --git a/jscripts/tiny_mce/plugins/style/editor_plugin.js b/jscripts/tiny_mce/plugins/style/editor_plugin.js new file mode 100644 index 000000000..cab2153c4 --- /dev/null +++ b/jscripts/tiny_mce/plugins/style/editor_plugin.js @@ -0,0 +1 @@ +(function(){tinymce.create("tinymce.plugins.StylePlugin",{init:function(a,b){a.addCommand("mceStyleProps",function(){a.windowManager.open({file:b+"/props.htm",width:480+parseInt(a.getLang("style.delta_width",0)),height:320+parseInt(a.getLang("style.delta_height",0)),inline:1},{plugin_url:b,style_text:a.selection.getNode().style.cssText})});a.addCommand("mceSetElementStyle",function(d,c){if(e=a.selection.getNode()){a.dom.setAttrib(e,"style",c);a.execCommand("mceRepaint")}});a.onNodeChange.add(function(d,c,f){c.setDisabled("styleprops",f.nodeName==="BODY")});a.addButton("styleprops",{title:"style.desc",cmd:"mceStyleProps"})},getInfo:function(){return{longname:"Style",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/style",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("style",tinymce.plugins.StylePlugin)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/style/editor_plugin_src.js b/jscripts/tiny_mce/plugins/style/editor_plugin_src.js new file mode 100644 index 000000000..5f7755f18 --- /dev/null +++ b/jscripts/tiny_mce/plugins/style/editor_plugin_src.js @@ -0,0 +1,55 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + tinymce.create('tinymce.plugins.StylePlugin', { + init : function(ed, url) { + // Register commands + ed.addCommand('mceStyleProps', function() { + ed.windowManager.open({ + file : url + '/props.htm', + width : 480 + parseInt(ed.getLang('style.delta_width', 0)), + height : 320 + parseInt(ed.getLang('style.delta_height', 0)), + inline : 1 + }, { + plugin_url : url, + style_text : ed.selection.getNode().style.cssText + }); + }); + + ed.addCommand('mceSetElementStyle', function(ui, v) { + if (e = ed.selection.getNode()) { + ed.dom.setAttrib(e, 'style', v); + ed.execCommand('mceRepaint'); + } + }); + + ed.onNodeChange.add(function(ed, cm, n) { + cm.setDisabled('styleprops', n.nodeName === 'BODY'); + }); + + // Register buttons + ed.addButton('styleprops', {title : 'style.desc', cmd : 'mceStyleProps'}); + }, + + getInfo : function() { + return { + longname : 'Style', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/style', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('style', tinymce.plugins.StylePlugin); +})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/style/js/props.js b/jscripts/tiny_mce/plugins/style/js/props.js new file mode 100644 index 000000000..a8dd93dec --- /dev/null +++ b/jscripts/tiny_mce/plugins/style/js/props.js @@ -0,0 +1,641 @@ +tinyMCEPopup.requireLangPack(); + +var defaultFonts = "" + + "Arial, Helvetica, sans-serif=Arial, Helvetica, sans-serif;" + + "Times New Roman, Times, serif=Times New Roman, Times, serif;" + + "Courier New, Courier, mono=Courier New, Courier, mono;" + + "Times New Roman, Times, serif=Times New Roman, Times, serif;" + + "Georgia, Times New Roman, Times, serif=Georgia, Times New Roman, Times, serif;" + + "Verdana, Arial, Helvetica, sans-serif=Verdana, Arial, Helvetica, sans-serif;" + + "Geneva, Arial, Helvetica, sans-serif=Geneva, Arial, Helvetica, sans-serif"; + +var defaultSizes = "9;10;12;14;16;18;24;xx-small;x-small;small;medium;large;x-large;xx-large;smaller;larger"; +var defaultMeasurement = "+pixels=px;points=pt;inches=in;centimetres=cm;millimetres=mm;picas=pc;ems=em;exs=ex;%"; +var defaultSpacingMeasurement = "pixels=px;points=pt;inches=in;centimetres=cm;millimetres=mm;picas=pc;+ems=em;exs=ex;%"; +var defaultIndentMeasurement = "pixels=px;+points=pt;inches=in;centimetres=cm;millimetres=mm;picas=pc;ems=em;exs=ex;%"; +var defaultWeight = "normal;bold;bolder;lighter;100;200;300;400;500;600;700;800;900"; +var defaultTextStyle = "normal;italic;oblique"; +var defaultVariant = "normal;small-caps"; +var defaultLineHeight = "normal"; +var defaultAttachment = "fixed;scroll"; +var defaultRepeat = "no-repeat;repeat;repeat-x;repeat-y"; +var defaultPosH = "left;center;right"; +var defaultPosV = "top;center;bottom"; +var defaultVAlign = "baseline;sub;super;top;text-top;middle;bottom;text-bottom"; +var defaultDisplay = "inline;block;list-item;run-in;compact;marker;table;inline-table;table-row-group;table-header-group;table-footer-group;table-row;table-column-group;table-column;table-cell;table-caption;none"; +var defaultBorderStyle = "none;solid;dashed;dotted;double;groove;ridge;inset;outset"; +var defaultBorderWidth = "thin;medium;thick"; +var defaultListType = "disc;circle;square;decimal;lower-roman;upper-roman;lower-alpha;upper-alpha;none"; + +function init() { + var ce = document.getElementById('container'), h; + + ce.style.cssText = tinyMCEPopup.getWindowArg('style_text'); + + h = getBrowserHTML('background_image_browser','background_image','image','advimage'); + document.getElementById("background_image_browser").innerHTML = h; + + document.getElementById('text_color_pickcontainer').innerHTML = getColorPickerHTML('text_color_pick','text_color'); + document.getElementById('background_color_pickcontainer').innerHTML = getColorPickerHTML('background_color_pick','background_color'); + document.getElementById('border_color_top_pickcontainer').innerHTML = getColorPickerHTML('border_color_top_pick','border_color_top'); + document.getElementById('border_color_right_pickcontainer').innerHTML = getColorPickerHTML('border_color_right_pick','border_color_right'); + document.getElementById('border_color_bottom_pickcontainer').innerHTML = getColorPickerHTML('border_color_bottom_pick','border_color_bottom'); + document.getElementById('border_color_left_pickcontainer').innerHTML = getColorPickerHTML('border_color_left_pick','border_color_left'); + + fillSelect(0, 'text_font', 'style_font', defaultFonts, ';', true); + fillSelect(0, 'text_size', 'style_font_size', defaultSizes, ';', true); + fillSelect(0, 'text_size_measurement', 'style_font_size_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'text_case', 'style_text_case', "capitalize;uppercase;lowercase", ';', true); + fillSelect(0, 'text_weight', 'style_font_weight', defaultWeight, ';', true); + fillSelect(0, 'text_style', 'style_font_style', defaultTextStyle, ';', true); + fillSelect(0, 'text_variant', 'style_font_variant', defaultVariant, ';', true); + fillSelect(0, 'text_lineheight', 'style_font_line_height', defaultLineHeight, ';', true); + fillSelect(0, 'text_lineheight_measurement', 'style_font_line_height_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'background_attachment', 'style_background_attachment', defaultAttachment, ';', true); + fillSelect(0, 'background_repeat', 'style_background_repeat', defaultRepeat, ';', true); + + fillSelect(0, 'background_hpos_measurement', 'style_background_hpos_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'background_vpos_measurement', 'style_background_vpos_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'background_hpos', 'style_background_hpos', defaultPosH, ';', true); + fillSelect(0, 'background_vpos', 'style_background_vpos', defaultPosV, ';', true); + + fillSelect(0, 'block_wordspacing', 'style_wordspacing', 'normal', ';', true); + fillSelect(0, 'block_wordspacing_measurement', 'style_wordspacing_measurement', defaultSpacingMeasurement, ';', true); + fillSelect(0, 'block_letterspacing', 'style_letterspacing', 'normal', ';', true); + fillSelect(0, 'block_letterspacing_measurement', 'style_letterspacing_measurement', defaultSpacingMeasurement, ';', true); + fillSelect(0, 'block_vertical_alignment', 'style_vertical_alignment', defaultVAlign, ';', true); + fillSelect(0, 'block_text_align', 'style_text_align', "left;right;center;justify", ';', true); + fillSelect(0, 'block_whitespace', 'style_whitespace', "normal;pre;nowrap", ';', true); + fillSelect(0, 'block_display', 'style_display', defaultDisplay, ';', true); + fillSelect(0, 'block_text_indent_measurement', 'style_text_indent_measurement', defaultIndentMeasurement, ';', true); + + fillSelect(0, 'box_width_measurement', 'style_box_width_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_height_measurement', 'style_box_height_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_float', 'style_float', 'left;right;none', ';', true); + fillSelect(0, 'box_clear', 'style_clear', 'left;right;both;none', ';', true); + fillSelect(0, 'box_padding_left_measurement', 'style_padding_left_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_padding_top_measurement', 'style_padding_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_padding_bottom_measurement', 'style_padding_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_padding_right_measurement', 'style_padding_right_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_margin_left_measurement', 'style_margin_left_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_margin_top_measurement', 'style_margin_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_margin_bottom_measurement', 'style_margin_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'box_margin_right_measurement', 'style_margin_right_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'border_style_top', 'style_border_style_top', defaultBorderStyle, ';', true); + fillSelect(0, 'border_style_right', 'style_border_style_right', defaultBorderStyle, ';', true); + fillSelect(0, 'border_style_bottom', 'style_border_style_bottom', defaultBorderStyle, ';', true); + fillSelect(0, 'border_style_left', 'style_border_style_left', defaultBorderStyle, ';', true); + + fillSelect(0, 'border_width_top', 'style_border_width_top', defaultBorderWidth, ';', true); + fillSelect(0, 'border_width_right', 'style_border_width_right', defaultBorderWidth, ';', true); + fillSelect(0, 'border_width_bottom', 'style_border_width_bottom', defaultBorderWidth, ';', true); + fillSelect(0, 'border_width_left', 'style_border_width_left', defaultBorderWidth, ';', true); + + fillSelect(0, 'border_width_top_measurement', 'style_border_width_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'border_width_right_measurement', 'style_border_width_right_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'border_width_bottom_measurement', 'style_border_width_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'border_width_left_measurement', 'style_border_width_left_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'list_type', 'style_list_type', defaultListType, ';', true); + fillSelect(0, 'list_position', 'style_list_position', "inside;outside", ';', true); + + fillSelect(0, 'positioning_type', 'style_positioning_type', "absolute;relative;static", ';', true); + fillSelect(0, 'positioning_visibility', 'style_positioning_visibility', "inherit;visible;hidden", ';', true); + + fillSelect(0, 'positioning_width_measurement', 'style_positioning_width_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_height_measurement', 'style_positioning_height_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_overflow', 'style_positioning_overflow', "visible;hidden;scroll;auto", ';', true); + + fillSelect(0, 'positioning_placement_top_measurement', 'style_positioning_placement_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_placement_right_measurement', 'style_positioning_placement_right_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_placement_bottom_measurement', 'style_positioning_placement_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_placement_left_measurement', 'style_positioning_placement_left_measurement', defaultMeasurement, ';', true); + + fillSelect(0, 'positioning_clip_top_measurement', 'style_positioning_clip_top_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_clip_right_measurement', 'style_positioning_clip_right_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_clip_bottom_measurement', 'style_positioning_clip_bottom_measurement', defaultMeasurement, ';', true); + fillSelect(0, 'positioning_clip_left_measurement', 'style_positioning_clip_left_measurement', defaultMeasurement, ';', true); + + TinyMCE_EditableSelects.init(); + setupFormData(); + showDisabledControls(); +} + +function setupFormData() { + var ce = document.getElementById('container'), f = document.forms[0], s, b, i; + + // Setup text fields + + selectByValue(f, 'text_font', ce.style.fontFamily, true, true); + selectByValue(f, 'text_size', getNum(ce.style.fontSize), true, true); + selectByValue(f, 'text_size_measurement', getMeasurement(ce.style.fontSize)); + selectByValue(f, 'text_weight', ce.style.fontWeight, true, true); + selectByValue(f, 'text_style', ce.style.fontStyle, true, true); + selectByValue(f, 'text_lineheight', getNum(ce.style.lineHeight), true, true); + selectByValue(f, 'text_lineheight_measurement', getMeasurement(ce.style.lineHeight)); + selectByValue(f, 'text_case', ce.style.textTransform, true, true); + selectByValue(f, 'text_variant', ce.style.fontVariant, true, true); + f.text_color.value = tinyMCEPopup.editor.dom.toHex(ce.style.color); + updateColor('text_color_pick', 'text_color'); + f.text_underline.checked = inStr(ce.style.textDecoration, 'underline'); + f.text_overline.checked = inStr(ce.style.textDecoration, 'overline'); + f.text_linethrough.checked = inStr(ce.style.textDecoration, 'line-through'); + f.text_blink.checked = inStr(ce.style.textDecoration, 'blink'); + + // Setup background fields + + f.background_color.value = tinyMCEPopup.editor.dom.toHex(ce.style.backgroundColor); + updateColor('background_color_pick', 'background_color'); + f.background_image.value = ce.style.backgroundImage.replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1"); + selectByValue(f, 'background_repeat', ce.style.backgroundRepeat, true, true); + selectByValue(f, 'background_attachment', ce.style.backgroundAttachment, true, true); + selectByValue(f, 'background_hpos', getNum(getVal(ce.style.backgroundPosition, 0)), true, true); + selectByValue(f, 'background_hpos_measurement', getMeasurement(getVal(ce.style.backgroundPosition, 0))); + selectByValue(f, 'background_vpos', getNum(getVal(ce.style.backgroundPosition, 1)), true, true); + selectByValue(f, 'background_vpos_measurement', getMeasurement(getVal(ce.style.backgroundPosition, 1))); + + // Setup block fields + + selectByValue(f, 'block_wordspacing', getNum(ce.style.wordSpacing), true, true); + selectByValue(f, 'block_wordspacing_measurement', getMeasurement(ce.style.wordSpacing)); + selectByValue(f, 'block_letterspacing', getNum(ce.style.letterSpacing), true, true); + selectByValue(f, 'block_letterspacing_measurement', getMeasurement(ce.style.letterSpacing)); + selectByValue(f, 'block_vertical_alignment', ce.style.verticalAlign, true, true); + selectByValue(f, 'block_text_align', ce.style.textAlign, true, true); + f.block_text_indent.value = getNum(ce.style.textIndent); + selectByValue(f, 'block_text_indent_measurement', getMeasurement(ce.style.textIndent)); + selectByValue(f, 'block_whitespace', ce.style.whiteSpace, true, true); + selectByValue(f, 'block_display', ce.style.display, true, true); + + // Setup box fields + + f.box_width.value = getNum(ce.style.width); + selectByValue(f, 'box_width_measurement', getMeasurement(ce.style.width)); + + f.box_height.value = getNum(ce.style.height); + selectByValue(f, 'box_height_measurement', getMeasurement(ce.style.height)); + + if (tinymce.isGecko) + selectByValue(f, 'box_float', ce.style.cssFloat, true, true); + else + selectByValue(f, 'box_float', ce.style.styleFloat, true, true); + + selectByValue(f, 'box_clear', ce.style.clear, true, true); + + setupBox(f, ce, 'box_padding', 'padding', ''); + setupBox(f, ce, 'box_margin', 'margin', ''); + + // Setup border fields + + setupBox(f, ce, 'border_style', 'border', 'Style'); + setupBox(f, ce, 'border_width', 'border', 'Width'); + setupBox(f, ce, 'border_color', 'border', 'Color'); + + updateColor('border_color_top_pick', 'border_color_top'); + updateColor('border_color_right_pick', 'border_color_right'); + updateColor('border_color_bottom_pick', 'border_color_bottom'); + updateColor('border_color_left_pick', 'border_color_left'); + + f.elements.border_color_top.value = tinyMCEPopup.editor.dom.toHex(f.elements.border_color_top.value); + f.elements.border_color_right.value = tinyMCEPopup.editor.dom.toHex(f.elements.border_color_right.value); + f.elements.border_color_bottom.value = tinyMCEPopup.editor.dom.toHex(f.elements.border_color_bottom.value); + f.elements.border_color_left.value = tinyMCEPopup.editor.dom.toHex(f.elements.border_color_left.value); + + // Setup list fields + + selectByValue(f, 'list_type', ce.style.listStyleType, true, true); + selectByValue(f, 'list_position', ce.style.listStylePosition, true, true); + f.list_bullet_image.value = ce.style.listStyleImage.replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1"); + + // Setup box fields + + selectByValue(f, 'positioning_type', ce.style.position, true, true); + selectByValue(f, 'positioning_visibility', ce.style.visibility, true, true); + selectByValue(f, 'positioning_overflow', ce.style.overflow, true, true); + f.positioning_zindex.value = ce.style.zIndex ? ce.style.zIndex : ""; + + f.positioning_width.value = getNum(ce.style.width); + selectByValue(f, 'positioning_width_measurement', getMeasurement(ce.style.width)); + + f.positioning_height.value = getNum(ce.style.height); + selectByValue(f, 'positioning_height_measurement', getMeasurement(ce.style.height)); + + setupBox(f, ce, 'positioning_placement', '', '', ['top', 'right', 'bottom', 'left']); + + s = ce.style.clip.replace(new RegExp("rect\\('?([^']*)'?\\)", 'gi'), "$1"); + s = s.replace(/,/g, ' '); + + if (!hasEqualValues([getVal(s, 0), getVal(s, 1), getVal(s, 2), getVal(s, 3)])) { + f.positioning_clip_top.value = getNum(getVal(s, 0)); + selectByValue(f, 'positioning_clip_top_measurement', getMeasurement(getVal(s, 0))); + f.positioning_clip_right.value = getNum(getVal(s, 1)); + selectByValue(f, 'positioning_clip_right_measurement', getMeasurement(getVal(s, 1))); + f.positioning_clip_bottom.value = getNum(getVal(s, 2)); + selectByValue(f, 'positioning_clip_bottom_measurement', getMeasurement(getVal(s, 2))); + f.positioning_clip_left.value = getNum(getVal(s, 3)); + selectByValue(f, 'positioning_clip_left_measurement', getMeasurement(getVal(s, 3))); + } else { + f.positioning_clip_top.value = getNum(getVal(s, 0)); + selectByValue(f, 'positioning_clip_top_measurement', getMeasurement(getVal(s, 0))); + f.positioning_clip_right.value = f.positioning_clip_bottom.value = f.positioning_clip_left.value; + } + +// setupBox(f, ce, '', 'border', 'Color'); +} + +function getMeasurement(s) { + return s.replace(/^([0-9.]+)(.*)$/, "$2"); +} + +function getNum(s) { + if (new RegExp('^(?:[0-9.]+)(?:[a-z%]+)$', 'gi').test(s)) + return s.replace(/[^0-9.]/g, ''); + + return s; +} + +function inStr(s, n) { + return new RegExp(n, 'gi').test(s); +} + +function getVal(s, i) { + var a = s.split(' '); + + if (a.length > 1) + return a[i]; + + return ""; +} + +function setValue(f, n, v) { + if (f.elements[n].type == "text") + f.elements[n].value = v; + else + selectByValue(f, n, v, true, true); +} + +function setupBox(f, ce, fp, pr, sf, b) { + if (typeof(b) == "undefined") + b = ['Top', 'Right', 'Bottom', 'Left']; + + if (isSame(ce, pr, sf, b)) { + f.elements[fp + "_same"].checked = true; + + setValue(f, fp + "_top", getNum(ce.style[pr + b[0] + sf])); + f.elements[fp + "_top"].disabled = false; + + f.elements[fp + "_right"].value = ""; + f.elements[fp + "_right"].disabled = true; + f.elements[fp + "_bottom"].value = ""; + f.elements[fp + "_bottom"].disabled = true; + f.elements[fp + "_left"].value = ""; + f.elements[fp + "_left"].disabled = true; + + if (f.elements[fp + "_top_measurement"]) { + selectByValue(f, fp + '_top_measurement', getMeasurement(ce.style[pr + b[0] + sf])); + f.elements[fp + "_left_measurement"].disabled = true; + f.elements[fp + "_bottom_measurement"].disabled = true; + f.elements[fp + "_right_measurement"].disabled = true; + } + } else { + f.elements[fp + "_same"].checked = false; + + setValue(f, fp + "_top", getNum(ce.style[pr + b[0] + sf])); + f.elements[fp + "_top"].disabled = false; + + setValue(f, fp + "_right", getNum(ce.style[pr + b[1] + sf])); + f.elements[fp + "_right"].disabled = false; + + setValue(f, fp + "_bottom", getNum(ce.style[pr + b[2] + sf])); + f.elements[fp + "_bottom"].disabled = false; + + setValue(f, fp + "_left", getNum(ce.style[pr + b[3] + sf])); + f.elements[fp + "_left"].disabled = false; + + if (f.elements[fp + "_top_measurement"]) { + selectByValue(f, fp + '_top_measurement', getMeasurement(ce.style[pr + b[0] + sf])); + selectByValue(f, fp + '_right_measurement', getMeasurement(ce.style[pr + b[1] + sf])); + selectByValue(f, fp + '_bottom_measurement', getMeasurement(ce.style[pr + b[2] + sf])); + selectByValue(f, fp + '_left_measurement', getMeasurement(ce.style[pr + b[3] + sf])); + f.elements[fp + "_left_measurement"].disabled = false; + f.elements[fp + "_bottom_measurement"].disabled = false; + f.elements[fp + "_right_measurement"].disabled = false; + } + } +} + +function isSame(e, pr, sf, b) { + var a = [], i, x; + + if (typeof(b) == "undefined") + b = ['Top', 'Right', 'Bottom', 'Left']; + + if (typeof(sf) == "undefined" || sf == null) + sf = ""; + + a[0] = e.style[pr + b[0] + sf]; + a[1] = e.style[pr + b[1] + sf]; + a[2] = e.style[pr + b[2] + sf]; + a[3] = e.style[pr + b[3] + sf]; + + for (i=0; i 0 ? s.substring(1) : s; + + if (f.text_none.checked) + s = "none"; + + ce.style.textDecoration = s; + + // Build background styles + + ce.style.backgroundColor = f.background_color.value; + ce.style.backgroundImage = f.background_image.value != "" ? "url(" + f.background_image.value + ")" : ""; + ce.style.backgroundRepeat = f.background_repeat.value; + ce.style.backgroundAttachment = f.background_attachment.value; + + if (f.background_hpos.value != "") { + s = ""; + s += f.background_hpos.value + (isNum(f.background_hpos.value) ? f.background_hpos_measurement.value : "") + " "; + s += f.background_vpos.value + (isNum(f.background_vpos.value) ? f.background_vpos_measurement.value : ""); + ce.style.backgroundPosition = s; + } + + // Build block styles + + ce.style.wordSpacing = f.block_wordspacing.value + (isNum(f.block_wordspacing.value) ? f.block_wordspacing_measurement.value : ""); + ce.style.letterSpacing = f.block_letterspacing.value + (isNum(f.block_letterspacing.value) ? f.block_letterspacing_measurement.value : ""); + ce.style.verticalAlign = f.block_vertical_alignment.value; + ce.style.textAlign = f.block_text_align.value; + ce.style.textIndent = f.block_text_indent.value + (isNum(f.block_text_indent.value) ? f.block_text_indent_measurement.value : ""); + ce.style.whiteSpace = f.block_whitespace.value; + ce.style.display = f.block_display.value; + + // Build box styles + + ce.style.width = f.box_width.value + (isNum(f.box_width.value) ? f.box_width_measurement.value : ""); + ce.style.height = f.box_height.value + (isNum(f.box_height.value) ? f.box_height_measurement.value : ""); + ce.style.styleFloat = f.box_float.value; + + if (tinymce.isGecko) + ce.style.cssFloat = f.box_float.value; + + ce.style.clear = f.box_clear.value; + + if (!f.box_padding_same.checked) { + ce.style.paddingTop = f.box_padding_top.value + (isNum(f.box_padding_top.value) ? f.box_padding_top_measurement.value : ""); + ce.style.paddingRight = f.box_padding_right.value + (isNum(f.box_padding_right.value) ? f.box_padding_right_measurement.value : ""); + ce.style.paddingBottom = f.box_padding_bottom.value + (isNum(f.box_padding_bottom.value) ? f.box_padding_bottom_measurement.value : ""); + ce.style.paddingLeft = f.box_padding_left.value + (isNum(f.box_padding_left.value) ? f.box_padding_left_measurement.value : ""); + } else + ce.style.padding = f.box_padding_top.value + (isNum(f.box_padding_top.value) ? f.box_padding_top_measurement.value : ""); + + if (!f.box_margin_same.checked) { + ce.style.marginTop = f.box_margin_top.value + (isNum(f.box_margin_top.value) ? f.box_margin_top_measurement.value : ""); + ce.style.marginRight = f.box_margin_right.value + (isNum(f.box_margin_right.value) ? f.box_margin_right_measurement.value : ""); + ce.style.marginBottom = f.box_margin_bottom.value + (isNum(f.box_margin_bottom.value) ? f.box_margin_bottom_measurement.value : ""); + ce.style.marginLeft = f.box_margin_left.value + (isNum(f.box_margin_left.value) ? f.box_margin_left_measurement.value : ""); + } else + ce.style.margin = f.box_margin_top.value + (isNum(f.box_margin_top.value) ? f.box_margin_top_measurement.value : ""); + + // Build border styles + + if (!f.border_style_same.checked) { + ce.style.borderTopStyle = f.border_style_top.value; + ce.style.borderRightStyle = f.border_style_right.value; + ce.style.borderBottomStyle = f.border_style_bottom.value; + ce.style.borderLeftStyle = f.border_style_left.value; + } else + ce.style.borderStyle = f.border_style_top.value; + + if (!f.border_width_same.checked) { + ce.style.borderTopWidth = f.border_width_top.value + (isNum(f.border_width_top.value) ? f.border_width_top_measurement.value : ""); + ce.style.borderRightWidth = f.border_width_right.value + (isNum(f.border_width_right.value) ? f.border_width_right_measurement.value : ""); + ce.style.borderBottomWidth = f.border_width_bottom.value + (isNum(f.border_width_bottom.value) ? f.border_width_bottom_measurement.value : ""); + ce.style.borderLeftWidth = f.border_width_left.value + (isNum(f.border_width_left.value) ? f.border_width_left_measurement.value : ""); + } else + ce.style.borderWidth = f.border_width_top.value + (isNum(f.border_width_top.value) ? f.border_width_top_measurement.value : ""); + + if (!f.border_color_same.checked) { + ce.style.borderTopColor = f.border_color_top.value; + ce.style.borderRightColor = f.border_color_right.value; + ce.style.borderBottomColor = f.border_color_bottom.value; + ce.style.borderLeftColor = f.border_color_left.value; + } else + ce.style.borderColor = f.border_color_top.value; + + // Build list styles + + ce.style.listStyleType = f.list_type.value; + ce.style.listStylePosition = f.list_position.value; + ce.style.listStyleImage = f.list_bullet_image.value != "" ? "url(" + f.list_bullet_image.value + ")" : ""; + + // Build positioning styles + + ce.style.position = f.positioning_type.value; + ce.style.visibility = f.positioning_visibility.value; + + if (ce.style.width == "") + ce.style.width = f.positioning_width.value + (isNum(f.positioning_width.value) ? f.positioning_width_measurement.value : ""); + + if (ce.style.height == "") + ce.style.height = f.positioning_height.value + (isNum(f.positioning_height.value) ? f.positioning_height_measurement.value : ""); + + ce.style.zIndex = f.positioning_zindex.value; + ce.style.overflow = f.positioning_overflow.value; + + if (!f.positioning_placement_same.checked) { + ce.style.top = f.positioning_placement_top.value + (isNum(f.positioning_placement_top.value) ? f.positioning_placement_top_measurement.value : ""); + ce.style.right = f.positioning_placement_right.value + (isNum(f.positioning_placement_right.value) ? f.positioning_placement_right_measurement.value : ""); + ce.style.bottom = f.positioning_placement_bottom.value + (isNum(f.positioning_placement_bottom.value) ? f.positioning_placement_bottom_measurement.value : ""); + ce.style.left = f.positioning_placement_left.value + (isNum(f.positioning_placement_left.value) ? f.positioning_placement_left_measurement.value : ""); + } else { + s = f.positioning_placement_top.value + (isNum(f.positioning_placement_top.value) ? f.positioning_placement_top_measurement.value : ""); + ce.style.top = s; + ce.style.right = s; + ce.style.bottom = s; + ce.style.left = s; + } + + if (!f.positioning_clip_same.checked) { + s = "rect("; + s += (isNum(f.positioning_clip_top.value) ? f.positioning_clip_top.value + f.positioning_clip_top_measurement.value : "auto") + " "; + s += (isNum(f.positioning_clip_right.value) ? f.positioning_clip_right.value + f.positioning_clip_right_measurement.value : "auto") + " "; + s += (isNum(f.positioning_clip_bottom.value) ? f.positioning_clip_bottom.value + f.positioning_clip_bottom_measurement.value : "auto") + " "; + s += (isNum(f.positioning_clip_left.value) ? f.positioning_clip_left.value + f.positioning_clip_left_measurement.value : "auto"); + s += ")"; + + if (s != "rect(auto auto auto auto)") + ce.style.clip = s; + } else { + s = "rect("; + t = isNum(f.positioning_clip_top.value) ? f.positioning_clip_top.value + f.positioning_clip_top_measurement.value : "auto"; + s += t + " "; + s += t + " "; + s += t + " "; + s += t + ")"; + + if (s != "rect(auto auto auto auto)") + ce.style.clip = s; + } + + ce.style.cssText = ce.style.cssText; +} + +function isNum(s) { + return new RegExp('[0-9]+', 'g').test(s); +} + +function showDisabledControls() { + var f = document.forms, i, a; + + for (i=0; i 1) { + addSelectValue(f, s, p[0], p[1]); + + if (se) + selectByValue(f, s, p[1]); + } else { + addSelectValue(f, s, p[0], p[0]); + + if (se) + selectByValue(f, s, p[0]); + } + } +} + +function toggleSame(ce, pre) { + var el = document.forms[0].elements, i; + + if (ce.checked) { + el[pre + "_top"].disabled = false; + el[pre + "_right"].disabled = true; + el[pre + "_bottom"].disabled = true; + el[pre + "_left"].disabled = true; + + if (el[pre + "_top_measurement"]) { + el[pre + "_top_measurement"].disabled = false; + el[pre + "_right_measurement"].disabled = true; + el[pre + "_bottom_measurement"].disabled = true; + el[pre + "_left_measurement"].disabled = true; + } + } else { + el[pre + "_top"].disabled = false; + el[pre + "_right"].disabled = false; + el[pre + "_bottom"].disabled = false; + el[pre + "_left"].disabled = false; + + if (el[pre + "_top_measurement"]) { + el[pre + "_top_measurement"].disabled = false; + el[pre + "_right_measurement"].disabled = false; + el[pre + "_bottom_measurement"].disabled = false; + el[pre + "_left_measurement"].disabled = false; + } + } + + showDisabledControls(); +} + +function synch(fr, to) { + var f = document.forms[0]; + + f.elements[to].value = f.elements[fr].value; + + if (f.elements[fr + "_measurement"]) + selectByValue(f, to + "_measurement", f.elements[fr + "_measurement"].value); +} + +tinyMCEPopup.onInit.add(init); diff --git a/jscripts/tiny_mce/plugins/style/langs/en_dlg.js b/jscripts/tiny_mce/plugins/style/langs/en_dlg.js new file mode 100644 index 000000000..5026313e2 --- /dev/null +++ b/jscripts/tiny_mce/plugins/style/langs/en_dlg.js @@ -0,0 +1,63 @@ +tinyMCE.addI18n('en.style_dlg',{ +title:"Edit CSS Style", +apply:"Apply", +text_tab:"Text", +background_tab:"Background", +block_tab:"Block", +box_tab:"Box", +border_tab:"Border", +list_tab:"List", +positioning_tab:"Positioning", +text_props:"Text", +text_font:"Font", +text_size:"Size", +text_weight:"Weight", +text_style:"Style", +text_variant:"Variant", +text_lineheight:"Line height", +text_case:"Case", +text_color:"Color", +text_decoration:"Decoration", +text_overline:"overline", +text_underline:"underline", +text_striketrough:"strikethrough", +text_blink:"blink", +text_none:"none", +background_color:"Background color", +background_image:"Background image", +background_repeat:"Repeat", +background_attachment:"Attachment", +background_hpos:"Horizontal position", +background_vpos:"Vertical position", +block_wordspacing:"Word spacing", +block_letterspacing:"Letter spacing", +block_vertical_alignment:"Vertical alignment", +block_text_align:"Text align", +block_text_indent:"Text indent", +block_whitespace:"Whitespace", +block_display:"Display", +box_width:"Width", +box_height:"Height", +box_float:"Float", +box_clear:"Clear", +padding:"Padding", +same:"Same for all", +top:"Top", +right:"Right", +bottom:"Bottom", +left:"Left", +margin:"Margin", +style:"Style", +width:"Width", +height:"Height", +color:"Color", +list_type:"Type", +bullet_image:"Bullet image", +position:"Position", +positioning_type:"Type", +visibility:"Visibility", +zindex:"Z-index", +overflow:"Overflow", +placement:"Placement", +clip:"Clip" +}); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/style/props.htm b/jscripts/tiny_mce/plugins/style/props.htm new file mode 100644 index 000000000..08e02f162 --- /dev/null +++ b/jscripts/tiny_mce/plugins/style/props.htm @@ -0,0 +1,726 @@ + + + + {#style_dlg.title} + + + + + + + + + +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + +
     
    +
    + +
    + + + +
    + + + + + + +
    + +  
    +
    + +
    + + + + + +
     
    +
    {#style_dlg.text_decoration} + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + +
     
    +
    + + + + +
     
    +
    + + + + + + +
     
    +
    + + + + + + +
     
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
     
    +
    + + + + + + +
     
    +
    + + + + + + +
     
    +
    +
    + +
    + + + + + + + + + + + + + + +
    + + + + + + +
     
    +
       
    + + + + + + +
     
    +
       
    +
    +
    + {#style_dlg.padding} + + + + + + + + + + + + + + + + + + + + + + +
     
    + + + + + + +
     
    +
    + + + + + + +
     
    +
    + + + + + + +
     
    +
    + + + + + + +
     
    +
    +
    +
    + +
    +
    + {#style_dlg.margin} + + + + + + + + + + + + + + + + + + + + + + +
     
    + + + + + + +
     
    +
    + + + + + + +
     
    +
    + + + + + + +
     
    +
    + + + + + + +
     
    +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      {#style_dlg.style} {#style_dlg.width} {#style_dlg.color}
          
    {#style_dlg.top}   + + + + + + +
     
    +
      + + + + + +
     
    +
    {#style_dlg.right}   + + + + + + +
     
    +
      + + + + + +
     
    +
    {#style_dlg.bottom}   + + + + + + +
     
    +
      + + + + + +
     
    +
    {#style_dlg.left}   + + + + + + +
     
    +
      + + + + + +
     
    +
    +
    + +
    + + + + + + + + + + + + + + + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
       
    + + + + + + +
     
    +
       
    + + + + + + +
     
    +
       
    + +
    +
    + {#style_dlg.placement} + + + + + + + + + + + + + + + + + + + + + + +
     
    {#style_dlg.top} + + + + + + +
     
    +
    {#style_dlg.right} + + + + + + +
     
    +
    {#style_dlg.bottom} + + + + + + +
     
    +
    {#style_dlg.left} + + + + + + +
     
    +
    +
    +
    + +
    +
    + {#style_dlg.clip} + + + + + + + + + + + + + + + + + + + + + + +
     
    {#style_dlg.top} + + + + + + +
     
    +
    {#style_dlg.right} + + + + + + +
     
    +
    {#style_dlg.bottom} + + + + + + +
     
    +
    {#style_dlg.left} + + + + + + +
     
    +
    +
    +
    +
    +
    +
    + +
    +
    + + +
    + + +
    +
    + +
    +
    +
    + + + diff --git a/jscripts/tiny_mce/plugins/tabfocus/editor_plugin.js b/jscripts/tiny_mce/plugins/tabfocus/editor_plugin.js new file mode 100644 index 000000000..27d244022 --- /dev/null +++ b/jscripts/tiny_mce/plugins/tabfocus/editor_plugin.js @@ -0,0 +1 @@ +(function(){var c=tinymce.DOM,a=tinymce.dom.Event,d=tinymce.each,b=tinymce.explode;tinymce.create("tinymce.plugins.TabFocusPlugin",{init:function(f,g){function e(i,j){if(j.keyCode===9){return a.cancel(j)}}function h(l,p){var j,m,o,n,k;function q(i){o=c.getParent(l.id,"form");n=o.elements;if(o){d(n,function(s,r){if(s.id==l.id){j=r;return false}});if(i>0){for(m=j+1;m=0;m--){if(n[m].type!="hidden"){return n[m]}}}}return null}if(p.keyCode===9){k=b(l.getParam("tab_focus",l.getParam("tabfocus_elements",":prev,:next")));if(k.length==1){k[1]=k[0];k[0]=":prev"}if(p.shiftKey){if(k[0]==":prev"){n=q(-1)}else{n=c.get(k[0])}}else{if(k[1]==":next"){n=q(1)}else{n=c.get(k[1])}}if(n){if(l=tinymce.get(n.id||n.name)){l.focus()}else{window.setTimeout(function(){window.focus();n.focus()},10)}return a.cancel(p)}}}f.onKeyUp.add(e);if(tinymce.isGecko){f.onKeyPress.add(h);f.onKeyDown.add(e)}else{f.onKeyDown.add(h)}f.onInit.add(function(){d(c.select("a:first,a:last",f.getContainer()),function(i){a.add(i,"focus",function(){f.focus()})})})},getInfo:function(){return{longname:"Tabfocus",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("tabfocus",tinymce.plugins.TabFocusPlugin)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/tabfocus/editor_plugin_src.js b/jscripts/tiny_mce/plugins/tabfocus/editor_plugin_src.js new file mode 100644 index 000000000..c2be2f40a --- /dev/null +++ b/jscripts/tiny_mce/plugins/tabfocus/editor_plugin_src.js @@ -0,0 +1,112 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, explode = tinymce.explode; + + tinymce.create('tinymce.plugins.TabFocusPlugin', { + init : function(ed, url) { + function tabCancel(ed, e) { + if (e.keyCode === 9) + return Event.cancel(e); + }; + + function tabHandler(ed, e) { + var x, i, f, el, v; + + function find(d) { + f = DOM.getParent(ed.id, 'form'); + el = f.elements; + + if (f) { + each(el, function(e, i) { + if (e.id == ed.id) { + x = i; + return false; + } + }); + + if (d > 0) { + for (i = x + 1; i < el.length; i++) { + if (el[i].type != 'hidden') + return el[i]; + } + } else { + for (i = x - 1; i >= 0; i--) { + if (el[i].type != 'hidden') + return el[i]; + } + } + } + + return null; + }; + + if (e.keyCode === 9) { + v = explode(ed.getParam('tab_focus', ed.getParam('tabfocus_elements', ':prev,:next'))); + + if (v.length == 1) { + v[1] = v[0]; + v[0] = ':prev'; + } + + // Find element to focus + if (e.shiftKey) { + if (v[0] == ':prev') + el = find(-1); + else + el = DOM.get(v[0]); + } else { + if (v[1] == ':next') + el = find(1); + else + el = DOM.get(v[1]); + } + + if (el) { + if (ed = tinymce.get(el.id || el.name)) + ed.focus(); + else + window.setTimeout(function() {window.focus();el.focus();}, 10); + + return Event.cancel(e); + } + } + }; + + ed.onKeyUp.add(tabCancel); + + if (tinymce.isGecko) { + ed.onKeyPress.add(tabHandler); + ed.onKeyDown.add(tabCancel); + } else + ed.onKeyDown.add(tabHandler); + + ed.onInit.add(function() { + each(DOM.select('a:first,a:last', ed.getContainer()), function(n) { + Event.add(n, 'focus', function() {ed.focus();}); + }); + }); + }, + + getInfo : function() { + return { + longname : 'Tabfocus', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('tabfocus', tinymce.plugins.TabFocusPlugin); +})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/table/cell.htm b/jscripts/tiny_mce/plugins/table/cell.htm new file mode 100644 index 000000000..d243e1d83 --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/cell.htm @@ -0,0 +1,178 @@ + + + + {#table_dlg.cell_title} + + + + + + + + +
    + + +
    +
    +
    + {#table_dlg.general_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + +
    +
    +
    + +
    +
    + {#table_dlg.advanced_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + + + + + +
     
    +
    + + + + + +
     
    +
    + + + + + +
     
    +
    +
    +
    +
    + +
    +
    + +
    + + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/table/css/cell.css b/jscripts/tiny_mce/plugins/table/css/cell.css new file mode 100644 index 000000000..a067ecdfe --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/css/cell.css @@ -0,0 +1,17 @@ +/* CSS file for cell dialog in the table plugin */ + +.panel_wrapper div.current { + height: 200px; +} + +.advfield { + width: 200px; +} + +#action { + margin-bottom: 3px; +} + +#class { + width: 150px; +} \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/table/css/row.css b/jscripts/tiny_mce/plugins/table/css/row.css new file mode 100644 index 000000000..1f7755daf --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/css/row.css @@ -0,0 +1,25 @@ +/* CSS file for row dialog in the table plugin */ + +.panel_wrapper div.current { + height: 200px; +} + +.advfield { + width: 200px; +} + +#action { + margin-bottom: 3px; +} + +#rowtype,#align,#valign,#class,#height { + width: 150px; +} + +#height { + width: 50px; +} + +.col2 { + padding-left: 20px; +} diff --git a/jscripts/tiny_mce/plugins/table/css/table.css b/jscripts/tiny_mce/plugins/table/css/table.css new file mode 100644 index 000000000..d11c3f69c --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/css/table.css @@ -0,0 +1,13 @@ +/* CSS file for table dialog in the table plugin */ + +.panel_wrapper div.current { + height: 245px; +} + +.advfield { + width: 200px; +} + +#class { + width: 150px; +} diff --git a/jscripts/tiny_mce/plugins/table/editor_plugin.js b/jscripts/tiny_mce/plugins/table/editor_plugin.js new file mode 100644 index 000000000..39f2c694b --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/editor_plugin.js @@ -0,0 +1 @@ +(function(b){var c=b.each;function a(E,D,H){var e,I,A,n;r();n=D.getParent(H.getStart(),"th,td");if(n){I=C(n);A=F();n=v(I.x,I.y)}function r(){var J=0;e=[];c(["thead","tbody","tfoot"],function(K){var L=D.select(K+" tr",E);c(L,function(M,N){N+=J;c(D.select("td,th",M),function(T,O){var P,Q,R,S;if(e[N]){while(e[N][O]){O++}}R=g(T,"rowspan");S=g(T,"colspan");for(Q=N;Q'}return false}},"childNodes");J=J.cloneNode(false);J.rowSpan=J.colSpan=1;if(K){J.appendChild(K)}else{if(!b.isIE){J.innerHTML='
    '}}return J}function p(){var J=D.createRng();c(D.select("tr",E),function(K){if(K.cells.length==0){D.remove(K)}});if(D.select("tr",E).length==0){J.setStartAfter(E);J.setEndAfter(E);H.setRng(J);D.remove(E);return}c(D.select("thead,tbody,tfoot",E),function(K){if(K.rows.length==0){D.remove(K)}});r();row=e[Math.min(e.length-1,I.y)];if(row){H.select(row[Math.min(row.length-1,I.x)].elm,true);H.collapse(true)}}function s(P,N,R,O){var M,K,J,L,Q;M=e[N][P].elm.parentNode;for(J=1;J<=R;J++){M=D.getNext(M,"tr");if(M){for(K=P;K>=0;K--){Q=e[N+J][K].elm;if(Q.parentNode==M){for(L=1;L<=O;L++){D.insertAfter(d(Q),Q)}break}}if(K==-1){for(L=1;L<=O;L++){M.insertBefore(d(M.cells[0]),M.cells[0])}}}}}function z(){c(e,function(J,K){c(J,function(M,L){var P,O,Q,N;if(h(M)){M=M.elm;P=g(M,"colspan");O=g(M,"rowspan");if(P>1||O>1){M.colSpan=M.rowSpan=1;for(N=0;N1){O.rowSpan=rowSpan+1;continue}}else{if(J>0&&e[J-1][N]){R=e[J-1][N].elm;rowSpan=g(R,"rowspan");if(rowSpan>1){R.rowSpan=rowSpan+1;continue}}}K=d(O);K.colSpan=O.colSpan;Q.appendChild(K);L=O}}if(Q.hasChildNodes()){if(!M){D.insertAfter(Q,P)}else{P.parentNode.insertBefore(Q,P)}}}function f(K){var L,J;c(e,function(M,N){c(M,function(P,O){if(h(P)){L=O;if(K){return false}}});if(K){return !L}});c(e,function(P,Q){var M=P[L].elm,N,O;if(M!=J){O=g(M,"colspan");N=g(M,"rowspan");if(O==1){if(!K){D.insertAfter(d(M),M);s(L,Q,N-1,O)}else{M.parentNode.insertBefore(d(M),M);s(L,Q,N-1,O)}}else{M.colSpan++}J=M}})}function m(){var J=[];c(e,function(K,L){c(K,function(N,M){if(h(N)&&b.inArray(J,M)===-1){c(e,function(Q){var O=Q[M].elm,P;P=g(O,"colspan");if(P>1){O.colSpan=P-1}else{D.remove(O)}});J.push(M)}})});p()}function l(){var K;function J(N){var M,O,L;M=D.getNext(N,"tr");c(N.cells,function(P){var Q=g(P,"rowspan");if(Q>1){P.rowSpan=Q-1;O=C(P);s(O.x,O.y,1,1)}});O=C(N.cells[0]);c(e[O.y],function(P){var Q;P=P.elm;if(P!=L){Q=g(P,"rowspan");if(Q<=1){D.remove(P)}else{P.rowSpan=Q-1}L=P}})}K=j();c(K.reverse(),function(L){J(L)});p()}function B(){var J=j();D.remove(J);p();return J}function G(){var J=j();c(J,function(L,K){J[K]=L.cloneNode(true)});return J}function w(L,K){var M=j(),J=M[K?0:M.length-1],N=J.cells.length;c(e,function(P){var O;N=0;c(P,function(R,Q){if(R.real){N+=R.colspan}if(R.elm.parentNode==J){O=1}});if(O){return false}});if(!K){L.reverse()}c(L,function(Q){var P=Q.cells.length,O;for(i=0;iK){K=O}if(N>J){J=N}if(P.real){R=P.colspan-1;Q=P.rowspan-1;if(R){if(O+R>K){K=O+R}}if(Q){if(N+Q>J){J=N+Q}}}}})});return{x:K,y:J}}function t(P){var M,L,R,Q,K,J,N,O;A=C(P);if(I&&A){M=Math.min(I.x,A.x);L=Math.min(I.y,A.y);R=Math.max(I.x,A.x);Q=Math.max(I.y,A.y);K=R;J=Q;for(y=L;y<=J;y++){P=e[y][M];if(!P.real){if(M-(P.colspan-1)K){K=x+N}}if(O){if(y+O>J){J=y+O}}}}}D.removeClass(D.select("td.mceSelected,th.mceSelected"),"mceSelected");for(y=L;y<=J;y++){for(x=M;x<=K;x++){D.addClass(e[y][x].elm,"mceSelected")}}}}b.extend(this,{deleteTable:q,split:z,merge:o,insertRow:k,insertCol:f,deleteCols:m,deleteRows:l,cutRows:B,copyRows:G,pasteRows:w,getPos:C,setStartCell:u,setEndCell:t})}b.create("tinymce.plugins.TablePlugin",{init:function(e,f){var d,j;function h(m){var l=e.selection,k=e.dom.getParent(m||l.getNode(),"table");if(k){return new a(k,e.dom,l)}}function g(){e.getBody().style.webkitUserSelect="";e.dom.removeClass(e.dom.select("td.mceSelected,th.mceSelected"),"mceSelected")}c([["table","table.desc","mceInsertTable",true],["delete_table","table.del","mceTableDelete"],["delete_col","table.delete_col_desc","mceTableDeleteCol"],["delete_row","table.delete_row_desc","mceTableDeleteRow"],["col_after","table.col_after_desc","mceTableInsertColAfter"],["col_before","table.col_before_desc","mceTableInsertColBefore"],["row_after","table.row_after_desc","mceTableInsertRowAfter"],["row_before","table.row_before_desc","mceTableInsertRowBefore"],["row_props","table.row_desc","mceTableRowProps",true],["cell_props","table.cell_desc","mceTableCellProps",true],["split_cells","table.split_cells_desc","mceTableSplitCells",true],["merge_cells","table.merge_cells_desc","mceTableMergeCells",true]],function(k){e.addButton(k[0],{title:k[1],cmd:k[2],ui:k[3]})});if(!b.isIE){e.onClick.add(function(k,l){l=l.target;if(l.nodeName==="TABLE"){k.selection.select(l)}})}e.onNodeChange.add(function(l,k,o){var m;o=l.selection.getStart();m=l.dom.getParent(o,"td,th,caption");k.setActive("table",o.nodeName==="TABLE"||!!m);if(m&&m.nodeName==="CAPTION"){m=0}k.setDisabled("delete_table",!m);k.setDisabled("delete_col",!m);k.setDisabled("delete_table",!m);k.setDisabled("delete_row",!m);k.setDisabled("col_after",!m);k.setDisabled("col_before",!m);k.setDisabled("row_after",!m);k.setDisabled("row_before",!m);k.setDisabled("row_props",!m);k.setDisabled("cell_props",!m);k.setDisabled("split_cells",!m);k.setDisabled("merge_cells",!m)});e.onInit.add(function(l){var k,o,p=l.dom,m;d=l.windowManager;l.onMouseDown.add(function(q,r){if(r.button!=2){g();o=p.getParent(r.target,"td,th");k=p.getParent(o,"table")}});p.bind(l.getDoc(),"mouseover",function(t){var r,q,s=t.target;if(o&&(m||s!=o)&&(s.nodeName=="TD"||s.nodeName=="TH")){q=p.getParent(s,"table");if(q==k){if(!m){m=h(q);m.setStartCell(o);l.getBody().style.webkitUserSelect="none"}m.setEndCell(s)}r=l.selection.getSel();if(r.removeAllRanges){r.removeAllRanges()}else{r.empty()}t.preventDefault()}});l.onMouseUp.add(function(z,A){var r,t=z.selection,B,C=t.getSel(),q,u,s,w;if(o){if(m){z.getBody().style.webkitUserSelect=""}function v(D,F){var E=new b.dom.TreeWalker(D,D);do{if(D.nodeType==3&&b.trim(D.nodeValue).length!=0){if(F){r.setStart(D,0)}else{r.setEnd(D,D.nodeValue.length)}return}if(D.nodeName=="BR"){if(F){r.setStartBefore(D)}else{r.setEndBefore(D)}return}}while(D=(F?E.next():E.prev()))}B=p.select("td.mceSelected,th.mceSelected");if(B.length>0){r=p.createRng();u=B[0];w=B[B.length-1];v(u,1);q=new b.dom.TreeWalker(u,p.getParent(B[0],"table"));do{if(u.nodeName=="TD"||u.nodeName=="TH"){if(!p.hasClass(u,"mceSelected")){break}s=u}}while(u=q.next());v(s);t.setRng(r)}z.nodeChanged();o=m=k=null}});l.onKeyUp.add(function(q,r){g()});if(l&&l.plugins.contextmenu){l.plugins.contextmenu.onContextMenu.add(function(s,q,u){var v,t=l.selection,r=t.getNode()||l.getBody();if(l.dom.getParent(u,"td")||l.dom.getParent(u,"th")){q.removeAll();if(r.nodeName=="A"&&!l.dom.getAttrib(r,"name")){q.add({title:"advanced.link_desc",icon:"link",cmd:l.plugins.advlink?"mceAdvLink":"mceLink",ui:true});q.add({title:"advanced.unlink_desc",icon:"unlink",cmd:"UnLink"});q.addSeparator()}if(r.nodeName=="IMG"&&r.className.indexOf("mceItem")==-1){q.add({title:"advanced.image_desc",icon:"image",cmd:l.plugins.advimage?"mceAdvImage":"mceImage",ui:true});q.addSeparator()}q.add({title:"table.desc",icon:"table",cmd:"mceInsertTable",value:{action:"insert"}});q.add({title:"table.props_desc",icon:"table_props",cmd:"mceInsertTable"});q.add({title:"table.del",icon:"delete_table",cmd:"mceTableDelete"});q.addSeparator();v=q.addMenu({title:"table.cell"});v.add({title:"table.cell_desc",icon:"cell_props",cmd:"mceTableCellProps"});v.add({title:"table.split_cells_desc",icon:"split_cells",cmd:"mceTableSplitCells"});v.add({title:"table.merge_cells_desc",icon:"merge_cells",cmd:"mceTableMergeCells"});v=q.addMenu({title:"table.row"});v.add({title:"table.row_desc",icon:"row_props",cmd:"mceTableRowProps"});v.add({title:"table.row_before_desc",icon:"row_before",cmd:"mceTableInsertRowBefore"});v.add({title:"table.row_after_desc",icon:"row_after",cmd:"mceTableInsertRowAfter"});v.add({title:"table.delete_row_desc",icon:"delete_row",cmd:"mceTableDeleteRow"});v.addSeparator();v.add({title:"table.cut_row_desc",icon:"cut",cmd:"mceTableCutRow"});v.add({title:"table.copy_row_desc",icon:"copy",cmd:"mceTableCopyRow"});v.add({title:"table.paste_row_before_desc",icon:"paste",cmd:"mceTablePasteRowBefore"}).setDisabled(!j);v.add({title:"table.paste_row_after_desc",icon:"paste",cmd:"mceTablePasteRowAfter"}).setDisabled(!j);v=q.addMenu({title:"table.col"});v.add({title:"table.col_before_desc",icon:"col_before",cmd:"mceTableInsertColBefore"});v.add({title:"table.col_after_desc",icon:"col_after",cmd:"mceTableInsertColAfter"});v.add({title:"table.delete_col_desc",icon:"delete_col",cmd:"mceTableDeleteCol"})}else{q.add({title:"table.desc",icon:"table",cmd:"mceInsertTable"})}})}if(!b.isIE){function n(){var q;for(q=l.getBody().lastChild;q&&q.nodeType==3&&!q.nodeValue.length;q=q.previousSibling){}if(q&&q.nodeName=="TABLE"){l.dom.add(l.getBody(),"p",null,'
    ')}}if(b.isGecko){l.onKeyDown.add(function(r,t){var q,s,u=r.dom;if(t.keyCode==37||t.keyCode==38){q=r.selection.getRng();s=u.getParent(q.startContainer,"table");if(s&&r.getBody().firstChild==s){if(isAtStart(q,s)){q=u.createRng();q.setStartBefore(s);q.setEndBefore(s);r.selection.setRng(q);t.preventDefault()}}}})}l.onKeyUp.add(n);l.onSetContent.add(n);l.onVisualAid.add(n);l.onPreProcess.add(function(q,s){var r=s.node.lastChild;if(r&&r.childNodes.length==1&&r.firstChild.nodeName=="BR"){q.dom.remove(r)}});n()}});c({mceTableSplitCells:function(k){k.split()},mceTableMergeCells:function(l){var m,n,k;k=e.dom.getParent(e.selection.getNode(),"th,td");if(k){m=k.rowSpan;n=k.colSpan}if(!e.dom.select("td.mceSelected,th.mceSelected").length){d.open({url:f+"/merge_cells.htm",width:240+parseInt(e.getLang("table.merge_cells_delta_width",0)),height:110+parseInt(e.getLang("table.merge_cells_delta_height",0)),inline:1},{rows:m,cols:n,onaction:function(o){l.merge(k,o.cols,o.rows)},plugin_url:f})}else{l.merge()}},mceTableInsertRowBefore:function(k){k.insertRow(true)},mceTableInsertRowAfter:function(k){k.insertRow()},mceTableInsertColBefore:function(k){k.insertCol(true)},mceTableInsertColAfter:function(k){k.insertCol()},mceTableDeleteCol:function(k){k.deleteCols()},mceTableDeleteRow:function(k){k.deleteRows()},mceTableCutRow:function(k){j=k.cutRows()},mceTableCopyRow:function(k){j=k.copyRows()},mceTablePasteRowBefore:function(k){k.pasteRows(j,true)},mceTablePasteRowAfter:function(k){k.pasteRows(j)},mceTableDelete:function(k){k.deleteTable()}},function(l,k){e.addCommand(k,function(){var m=h();if(m){l(m);e.execCommand("mceRepaint");g()}})});c({mceInsertTable:function(k){d.open({url:f+"/table.htm",width:400+parseInt(e.getLang("table.table_delta_width",0)),height:320+parseInt(e.getLang("table.table_delta_height",0)),inline:1},{plugin_url:f,action:k?k.action:0})},mceTableRowProps:function(){d.open({url:f+"/row.htm",width:400+parseInt(e.getLang("table.rowprops_delta_width",0)),height:295+parseInt(e.getLang("table.rowprops_delta_height",0)),inline:1},{plugin_url:f})},mceTableCellProps:function(){d.open({url:f+"/cell.htm",width:400+parseInt(e.getLang("table.cellprops_delta_width",0)),height:295+parseInt(e.getLang("table.cellprops_delta_height",0)),inline:1},{plugin_url:f})}},function(l,k){e.addCommand(k,function(m,n){l(n)})})}});b.PluginManager.add("table",b.plugins.TablePlugin)})(tinymce); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/table/editor_plugin_src.js b/jscripts/tiny_mce/plugins/table/editor_plugin_src.js new file mode 100644 index 000000000..2260f34a3 --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/editor_plugin_src.js @@ -0,0 +1,1118 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function(tinymce) { + var each = tinymce.each; + + /** + * Table Grid class. + */ + function TableGrid(table, dom, selection) { + var grid, startPos, endPos, selectedCell; + + buildGrid(); + selectedCell = dom.getParent(selection.getStart(), 'th,td'); + if (selectedCell) { + startPos = getPos(selectedCell); + endPos = findEndPos(); + selectedCell = getCell(startPos.x, startPos.y); + } + + function buildGrid() { + var startY = 0; + + grid = []; + + each(['thead', 'tbody', 'tfoot'], function(part) { + var rows = dom.select(part + ' tr', table); + + each(rows, function(tr, y) { + y += startY; + + each(dom.select('td,th', tr), function(td, x) { + var x2, y2, rowspan, colspan; + + // Skip over existing cells produced by rowspan + if (grid[y]) { + while (grid[y][x]) + x++; + } + + // Get col/rowspan from cell + rowspan = getSpanVal(td, 'rowspan'); + colspan = getSpanVal(td, 'colspan'); + + // Fill out rowspan/colspan right and down + for (y2 = y; y2 < y + rowspan; y2++) { + if (!grid[y2]) + grid[y2] = []; + + for (x2 = x; x2 < x + colspan; x2++) { + grid[y2][x2] = { + part : part, + real : y2 == y && x2 == x, + elm : td, + rowspan : rowspan, + colspan : colspan + }; + } + } + }); + }); + + startY += rows.length; + }); + }; + + function getCell(x, y) { + var row; + + row = grid[y]; + if (row) + return row[x]; + }; + + function getSpanVal(td, name) { + return parseInt(td.getAttribute(name) || 1); + }; + + function isCellSelected(cell) { + return dom.hasClass(cell.elm, 'mceSelected') || cell == selectedCell; + }; + + function getSelectedRows() { + var rows = []; + + each(table.rows, function(row) { + each(row.cells, function(cell) { + if (dom.hasClass(cell, 'mceSelected') || cell == selectedCell.elm) { + rows.push(row); + return false; + } + }); + }); + + return rows; + }; + + function deleteTable() { + var rng = dom.createRng(); + + rng.setStartAfter(table); + rng.setEndAfter(table); + + selection.setRng(rng); + + dom.remove(table); + }; + + function cloneCell(cell) { + var formatNode; + + // Clone formats + tinymce.walk(cell, function(node) { + var curNode; + + if (node.nodeType == 3) { + each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) { + node = node.cloneNode(false); + + if (!formatNode) + formatNode = curNode = node; + else if (curNode) + curNode.appendChild(node); + + curNode = node; + }); + + // Add something to the inner node + if (curNode) + curNode.innerHTML = tinymce.isIE ? ' ' : '
    '; + + return false; + } + }, 'childNodes'); + + cell = cell.cloneNode(false); + cell.rowSpan = cell.colSpan = 1; + + if (formatNode) { + cell.appendChild(formatNode); + } else { + if (!tinymce.isIE) + cell.innerHTML = '
    '; + } + + return cell; + }; + + function cleanup() { + var rng = dom.createRng(); + + // Empty rows + each(dom.select('tr', table), function(tr) { + if (tr.cells.length == 0) + dom.remove(tr); + }); + + // Empty table + if (dom.select('tr', table).length == 0) { + rng.setStartAfter(table); + rng.setEndAfter(table); + selection.setRng(rng); + dom.remove(table); + return; + } + + // Empty header/body/footer + each(dom.select('thead,tbody,tfoot', table), function(part) { + if (part.rows.length == 0) + dom.remove(part); + }); + + // Restore selection to start position if it still exists + buildGrid(); + + // Restore the selection to the closest table position + row = grid[Math.min(grid.length - 1, startPos.y)]; + if (row) { + selection.select(row[Math.min(row.length - 1, startPos.x)].elm, true); + selection.collapse(true); + } + }; + + function fillLeftDown(x, y, rows, cols) { + var tr, x2, r, c, cell; + + tr = grid[y][x].elm.parentNode; + for (r = 1; r <= rows; r++) { + tr = dom.getNext(tr, 'tr'); + + if (tr) { + // Loop left to find real cell + for (x2 = x; x2 >= 0; x2--) { + cell = grid[y + r][x2].elm; + + if (cell.parentNode == tr) { + // Append clones after + for (c = 1; c <= cols; c++) + dom.insertAfter(cloneCell(cell), cell); + + break; + } + } + + if (x2 == -1) { + // Insert nodes before first cell + for (c = 1; c <= cols; c++) + tr.insertBefore(cloneCell(tr.cells[0]), tr.cells[0]); + } + } + } + }; + + function split() { + each(grid, function(row, y) { + each(row, function(cell, x) { + var colSpan, rowSpan, newCell, i; + + if (isCellSelected(cell)) { + cell = cell.elm; + colSpan = getSpanVal(cell, 'colspan'); + rowSpan = getSpanVal(cell, 'rowspan'); + + if (colSpan > 1 || rowSpan > 1) { + cell.colSpan = cell.rowSpan = 1; + + // Insert cells right + for (i = 0; i < colSpan - 1; i++) + dom.insertAfter(cloneCell(cell), cell); + + fillLeftDown(x, y, rowSpan - 1, colSpan); + } + } + }); + }); + }; + + function merge(cell, cols, rows) { + var startX, startY, endX, endY, x, y, startCell, endCell, cell, children; + + // Use specified cell and cols/rows + if (cell) { + pos = getPos(cell); + startX = pos.x; + startY = pos.y; + endX = startX + (cols - 1); + endY = startY + (rows - 1); + } else { + // Use selection + startX = startPos.x; + startY = startPos.y; + endX = endPos.x; + endY = endPos.y; + } + + // Find start/end cells + startCell = getCell(startX, startY); + endCell = getCell(endX, endY); + + // Check if the cells exists and if they are of the same part for example tbody = tbody + if (startCell && endCell && startCell.part == endCell.part) { + // Split and rebuild grid + split(); + buildGrid(); + + // Set row/col span to start cell + startCell = getCell(startX, startY).elm; + startCell.colSpan = (endX - startX) + 1; + startCell.rowSpan = (endY - startY) + 1; + + // Remove other cells and add it's contents to the start cell + for (y = startY; y <= endY; y++) { + for (x = startX; x <= endX; x++) { + cell = grid[y][x].elm; + + if (cell != startCell) { + // Move children to startCell + children = tinymce.grep(cell.childNodes); + each(children, function(node, i) { + // Jump over last BR element + if (node.nodeName != 'BR' || i != children.length - 1) + startCell.appendChild(node); + }); + + // Remove cell + dom.remove(cell); + } + } + } + + // Remove empty rows etc and restore caret location + cleanup(); + } + }; + + function insertRow(before) { + var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell; + + // Find first/last row + each(grid, function(row, y) { + each(row, function(cell, x) { + if (isCellSelected(cell)) { + cell = cell.elm; + rowElm = cell.parentNode; + newRow = rowElm.cloneNode(false); + posY = y; + + if (before) + return false; + } + }); + + if (before) + return !posY; + }); + + for (x = 0; x < grid[0].length; x++) { + cell = grid[posY][x].elm; + + if (cell != lastCell) { + if (!before) { + rowSpan = getSpanVal(cell, 'rowspan'); + if (rowSpan > 1) { + cell.rowSpan = rowSpan + 1; + continue; + } + } else { + // Check if cell above can be expanded + if (posY > 0 && grid[posY - 1][x]) { + otherCell = grid[posY - 1][x].elm; + rowSpan = getSpanVal(otherCell, 'rowspan'); + if (rowSpan > 1) { + otherCell.rowSpan = rowSpan + 1; + continue; + } + } + } + + // Insert new cell into new row + newCell = cloneCell(cell) + newCell.colSpan = cell.colSpan; + newRow.appendChild(newCell); + + lastCell = cell; + } + } + + if (newRow.hasChildNodes()) { + if (!before) + dom.insertAfter(newRow, rowElm); + else + rowElm.parentNode.insertBefore(newRow, rowElm); + } + }; + + function insertCol(before) { + var posX, lastCell; + + // Find first/last column + each(grid, function(row, y) { + each(row, function(cell, x) { + if (isCellSelected(cell)) { + posX = x; + + if (before) + return false; + } + }); + + if (before) + return !posX; + }); + + each(grid, function(row, y) { + var cell = row[posX].elm, rowSpan, colSpan; + + if (cell != lastCell) { + colSpan = getSpanVal(cell, 'colspan'); + rowSpan = getSpanVal(cell, 'rowspan'); + + if (colSpan == 1) { + if (!before) { + dom.insertAfter(cloneCell(cell), cell); + fillLeftDown(posX, y, rowSpan - 1, colSpan); + } else { + cell.parentNode.insertBefore(cloneCell(cell), cell); + fillLeftDown(posX, y, rowSpan - 1, colSpan); + } + } else + cell.colSpan++; + + lastCell = cell; + } + }); + }; + + function deleteCols() { + var cols = []; + + // Get selected column indexes + each(grid, function(row, y) { + each(row, function(cell, x) { + if (isCellSelected(cell) && tinymce.inArray(cols, x) === -1) { + each(grid, function(row) { + var cell = row[x].elm, colSpan; + + colSpan = getSpanVal(cell, 'colspan'); + + if (colSpan > 1) + cell.colSpan = colSpan - 1; + else + dom.remove(cell); + }); + + cols.push(x); + } + }); + }); + + cleanup(); + }; + + function deleteRows() { + var rows; + + function deleteRow(tr) { + var nextTr, pos, lastCell; + + nextTr = dom.getNext(tr, 'tr'); + + // Move down row spanned cells + each(tr.cells, function(cell) { + var rowSpan = getSpanVal(cell, 'rowspan'); + + if (rowSpan > 1) { + cell.rowSpan = rowSpan - 1; + pos = getPos(cell); + fillLeftDown(pos.x, pos.y, 1, 1); + } + }); + + // Delete cells + pos = getPos(tr.cells[0]); + each(grid[pos.y], function(cell) { + var rowSpan; + + cell = cell.elm; + + if (cell != lastCell) { + rowSpan = getSpanVal(cell, 'rowspan'); + + if (rowSpan <= 1) + dom.remove(cell); + else + cell.rowSpan = rowSpan - 1; + + lastCell = cell; + } + }); + }; + + // Get selected rows and move selection out of scope + rows = getSelectedRows(); + + // Delete all selected rows + each(rows.reverse(), function(tr) { + deleteRow(tr); + }); + + cleanup(); + }; + + function cutRows() { + var rows = getSelectedRows(); + + dom.remove(rows); + cleanup(); + + return rows; + }; + + function copyRows() { + var rows = getSelectedRows(); + + each(rows, function(row, i) { + rows[i] = row.cloneNode(true); + }); + + return rows; + }; + + function pasteRows(rows, before) { + var selectedRows = getSelectedRows(), + targetRow = selectedRows[before ? 0 : selectedRows.length - 1], + targetCellCount = targetRow.cells.length; + + // Calc target cell count + each(grid, function(row) { + var match; + + targetCellCount = 0; + each(row, function(cell, x) { + if (cell.real) + targetCellCount += cell.colspan; + + if (cell.elm.parentNode == targetRow) + match = 1; + }); + + if (match) + return false; + }); + + if (!before) + rows.reverse(); + + each(rows, function(row) { + var cellCount = row.cells.length, cell; + + // Remove col/rowspans + for (i = 0; i < cellCount; i++) { + cell = row.cells[i]; + cell.colSpan = cell.rowSpan = 1; + } + + // Needs more cells + for (i = cellCount; i < targetCellCount; i++) + row.appendChild(cloneCell(row.cells[cellCount - 1])); + + // Needs less cells + for (i = targetCellCount; i < cellCount; i++) + dom.remove(row.cells[i]); + + // Add before/after + if (before) + targetRow.parentNode.insertBefore(row, targetRow); + else + dom.insertAfter(row, targetRow); + }); + }; + + function getPos(target) { + var pos; + + each(grid, function(row, y) { + each(row, function(cell, x) { + if (cell.elm == target) { + pos = {x : x, y : y}; + return false; + } + }); + + return !pos; + }); + + return pos; + }; + + function setStartCell(cell) { + startPos = getPos(cell); + }; + + function findEndPos() { + var pos, maxX, maxY; + + maxX = maxY = 0; + + each(grid, function(row, y) { + each(row, function(cell, x) { + var colSpan, rowSpan; + + if (isCellSelected(cell)) { + cell = grid[y][x]; + + if (x > maxX) + maxX = x; + + if (y > maxY) + maxY = y; + + if (cell.real) { + colSpan = cell.colspan - 1; + rowSpan = cell.rowspan - 1; + + if (colSpan) { + if (x + colSpan > maxX) + maxX = x + colSpan; + } + + if (rowSpan) { + if (y + rowSpan > maxY) + maxY = y + rowSpan; + } + } + } + }); + }); + + return {x : maxX, y : maxY}; + }; + + function setEndCell(cell) { + var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan; + + endPos = getPos(cell); + + if (startPos && endPos) { + // Get start/end positions + startX = Math.min(startPos.x, endPos.x); + startY = Math.min(startPos.y, endPos.y); + endX = Math.max(startPos.x, endPos.x); + endY = Math.max(startPos.y, endPos.y); + + // Expand end positon to include spans + maxX = endX; + maxY = endY; + + // Expand startX + for (y = startY; y <= maxY; y++) { + cell = grid[y][startX]; + + if (!cell.real) { + if (startX - (cell.colspan - 1) < startX) + startX -= cell.colspan - 1; + } + } + + // Expand startY + for (x = startX; x <= maxX; x++) { + cell = grid[startY][x]; + + if (!cell.real) { + if (startY - (cell.rowspan - 1) < startY) + startY -= cell.rowspan - 1; + } + } + + // Find max X, Y + for (y = startY; y <= endY; y++) { + for (x = startX; x <= endX; x++) { + cell = grid[y][x]; + + if (cell.real) { + colSpan = cell.colspan - 1; + rowSpan = cell.rowspan - 1; + + if (colSpan) { + if (x + colSpan > maxX) + maxX = x + colSpan; + } + + if (rowSpan) { + if (y + rowSpan > maxY) + maxY = y + rowSpan; + } + } + } + } + + // Remove current selection + dom.removeClass(dom.select('td.mceSelected,th.mceSelected'), 'mceSelected'); + + // Add new selection + for (y = startY; y <= maxY; y++) { + for (x = startX; x <= maxX; x++) + dom.addClass(grid[y][x].elm, 'mceSelected'); + } + } + }; + + // Expose to public + tinymce.extend(this, { + deleteTable : deleteTable, + split : split, + merge : merge, + insertRow : insertRow, + insertCol : insertCol, + deleteCols : deleteCols, + deleteRows : deleteRows, + cutRows : cutRows, + copyRows : copyRows, + pasteRows : pasteRows, + getPos : getPos, + setStartCell : setStartCell, + setEndCell : setEndCell + }); + }; + + tinymce.create('tinymce.plugins.TablePlugin', { + init : function(ed, url) { + var winMan, clipboardRows; + + function createTableGrid(node) { + var selection = ed.selection, tblElm = ed.dom.getParent(node || selection.getNode(), 'table'); + + if (tblElm) + return new TableGrid(tblElm, ed.dom, selection); + }; + + function cleanup() { + // Restore selection possibilities + ed.getBody().style.webkitUserSelect = ''; + ed.dom.removeClass(ed.dom.select('td.mceSelected,th.mceSelected'), 'mceSelected'); + }; + + // Register buttons + each([ + ['table', 'table.desc', 'mceInsertTable', true], + ['delete_table', 'table.del', 'mceTableDelete'], + ['delete_col', 'table.delete_col_desc', 'mceTableDeleteCol'], + ['delete_row', 'table.delete_row_desc', 'mceTableDeleteRow'], + ['col_after', 'table.col_after_desc', 'mceTableInsertColAfter'], + ['col_before', 'table.col_before_desc', 'mceTableInsertColBefore'], + ['row_after', 'table.row_after_desc', 'mceTableInsertRowAfter'], + ['row_before', 'table.row_before_desc', 'mceTableInsertRowBefore'], + ['row_props', 'table.row_desc', 'mceTableRowProps', true], + ['cell_props', 'table.cell_desc', 'mceTableCellProps', true], + ['split_cells', 'table.split_cells_desc', 'mceTableSplitCells', true], + ['merge_cells', 'table.merge_cells_desc', 'mceTableMergeCells', true] + ], function(c) { + ed.addButton(c[0], {title : c[1], cmd : c[2], ui : c[3]}); + }); + + // Select whole table is a table border is clicked + if (!tinymce.isIE) { + ed.onClick.add(function(ed, e) { + e = e.target; + + if (e.nodeName === 'TABLE') + ed.selection.select(e); + }); + } + + // Handle node change updates + ed.onNodeChange.add(function(ed, cm, n) { + var p; + + n = ed.selection.getStart(); + p = ed.dom.getParent(n, 'td,th,caption'); + cm.setActive('table', n.nodeName === 'TABLE' || !!p); + + // Disable table tools if we are in caption + if (p && p.nodeName === 'CAPTION') + p = 0; + + cm.setDisabled('delete_table', !p); + cm.setDisabled('delete_col', !p); + cm.setDisabled('delete_table', !p); + cm.setDisabled('delete_row', !p); + cm.setDisabled('col_after', !p); + cm.setDisabled('col_before', !p); + cm.setDisabled('row_after', !p); + cm.setDisabled('row_before', !p); + cm.setDisabled('row_props', !p); + cm.setDisabled('cell_props', !p); + cm.setDisabled('split_cells', !p); + cm.setDisabled('merge_cells', !p); + }); + + ed.onInit.add(function(ed) { + var startTable, startCell, dom = ed.dom, tableGrid; + + winMan = ed.windowManager; + + // Add cell selection logic + ed.onMouseDown.add(function(ed, e) { + if (e.button != 2) { + cleanup(); + + startCell = dom.getParent(e.target, 'td,th'); + startTable = dom.getParent(startCell, 'table'); + } + }); + + dom.bind(ed.getDoc(), 'mouseover', function(e) { + var sel, table, target = e.target; + + if (startCell && (tableGrid || target != startCell) && (target.nodeName == 'TD' || target.nodeName == 'TH')) { + table = dom.getParent(target, 'table'); + if (table == startTable) { + if (!tableGrid) { + tableGrid = createTableGrid(table); + tableGrid.setStartCell(startCell); + + ed.getBody().style.webkitUserSelect = 'none'; + } + + tableGrid.setEndCell(target); + } + + // Remove current selection + sel = ed.selection.getSel(); + + if (sel.removeAllRanges) + sel.removeAllRanges(); + else + sel.empty(); + + e.preventDefault(); + } + }); + + ed.onMouseUp.add(function(ed, e) { + var rng, sel = ed.selection, selectedCells, nativeSel = sel.getSel(), walker, node, lastNode, endNode; + + // Move selection to startCell + if (startCell) { + if (tableGrid) + ed.getBody().style.webkitUserSelect = ''; + + function setPoint(node, start) { + var walker = new tinymce.dom.TreeWalker(node, node); + + do { + // Text node + if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) { + if (start) + rng.setStart(node, 0); + else + rng.setEnd(node, node.nodeValue.length); + + return; + } + + // BR element + if (node.nodeName == 'BR') { + if (start) + rng.setStartBefore(node); + else + rng.setEndBefore(node); + + return; + } + } while (node = (start ? walker.next() : walker.prev())); + }; + + // Try to expand text selection as much as we can only Gecko supports cell selection + selectedCells = dom.select('td.mceSelected,th.mceSelected'); + if (selectedCells.length > 0) { + rng = dom.createRng(); + node = selectedCells[0]; + endNode = selectedCells[selectedCells.length - 1]; + + setPoint(node, 1); + walker = new tinymce.dom.TreeWalker(node, dom.getParent(selectedCells[0], 'table')); + + do { + if (node.nodeName == 'TD' || node.nodeName == 'TH') { + if (!dom.hasClass(node, 'mceSelected')) + break; + + lastNode = node; + } + } while (node = walker.next()); + + setPoint(lastNode); + + sel.setRng(rng); + } + + ed.nodeChanged(); + startCell = tableGrid = startTable = null; + } + }); + + ed.onKeyUp.add(function(ed, e) { + cleanup(); + }); + + // Add context menu + if (ed && ed.plugins.contextmenu) { + ed.plugins.contextmenu.onContextMenu.add(function(th, m, e) { + var sm, se = ed.selection, el = se.getNode() || ed.getBody(); + + if (ed.dom.getParent(e, 'td') || ed.dom.getParent(e, 'th')) { + m.removeAll(); + + if (el.nodeName == 'A' && !ed.dom.getAttrib(el, 'name')) { + m.add({title : 'advanced.link_desc', icon : 'link', cmd : ed.plugins.advlink ? 'mceAdvLink' : 'mceLink', ui : true}); + m.add({title : 'advanced.unlink_desc', icon : 'unlink', cmd : 'UnLink'}); + m.addSeparator(); + } + + if (el.nodeName == 'IMG' && el.className.indexOf('mceItem') == -1) { + m.add({title : 'advanced.image_desc', icon : 'image', cmd : ed.plugins.advimage ? 'mceAdvImage' : 'mceImage', ui : true}); + m.addSeparator(); + } + + m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable', value : {action : 'insert'}}); + m.add({title : 'table.props_desc', icon : 'table_props', cmd : 'mceInsertTable'}); + m.add({title : 'table.del', icon : 'delete_table', cmd : 'mceTableDelete'}); + m.addSeparator(); + + // Cell menu + sm = m.addMenu({title : 'table.cell'}); + sm.add({title : 'table.cell_desc', icon : 'cell_props', cmd : 'mceTableCellProps'}); + sm.add({title : 'table.split_cells_desc', icon : 'split_cells', cmd : 'mceTableSplitCells'}); + sm.add({title : 'table.merge_cells_desc', icon : 'merge_cells', cmd : 'mceTableMergeCells'}); + + // Row menu + sm = m.addMenu({title : 'table.row'}); + sm.add({title : 'table.row_desc', icon : 'row_props', cmd : 'mceTableRowProps'}); + sm.add({title : 'table.row_before_desc', icon : 'row_before', cmd : 'mceTableInsertRowBefore'}); + sm.add({title : 'table.row_after_desc', icon : 'row_after', cmd : 'mceTableInsertRowAfter'}); + sm.add({title : 'table.delete_row_desc', icon : 'delete_row', cmd : 'mceTableDeleteRow'}); + sm.addSeparator(); + sm.add({title : 'table.cut_row_desc', icon : 'cut', cmd : 'mceTableCutRow'}); + sm.add({title : 'table.copy_row_desc', icon : 'copy', cmd : 'mceTableCopyRow'}); + sm.add({title : 'table.paste_row_before_desc', icon : 'paste', cmd : 'mceTablePasteRowBefore'}).setDisabled(!clipboardRows); + sm.add({title : 'table.paste_row_after_desc', icon : 'paste', cmd : 'mceTablePasteRowAfter'}).setDisabled(!clipboardRows); + + // Column menu + sm = m.addMenu({title : 'table.col'}); + sm.add({title : 'table.col_before_desc', icon : 'col_before', cmd : 'mceTableInsertColBefore'}); + sm.add({title : 'table.col_after_desc', icon : 'col_after', cmd : 'mceTableInsertColAfter'}); + sm.add({title : 'table.delete_col_desc', icon : 'delete_col', cmd : 'mceTableDeleteCol'}); + } else + m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable'}); + }); + } + + // Fixes an issue on Gecko where it's impossible to place the caret behind a table + // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled + if (!tinymce.isIE) { + function fixTableCaretPos() { + var last; + + // Skip empty text nodes form the end + for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ; + + if (last && last.nodeName == 'TABLE') + ed.dom.add(ed.getBody(), 'p', null, '
    '); + }; + + // Fixes an bug where it's impossible to place the caret before a table in Gecko + // this fix solves it by detecting when the caret is at the beginning of such a table + // and then manually moves the caret infront of the table + if (tinymce.isGecko) { + ed.onKeyDown.add(function(ed, e) { + var rng, table, dom = ed.dom; + + // On gecko it's not possible to place the caret before a table + if (e.keyCode == 37 || e.keyCode == 38) { + rng = ed.selection.getRng(); + table = dom.getParent(rng.startContainer, 'table'); + + if (table && ed.getBody().firstChild == table) { + if (isAtStart(rng, table)) { + rng = dom.createRng(); + + rng.setStartBefore(table); + rng.setEndBefore(table); + + ed.selection.setRng(rng); + + e.preventDefault(); + } + } + } + }); + } + + ed.onKeyUp.add(fixTableCaretPos); + ed.onSetContent.add(fixTableCaretPos); + ed.onVisualAid.add(fixTableCaretPos); + + ed.onPreProcess.add(function(ed, o) { + var last = o.node.lastChild; + + if (last && last.childNodes.length == 1 && last.firstChild.nodeName == 'BR') + ed.dom.remove(last); + }); + + fixTableCaretPos(); + } + }); + + // Register action commands + each({ + mceTableSplitCells : function(grid) { + grid.split(); + }, + + mceTableMergeCells : function(grid) { + var rowSpan, colSpan, cell; + + cell = ed.dom.getParent(ed.selection.getNode(), 'th,td'); + if (cell) { + rowSpan = cell.rowSpan; + colSpan = cell.colSpan; + } + + if (!ed.dom.select('td.mceSelected,th.mceSelected').length) { + winMan.open({ + url : url + '/merge_cells.htm', + width : 240 + parseInt(ed.getLang('table.merge_cells_delta_width', 0)), + height : 110 + parseInt(ed.getLang('table.merge_cells_delta_height', 0)), + inline : 1 + }, { + rows : rowSpan, + cols : colSpan, + onaction : function(data) { + grid.merge(cell, data.cols, data.rows); + }, + plugin_url : url + }); + } else + grid.merge(); + }, + + mceTableInsertRowBefore : function(grid) { + grid.insertRow(true); + }, + + mceTableInsertRowAfter : function(grid) { + grid.insertRow(); + }, + + mceTableInsertColBefore : function(grid) { + grid.insertCol(true); + }, + + mceTableInsertColAfter : function(grid) { + grid.insertCol(); + }, + + mceTableDeleteCol : function(grid) { + grid.deleteCols(); + }, + + mceTableDeleteRow : function(grid) { + grid.deleteRows(); + }, + + mceTableCutRow : function(grid) { + clipboardRows = grid.cutRows(); + }, + + mceTableCopyRow : function(grid) { + clipboardRows = grid.copyRows(); + }, + + mceTablePasteRowBefore : function(grid) { + grid.pasteRows(clipboardRows, true); + }, + + mceTablePasteRowAfter : function(grid) { + grid.pasteRows(clipboardRows); + }, + + mceTableDelete : function(grid) { + grid.deleteTable(); + } + }, function(func, name) { + ed.addCommand(name, function() { + var grid = createTableGrid(); + + if (grid) { + func(grid); + ed.execCommand('mceRepaint'); + cleanup(); + } + }); + }); + + // Register dialog commands + each({ + mceInsertTable : function(val) { + winMan.open({ + url : url + '/table.htm', + width : 400 + parseInt(ed.getLang('table.table_delta_width', 0)), + height : 320 + parseInt(ed.getLang('table.table_delta_height', 0)), + inline : 1 + }, { + plugin_url : url, + action : val ? val.action : 0 + }); + }, + + mceTableRowProps : function() { + winMan.open({ + url : url + '/row.htm', + width : 400 + parseInt(ed.getLang('table.rowprops_delta_width', 0)), + height : 295 + parseInt(ed.getLang('table.rowprops_delta_height', 0)), + inline : 1 + }, { + plugin_url : url + }); + }, + + mceTableCellProps : function() { + winMan.open({ + url : url + '/cell.htm', + width : 400 + parseInt(ed.getLang('table.cellprops_delta_width', 0)), + height : 295 + parseInt(ed.getLang('table.cellprops_delta_height', 0)), + inline : 1 + }, { + plugin_url : url + }); + } + }, function(func, name) { + ed.addCommand(name, function(ui, val) { + func(val); + }); + }); + } + }); + + // Register plugin + tinymce.PluginManager.add('table', tinymce.plugins.TablePlugin); +})(tinymce); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/table/js/cell.js b/jscripts/tiny_mce/plugins/table/js/cell.js new file mode 100644 index 000000000..f24619172 --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/js/cell.js @@ -0,0 +1,286 @@ +tinyMCEPopup.requireLangPack(); + +var ed; + +function init() { + ed = tinyMCEPopup.editor; + tinyMCEPopup.resizeToInnerSize(); + + document.getElementById('backgroundimagebrowsercontainer').innerHTML = getBrowserHTML('backgroundimagebrowser','backgroundimage','image','table'); + document.getElementById('bordercolor_pickcontainer').innerHTML = getColorPickerHTML('bordercolor_pick','bordercolor'); + document.getElementById('bgcolor_pickcontainer').innerHTML = getColorPickerHTML('bgcolor_pick','bgcolor') + + var inst = ed; + var tdElm = ed.dom.getParent(ed.selection.getStart(), "td,th"); + var formObj = document.forms[0]; + var st = ed.dom.parseStyle(ed.dom.getAttrib(tdElm, "style")); + + // Get table cell data + var celltype = tdElm.nodeName.toLowerCase(); + var align = ed.dom.getAttrib(tdElm, 'align'); + var valign = ed.dom.getAttrib(tdElm, 'valign'); + var width = trimSize(getStyle(tdElm, 'width', 'width')); + var height = trimSize(getStyle(tdElm, 'height', 'height')); + var bordercolor = convertRGBToHex(getStyle(tdElm, 'bordercolor', 'borderLeftColor')); + var bgcolor = convertRGBToHex(getStyle(tdElm, 'bgcolor', 'backgroundColor')); + var className = ed.dom.getAttrib(tdElm, 'class'); + var backgroundimage = getStyle(tdElm, 'background', 'backgroundImage').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1");; + var id = ed.dom.getAttrib(tdElm, 'id'); + var lang = ed.dom.getAttrib(tdElm, 'lang'); + var dir = ed.dom.getAttrib(tdElm, 'dir'); + var scope = ed.dom.getAttrib(tdElm, 'scope'); + + // Setup form + addClassesToList('class', 'table_cell_styles'); + TinyMCE_EditableSelects.init(); + + if (!ed.dom.hasClass(tdElm, 'mceSelected')) { + formObj.bordercolor.value = bordercolor; + formObj.bgcolor.value = bgcolor; + formObj.backgroundimage.value = backgroundimage; + formObj.width.value = width; + formObj.height.value = height; + formObj.id.value = id; + formObj.lang.value = lang; + formObj.style.value = ed.dom.serializeStyle(st); + selectByValue(formObj, 'align', align); + selectByValue(formObj, 'valign', valign); + selectByValue(formObj, 'class', className, true, true); + selectByValue(formObj, 'celltype', celltype); + selectByValue(formObj, 'dir', dir); + selectByValue(formObj, 'scope', scope); + + // Resize some elements + if (isVisible('backgroundimagebrowser')) + document.getElementById('backgroundimage').style.width = '180px'; + + updateColor('bordercolor_pick', 'bordercolor'); + updateColor('bgcolor_pick', 'bgcolor'); + } else + tinyMCEPopup.dom.hide('action'); +} + +function updateAction() { + var el, inst = ed, tdElm, trElm, tableElm, formObj = document.forms[0]; + + tinyMCEPopup.restoreSelection(); + el = ed.selection.getStart(); + tdElm = ed.dom.getParent(el, "td,th"); + trElm = ed.dom.getParent(el, "tr"); + tableElm = ed.dom.getParent(el, "table"); + + // Cell is selected + if (ed.dom.hasClass(tdElm, 'mceSelected')) { + // Update all selected sells + tinymce.each(ed.dom.select('td.mceSelected,th.mceSelected'), function(td) { + updateCell(td); + }); + + ed.addVisual(); + ed.nodeChanged(); + inst.execCommand('mceEndUndoLevel'); + tinyMCEPopup.close(); + return; + } + + ed.execCommand('mceBeginUndoLevel'); + + switch (getSelectValue(formObj, 'action')) { + case "cell": + var celltype = getSelectValue(formObj, 'celltype'); + var scope = getSelectValue(formObj, 'scope'); + + function doUpdate(s) { + if (s) { + updateCell(tdElm); + + ed.addVisual(); + ed.nodeChanged(); + inst.execCommand('mceEndUndoLevel'); + tinyMCEPopup.close(); + } + }; + + if (ed.getParam("accessibility_warnings", 1)) { + if (celltype == "th" && scope == "") + tinyMCEPopup.confirm(ed.getLang('table_dlg.missing_scope', '', true), doUpdate); + else + doUpdate(1); + + return; + } + + updateCell(tdElm); + break; + + case "row": + var cell = trElm.firstChild; + + if (cell.nodeName != "TD" && cell.nodeName != "TH") + cell = nextCell(cell); + + do { + cell = updateCell(cell, true); + } while ((cell = nextCell(cell)) != null); + + break; + + case "all": + var rows = tableElm.getElementsByTagName("tr"); + + for (var i=0; i 0) { + tinymce.each(tableElm.rows, function(tr) { + var i; + + for (i = 0; i < tr.cells.length; i++) { + if (dom.hasClass(tr.cells[i], 'mceSelected')) { + updateRow(tr, true); + return; + } + } + }); + + inst.addVisual(); + inst.nodeChanged(); + inst.execCommand('mceEndUndoLevel'); + tinyMCEPopup.close(); + return; + } + + inst.execCommand('mceBeginUndoLevel'); + + switch (action) { + case "row": + updateRow(trElm); + break; + + case "all": + var rows = tableElm.getElementsByTagName("tr"); + + for (var i=0; i colLimit) { + tinyMCEPopup.alert(inst.getLang('table_dlg.col_limit').replace(/\{\$cols\}/g, colLimit)); + return false; + } else if (rowLimit && rows > rowLimit) { + tinyMCEPopup.alert(inst.getLang('table_dlg.row_limit').replace(/\{\$rows\}/g, rowLimit)); + return false; + } else if (cellLimit && cols * rows > cellLimit) { + tinyMCEPopup.alert(inst.getLang('table_dlg.cell_limit').replace(/\{\$cells\}/g, cellLimit)); + return false; + } + + // Update table + if (action == "update") { + inst.execCommand('mceBeginUndoLevel'); + + dom.setAttrib(elm, 'cellPadding', cellpadding, true); + dom.setAttrib(elm, 'cellSpacing', cellspacing, true); + dom.setAttrib(elm, 'border', border); + dom.setAttrib(elm, 'align', align); + dom.setAttrib(elm, 'frame', frame); + dom.setAttrib(elm, 'rules', rules); + dom.setAttrib(elm, 'class', className); + dom.setAttrib(elm, 'style', style); + dom.setAttrib(elm, 'id', id); + dom.setAttrib(elm, 'summary', summary); + dom.setAttrib(elm, 'dir', dir); + dom.setAttrib(elm, 'lang', lang); + + capEl = inst.dom.select('caption', elm)[0]; + + if (capEl && !caption) + capEl.parentNode.removeChild(capEl); + + if (!capEl && caption) { + capEl = elm.ownerDocument.createElement('caption'); + + if (!tinymce.isIE) + capEl.innerHTML = '
    '; + + elm.insertBefore(capEl, elm.firstChild); + } + + if (width && inst.settings.inline_styles) { + dom.setStyle(elm, 'width', width); + dom.setAttrib(elm, 'width', ''); + } else { + dom.setAttrib(elm, 'width', width, true); + dom.setStyle(elm, 'width', ''); + } + + // Remove these since they are not valid XHTML + dom.setAttrib(elm, 'borderColor', ''); + dom.setAttrib(elm, 'bgColor', ''); + dom.setAttrib(elm, 'background', ''); + + if (height && inst.settings.inline_styles) { + dom.setStyle(elm, 'height', height); + dom.setAttrib(elm, 'height', ''); + } else { + dom.setAttrib(elm, 'height', height, true); + dom.setStyle(elm, 'height', ''); + } + + if (background != '') + elm.style.backgroundImage = "url('" + background + "')"; + else + elm.style.backgroundImage = ''; + +/* if (tinyMCEPopup.getParam("inline_styles")) { + if (width != '') + elm.style.width = getCSSSize(width); + }*/ + + if (bordercolor != "") { + elm.style.borderColor = bordercolor; + elm.style.borderStyle = elm.style.borderStyle == "" ? "solid" : elm.style.borderStyle; + elm.style.borderWidth = border == "" ? "1px" : border; + } else + elm.style.borderColor = ''; + + elm.style.backgroundColor = bgcolor; + elm.style.height = getCSSSize(height); + + inst.addVisual(); + + // Fix for stange MSIE align bug + //elm.outerHTML = elm.outerHTML; + + inst.nodeChanged(); + inst.execCommand('mceEndUndoLevel'); + + // Repaint if dimensions changed + if (formObj.width.value != orgTableWidth || formObj.height.value != orgTableHeight) + inst.execCommand('mceRepaint'); + + tinyMCEPopup.close(); + return true; + } + + // Create new table + html += ''); + + tinymce.each('h1,h2,h3,h4,h5,h6,p'.split(','), function(n) { + if (patt) + patt += ','; + + patt += n + ' ._mce_marker'; + }); + + tinymce.each(inst.dom.select(patt), function(n) { + inst.dom.split(inst.dom.getParent(n, 'h1,h2,h3,h4,h5,h6,p'), n); + }); + + dom.setOuterHTML(dom.select('br._mce_marker')[0], html); + } else + inst.execCommand('mceInsertContent', false, html); + + tinymce.each(dom.select('table[_mce_new]'), function(node) { + var td = dom.select('td', node); + + inst.selection.select(td[0], true); + inst.selection.collapse(); + + dom.setAttrib(node, '_mce_new', ''); + }); + + inst.addVisual(); + inst.execCommand('mceEndUndoLevel'); + + tinyMCEPopup.close(); +} + +function makeAttrib(attrib, value) { + var formObj = document.forms[0]; + var valueElm = formObj.elements[attrib]; + + if (typeof(value) == "undefined" || value == null) { + value = ""; + + if (valueElm) + value = valueElm.value; + } + + if (value == "") + return ""; + + // XML encode it + value = value.replace(/&/g, '&'); + value = value.replace(/\"/g, '"'); + value = value.replace(//g, '>'); + + return ' ' + attrib + '="' + value + '"'; +} + +function init() { + tinyMCEPopup.resizeToInnerSize(); + + document.getElementById('backgroundimagebrowsercontainer').innerHTML = getBrowserHTML('backgroundimagebrowser','backgroundimage','image','table'); + document.getElementById('backgroundimagebrowsercontainer').innerHTML = getBrowserHTML('backgroundimagebrowser','backgroundimage','image','table'); + document.getElementById('bordercolor_pickcontainer').innerHTML = getColorPickerHTML('bordercolor_pick','bordercolor'); + document.getElementById('bgcolor_pickcontainer').innerHTML = getColorPickerHTML('bgcolor_pick','bgcolor'); + + var cols = 2, rows = 2, border = tinyMCEPopup.getParam('table_default_border', '0'), cellpadding = tinyMCEPopup.getParam('table_default_cellpadding', ''), cellspacing = tinyMCEPopup.getParam('table_default_cellspacing', ''); + var align = "", width = "", height = "", bordercolor = "", bgcolor = "", className = ""; + var id = "", summary = "", style = "", dir = "", lang = "", background = "", bgcolor = "", bordercolor = "", rules, frame; + var inst = tinyMCEPopup.editor, dom = inst.dom; + var formObj = document.forms[0]; + var elm = dom.getParent(inst.selection.getNode(), "table"); + + action = tinyMCEPopup.getWindowArg('action'); + + if (!action) + action = elm ? "update" : "insert"; + + if (elm && action != "insert") { + var rowsAr = elm.rows; + var cols = 0; + for (var i=0; i cols) + cols = rowsAr[i].cells.length; + + cols = cols; + rows = rowsAr.length; + + st = dom.parseStyle(dom.getAttrib(elm, "style")); + border = trimSize(getStyle(elm, 'border', 'borderWidth')); + cellpadding = dom.getAttrib(elm, 'cellpadding', ""); + cellspacing = dom.getAttrib(elm, 'cellspacing', ""); + width = trimSize(getStyle(elm, 'width', 'width')); + height = trimSize(getStyle(elm, 'height', 'height')); + bordercolor = convertRGBToHex(getStyle(elm, 'bordercolor', 'borderLeftColor')); + bgcolor = convertRGBToHex(getStyle(elm, 'bgcolor', 'backgroundColor')); + align = dom.getAttrib(elm, 'align', align); + frame = dom.getAttrib(elm, 'frame'); + rules = dom.getAttrib(elm, 'rules'); + className = tinymce.trim(dom.getAttrib(elm, 'class').replace(/mceItem.+/g, '')); + id = dom.getAttrib(elm, 'id'); + summary = dom.getAttrib(elm, 'summary'); + style = dom.serializeStyle(st); + dir = dom.getAttrib(elm, 'dir'); + lang = dom.getAttrib(elm, 'lang'); + background = getStyle(elm, 'background', 'backgroundImage').replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1"); + formObj.caption.checked = elm.getElementsByTagName('caption').length > 0; + + orgTableWidth = width; + orgTableHeight = height; + + action = "update"; + formObj.insert.value = inst.getLang('update'); + } + + addClassesToList('class', "table_styles"); + TinyMCE_EditableSelects.init(); + + // Update form + selectByValue(formObj, 'align', align); + selectByValue(formObj, 'tframe', frame); + selectByValue(formObj, 'rules', rules); + selectByValue(formObj, 'class', className, true, true); + formObj.cols.value = cols; + formObj.rows.value = rows; + formObj.border.value = border; + formObj.cellpadding.value = cellpadding; + formObj.cellspacing.value = cellspacing; + formObj.width.value = width; + formObj.height.value = height; + formObj.bordercolor.value = bordercolor; + formObj.bgcolor.value = bgcolor; + formObj.id.value = id; + formObj.summary.value = summary; + formObj.style.value = style; + formObj.dir.value = dir; + formObj.lang.value = lang; + formObj.backgroundimage.value = background; + + updateColor('bordercolor_pick', 'bordercolor'); + updateColor('bgcolor_pick', 'bgcolor'); + + // Resize some elements + if (isVisible('backgroundimagebrowser')) + document.getElementById('backgroundimage').style.width = '180px'; + + // Disable some fields in update mode + if (action == "update") { + formObj.cols.disabled = true; + formObj.rows.disabled = true; + } +} + +function changedSize() { + var formObj = document.forms[0]; + var st = dom.parseStyle(formObj.style.value); + +/* var width = formObj.width.value; + if (width != "") + st['width'] = tinyMCEPopup.getParam("inline_styles") ? getCSSSize(width) : ""; + else + st['width'] = "";*/ + + var height = formObj.height.value; + if (height != "") + st['height'] = getCSSSize(height); + else + st['height'] = ""; + + formObj.style.value = dom.serializeStyle(st); +} + +function changedBackgroundImage() { + var formObj = document.forms[0]; + var st = dom.parseStyle(formObj.style.value); + + st['background-image'] = "url('" + formObj.backgroundimage.value + "')"; + + formObj.style.value = dom.serializeStyle(st); +} + +function changedBorder() { + var formObj = document.forms[0]; + var st = dom.parseStyle(formObj.style.value); + + // Update border width if the element has a color + if (formObj.border.value != "" && formObj.bordercolor.value != "") + st['border-width'] = formObj.border.value + "px"; + + formObj.style.value = dom.serializeStyle(st); +} + +function changedColor() { + var formObj = document.forms[0]; + var st = dom.parseStyle(formObj.style.value); + + st['background-color'] = formObj.bgcolor.value; + + if (formObj.bordercolor.value != "") { + st['border-color'] = formObj.bordercolor.value; + + // Add border-width if it's missing + if (!st['border-width']) + st['border-width'] = formObj.border.value == "" ? "1px" : formObj.border.value + "px"; + } + + formObj.style.value = dom.serializeStyle(st); +} + +function changedStyle() { + var formObj = document.forms[0]; + var st = dom.parseStyle(formObj.style.value); + + if (st['background-image']) + formObj.backgroundimage.value = st['background-image'].replace(new RegExp("url\\('?([^']*)'?\\)", 'gi'), "$1"); + else + formObj.backgroundimage.value = ''; + + if (st['width']) + formObj.width.value = trimSize(st['width']); + + if (st['height']) + formObj.height.value = trimSize(st['height']); + + if (st['background-color']) { + formObj.bgcolor.value = st['background-color']; + updateColor('bgcolor_pick','bgcolor'); + } + + if (st['border-color']) { + formObj.bordercolor.value = st['border-color']; + updateColor('bordercolor_pick','bordercolor'); + } +} + +tinyMCEPopup.onInit.add(init); diff --git a/jscripts/tiny_mce/plugins/table/langs/en_dlg.js b/jscripts/tiny_mce/plugins/table/langs/en_dlg.js new file mode 100644 index 000000000..000332a35 --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/langs/en_dlg.js @@ -0,0 +1,74 @@ +tinyMCE.addI18n('en.table_dlg',{ +general_tab:"General", +advanced_tab:"Advanced", +general_props:"General properties", +advanced_props:"Advanced properties", +rowtype:"Row in table part", +title:"Insert/Modify table", +width:"Width", +height:"Height", +cols:"Cols", +rows:"Rows", +cellspacing:"Cellspacing", +cellpadding:"Cellpadding", +border:"Border", +align:"Alignment", +align_default:"Default", +align_left:"Left", +align_right:"Right", +align_middle:"Center", +row_title:"Table row properties", +cell_title:"Table cell properties", +cell_type:"Cell type", +valign:"Vertical alignment", +align_top:"Top", +align_bottom:"Bottom", +bordercolor:"Border color", +bgcolor:"Background color", +merge_cells_title:"Merge table cells", +id:"Id", +style:"Style", +langdir:"Language direction", +langcode:"Language code", +mime:"Target MIME type", +ltr:"Left to right", +rtl:"Right to left", +bgimage:"Background image", +summary:"Summary", +td:"Data", +th:"Header", +cell_cell:"Update current cell", +cell_row:"Update all cells in row", +cell_all:"Update all cells in table", +row_row:"Update current row", +row_odd:"Update odd rows in table", +row_even:"Update even rows in table", +row_all:"Update all rows in table", +thead:"Table Head", +tbody:"Table Body", +tfoot:"Table Foot", +scope:"Scope", +rowgroup:"Row Group", +colgroup:"Col Group", +col_limit:"You've exceeded the maximum number of columns of {$cols}.", +row_limit:"You've exceeded the maximum number of rows of {$rows}.", +cell_limit:"You've exceeded the maximum number of cells of {$cells}.", +missing_scope:"Are you sure you want to continue without specifying a scope for this table header cell. Without it, it may be difficult for some users with disabilities to understand the content or data displayed of the table.", +caption:"Table caption", +frame:"Frame", +frame_none:"none", +frame_groups:"groups", +frame_rows:"rows", +frame_cols:"cols", +frame_all:"all", +rules:"Rules", +rules_void:"void", +rules_above:"above", +rules_below:"below", +rules_hsides:"hsides", +rules_lhs:"lhs", +rules_rhs:"rhs", +rules_vsides:"vsides", +rules_box:"box", +rules_border:"border" +}); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/table/merge_cells.htm b/jscripts/tiny_mce/plugins/table/merge_cells.htm new file mode 100644 index 000000000..9736ed8c0 --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/merge_cells.htm @@ -0,0 +1,32 @@ + + + + {#table_dlg.merge_cells_title} + + + + + + +
    +
    + {#table_dlg.merge_cells_title} + + + + + + + + + +
    {#table_dlg.cols}:
    {#table_dlg.rows}:
    +
    + +
    + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/table/row.htm b/jscripts/tiny_mce/plugins/table/row.htm new file mode 100644 index 000000000..092e6c827 --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/row.htm @@ -0,0 +1,155 @@ + + + + {#table_dlg.row_title} + + + + + + + + +
    + + +
    +
    +
    + {#table_dlg.general_props} + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    + +
    +
    +
    + +
    +
    + {#table_dlg.advanced_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + + + + + +
     
    +
    + + + + + +
     
    +
    +
    +
    +
    + +
    +
    + +
    + + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/table/table.htm b/jscripts/tiny_mce/plugins/table/table.htm new file mode 100644 index 000000000..f26903922 --- /dev/null +++ b/jscripts/tiny_mce/plugins/table/table.htm @@ -0,0 +1,187 @@ + + + + {#table_dlg.title} + + + + + + + + + +
    + + +
    +
    +
    + {#table_dlg.general_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +
    +
    + {#table_dlg.advanced_props} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + +
     
    +
    + +
    + +
    + +
    + + + + + +
     
    +
    + + + + + +
     
    +
    +
    +
    +
    + +
    + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/template/blank.htm b/jscripts/tiny_mce/plugins/template/blank.htm new file mode 100644 index 000000000..ecde53fae --- /dev/null +++ b/jscripts/tiny_mce/plugins/template/blank.htm @@ -0,0 +1,12 @@ + + + blank_page + + + + + + + diff --git a/jscripts/tiny_mce/plugins/template/css/template.css b/jscripts/tiny_mce/plugins/template/css/template.css new file mode 100644 index 000000000..2d23a4938 --- /dev/null +++ b/jscripts/tiny_mce/plugins/template/css/template.css @@ -0,0 +1,23 @@ +#frmbody { + padding: 10px; + background-color: #FFF; + border: 1px solid #CCC; +} + +.frmRow { + margin-bottom: 10px; +} + +#templatesrc { + border: none; + width: 320px; + height: 240px; +} + +.title { + padding-bottom: 5px; +} + +.mceActionPanel { + padding-top: 5px; +} diff --git a/jscripts/tiny_mce/plugins/template/editor_plugin.js b/jscripts/tiny_mce/plugins/template/editor_plugin.js new file mode 100644 index 000000000..ebe3c27d7 --- /dev/null +++ b/jscripts/tiny_mce/plugins/template/editor_plugin.js @@ -0,0 +1 @@ +(function(){var a=tinymce.each;tinymce.create("tinymce.plugins.TemplatePlugin",{init:function(b,c){var d=this;d.editor=b;b.addCommand("mceTemplate",function(e){b.windowManager.open({file:c+"/template.htm",width:b.getParam("template_popup_width",750),height:b.getParam("template_popup_height",600),inline:1},{plugin_url:c})});b.addCommand("mceInsertTemplate",d._insertTemplate,d);b.addButton("template",{title:"template.desc",cmd:"mceTemplate"});b.onPreProcess.add(function(e,g){var f=e.dom;a(f.select("div",g.node),function(h){if(f.hasClass(h,"mceTmpl")){a(f.select("*",h),function(i){if(f.hasClass(i,e.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))){i.innerHTML=d._getDateTime(new Date(),e.getParam("template_mdate_format",e.getLang("template.mdate_format")))}});d._replaceVals(h)}})})},getInfo:function(){return{longname:"Template plugin",author:"Moxiecode Systems AB",authorurl:"http://www.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/template",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_insertTemplate:function(i,j){var k=this,g=k.editor,f,c,d=g.dom,b=g.selection.getContent();f=j.content;a(k.editor.getParam("template_replace_values"),function(l,h){if(typeof(l)!="function"){f=f.replace(new RegExp("\\{\\$"+h+"\\}","g"),l)}});c=d.create("div",null,f);n=d.select(".mceTmpl",c);if(n&&n.length>0){c=d.create("div",null);c.appendChild(n[0].cloneNode(true))}function e(l,h){return new RegExp("\\b"+h+"\\b","g").test(l.className)}a(d.select("*",c),function(h){if(e(h,g.getParam("template_cdate_classes","cdate").replace(/\s+/g,"|"))){h.innerHTML=k._getDateTime(new Date(),g.getParam("template_cdate_format",g.getLang("template.cdate_format")))}if(e(h,g.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))){h.innerHTML=k._getDateTime(new Date(),g.getParam("template_mdate_format",g.getLang("template.mdate_format")))}if(e(h,g.getParam("template_selected_content_classes","selcontent").replace(/\s+/g,"|"))){h.innerHTML=b}});k._replaceVals(c);g.execCommand("mceInsertContent",false,c.innerHTML);g.addVisual()},_replaceVals:function(c){var d=this.editor.dom,b=this.editor.getParam("template_replace_values");a(d.select("*",c),function(f){a(b,function(g,e){if(d.hasClass(f,e)){if(typeof(b[e])=="function"){b[e](f)}}})})},_getDateTime:function(e,b){if(!b){return""}function c(g,d){var f;g=""+g;if(g.length 0) { + el = dom.create('div', null); + el.appendChild(n[0].cloneNode(true)); + } + + function hasClass(n, c) { + return new RegExp('\\b' + c + '\\b', 'g').test(n.className); + }; + + each(dom.select('*', el), function(n) { + // Replace cdate + if (hasClass(n, ed.getParam('template_cdate_classes', 'cdate').replace(/\s+/g, '|'))) + n.innerHTML = t._getDateTime(new Date(), ed.getParam("template_cdate_format", ed.getLang("template.cdate_format"))); + + // Replace mdate + if (hasClass(n, ed.getParam('template_mdate_classes', 'mdate').replace(/\s+/g, '|'))) + n.innerHTML = t._getDateTime(new Date(), ed.getParam("template_mdate_format", ed.getLang("template.mdate_format"))); + + // Replace selection + if (hasClass(n, ed.getParam('template_selected_content_classes', 'selcontent').replace(/\s+/g, '|'))) + n.innerHTML = sel; + }); + + t._replaceVals(el); + + ed.execCommand('mceInsertContent', false, el.innerHTML); + ed.addVisual(); + }, + + _replaceVals : function(e) { + var dom = this.editor.dom, vl = this.editor.getParam('template_replace_values'); + + each(dom.select('*', e), function(e) { + each(vl, function(v, k) { + if (dom.hasClass(e, k)) { + if (typeof(vl[k]) == 'function') + vl[k](e); + } + }); + }); + }, + + _getDateTime : function(d, fmt) { + if (!fmt) + return ""; + + function addZeros(value, len) { + var i; + + value = "" + value; + + if (value.length < len) { + for (i=0; i<(len-value.length); i++) + value = "0" + value; + } + + return value; + } + + fmt = fmt.replace("%D", "%m/%d/%y"); + fmt = fmt.replace("%r", "%I:%M:%S %p"); + fmt = fmt.replace("%Y", "" + d.getFullYear()); + fmt = fmt.replace("%y", "" + d.getYear()); + fmt = fmt.replace("%m", addZeros(d.getMonth()+1, 2)); + fmt = fmt.replace("%d", addZeros(d.getDate(), 2)); + fmt = fmt.replace("%H", "" + addZeros(d.getHours(), 2)); + fmt = fmt.replace("%M", "" + addZeros(d.getMinutes(), 2)); + fmt = fmt.replace("%S", "" + addZeros(d.getSeconds(), 2)); + fmt = fmt.replace("%I", "" + ((d.getHours() + 11) % 12 + 1)); + fmt = fmt.replace("%p", "" + (d.getHours() < 12 ? "AM" : "PM")); + fmt = fmt.replace("%B", "" + this.editor.getLang("template_months_long").split(',')[d.getMonth()]); + fmt = fmt.replace("%b", "" + this.editor.getLang("template_months_short").split(',')[d.getMonth()]); + fmt = fmt.replace("%A", "" + this.editor.getLang("template_day_long").split(',')[d.getDay()]); + fmt = fmt.replace("%a", "" + this.editor.getLang("template_day_short").split(',')[d.getDay()]); + fmt = fmt.replace("%%", "%"); + + return fmt; + } + }); + + // Register plugin + tinymce.PluginManager.add('template', tinymce.plugins.TemplatePlugin); +})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/template/js/template.js b/jscripts/tiny_mce/plugins/template/js/template.js new file mode 100644 index 000000000..24045d731 --- /dev/null +++ b/jscripts/tiny_mce/plugins/template/js/template.js @@ -0,0 +1,106 @@ +tinyMCEPopup.requireLangPack(); + +var TemplateDialog = { + preInit : function() { + var url = tinyMCEPopup.getParam("template_external_list_url"); + + if (url != null) + document.write(''); + }, + + init : function() { + var ed = tinyMCEPopup.editor, tsrc, sel, x, u; + + tsrc = ed.getParam("template_templates", false); + sel = document.getElementById('tpath'); + + // Setup external template list + if (!tsrc && typeof(tinyMCETemplateList) != 'undefined') { + for (x=0, tsrc = []; x'); + }); + }, + + selectTemplate : function(u, ti) { + var d = window.frames['templatesrc'].document, x, tsrc = this.tsrc; + + if (!u) + return; + + d.body.innerHTML = this.templateHTML = this.getFileContents(u); + + for (x=0; x + + {#template_dlg.title} + + + + + +
    +
    +
    {#template_dlg.desc}
    +
    + +
    +
    +
    +
    + {#template_dlg.preview} + +
    +
    + +
    + + +
    +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/visualchars/editor_plugin.js b/jscripts/tiny_mce/plugins/visualchars/editor_plugin.js new file mode 100644 index 000000000..53d31c44f --- /dev/null +++ b/jscripts/tiny_mce/plugins/visualchars/editor_plugin.js @@ -0,0 +1 @@ +(function(){tinymce.create("tinymce.plugins.VisualChars",{init:function(a,b){var c=this;c.editor=a;a.addCommand("mceVisualChars",c._toggleVisualChars,c);a.addButton("visualchars",{title:"visualchars.desc",cmd:"mceVisualChars"});a.onBeforeGetContent.add(function(d,e){if(c.state){c.state=true;c._toggleVisualChars()}})},getInfo:function(){return{longname:"Visual characters",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/visualchars",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_toggleVisualChars:function(){var m=this,g=m.editor,a,e,f,k=g.getDoc(),l=g.getBody(),j,n=g.selection,c;m.state=!m.state;g.controlManager.setActive("visualchars",m.state);if(m.state){a=[];tinymce.walk(l,function(b){if(b.nodeType==3&&b.nodeValue&&b.nodeValue.indexOf("\u00a0")!=-1){a.push(b)}},"childNodes");for(e=0;e$1');j=j.replace(/\u00a0/g,"\u00b7");g.dom.setOuterHTML(a[e],j,k)}}else{a=tinymce.grep(g.dom.select("span",l),function(b){return g.dom.hasClass(b,"mceVisualNbsp")});for(e=0;e$1'); + nv = nv.replace(/\u00a0/g, '\u00b7'); + ed.dom.setOuterHTML(nl[i], nv, d); + } + } else { + nl = tinymce.grep(ed.dom.select('span', b), function(n) { + return ed.dom.hasClass(n, 'mceVisualNbsp'); + }); + + for (i=0; i0')}}else{tinymce.DOM.add(h,"span",{},'0')}});a.onInit.add(function(e){e.selection.onSetContent.add(function(){c._count(e)});c._count(e)});a.onSetContent.add(function(e){c._count(e)});a.onKeyUp.add(function(f,g){if(g.keyCode==d){return}if(13==g.keyCode||8==d||46==d){c._count(f)}d=g.keyCode})},_count:function(b){var c=this,a=0;if(c.block){return}c.block=1;setTimeout(function(){var d=b.getContent({format:"raw"});if(d){d=d.replace(/<.[^<>]*?>/g," ").replace(/ | /gi," ");d=d.replace(c.cleanre,"");d.replace(c.countre,function(){a++})}tinymce.DOM.setHTML(c.id,a.toString());setTimeout(function(){c.block=0},2000)},1)},getInfo:function(){return{longname:"Word Count plugin",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/wordcount",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("wordcount",tinymce.plugins.WordCount)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/wordcount/editor_plugin_src.js b/jscripts/tiny_mce/plugins/wordcount/editor_plugin_src.js new file mode 100644 index 000000000..bdfebf1b3 --- /dev/null +++ b/jscripts/tiny_mce/plugins/wordcount/editor_plugin_src.js @@ -0,0 +1,98 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + tinymce.create('tinymce.plugins.WordCount', { + block : 0, + id : null, + countre : null, + cleanre : null, + + init : function(ed, url) { + var t = this, last = 0; + + t.countre = ed.getParam('wordcount_countregex', /\S\s+/g); + t.cleanre = ed.getParam('wordcount_cleanregex', /[0-9.(),;:!?%#$¿'"_+=\\/-]*/g); + t.id = ed.id + '-word-count'; + + ed.onPostRender.add(function(ed, cm) { + var row, id; + + // Add it to the specified id or the theme advanced path + id = ed.getParam('wordcount_target_id'); + if (!id) { + row = tinymce.DOM.get(ed.id + '_path_row'); + + if (row) + tinymce.DOM.add(row.parentNode, 'div', {'style': 'float: right'}, ed.getLang('wordcount.words', 'Words: ') + '0'); + } else + tinymce.DOM.add(id, 'span', {}, '0'); + }); + + ed.onInit.add(function(ed) { + ed.selection.onSetContent.add(function() { + t._count(ed); + }); + + t._count(ed); + }); + + ed.onSetContent.add(function(ed) { + t._count(ed); + }); + + ed.onKeyUp.add(function(ed, e) { + if (e.keyCode == last) + return; + + if (13 == e.keyCode || 8 == last || 46 == last) + t._count(ed); + + last = e.keyCode; + }); + }, + + _count : function(ed) { + var t = this, tc = 0; + + // Keep multiple calls from happening at the same time + if (t.block) + return; + + t.block = 1; + + setTimeout(function() { + var tx = ed.getContent({format : 'raw'}); + + if (tx) { + tx = tx.replace(/<.[^<>]*?>/g, ' ').replace(/ | /gi, ' '); // remove html tags and space chars + tx = tx.replace(t.cleanre, ''); // remove numbers and punctuation + tx.replace(t.countre, function() {tc++;}); // count the words + } + + tinymce.DOM.setHTML(t.id, tc.toString()); + + setTimeout(function() {t.block = 0;}, 2000); + }, 1); + }, + + getInfo: function() { + return { + longname : 'Word Count plugin', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/wordcount', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + } + }); + + tinymce.PluginManager.add('wordcount', tinymce.plugins.WordCount); +})(); diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/abbr.htm b/jscripts/tiny_mce/plugins/xhtmlxtras/abbr.htm new file mode 100644 index 000000000..3aeac0deb --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/abbr.htm @@ -0,0 +1,141 @@ + + + + {#xhtmlxtras_dlg.title_abbr_element} + + + + + + + + + +
    + + +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    : + +
    :
    : + +
    : + +
    +
    +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    +
    +
    +
    +
    + + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/acronym.htm b/jscripts/tiny_mce/plugins/xhtmlxtras/acronym.htm new file mode 100644 index 000000000..31ee7b70f --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/acronym.htm @@ -0,0 +1,141 @@ + + + + {#xhtmlxtras_dlg.title_acronym_element} + + + + + + + + + +
    + + +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    : + +
    :
    : + +
    : + +
    +
    +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    +
    +
    +
    +
    + + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/attributes.htm b/jscripts/tiny_mce/plugins/xhtmlxtras/attributes.htm new file mode 100644 index 000000000..17054da3e --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/attributes.htm @@ -0,0 +1,148 @@ + + + + {#xhtmlxtras_dlg.attribs_title} + + + + + + + + +
    + + +
    +
    +
    + {#xhtmlxtras_dlg.attribute_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    + +
    :
    : + +
    : + +
    +
    +
    +
    +
    + {#xhtmlxtras_dlg.attribute_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    +
    +
    +
    +
    + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/cite.htm b/jscripts/tiny_mce/plugins/xhtmlxtras/cite.htm new file mode 100644 index 000000000..d0a3e3a8e --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/cite.htm @@ -0,0 +1,141 @@ + + + + {#xhtmlxtras_dlg.title_cite_element} + + + + + + + + + +
    + + +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    : + +
    :
    : + +
    : + +
    +
    +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    +
    +
    +
    +
    + + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/css/attributes.css b/jscripts/tiny_mce/plugins/xhtmlxtras/css/attributes.css new file mode 100644 index 000000000..9a6a235c3 --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/css/attributes.css @@ -0,0 +1,11 @@ +.panel_wrapper div.current { + height: 290px; +} + +#id, #style, #title, #dir, #hreflang, #lang, #classlist, #tabindex, #accesskey { + width: 200px; +} + +#events_panel input { + width: 200px; +} diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/css/popup.css b/jscripts/tiny_mce/plugins/xhtmlxtras/css/popup.css new file mode 100644 index 000000000..e67114dba --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/css/popup.css @@ -0,0 +1,9 @@ +input.field, select.field {width:200px;} +input.picker {width:179px; margin-left: 5px;} +input.disabled {border-color:#F2F2F2;} +img.picker {vertical-align:text-bottom; cursor:pointer;} +h1 {padding: 0 0 5px 0;} +.panel_wrapper div.current {height:160px;} +#xhtmlxtrasdel .panel_wrapper div.current, #xhtmlxtrasins .panel_wrapper div.current {height: 230px;} +a.browse span {display:block; width:20px; height:20px; background:url('../../../themes/advanced/img/icons.gif') -140px -20px;} +#datetime {width:180px;} diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/del.htm b/jscripts/tiny_mce/plugins/xhtmlxtras/del.htm new file mode 100644 index 000000000..8b07fa842 --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/del.htm @@ -0,0 +1,161 @@ + + + + {#xhtmlxtras_dlg.title_del_element} + + + + + + + + + +
    + + +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_general_tab} + + + + + + + + + +
    : + + + + + +
    +
    :
    +
    +
    + {#xhtmlxtras_dlg.fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    : + +
    :
    : + +
    : + +
    +
    +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    +
    +
    +
    +
    + + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/editor_plugin.js b/jscripts/tiny_mce/plugins/xhtmlxtras/editor_plugin.js new file mode 100644 index 000000000..e5195265e --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/editor_plugin.js @@ -0,0 +1 @@ +(function(){tinymce.create("tinymce.plugins.XHTMLXtrasPlugin",{init:function(b,c){b.addCommand("mceCite",function(){b.windowManager.open({file:c+"/cite.htm",width:350+parseInt(b.getLang("xhtmlxtras.cite_delta_width",0)),height:250+parseInt(b.getLang("xhtmlxtras.cite_delta_height",0)),inline:1},{plugin_url:c})});b.addCommand("mceAcronym",function(){b.windowManager.open({file:c+"/acronym.htm",width:350+parseInt(b.getLang("xhtmlxtras.acronym_delta_width",0)),height:250+parseInt(b.getLang("xhtmlxtras.acronym_delta_width",0)),inline:1},{plugin_url:c})});b.addCommand("mceAbbr",function(){b.windowManager.open({file:c+"/abbr.htm",width:350+parseInt(b.getLang("xhtmlxtras.abbr_delta_width",0)),height:250+parseInt(b.getLang("xhtmlxtras.abbr_delta_width",0)),inline:1},{plugin_url:c})});b.addCommand("mceDel",function(){b.windowManager.open({file:c+"/del.htm",width:340+parseInt(b.getLang("xhtmlxtras.del_delta_width",0)),height:310+parseInt(b.getLang("xhtmlxtras.del_delta_width",0)),inline:1},{plugin_url:c})});b.addCommand("mceIns",function(){b.windowManager.open({file:c+"/ins.htm",width:340+parseInt(b.getLang("xhtmlxtras.ins_delta_width",0)),height:310+parseInt(b.getLang("xhtmlxtras.ins_delta_width",0)),inline:1},{plugin_url:c})});b.addCommand("mceAttributes",function(){b.windowManager.open({file:c+"/attributes.htm",width:380,height:370,inline:1},{plugin_url:c})});b.addButton("cite",{title:"xhtmlxtras.cite_desc",cmd:"mceCite"});b.addButton("acronym",{title:"xhtmlxtras.acronym_desc",cmd:"mceAcronym"});b.addButton("abbr",{title:"xhtmlxtras.abbr_desc",cmd:"mceAbbr"});b.addButton("del",{title:"xhtmlxtras.del_desc",cmd:"mceDel"});b.addButton("ins",{title:"xhtmlxtras.ins_desc",cmd:"mceIns"});b.addButton("attribs",{title:"xhtmlxtras.attribs_desc",cmd:"mceAttributes"});if(tinymce.isIE){function a(d,e){if(e.set){e.content=e.content.replace(/]+)>/gi,"");e.content=e.content.replace(/<\/abbr>/gi,"")}}b.onBeforeSetContent.add(a);b.onPostProcess.add(a)}b.onNodeChange.add(function(e,d,g,f){g=e.dom.getParent(g,"CITE,ACRONYM,ABBR,DEL,INS");d.setDisabled("cite",f);d.setDisabled("acronym",f);d.setDisabled("abbr",f);d.setDisabled("del",f);d.setDisabled("ins",f);d.setDisabled("attribs",g&&g.nodeName=="BODY");d.setActive("cite",0);d.setActive("acronym",0);d.setActive("abbr",0);d.setActive("del",0);d.setActive("ins",0);if(g){do{d.setDisabled(g.nodeName.toLowerCase(),0);d.setActive(g.nodeName.toLowerCase(),1)}while(g=g.parentNode)}});b.onPreInit.add(function(){b.dom.create("abbr")})},getInfo:function(){return{longname:"XHTML Xtras Plugin",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/xhtmlxtras",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("xhtmlxtras",tinymce.plugins.XHTMLXtrasPlugin)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/editor_plugin_src.js b/jscripts/tiny_mce/plugins/xhtmlxtras/editor_plugin_src.js new file mode 100644 index 000000000..9b51b8368 --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/editor_plugin_src.js @@ -0,0 +1,144 @@ +/** + * editor_plugin_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + tinymce.create('tinymce.plugins.XHTMLXtrasPlugin', { + init : function(ed, url) { + // Register commands + ed.addCommand('mceCite', function() { + ed.windowManager.open({ + file : url + '/cite.htm', + width : 350 + parseInt(ed.getLang('xhtmlxtras.cite_delta_width', 0)), + height : 250 + parseInt(ed.getLang('xhtmlxtras.cite_delta_height', 0)), + inline : 1 + }, { + plugin_url : url + }); + }); + + ed.addCommand('mceAcronym', function() { + ed.windowManager.open({ + file : url + '/acronym.htm', + width : 350 + parseInt(ed.getLang('xhtmlxtras.acronym_delta_width', 0)), + height : 250 + parseInt(ed.getLang('xhtmlxtras.acronym_delta_width', 0)), + inline : 1 + }, { + plugin_url : url + }); + }); + + ed.addCommand('mceAbbr', function() { + ed.windowManager.open({ + file : url + '/abbr.htm', + width : 350 + parseInt(ed.getLang('xhtmlxtras.abbr_delta_width', 0)), + height : 250 + parseInt(ed.getLang('xhtmlxtras.abbr_delta_width', 0)), + inline : 1 + }, { + plugin_url : url + }); + }); + + ed.addCommand('mceDel', function() { + ed.windowManager.open({ + file : url + '/del.htm', + width : 340 + parseInt(ed.getLang('xhtmlxtras.del_delta_width', 0)), + height : 310 + parseInt(ed.getLang('xhtmlxtras.del_delta_width', 0)), + inline : 1 + }, { + plugin_url : url + }); + }); + + ed.addCommand('mceIns', function() { + ed.windowManager.open({ + file : url + '/ins.htm', + width : 340 + parseInt(ed.getLang('xhtmlxtras.ins_delta_width', 0)), + height : 310 + parseInt(ed.getLang('xhtmlxtras.ins_delta_width', 0)), + inline : 1 + }, { + plugin_url : url + }); + }); + + ed.addCommand('mceAttributes', function() { + ed.windowManager.open({ + file : url + '/attributes.htm', + width : 380, + height : 370, + inline : 1 + }, { + plugin_url : url + }); + }); + + // Register buttons + ed.addButton('cite', {title : 'xhtmlxtras.cite_desc', cmd : 'mceCite'}); + ed.addButton('acronym', {title : 'xhtmlxtras.acronym_desc', cmd : 'mceAcronym'}); + ed.addButton('abbr', {title : 'xhtmlxtras.abbr_desc', cmd : 'mceAbbr'}); + ed.addButton('del', {title : 'xhtmlxtras.del_desc', cmd : 'mceDel'}); + ed.addButton('ins', {title : 'xhtmlxtras.ins_desc', cmd : 'mceIns'}); + ed.addButton('attribs', {title : 'xhtmlxtras.attribs_desc', cmd : 'mceAttributes'}); + + if (tinymce.isIE) { + function fix(ed, o) { + if (o.set) { + o.content = o.content.replace(/]+)>/gi, ''); + o.content = o.content.replace(/<\/abbr>/gi, ''); + } + }; + + ed.onBeforeSetContent.add(fix); + ed.onPostProcess.add(fix); + } + + ed.onNodeChange.add(function(ed, cm, n, co) { + n = ed.dom.getParent(n, 'CITE,ACRONYM,ABBR,DEL,INS'); + + cm.setDisabled('cite', co); + cm.setDisabled('acronym', co); + cm.setDisabled('abbr', co); + cm.setDisabled('del', co); + cm.setDisabled('ins', co); + cm.setDisabled('attribs', n && n.nodeName == 'BODY'); + cm.setActive('cite', 0); + cm.setActive('acronym', 0); + cm.setActive('abbr', 0); + cm.setActive('del', 0); + cm.setActive('ins', 0); + + // Activate all + if (n) { + do { + cm.setDisabled(n.nodeName.toLowerCase(), 0); + cm.setActive(n.nodeName.toLowerCase(), 1); + } while (n = n.parentNode); + } + }); + + ed.onPreInit.add(function() { + // Fixed IE issue where it can't handle these elements correctly + ed.dom.create('abbr'); + }); + }, + + getInfo : function() { + return { + longname : 'XHTML Xtras Plugin', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/xhtmlxtras', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + } + }); + + // Register plugin + tinymce.PluginManager.add('xhtmlxtras', tinymce.plugins.XHTMLXtrasPlugin); +})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/ins.htm b/jscripts/tiny_mce/plugins/xhtmlxtras/ins.htm new file mode 100644 index 000000000..6c5470cfc --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/ins.htm @@ -0,0 +1,161 @@ + + + + {#xhtmlxtras_dlg.title_ins_element} + + + + + + + + + +
    + + +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_general_tab} + + + + + + + + + +
    : + + + + + +
    +
    :
    +
    +
    + {#xhtmlxtras_dlg.fieldset_attrib_tab} + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    : + +
    :
    : + +
    : + +
    +
    +
    +
    +
    + {#xhtmlxtras_dlg.fieldset_events_tab} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    :
    +
    +
    +
    +
    + + + +
    +
    + + diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/js/abbr.js b/jscripts/tiny_mce/plugins/xhtmlxtras/js/abbr.js new file mode 100644 index 000000000..4b51a2572 --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/js/abbr.js @@ -0,0 +1,28 @@ +/** + * abbr.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +function init() { + SXE.initElementDialog('abbr'); + if (SXE.currentAction == "update") { + SXE.showRemoveButton(); + } +} + +function insertAbbr() { + SXE.insertElement('abbr'); + tinyMCEPopup.close(); +} + +function removeAbbr() { + SXE.removeElement('abbr'); + tinyMCEPopup.close(); +} + +tinyMCEPopup.onInit.add(init); diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/js/acronym.js b/jscripts/tiny_mce/plugins/xhtmlxtras/js/acronym.js new file mode 100644 index 000000000..6ec2f8871 --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/js/acronym.js @@ -0,0 +1,28 @@ +/** + * acronym.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +function init() { + SXE.initElementDialog('acronym'); + if (SXE.currentAction == "update") { + SXE.showRemoveButton(); + } +} + +function insertAcronym() { + SXE.insertElement('acronym'); + tinyMCEPopup.close(); +} + +function removeAcronym() { + SXE.removeElement('acronym'); + tinyMCEPopup.close(); +} + +tinyMCEPopup.onInit.add(init); diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/js/attributes.js b/jscripts/tiny_mce/plugins/xhtmlxtras/js/attributes.js new file mode 100644 index 000000000..d62a219e6 --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/js/attributes.js @@ -0,0 +1,126 @@ +/** + * attributes.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +function init() { + tinyMCEPopup.resizeToInnerSize(); + var inst = tinyMCEPopup.editor; + var dom = inst.dom; + var elm = inst.selection.getNode(); + var f = document.forms[0]; + var onclick = dom.getAttrib(elm, 'onclick'); + + setFormValue('title', dom.getAttrib(elm, 'title')); + setFormValue('id', dom.getAttrib(elm, 'id')); + setFormValue('style', dom.getAttrib(elm, "style")); + setFormValue('dir', dom.getAttrib(elm, 'dir')); + setFormValue('lang', dom.getAttrib(elm, 'lang')); + setFormValue('tabindex', dom.getAttrib(elm, 'tabindex', typeof(elm.tabindex) != "undefined" ? elm.tabindex : "")); + setFormValue('accesskey', dom.getAttrib(elm, 'accesskey', typeof(elm.accesskey) != "undefined" ? elm.accesskey : "")); + setFormValue('onfocus', dom.getAttrib(elm, 'onfocus')); + setFormValue('onblur', dom.getAttrib(elm, 'onblur')); + setFormValue('onclick', onclick); + setFormValue('ondblclick', dom.getAttrib(elm, 'ondblclick')); + setFormValue('onmousedown', dom.getAttrib(elm, 'onmousedown')); + setFormValue('onmouseup', dom.getAttrib(elm, 'onmouseup')); + setFormValue('onmouseover', dom.getAttrib(elm, 'onmouseover')); + setFormValue('onmousemove', dom.getAttrib(elm, 'onmousemove')); + setFormValue('onmouseout', dom.getAttrib(elm, 'onmouseout')); + setFormValue('onkeypress', dom.getAttrib(elm, 'onkeypress')); + setFormValue('onkeydown', dom.getAttrib(elm, 'onkeydown')); + setFormValue('onkeyup', dom.getAttrib(elm, 'onkeyup')); + className = dom.getAttrib(elm, 'class'); + + addClassesToList('classlist', 'advlink_styles'); + selectByValue(f, 'classlist', className, true); + + TinyMCE_EditableSelects.init(); +} + +function setFormValue(name, value) { + if(value && document.forms[0].elements[name]){ + document.forms[0].elements[name].value = value; + } +} + +function insertAction() { + var inst = tinyMCEPopup.editor; + var elm = inst.selection.getNode(); + + tinyMCEPopup.execCommand("mceBeginUndoLevel"); + setAllAttribs(elm); + tinyMCEPopup.execCommand("mceEndUndoLevel"); + tinyMCEPopup.close(); +} + +function setAttrib(elm, attrib, value) { + var formObj = document.forms[0]; + var valueElm = formObj.elements[attrib.toLowerCase()]; + var inst = tinyMCEPopup.editor; + var dom = inst.dom; + + if (typeof(value) == "undefined" || value == null) { + value = ""; + + if (valueElm) + value = valueElm.value; + } + + if (value != "") { + dom.setAttrib(elm, attrib.toLowerCase(), value); + + if (attrib == "style") + attrib = "style.cssText"; + + if (attrib.substring(0, 2) == 'on') + value = 'return true;' + value; + + if (attrib == "class") + attrib = "className"; + + elm[attrib]=value; + } else + elm.removeAttribute(attrib); +} + +function setAllAttribs(elm) { + var f = document.forms[0]; + + setAttrib(elm, 'title'); + setAttrib(elm, 'id'); + setAttrib(elm, 'style'); + setAttrib(elm, 'class', getSelectValue(f, 'classlist')); + setAttrib(elm, 'dir'); + setAttrib(elm, 'lang'); + setAttrib(elm, 'tabindex'); + setAttrib(elm, 'accesskey'); + setAttrib(elm, 'onfocus'); + setAttrib(elm, 'onblur'); + setAttrib(elm, 'onclick'); + setAttrib(elm, 'ondblclick'); + setAttrib(elm, 'onmousedown'); + setAttrib(elm, 'onmouseup'); + setAttrib(elm, 'onmouseover'); + setAttrib(elm, 'onmousemove'); + setAttrib(elm, 'onmouseout'); + setAttrib(elm, 'onkeypress'); + setAttrib(elm, 'onkeydown'); + setAttrib(elm, 'onkeyup'); + + // Refresh in old MSIE +// if (tinyMCE.isMSIE5) +// elm.outerHTML = elm.outerHTML; +} + +function insertAttribute() { + tinyMCEPopup.close(); +} + +tinyMCEPopup.onInit.add(init); +tinyMCEPopup.requireLangPack(); diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/js/cite.js b/jscripts/tiny_mce/plugins/xhtmlxtras/js/cite.js new file mode 100644 index 000000000..009b71546 --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/js/cite.js @@ -0,0 +1,28 @@ +/** + * cite.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +function init() { + SXE.initElementDialog('cite'); + if (SXE.currentAction == "update") { + SXE.showRemoveButton(); + } +} + +function insertCite() { + SXE.insertElement('cite'); + tinyMCEPopup.close(); +} + +function removeCite() { + SXE.removeElement('cite'); + tinyMCEPopup.close(); +} + +tinyMCEPopup.onInit.add(init); diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/js/del.js b/jscripts/tiny_mce/plugins/xhtmlxtras/js/del.js new file mode 100644 index 000000000..9e5d8c571 --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/js/del.js @@ -0,0 +1,63 @@ +/** + * del.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +function init() { + SXE.initElementDialog('del'); + if (SXE.currentAction == "update") { + setFormValue('datetime', tinyMCEPopup.editor.dom.getAttrib(SXE.updateElement, 'datetime')); + setFormValue('cite', tinyMCEPopup.editor.dom.getAttrib(SXE.updateElement, 'cite')); + SXE.showRemoveButton(); + } +} + +function setElementAttribs(elm) { + setAllCommonAttribs(elm); + setAttrib(elm, 'datetime'); + setAttrib(elm, 'cite'); +} + +function insertDel() { + var elm = tinyMCEPopup.editor.dom.getParent(SXE.focusElement, 'DEL'); + + tinyMCEPopup.execCommand('mceBeginUndoLevel'); + if (elm == null) { + var s = SXE.inst.selection.getContent(); + if(s.length > 0) { + insertInlineElement('del'); + var elementArray = tinymce.grep(SXE.inst.dom.select('del'), function(n) {return n.id == '#sxe_temp_del#';}); + for (var i=0; i 0) { + tagName = element_name; + + insertInlineElement(element_name); + var elementArray = tinymce.grep(SXE.inst.dom.select(element_name)); + for (var i=0; i -1) ? true : false; +} + +SXE.removeClass = function(elm,cl) { + if(elm.className == null || elm.className == "" || !SXE.containsClass(elm,cl)) { + return true; + } + var classNames = elm.className.split(" "); + var newClassNames = ""; + for (var x = 0, cnl = classNames.length; x < cnl; x++) { + if (classNames[x] != cl) { + newClassNames += (classNames[x] + " "); + } + } + elm.className = newClassNames.substring(0,newClassNames.length-1); //removes extra space at the end +} + +SXE.addClass = function(elm,cl) { + if(!SXE.containsClass(elm,cl)) elm.className ? elm.className += " " + cl : elm.className = cl; + return true; +} + +function insertInlineElement(en) { + var ed = tinyMCEPopup.editor, dom = ed.dom; + + ed.getDoc().execCommand('FontName', false, 'mceinline'); + tinymce.each(dom.select('span,font'), function(n) { + if (n.style.fontFamily == 'mceinline' || n.face == 'mceinline') + dom.replace(dom.create(en, {_mce_new : 1}), n, 1); + }); +} diff --git a/jscripts/tiny_mce/plugins/xhtmlxtras/js/ins.js b/jscripts/tiny_mce/plugins/xhtmlxtras/js/ins.js new file mode 100644 index 000000000..3774f0a18 --- /dev/null +++ b/jscripts/tiny_mce/plugins/xhtmlxtras/js/ins.js @@ -0,0 +1,62 @@ +/** + * ins.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +function init() { + SXE.initElementDialog('ins'); + if (SXE.currentAction == "update") { + setFormValue('datetime', tinyMCEPopup.editor.dom.getAttrib(SXE.updateElement, 'datetime')); + setFormValue('cite', tinyMCEPopup.editor.dom.getAttrib(SXE.updateElement, 'cite')); + SXE.showRemoveButton(); + } +} + +function setElementAttribs(elm) { + setAllCommonAttribs(elm); + setAttrib(elm, 'datetime'); + setAttrib(elm, 'cite'); +} + +function insertIns() { + var elm = tinyMCEPopup.editor.dom.getParent(SXE.focusElement, 'INS'); + tinyMCEPopup.execCommand('mceBeginUndoLevel'); + if (elm == null) { + var s = SXE.inst.selection.getContent(); + if(s.length > 0) { + insertInlineElement('INS'); + var elementArray = tinymce.grep(SXE.inst.dom.select('ins'), function(n) {return n.id == '#sxe_temp_ins#';}); + for (var i=0; i + + + {#advanced_dlg.about_title} + + + + + + + +
    +
    +

    {#advanced_dlg.about_title}

    +

    Version: ()

    +

    TinyMCE is a platform independent web based Javascript HTML WYSIWYG editor control released as Open Source under LGPL + by Moxiecode Systems AB. It has the ability to convert HTML TEXTAREA fields or other HTML elements to editor instances.

    +

    Copyright © 2003-2008, Moxiecode Systems AB, All rights reserved.

    +

    For more information about this software visit the TinyMCE website.

    + +
    + Got Moxie? + Hosted By Sourceforge + Also on freshmeat +
    +
    + +
    +
    +

    {#advanced_dlg.about_loaded}

    + +
    +
    + +

     

    +
    +
    + +
    +
    +
    +
    + +
    + +
    + + diff --git a/jscripts/tiny_mce/themes/advanced/anchor.htm b/jscripts/tiny_mce/themes/advanced/anchor.htm new file mode 100644 index 000000000..2bc63fcfd --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/anchor.htm @@ -0,0 +1,26 @@ + + + + {#advanced_dlg.anchor_title} + + + + +
    + + + + + + + + +
    {#advanced_dlg.anchor_title}
    {#advanced_dlg.anchor_name}:
    + +
    + + +
    +
    + + diff --git a/jscripts/tiny_mce/themes/advanced/charmap.htm b/jscripts/tiny_mce/themes/advanced/charmap.htm new file mode 100644 index 000000000..f11a38ad8 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/charmap.htm @@ -0,0 +1,53 @@ + + + + {#advanced_dlg.charmap_title} + + + + + + + + + + + + + + + + +
    {#advanced_dlg.charmap_title}
    + + + + + + + + + +
     
     
    +
    + + + + + + + + + + + + + + + + +
    HTML-Code
     
     
    NUM-Code
     
    +
    + + + diff --git a/jscripts/tiny_mce/themes/advanced/color_picker.htm b/jscripts/tiny_mce/themes/advanced/color_picker.htm new file mode 100644 index 000000000..096e7550c --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/color_picker.htm @@ -0,0 +1,73 @@ + + + + {#advanced_dlg.colorpicker_title} + + + + + +
    + + +
    +
    +
    + {#advanced_dlg.colorpicker_picker_title} +
    + + +
    + +
    + +
    +
    +
    +
    + +
    +
    + {#advanced_dlg.colorpicker_palette_title} +
    + +
    + +
    +
    +
    + +
    +
    + {#advanced_dlg.colorpicker_named_title} +
    + +
    + +
    + +
    + {#advanced_dlg.colorpicker_name} +
    +
    +
    +
    + +
    + + +
    + +
    + +
    +
    +
    + + diff --git a/jscripts/tiny_mce/themes/advanced/editor_template.js b/jscripts/tiny_mce/themes/advanced/editor_template.js new file mode 100644 index 000000000..914b9f49b --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/editor_template.js @@ -0,0 +1 @@ +(function(e){var d=e.DOM,b=e.dom.Event,h=e.extend,f=e.each,a=e.util.Cookie,g,c=e.explode;e.ThemeManager.requireLangPack("advanced");e.create("tinymce.themes.AdvancedTheme",{sizes:[8,10,12,14,18,24,36],controls:{bold:["bold_desc","Bold"],italic:["italic_desc","Italic"],underline:["underline_desc","Underline"],strikethrough:["striketrough_desc","Strikethrough"],justifyleft:["justifyleft_desc","JustifyLeft"],justifycenter:["justifycenter_desc","JustifyCenter"],justifyright:["justifyright_desc","JustifyRight"],justifyfull:["justifyfull_desc","JustifyFull"],bullist:["bullist_desc","InsertUnorderedList"],numlist:["numlist_desc","InsertOrderedList"],outdent:["outdent_desc","Outdent"],indent:["indent_desc","Indent"],cut:["cut_desc","Cut"],copy:["copy_desc","Copy"],paste:["paste_desc","Paste"],undo:["undo_desc","Undo"],redo:["redo_desc","Redo"],link:["link_desc","mceLink"],unlink:["unlink_desc","unlink"],image:["image_desc","mceImage"],cleanup:["cleanup_desc","mceCleanup"],help:["help_desc","mceHelp"],code:["code_desc","mceCodeEditor"],hr:["hr_desc","InsertHorizontalRule"],removeformat:["removeformat_desc","RemoveFormat"],sub:["sub_desc","subscript"],sup:["sup_desc","superscript"],forecolor:["forecolor_desc","ForeColor"],forecolorpicker:["forecolor_desc","mceForeColor"],backcolor:["backcolor_desc","HiliteColor"],backcolorpicker:["backcolor_desc","mceBackColor"],charmap:["charmap_desc","mceCharMap"],visualaid:["visualaid_desc","mceToggleVisualAid"],anchor:["anchor_desc","mceInsertAnchor"],newdocument:["newdocument_desc","mceNewDocument"],blockquote:["blockquote_desc","mceBlockQuote"]},stateControls:["bold","italic","underline","strikethrough","bullist","numlist","justifyleft","justifycenter","justifyright","justifyfull","sub","sup","blockquote"],init:function(j,k){var l=this,m,i,n;l.editor=j;l.url=k;l.onResolveName=new e.util.Dispatcher(this);l.settings=m=h({theme_advanced_path:true,theme_advanced_toolbar_location:"bottom",theme_advanced_buttons1:"bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect",theme_advanced_buttons2:"bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code",theme_advanced_buttons3:"hr,removeformat,visualaid,|,sub,sup,|,charmap",theme_advanced_blockformats:"p,address,pre,h1,h2,h3,h4,h5,h6",theme_advanced_toolbar_align:"center",theme_advanced_fonts:"Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",theme_advanced_more_colors:1,theme_advanced_row_height:23,theme_advanced_resize_horizontal:1,theme_advanced_resizing_use_cookie:1,theme_advanced_font_sizes:"1,2,3,4,5,6,7",readonly:j.settings.readonly},j.settings);if(!m.font_size_style_values){m.font_size_style_values="8pt,10pt,12pt,14pt,18pt,24pt,36pt"}if(e.is(m.theme_advanced_font_sizes,"string")){m.font_size_style_values=e.explode(m.font_size_style_values);m.font_size_classes=e.explode(m.font_size_classes||"");n={};j.settings.theme_advanced_font_sizes=m.theme_advanced_font_sizes;f(j.getParam("theme_advanced_font_sizes","","hash"),function(q,p){var o;if(p==q&&q>=1&&q<=7){p=q+" ("+l.sizes[q-1]+"pt)";o=m.font_size_classes[q-1];q=m.font_size_style_values[q-1]||(l.sizes[q-1]+"pt")}if(/^\s*\./.test(q)){o=q.replace(/\./g,"")}n[p]=o?{"class":o}:{fontSize:q}});m.theme_advanced_font_sizes=n}if((i=m.theme_advanced_path_location)&&i!="none"){m.theme_advanced_statusbar_location=m.theme_advanced_path_location}if(m.theme_advanced_statusbar_location=="none"){m.theme_advanced_statusbar_location=0}j.onInit.add(function(){if(!j.settings.readonly){j.onNodeChange.add(l._nodeChanged,l)}if(j.settings.content_css!==false){j.dom.loadCSS(j.baseURI.toAbsolute("themes/advanced/skins/"+j.settings.skin+"/content.css"))}});j.onSetProgressState.add(function(q,o,r){var s,t=q.id,p;if(o){l.progressTimer=setTimeout(function(){s=q.getContainer();s=s.insertBefore(d.create("DIV",{style:"position:relative"}),s.firstChild);p=d.get(q.id+"_tbl");d.add(s,"div",{id:t+"_blocker","class":"mceBlocker",style:{width:p.clientWidth+2,height:p.clientHeight+2}});d.add(s,"div",{id:t+"_progress","class":"mceProgress",style:{left:p.clientWidth/2,top:p.clientHeight/2}})},r||0)}else{d.remove(t+"_blocker");d.remove(t+"_progress");clearTimeout(l.progressTimer)}});d.loadCSS(m.editor_css?j.documentBaseURI.toAbsolute(m.editor_css):k+"/skins/"+j.settings.skin+"/ui.css");if(m.skin_variant){d.loadCSS(k+"/skins/"+j.settings.skin+"/ui_"+m.skin_variant+".css")}},createControl:function(l,i){var j,k;if(k=i.createControl(l)){return k}switch(l){case"styleselect":return this._createStyleSelect();case"formatselect":return this._createBlockFormats();case"fontselect":return this._createFontSelect();case"fontsizeselect":return this._createFontSizeSelect();case"forecolor":return this._createForeColorMenu();case"backcolor":return this._createBackColorMenu()}if((j=this.controls[l])){return i.createButton(l,{title:"advanced."+j[0],cmd:j[1],ui:j[2],value:j[3]})}},execCommand:function(k,j,l){var i=this["_"+k];if(i){i.call(this,j,l);return true}return false},_importClasses:function(k){var i=this.editor,j=i.controlManager.get("styleselect");if(j.getLength()==0){f(i.dom.getClasses(),function(n,l){var m="style_"+l;i.formatter.register(m,{inline:"span",classes:n["class"]});j.add(n["class"],m)})}},_createStyleSelect:function(m){var k=this,i=k.editor,j=i.controlManager,l;l=j.createListBox("styleselect",{title:"advanced.style_select",onselect:function(n){i.focus();i.formatter.toggle(n);return false}});i.onInit.add(function(){var o=0,n=i.getParam("style_formats");if(n){f(n,function(p){var q,r=0;f(p,function(){r++});if(r>1){q=p.name=p.name||"style_"+(o++);i.formatter.register(q,p);l.add(p.title,q)}else{l.add(p.title)}})}else{f(i.getParam("theme_advanced_styles","","hash"),function(r,q){var p;if(r){p="style_"+(o++);i.formatter.register(p,{inline:"span",classes:r});l.add(k.editor.translate(q),p)}})}});if(l.getLength()==0){l.onPostRender.add(function(o,p){if(!l.NativeListBox){b.add(p.id+"_text","focus",k._importClasses,k);b.add(p.id+"_text","mousedown",k._importClasses,k);b.add(p.id+"_open","focus",k._importClasses,k);b.add(p.id+"_open","mousedown",k._importClasses,k)}else{b.add(p.id,"focus",k._importClasses,k)}})}return l},_createFontSelect:function(){var k,j=this,i=j.editor;k=i.controlManager.createListBox("fontselect",{title:"advanced.fontdefault",onselect:function(l){i.execCommand("FontName",false,l);return false}});if(k){f(i.getParam("theme_advanced_fonts",j.settings.theme_advanced_fonts,"hash"),function(m,l){k.add(i.translate(l),m,{style:m.indexOf("dings")==-1?"font-family:"+m:""})})}return k},_createFontSizeSelect:function(){var m=this,k=m.editor,n,l=0,j=[];n=k.controlManager.createListBox("fontsizeselect",{title:"advanced.font_size",onselect:function(i){if(i.fontSize){k.execCommand("FontSize",false,i.fontSize)}else{f(m.settings.theme_advanced_font_sizes,function(p,o){if(p["class"]){j.push(p["class"])}});k.editorCommands._applyInlineStyle("span",{"class":i["class"]},{check_classes:j})}return false}});if(n){f(m.settings.theme_advanced_font_sizes,function(o,i){var p=o.fontSize;if(p>=1&&p<=7){p=m.sizes[parseInt(p)-1]+"pt"}n.add(i,o,{style:"font-size:"+p,"class":"mceFontSize"+(l++)+(" "+(o["class"]||""))})})}return n},_createBlockFormats:function(){var k,i={p:"advanced.paragraph",address:"advanced.address",pre:"advanced.pre",h1:"advanced.h1",h2:"advanced.h2",h3:"advanced.h3",h4:"advanced.h4",h5:"advanced.h5",h6:"advanced.h6",div:"advanced.div",blockquote:"advanced.blockquote",code:"advanced.code",dt:"advanced.dt",dd:"advanced.dd",samp:"advanced.samp"},j=this;k=j.editor.controlManager.createListBox("formatselect",{title:"advanced.block",cmd:"FormatBlock"});if(k){f(j.editor.getParam("theme_advanced_blockformats",j.settings.theme_advanced_blockformats,"hash"),function(m,l){k.add(j.editor.translate(l!=m?l:i[m]),m,{"class":"mce_formatPreview mce_"+m})})}return k},_createForeColorMenu:function(){var m,j=this,k=j.settings,l={},i;if(k.theme_advanced_more_colors){l.more_colors_func=function(){j._mceColorPicker(0,{color:m.value,func:function(n){m.setColor(n)}})}}if(i=k.theme_advanced_text_colors){l.colors=i}if(k.theme_advanced_default_foreground_color){l.default_color=k.theme_advanced_default_foreground_color}l.title="advanced.forecolor_desc";l.cmd="ForeColor";l.scope=this;m=j.editor.controlManager.createColorSplitButton("forecolor",l);return m},_createBackColorMenu:function(){var m,j=this,k=j.settings,l={},i;if(k.theme_advanced_more_colors){l.more_colors_func=function(){j._mceColorPicker(0,{color:m.value,func:function(n){m.setColor(n)}})}}if(i=k.theme_advanced_background_colors){l.colors=i}if(k.theme_advanced_default_background_color){l.default_color=k.theme_advanced_default_background_color}l.title="advanced.backcolor_desc";l.cmd="HiliteColor";l.scope=this;m=j.editor.controlManager.createColorSplitButton("backcolor",l);return m},renderUI:function(k){var m,l,q,v=this,r=v.editor,w=v.settings,u,j,i;m=j=d.create("span",{id:r.id+"_parent","class":"mceEditor "+r.settings.skin+"Skin"+(w.skin_variant?" "+r.settings.skin+"Skin"+v._ufirst(w.skin_variant):"")});if(!d.boxModel){m=d.add(m,"div",{"class":"mceOldBoxModel"})}m=u=d.add(m,"table",{id:r.id+"_tbl","class":"mceLayout",cellSpacing:0,cellPadding:0});m=q=d.add(m,"tbody");switch((w.theme_advanced_layout_manager||"").toLowerCase()){case"rowlayout":l=v._rowLayout(w,q,k);break;case"customlayout":l=r.execCallback("theme_advanced_custom_layout",w,q,k,j);break;default:l=v._simpleLayout(w,q,k,j)}m=k.targetNode;i=d.stdMode?u.getElementsByTagName("tr"):u.rows;d.addClass(i[0],"mceFirst");d.addClass(i[i.length-1],"mceLast");f(d.select("tr",q),function(o){d.addClass(o.firstChild,"mceFirst");d.addClass(o.childNodes[o.childNodes.length-1],"mceLast")});if(d.get(w.theme_advanced_toolbar_container)){d.get(w.theme_advanced_toolbar_container).appendChild(j)}else{d.insertAfter(j,m)}b.add(r.id+"_path_row","click",function(n){n=n.target;if(n.nodeName=="A"){v._sel(n.className.replace(/^.*mcePath_([0-9]+).*$/,"$1"));return b.cancel(n)}});if(!r.getParam("accessibility_focus")){b.add(d.add(j,"a",{href:"#"},""),"focus",function(){tinyMCE.get(r.id).focus()})}if(w.theme_advanced_toolbar_location=="external"){k.deltaHeight=0}v.deltaHeight=k.deltaHeight;k.targetNode=null;return{iframeContainer:l,editorContainer:r.id+"_parent",sizeContainer:u,deltaHeight:k.deltaHeight}},getInfo:function(){return{longname:"Advanced theme",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",version:e.majorVersion+"."+e.minorVersion}},resizeBy:function(i,j){var k=d.get(this.editor.id+"_tbl");this.resizeTo(k.clientWidth+i,k.clientHeight+j)},resizeTo:function(i,l){var j=this.editor,k=j.settings,n=d.get(j.id+"_tbl"),o=d.get(j.id+"_ifr"),m;i=Math.max(k.theme_advanced_resizing_min_width||100,i);l=Math.max(k.theme_advanced_resizing_min_height||100,l);i=Math.min(k.theme_advanced_resizing_max_width||65535,i);l=Math.min(k.theme_advanced_resizing_max_height||65535,l);m=n.clientHeight-o.clientHeight;d.setStyle(o,"height",l-m);d.setStyles(n,{width:i,height:l})},destroy:function(){var i=this.editor.id;b.clear(i+"_resize");b.clear(i+"_path_row");b.clear(i+"_external_close")},_simpleLayout:function(y,r,k,i){var x=this,u=x.editor,v=y.theme_advanced_toolbar_location,m=y.theme_advanced_statusbar_location,l,j,q,w;if(y.readonly){l=d.add(r,"tr");l=j=d.add(l,"td",{"class":"mceIframeContainer"});return j}if(v=="top"){x._addToolbars(r,k)}if(v=="external"){l=w=d.create("div",{style:"position:relative"});l=d.add(l,"div",{id:u.id+"_external","class":"mceExternalToolbar"});d.add(l,"a",{id:u.id+"_external_close",href:"javascript:;","class":"mceExternalClose"});l=d.add(l,"table",{id:u.id+"_tblext",cellSpacing:0,cellPadding:0});q=d.add(l,"tbody");if(i.firstChild.className=="mceOldBoxModel"){i.firstChild.appendChild(w)}else{i.insertBefore(w,i.firstChild)}x._addToolbars(q,k);u.onMouseUp.add(function(){var o=d.get(u.id+"_external");d.show(o);d.hide(g);var n=b.add(u.id+"_external_close","click",function(){d.hide(u.id+"_external");b.remove(u.id+"_external_close","click",n)});d.show(o);d.setStyle(o,"top",0-d.getRect(u.id+"_tblext").h-1);d.hide(o);d.show(o);o.style.filter="";g=u.id+"_external";o=null})}if(m=="top"){x._addStatusBar(r,k)}if(!y.theme_advanced_toolbar_container){l=d.add(r,"tr");l=j=d.add(l,"td",{"class":"mceIframeContainer"})}if(v=="bottom"){x._addToolbars(r,k)}if(m=="bottom"){x._addStatusBar(r,k)}return j},_rowLayout:function(w,m,k){var v=this,p=v.editor,u,x,i=p.controlManager,l,j,r,q;u=w.theme_advanced_containers_default_class||"";x=w.theme_advanced_containers_default_align||"center";f(c(w.theme_advanced_containers||""),function(s,o){var n=w["theme_advanced_container_"+s]||"";switch(n.toLowerCase()){case"mceeditor":l=d.add(m,"tr");l=j=d.add(l,"td",{"class":"mceIframeContainer"});break;case"mceelementpath":v._addStatusBar(m,k);break;default:q=(w["theme_advanced_container_"+s+"_align"]||x).toLowerCase();q="mce"+v._ufirst(q);l=d.add(d.add(m,"tr"),"td",{"class":"mceToolbar "+(w["theme_advanced_container_"+s+"_class"]||u)+" "+q||x});r=i.createToolbar("toolbar"+o);v._addControls(n,r);d.setHTML(l,r.renderHTML());k.deltaHeight-=w.theme_advanced_row_height}});return j},_addControls:function(j,i){var k=this,l=k.settings,m,n=k.editor.controlManager;if(l.theme_advanced_disable&&!k._disabled){m={};f(c(l.theme_advanced_disable),function(o){m[o]=1});k._disabled=m}else{m=k._disabled}f(c(j),function(p){var o;if(m&&m[p]){return}if(p=="tablecontrols"){f(["table","|","row_props","cell_props","|","row_before","row_after","delete_row","|","col_before","col_after","delete_col","|","split_cells","merge_cells"],function(q){q=k.createControl(q,n);if(q){i.add(q)}});return}o=k.createControl(p,n);if(o){i.add(o)}})},_addToolbars:function(w,k){var z=this,p,m,r=z.editor,A=z.settings,y,j=r.controlManager,u,l,q=[],x;x=A.theme_advanced_toolbar_align.toLowerCase();x="mce"+z._ufirst(x);l=d.add(d.add(w,"tr"),"td",{"class":"mceToolbar "+x});if(!r.getParam("accessibility_focus")){q.push(d.createHTML("a",{href:"#",onfocus:"tinyMCE.get('"+r.id+"').focus();"},""))}q.push(d.createHTML("a",{href:"#",accesskey:"q",title:r.getLang("advanced.toolbar_focus")},""));for(p=1;(y=A["theme_advanced_buttons"+p]);p++){m=j.createToolbar("toolbar"+p,{"class":"mceToolbarRow"+p});if(A["theme_advanced_buttons"+p+"_add"]){y+=","+A["theme_advanced_buttons"+p+"_add"]}if(A["theme_advanced_buttons"+p+"_add_before"]){y=A["theme_advanced_buttons"+p+"_add_before"]+","+y}z._addControls(y,m);q.push(m.renderHTML());k.deltaHeight-=A.theme_advanced_row_height}q.push(d.createHTML("a",{href:"#",accesskey:"z",title:r.getLang("advanced.toolbar_focus"),onfocus:"tinyMCE.getInstanceById('"+r.id+"').focus();"},""));d.setHTML(l,q.join(""))},_addStatusBar:function(m,j){var k,v=this,p=v.editor,w=v.settings,i,q,u,l;k=d.add(m,"tr");k=l=d.add(k,"td",{"class":"mceStatusbar"});k=d.add(k,"div",{id:p.id+"_path_row"},w.theme_advanced_path?p.translate("advanced.path")+": ":" ");d.add(k,"a",{href:"#",accesskey:"x"});if(w.theme_advanced_resizing){d.add(l,"a",{id:p.id+"_resize",href:"javascript:;",onclick:"return false;","class":"mceResize"});if(w.theme_advanced_resizing_use_cookie){p.onPostRender.add(function(){var n=a.getHash("TinyMCE_"+p.id+"_size"),r=d.get(p.id+"_tbl");if(!n){return}if(w.theme_advanced_resize_horizontal){r.style.width=Math.max(10,n.cw)+"px"}r.style.height=Math.max(10,n.ch)+"px";d.get(p.id+"_ifr").style.height=Math.max(10,parseInt(n.ch)+v.deltaHeight)+"px"})}p.onPostRender.add(function(){b.add(p.id+"_resize","mousedown",function(x){var z,t,o,s,y,r;z=d.get(p.id+"_tbl");o=z.clientWidth;s=z.clientHeight;miw=w.theme_advanced_resizing_min_width||100;mih=w.theme_advanced_resizing_min_height||100;maw=w.theme_advanced_resizing_max_width||65535;mah=w.theme_advanced_resizing_max_height||65535;t=d.add(d.get(p.id+"_parent"),"div",{"class":"mcePlaceHolder"});d.setStyles(t,{width:o,height:s});d.hide(z);d.show(t);i={x:x.screenX,y:x.screenY,w:o,h:s,dx:null,dy:null};q=b.add(d.doc,"mousemove",function(B){var n,A;i.dx=B.screenX-i.x;i.dy=B.screenY-i.y;n=Math.max(miw,i.w+i.dx);A=Math.max(mih,i.h+i.dy);n=Math.min(maw,n);A=Math.min(mah,A);if(w.theme_advanced_resize_horizontal){t.style.width=n+"px"}t.style.height=A+"px";return b.cancel(B)});u=b.add(d.doc,"mouseup",function(n){var A;b.remove(d.doc,"mousemove",q);b.remove(d.doc,"mouseup",u);z.style.display="";d.remove(t);if(i.dx===null){return}A=d.get(p.id+"_ifr");if(w.theme_advanced_resize_horizontal){z.style.width=Math.max(10,i.w+i.dx)+"px"}z.style.height=Math.max(10,i.h+i.dy)+"px";A.style.height=Math.max(10,A.clientHeight+i.dy)+"px";if(w.theme_advanced_resizing_use_cookie){a.setHash("TinyMCE_"+p.id+"_size",{cw:i.w+i.dx,ch:i.h+i.dy})}});return b.cancel(x)})})}j.deltaHeight-=21;k=m=null},_nodeChanged:function(o,x,l,u,j){var A=this,i,w=0,z,q,B=A.settings,y,k,r;e.each(A.stateControls,function(n){x.setActive(n,o.queryCommandState(A.controls[n][1]))});function m(p){var s,n=j.parents,t=p;if(typeof(p)=="string"){t=function(v){return v.nodeName==p}}for(s=0;s= 1 && v <= 7) { + k = v + ' (' + t.sizes[v - 1] + 'pt)'; + cl = s.font_size_classes[v - 1]; + v = s.font_size_style_values[v - 1] || (t.sizes[v - 1] + 'pt'); + } + + if (/^\s*\./.test(v)) + cl = v.replace(/\./g, ''); + + o[k] = cl ? {'class' : cl} : {fontSize : v}; + }); + + s.theme_advanced_font_sizes = o; + } + + if ((v = s.theme_advanced_path_location) && v != 'none') + s.theme_advanced_statusbar_location = s.theme_advanced_path_location; + + if (s.theme_advanced_statusbar_location == 'none') + s.theme_advanced_statusbar_location = 0; + + // Init editor + ed.onInit.add(function() { + if (!ed.settings.readonly) + ed.onNodeChange.add(t._nodeChanged, t); + + if (ed.settings.content_css !== false) + ed.dom.loadCSS(ed.baseURI.toAbsolute("themes/advanced/skins/" + ed.settings.skin + "/content.css")); + }); + + ed.onSetProgressState.add(function(ed, b, ti) { + var co, id = ed.id, tb; + + if (b) { + t.progressTimer = setTimeout(function() { + co = ed.getContainer(); + co = co.insertBefore(DOM.create('DIV', {style : 'position:relative'}), co.firstChild); + tb = DOM.get(ed.id + '_tbl'); + + DOM.add(co, 'div', {id : id + '_blocker', 'class' : 'mceBlocker', style : {width : tb.clientWidth + 2, height : tb.clientHeight + 2}}); + DOM.add(co, 'div', {id : id + '_progress', 'class' : 'mceProgress', style : {left : tb.clientWidth / 2, top : tb.clientHeight / 2}}); + }, ti || 0); + } else { + DOM.remove(id + '_blocker'); + DOM.remove(id + '_progress'); + clearTimeout(t.progressTimer); + } + }); + + DOM.loadCSS(s.editor_css ? ed.documentBaseURI.toAbsolute(s.editor_css) : url + "/skins/" + ed.settings.skin + "/ui.css"); + + if (s.skin_variant) + DOM.loadCSS(url + "/skins/" + ed.settings.skin + "/ui_" + s.skin_variant + ".css"); + }, + + createControl : function(n, cf) { + var cd, c; + + if (c = cf.createControl(n)) + return c; + + switch (n) { + case "styleselect": + return this._createStyleSelect(); + + case "formatselect": + return this._createBlockFormats(); + + case "fontselect": + return this._createFontSelect(); + + case "fontsizeselect": + return this._createFontSizeSelect(); + + case "forecolor": + return this._createForeColorMenu(); + + case "backcolor": + return this._createBackColorMenu(); + } + + if ((cd = this.controls[n])) + return cf.createButton(n, {title : "advanced." + cd[0], cmd : cd[1], ui : cd[2], value : cd[3]}); + }, + + execCommand : function(cmd, ui, val) { + var f = this['_' + cmd]; + + if (f) { + f.call(this, ui, val); + return true; + } + + return false; + }, + + _importClasses : function(e) { + var ed = this.editor, ctrl = ed.controlManager.get('styleselect'); + + if (ctrl.getLength() == 0) { + each(ed.dom.getClasses(), function(o, idx) { + var name = 'style_' + idx; + + ed.formatter.register(name, { + inline : 'span', + classes : o['class'] + }); + + ctrl.add(o['class'], name); + }); + } + }, + + _createStyleSelect : function(n) { + var t = this, ed = t.editor, ctrlMan = ed.controlManager, ctrl; + + // Setup style select box + ctrl = ctrlMan.createListBox('styleselect', { + title : 'advanced.style_select', + onselect : function(name) { + ed.focus(); + ed.formatter.toggle(name); + + return false; // No auto select + } + }); + + // Handle specified format + ed.onInit.add(function() { + var counter = 0, formats = ed.getParam('style_formats'); + + if (formats) { + each(formats, function(fmt) { + var name, keys = 0; + + each(fmt, function() {keys++;}); + + if (keys > 1) { + name = fmt.name = fmt.name || 'style_' + (counter++); + ed.formatter.register(name, fmt); + ctrl.add(fmt.title, name); + } else + ctrl.add(fmt.title); + }); + } else { + each(ed.getParam('theme_advanced_styles', '', 'hash'), function(val, key) { + var name; + + if (val) { + name = 'style_' + (counter++); + + ed.formatter.register(name, { + inline : 'span', + classes : val + }); + + ctrl.add(t.editor.translate(key), name); + } + }); + } + }); + + // Auto import classes if the ctrl box is empty + if (ctrl.getLength() == 0) { + ctrl.onPostRender.add(function(ed, n) { + if (!ctrl.NativeListBox) { + Event.add(n.id + '_text', 'focus', t._importClasses, t); + Event.add(n.id + '_text', 'mousedown', t._importClasses, t); + Event.add(n.id + '_open', 'focus', t._importClasses, t); + Event.add(n.id + '_open', 'mousedown', t._importClasses, t); + } else + Event.add(n.id, 'focus', t._importClasses, t); + }); + } + + return ctrl; + }, + + _createFontSelect : function() { + var c, t = this, ed = t.editor; + + c = ed.controlManager.createListBox('fontselect', { + title : 'advanced.fontdefault', + onselect : function(v) { + ed.execCommand('FontName', false, v); + return false; // No auto select + } + }); + + if (c) { + each(ed.getParam('theme_advanced_fonts', t.settings.theme_advanced_fonts, 'hash'), function(v, k) { + c.add(ed.translate(k), v, {style : v.indexOf('dings') == -1 ? 'font-family:' + v : ''}); + }); + } + + return c; + }, + + _createFontSizeSelect : function() { + var t = this, ed = t.editor, c, i = 0, cl = []; + + c = ed.controlManager.createListBox('fontsizeselect', {title : 'advanced.font_size', onselect : function(v) { + if (v.fontSize) + ed.execCommand('FontSize', false, v.fontSize); + else { + each(t.settings.theme_advanced_font_sizes, function(v, k) { + if (v['class']) + cl.push(v['class']); + }); + + ed.editorCommands._applyInlineStyle('span', {'class' : v['class']}, {check_classes : cl}); + } + + return false; // No auto select + }}); + + if (c) { + each(t.settings.theme_advanced_font_sizes, function(v, k) { + var fz = v.fontSize; + + if (fz >= 1 && fz <= 7) + fz = t.sizes[parseInt(fz) - 1] + 'pt'; + + c.add(k, v, {'style' : 'font-size:' + fz, 'class' : 'mceFontSize' + (i++) + (' ' + (v['class'] || ''))}); + }); + } + + return c; + }, + + _createBlockFormats : function() { + var c, fmts = { + p : 'advanced.paragraph', + address : 'advanced.address', + pre : 'advanced.pre', + h1 : 'advanced.h1', + h2 : 'advanced.h2', + h3 : 'advanced.h3', + h4 : 'advanced.h4', + h5 : 'advanced.h5', + h6 : 'advanced.h6', + div : 'advanced.div', + blockquote : 'advanced.blockquote', + code : 'advanced.code', + dt : 'advanced.dt', + dd : 'advanced.dd', + samp : 'advanced.samp' + }, t = this; + + c = t.editor.controlManager.createListBox('formatselect', {title : 'advanced.block', cmd : 'FormatBlock'}); + if (c) { + each(t.editor.getParam('theme_advanced_blockformats', t.settings.theme_advanced_blockformats, 'hash'), function(v, k) { + c.add(t.editor.translate(k != v ? k : fmts[v]), v, {'class' : 'mce_formatPreview mce_' + v}); + }); + } + + return c; + }, + + _createForeColorMenu : function() { + var c, t = this, s = t.settings, o = {}, v; + + if (s.theme_advanced_more_colors) { + o.more_colors_func = function() { + t._mceColorPicker(0, { + color : c.value, + func : function(co) { + c.setColor(co); + } + }); + }; + } + + if (v = s.theme_advanced_text_colors) + o.colors = v; + + if (s.theme_advanced_default_foreground_color) + o.default_color = s.theme_advanced_default_foreground_color; + + o.title = 'advanced.forecolor_desc'; + o.cmd = 'ForeColor'; + o.scope = this; + + c = t.editor.controlManager.createColorSplitButton('forecolor', o); + + return c; + }, + + _createBackColorMenu : function() { + var c, t = this, s = t.settings, o = {}, v; + + if (s.theme_advanced_more_colors) { + o.more_colors_func = function() { + t._mceColorPicker(0, { + color : c.value, + func : function(co) { + c.setColor(co); + } + }); + }; + } + + if (v = s.theme_advanced_background_colors) + o.colors = v; + + if (s.theme_advanced_default_background_color) + o.default_color = s.theme_advanced_default_background_color; + + o.title = 'advanced.backcolor_desc'; + o.cmd = 'HiliteColor'; + o.scope = this; + + c = t.editor.controlManager.createColorSplitButton('backcolor', o); + + return c; + }, + + renderUI : function(o) { + var n, ic, tb, t = this, ed = t.editor, s = t.settings, sc, p, nl; + + n = p = DOM.create('span', {id : ed.id + '_parent', 'class' : 'mceEditor ' + ed.settings.skin + 'Skin' + (s.skin_variant ? ' ' + ed.settings.skin + 'Skin' + t._ufirst(s.skin_variant) : '')}); + + if (!DOM.boxModel) + n = DOM.add(n, 'div', {'class' : 'mceOldBoxModel'}); + + n = sc = DOM.add(n, 'table', {id : ed.id + '_tbl', 'class' : 'mceLayout', cellSpacing : 0, cellPadding : 0}); + n = tb = DOM.add(n, 'tbody'); + + switch ((s.theme_advanced_layout_manager || '').toLowerCase()) { + case "rowlayout": + ic = t._rowLayout(s, tb, o); + break; + + case "customlayout": + ic = ed.execCallback("theme_advanced_custom_layout", s, tb, o, p); + break; + + default: + ic = t._simpleLayout(s, tb, o, p); + } + + n = o.targetNode; + + // Add classes to first and last TRs + nl = DOM.stdMode ? sc.getElementsByTagName('tr') : sc.rows; // Quick fix for IE 8 + DOM.addClass(nl[0], 'mceFirst'); + DOM.addClass(nl[nl.length - 1], 'mceLast'); + + // Add classes to first and last TDs + each(DOM.select('tr', tb), function(n) { + DOM.addClass(n.firstChild, 'mceFirst'); + DOM.addClass(n.childNodes[n.childNodes.length - 1], 'mceLast'); + }); + + if (DOM.get(s.theme_advanced_toolbar_container)) + DOM.get(s.theme_advanced_toolbar_container).appendChild(p); + else + DOM.insertAfter(p, n); + + Event.add(ed.id + '_path_row', 'click', function(e) { + e = e.target; + + if (e.nodeName == 'A') { + t._sel(e.className.replace(/^.*mcePath_([0-9]+).*$/, '$1')); + + return Event.cancel(e); + } + }); +/* + if (DOM.get(ed.id + '_path_row')) { + Event.add(ed.id + '_tbl', 'mouseover', function(e) { + var re; + + e = e.target; + + if (e.nodeName == 'SPAN' && DOM.hasClass(e.parentNode, 'mceButton')) { + re = DOM.get(ed.id + '_path_row'); + t.lastPath = re.innerHTML; + DOM.setHTML(re, e.parentNode.title); + } + }); + + Event.add(ed.id + '_tbl', 'mouseout', function(e) { + if (t.lastPath) { + DOM.setHTML(ed.id + '_path_row', t.lastPath); + t.lastPath = 0; + } + }); + } +*/ + + if (!ed.getParam('accessibility_focus')) + Event.add(DOM.add(p, 'a', {href : '#'}, ''), 'focus', function() {tinyMCE.get(ed.id).focus();}); + + if (s.theme_advanced_toolbar_location == 'external') + o.deltaHeight = 0; + + t.deltaHeight = o.deltaHeight; + o.targetNode = null; + + return { + iframeContainer : ic, + editorContainer : ed.id + '_parent', + sizeContainer : sc, + deltaHeight : o.deltaHeight + }; + }, + + getInfo : function() { + return { + longname : 'Advanced theme', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + version : tinymce.majorVersion + "." + tinymce.minorVersion + } + }, + + resizeBy : function(dw, dh) { + var e = DOM.get(this.editor.id + '_tbl'); + + this.resizeTo(e.clientWidth + dw, e.clientHeight + dh); + }, + + resizeTo : function(w, h) { + var ed = this.editor, s = ed.settings, e = DOM.get(ed.id + '_tbl'), ifr = DOM.get(ed.id + '_ifr'), dh; + + // Boundery fix box + w = Math.max(s.theme_advanced_resizing_min_width || 100, w); + h = Math.max(s.theme_advanced_resizing_min_height || 100, h); + w = Math.min(s.theme_advanced_resizing_max_width || 0xFFFF, w); + h = Math.min(s.theme_advanced_resizing_max_height || 0xFFFF, h); + + // Calc difference between iframe and container + dh = e.clientHeight - ifr.clientHeight; + + // Resize iframe and container + DOM.setStyle(ifr, 'height', h - dh); + DOM.setStyles(e, {width : w, height : h}); + }, + + destroy : function() { + var id = this.editor.id; + + Event.clear(id + '_resize'); + Event.clear(id + '_path_row'); + Event.clear(id + '_external_close'); + }, + + // Internal functions + + _simpleLayout : function(s, tb, o, p) { + var t = this, ed = t.editor, lo = s.theme_advanced_toolbar_location, sl = s.theme_advanced_statusbar_location, n, ic, etb, c; + + if (s.readonly) { + n = DOM.add(tb, 'tr'); + n = ic = DOM.add(n, 'td', {'class' : 'mceIframeContainer'}); + return ic; + } + + // Create toolbar container at top + if (lo == 'top') + t._addToolbars(tb, o); + + // Create external toolbar + if (lo == 'external') { + n = c = DOM.create('div', {style : 'position:relative'}); + n = DOM.add(n, 'div', {id : ed.id + '_external', 'class' : 'mceExternalToolbar'}); + DOM.add(n, 'a', {id : ed.id + '_external_close', href : 'javascript:;', 'class' : 'mceExternalClose'}); + n = DOM.add(n, 'table', {id : ed.id + '_tblext', cellSpacing : 0, cellPadding : 0}); + etb = DOM.add(n, 'tbody'); + + if (p.firstChild.className == 'mceOldBoxModel') + p.firstChild.appendChild(c); + else + p.insertBefore(c, p.firstChild); + + t._addToolbars(etb, o); + + ed.onMouseUp.add(function() { + var e = DOM.get(ed.id + '_external'); + DOM.show(e); + + DOM.hide(lastExtID); + + var f = Event.add(ed.id + '_external_close', 'click', function() { + DOM.hide(ed.id + '_external'); + Event.remove(ed.id + '_external_close', 'click', f); + }); + + DOM.show(e); + DOM.setStyle(e, 'top', 0 - DOM.getRect(ed.id + '_tblext').h - 1); + + // Fixes IE rendering bug + DOM.hide(e); + DOM.show(e); + e.style.filter = ''; + + lastExtID = ed.id + '_external'; + + e = null; + }); + } + + if (sl == 'top') + t._addStatusBar(tb, o); + + // Create iframe container + if (!s.theme_advanced_toolbar_container) { + n = DOM.add(tb, 'tr'); + n = ic = DOM.add(n, 'td', {'class' : 'mceIframeContainer'}); + } + + // Create toolbar container at bottom + if (lo == 'bottom') + t._addToolbars(tb, o); + + if (sl == 'bottom') + t._addStatusBar(tb, o); + + return ic; + }, + + _rowLayout : function(s, tb, o) { + var t = this, ed = t.editor, dc, da, cf = ed.controlManager, n, ic, to, a; + + dc = s.theme_advanced_containers_default_class || ''; + da = s.theme_advanced_containers_default_align || 'center'; + + each(explode(s.theme_advanced_containers || ''), function(c, i) { + var v = s['theme_advanced_container_' + c] || ''; + + switch (v.toLowerCase()) { + case 'mceeditor': + n = DOM.add(tb, 'tr'); + n = ic = DOM.add(n, 'td', {'class' : 'mceIframeContainer'}); + break; + + case 'mceelementpath': + t._addStatusBar(tb, o); + break; + + default: + a = (s['theme_advanced_container_' + c + '_align'] || da).toLowerCase(); + a = 'mce' + t._ufirst(a); + + n = DOM.add(DOM.add(tb, 'tr'), 'td', { + 'class' : 'mceToolbar ' + (s['theme_advanced_container_' + c + '_class'] || dc) + ' ' + a || da + }); + + to = cf.createToolbar("toolbar" + i); + t._addControls(v, to); + DOM.setHTML(n, to.renderHTML()); + o.deltaHeight -= s.theme_advanced_row_height; + } + }); + + return ic; + }, + + _addControls : function(v, tb) { + var t = this, s = t.settings, di, cf = t.editor.controlManager; + + if (s.theme_advanced_disable && !t._disabled) { + di = {}; + + each(explode(s.theme_advanced_disable), function(v) { + di[v] = 1; + }); + + t._disabled = di; + } else + di = t._disabled; + + each(explode(v), function(n) { + var c; + + if (di && di[n]) + return; + + // Compatiblity with 2.x + if (n == 'tablecontrols') { + each(["table","|","row_props","cell_props","|","row_before","row_after","delete_row","|","col_before","col_after","delete_col","|","split_cells","merge_cells"], function(n) { + n = t.createControl(n, cf); + + if (n) + tb.add(n); + }); + + return; + } + + c = t.createControl(n, cf); + + if (c) + tb.add(c); + }); + }, + + _addToolbars : function(c, o) { + var t = this, i, tb, ed = t.editor, s = t.settings, v, cf = ed.controlManager, di, n, h = [], a; + + a = s.theme_advanced_toolbar_align.toLowerCase(); + a = 'mce' + t._ufirst(a); + + n = DOM.add(DOM.add(c, 'tr'), 'td', {'class' : 'mceToolbar ' + a}); + + if (!ed.getParam('accessibility_focus')) + h.push(DOM.createHTML('a', {href : '#', onfocus : 'tinyMCE.get(\'' + ed.id + '\').focus();'}, '')); + + h.push(DOM.createHTML('a', {href : '#', accesskey : 'q', title : ed.getLang("advanced.toolbar_focus")}, '')); + + // Create toolbar and add the controls + for (i=1; (v = s['theme_advanced_buttons' + i]); i++) { + tb = cf.createToolbar("toolbar" + i, {'class' : 'mceToolbarRow' + i}); + + if (s['theme_advanced_buttons' + i + '_add']) + v += ',' + s['theme_advanced_buttons' + i + '_add']; + + if (s['theme_advanced_buttons' + i + '_add_before']) + v = s['theme_advanced_buttons' + i + '_add_before'] + ',' + v; + + t._addControls(v, tb); + + //n.appendChild(n = tb.render()); + h.push(tb.renderHTML()); + + o.deltaHeight -= s.theme_advanced_row_height; + } + + h.push(DOM.createHTML('a', {href : '#', accesskey : 'z', title : ed.getLang("advanced.toolbar_focus"), onfocus : 'tinyMCE.getInstanceById(\'' + ed.id + '\').focus();'}, '')); + DOM.setHTML(n, h.join('')); + }, + + _addStatusBar : function(tb, o) { + var n, t = this, ed = t.editor, s = t.settings, r, mf, me, td; + + n = DOM.add(tb, 'tr'); + n = td = DOM.add(n, 'td', {'class' : 'mceStatusbar'}); + n = DOM.add(n, 'div', {id : ed.id + '_path_row'}, s.theme_advanced_path ? ed.translate('advanced.path') + ': ' : ' '); + DOM.add(n, 'a', {href : '#', accesskey : 'x'}); + + if (s.theme_advanced_resizing) { + DOM.add(td, 'a', {id : ed.id + '_resize', href : 'javascript:;', onclick : "return false;", 'class' : 'mceResize'}); + + if (s.theme_advanced_resizing_use_cookie) { + ed.onPostRender.add(function() { + var o = Cookie.getHash("TinyMCE_" + ed.id + "_size"), c = DOM.get(ed.id + '_tbl'); + + if (!o) + return; + + if (s.theme_advanced_resize_horizontal) + c.style.width = Math.max(10, o.cw) + 'px'; + + c.style.height = Math.max(10, o.ch) + 'px'; + DOM.get(ed.id + '_ifr').style.height = Math.max(10, parseInt(o.ch) + t.deltaHeight) + 'px'; + }); + } + + ed.onPostRender.add(function() { + Event.add(ed.id + '_resize', 'mousedown', function(e) { + var c, p, w, h, n, pa; + + // Measure container + c = DOM.get(ed.id + '_tbl'); + w = c.clientWidth; + h = c.clientHeight; + + miw = s.theme_advanced_resizing_min_width || 100; + mih = s.theme_advanced_resizing_min_height || 100; + maw = s.theme_advanced_resizing_max_width || 0xFFFF; + mah = s.theme_advanced_resizing_max_height || 0xFFFF; + + // Setup placeholder + p = DOM.add(DOM.get(ed.id + '_parent'), 'div', {'class' : 'mcePlaceHolder'}); + DOM.setStyles(p, {width : w, height : h}); + + // Replace with placeholder + DOM.hide(c); + DOM.show(p); + + // Create internal resize obj + r = { + x : e.screenX, + y : e.screenY, + w : w, + h : h, + dx : null, + dy : null + }; + + // Start listening + mf = Event.add(DOM.doc, 'mousemove', function(e) { + var w, h; + + // Calc delta values + r.dx = e.screenX - r.x; + r.dy = e.screenY - r.y; + + // Boundery fix box + w = Math.max(miw, r.w + r.dx); + h = Math.max(mih, r.h + r.dy); + w = Math.min(maw, w); + h = Math.min(mah, h); + + // Resize placeholder + if (s.theme_advanced_resize_horizontal) + p.style.width = w + 'px'; + + p.style.height = h + 'px'; + + return Event.cancel(e); + }); + + me = Event.add(DOM.doc, 'mouseup', function(e) { + var ifr; + + // Stop listening + Event.remove(DOM.doc, 'mousemove', mf); + Event.remove(DOM.doc, 'mouseup', me); + + c.style.display = ''; + DOM.remove(p); + + if (r.dx === null) + return; + + ifr = DOM.get(ed.id + '_ifr'); + + if (s.theme_advanced_resize_horizontal) + c.style.width = Math.max(10, r.w + r.dx) + 'px'; + + c.style.height = Math.max(10, r.h + r.dy) + 'px'; + ifr.style.height = Math.max(10, ifr.clientHeight + r.dy) + 'px'; + + if (s.theme_advanced_resizing_use_cookie) { + Cookie.setHash("TinyMCE_" + ed.id + "_size", { + cw : r.w + r.dx, + ch : r.h + r.dy + }); + } + }); + + return Event.cancel(e); + }); + }); + } + + o.deltaHeight -= 21; + n = tb = null; + }, + + _nodeChanged : function(ed, cm, n, co, ob) { + var t = this, p, de = 0, v, c, s = t.settings, cl, fz, fn; + + tinymce.each(t.stateControls, function(c) { + cm.setActive(c, ed.queryCommandState(t.controls[c][1])); + }); + + function getParent(name) { + var i, parents = ob.parents, func = name; + + if (typeof(name) == 'string') { + func = function(node) { + return node.nodeName == name; + }; + } + + for (i = 0; i < parents.length; i++) { + if (func(parents[i])) + return parents[i]; + } + }; + + cm.setActive('visualaid', ed.hasVisual); + cm.setDisabled('undo', !ed.undoManager.hasUndo() && !ed.typing); + cm.setDisabled('redo', !ed.undoManager.hasRedo()); + cm.setDisabled('outdent', !ed.queryCommandState('Outdent')); + + p = getParent('A'); + if (c = cm.get('link')) { + if (!p || !p.name) { + c.setDisabled(!p && co); + c.setActive(!!p); + } + } + + if (c = cm.get('unlink')) { + c.setDisabled(!p && co); + c.setActive(!!p && !p.name); + } + + if (c = cm.get('anchor')) { + c.setActive(!!p && p.name); + } + + p = getParent('IMG'); + if (c = cm.get('image')) + c.setActive(!!p && n.className.indexOf('mceItem') == -1); + + if (c = cm.get('styleselect')) { + t._importClasses(); + + // Check each format and update + c.select(function(fmt) { + return !!ed.formatter.match(fmt); + }); + } + + if (c = cm.get('formatselect')) { + p = getParent(DOM.isBlock); + + if (p) + c.select(p.nodeName.toLowerCase()); + } + + // Find out current fontSize, fontFamily and fontClass + getParent(function(n) { + if (n.nodeName === 'SPAN') { + if (!cl && n.className) + cl = n.className; + + if (!fz && n.style.fontSize) + fz = n.style.fontSize; + + if (!fn && n.style.fontFamily) + fn = n.style.fontFamily.replace(/[\"\']+/g, '').replace(/^([^,]+).*/, '$1').toLowerCase(); + } + + return false; + }); + + if (c = cm.get('fontselect')) { + c.select(function(v) { + return v.replace(/^([^,]+).*/, '$1').toLowerCase() == fn; + }); + } + + // Select font size + if (c = cm.get('fontsizeselect')) { + // Use computed style + if (s.theme_advanced_runtime_fontsize && !fz && !cl) + fz = ed.dom.getStyle(n, 'fontSize', true); + + c.select(function(v) { + if (v.fontSize && v.fontSize === fz) + return true; + + if (v['class'] && v['class'] === cl) + return true; + }); + } + + if (s.theme_advanced_path && s.theme_advanced_statusbar_location) { + p = DOM.get(ed.id + '_path') || DOM.add(ed.id + '_path_row', 'span', {id : ed.id + '_path'}); + DOM.setHTML(p, ''); + + getParent(function(n) { + var na = n.nodeName.toLowerCase(), u, pi, ti = ''; + + /*if (n.getAttribute('_mce_bogus')) + return; +*/ + // Ignore non element and hidden elements + if (n.nodeType != 1 || n.nodeName === 'BR' || (DOM.hasClass(n, 'mceItemHidden') || DOM.hasClass(n, 'mceItemRemoved'))) + return; + + // Fake name + if (v = DOM.getAttrib(n, 'mce_name')) + na = v; + + // Handle prefix + if (tinymce.isIE && n.scopeName !== 'HTML') + na = n.scopeName + ':' + na; + + // Remove internal prefix + na = na.replace(/mce\:/g, ''); + + // Handle node name + switch (na) { + case 'b': + na = 'strong'; + break; + + case 'i': + na = 'em'; + break; + + case 'img': + if (v = DOM.getAttrib(n, 'src')) + ti += 'src: ' + v + ' '; + + break; + + case 'a': + if (v = DOM.getAttrib(n, 'name')) { + ti += 'name: ' + v + ' '; + na += '#' + v; + } + + if (v = DOM.getAttrib(n, 'href')) + ti += 'href: ' + v + ' '; + + break; + + case 'font': + if (v = DOM.getAttrib(n, 'face')) + ti += 'font: ' + v + ' '; + + if (v = DOM.getAttrib(n, 'size')) + ti += 'size: ' + v + ' '; + + if (v = DOM.getAttrib(n, 'color')) + ti += 'color: ' + v + ' '; + + break; + + case 'span': + if (v = DOM.getAttrib(n, 'style')) + ti += 'style: ' + v + ' '; + + break; + } + + if (v = DOM.getAttrib(n, 'id')) + ti += 'id: ' + v + ' '; + + if (v = n.className) { + v = v.replace(/\b\s*(webkit|mce|Apple-)\w+\s*\b/g, '') + + if (v) { + ti += 'class: ' + v + ' '; + + if (DOM.isBlock(n) || na == 'img' || na == 'span') + na += '.' + v; + } + } + + na = na.replace(/(html:)/g, ''); + na = {name : na, node : n, title : ti}; + t.onResolveName.dispatch(t, na); + ti = na.title; + na = na.name; + + //u = "javascript:tinymce.EditorManager.get('" + ed.id + "').theme._sel('" + (de++) + "');"; + pi = DOM.create('a', {'href' : "javascript:;", onmousedown : "return false;", title : ti, 'class' : 'mcePath_' + (de++)}, na); + + if (p.hasChildNodes()) { + p.insertBefore(DOM.doc.createTextNode(' \u00bb '), p.firstChild); + p.insertBefore(pi, p.firstChild); + } else + p.appendChild(pi); + }, ed.getBody()); + } + }, + + // Commands gets called by execCommand + + _sel : function(v) { + this.editor.execCommand('mceSelectNodeDepth', false, v); + }, + + _mceInsertAnchor : function(ui, v) { + var ed = this.editor; + + ed.windowManager.open({ + url : tinymce.baseURL + '/themes/advanced/anchor.htm', + width : 320 + parseInt(ed.getLang('advanced.anchor_delta_width', 0)), + height : 90 + parseInt(ed.getLang('advanced.anchor_delta_height', 0)), + inline : true + }, { + theme_url : this.url + }); + }, + + _mceCharMap : function() { + var ed = this.editor; + + ed.windowManager.open({ + url : tinymce.baseURL + '/themes/advanced/charmap.htm', + width : 550 + parseInt(ed.getLang('advanced.charmap_delta_width', 0)), + height : 250 + parseInt(ed.getLang('advanced.charmap_delta_height', 0)), + inline : true + }, { + theme_url : this.url + }); + }, + + _mceHelp : function() { + var ed = this.editor; + + ed.windowManager.open({ + url : tinymce.baseURL + '/themes/advanced/about.htm', + width : 480, + height : 380, + inline : true + }, { + theme_url : this.url + }); + }, + + _mceColorPicker : function(u, v) { + var ed = this.editor; + + v = v || {}; + + ed.windowManager.open({ + url : tinymce.baseURL + '/themes/advanced/color_picker.htm', + width : 375 + parseInt(ed.getLang('advanced.colorpicker_delta_width', 0)), + height : 250 + parseInt(ed.getLang('advanced.colorpicker_delta_height', 0)), + close_previous : false, + inline : true + }, { + input_color : v.color, + func : v.func, + theme_url : this.url + }); + }, + + _mceCodeEditor : function(ui, val) { + var ed = this.editor; + + ed.windowManager.open({ + url : tinymce.baseURL + '/themes/advanced/source_editor.htm', + width : parseInt(ed.getParam("theme_advanced_source_editor_width", 720)), + height : parseInt(ed.getParam("theme_advanced_source_editor_height", 580)), + inline : true, + resizable : true, + maximizable : true + }, { + theme_url : this.url + }); + }, + + _mceImage : function(ui, val) { + var ed = this.editor; + + // Internal image object like a flash placeholder + if (ed.dom.getAttrib(ed.selection.getNode(), 'class').indexOf('mceItem') != -1) + return; + + ed.windowManager.open({ + url : tinymce.baseURL + '/themes/advanced/image.htm', + width : 355 + parseInt(ed.getLang('advanced.image_delta_width', 0)), + height : 275 + parseInt(ed.getLang('advanced.image_delta_height', 0)), + inline : true + }, { + theme_url : this.url + }); + }, + + _mceLink : function(ui, val) { + var ed = this.editor; + + ed.windowManager.open({ + url : tinymce.baseURL + '/themes/advanced/link.htm', + width : 310 + parseInt(ed.getLang('advanced.link_delta_width', 0)), + height : 200 + parseInt(ed.getLang('advanced.link_delta_height', 0)), + inline : true + }, { + theme_url : this.url + }); + }, + + _mceNewDocument : function() { + var ed = this.editor; + + ed.windowManager.confirm('advanced.newdocument', function(s) { + if (s) + ed.execCommand('mceSetContent', false, ''); + }); + }, + + _mceForeColor : function() { + var t = this; + + this._mceColorPicker(0, { + color: t.fgColor, + func : function(co) { + t.fgColor = co; + t.editor.execCommand('ForeColor', false, co); + } + }); + }, + + _mceBackColor : function() { + var t = this; + + this._mceColorPicker(0, { + color: t.bgColor, + func : function(co) { + t.bgColor = co; + t.editor.execCommand('HiliteColor', false, co); + } + }); + }, + + _ufirst : function(s) { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + }); + + tinymce.ThemeManager.add('advanced', tinymce.themes.AdvancedTheme); +}(tinymce)); \ No newline at end of file diff --git a/jscripts/tiny_mce/themes/advanced/image.htm b/jscripts/tiny_mce/themes/advanced/image.htm new file mode 100644 index 000000000..f30d67064 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/image.htm @@ -0,0 +1,80 @@ + + + + {#advanced_dlg.image_title} + + + + + + +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
     
    + x +
    +
    +
    + +
    + + +
    +
    + + diff --git a/jscripts/tiny_mce/themes/advanced/img/colorpicker.jpg b/jscripts/tiny_mce/themes/advanced/img/colorpicker.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b4c542d107b25f68a9d4f9d7a109d0565d1f1437 GIT binary patch literal 3189 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 literal 0 HcmV?d00001 diff --git a/jscripts/tiny_mce/themes/advanced/img/icons.gif b/jscripts/tiny_mce/themes/advanced/img/icons.gif new file mode 100644 index 0000000000000000000000000000000000000000..e46de5333082869b9bdab2576a554a2f9d01a966 GIT binary patch literal 11794 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 F{{gR&nqvR} literal 0 HcmV?d00001 diff --git a/jscripts/tiny_mce/themes/advanced/js/about.js b/jscripts/tiny_mce/themes/advanced/js/about.js new file mode 100644 index 000000000..5cee9ed86 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/js/about.js @@ -0,0 +1,72 @@ +tinyMCEPopup.requireLangPack(); + +function init() { + var ed, tcont; + + tinyMCEPopup.resizeToInnerSize(); + ed = tinyMCEPopup.editor; + + // Give FF some time + window.setTimeout(insertHelpIFrame, 10); + + tcont = document.getElementById('plugintablecontainer'); + document.getElementById('plugins_tab').style.display = 'none'; + + var html = ""; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + + tinymce.each(ed.plugins, function(p, n) { + var info; + + if (!p.getInfo) + return; + + html += ''; + + info = p.getInfo(); + + if (info.infourl != null && info.infourl != '') + html += ''; + else + html += ''; + + if (info.authorurl != null && info.authorurl != '') + html += ''; + else + html += ''; + + html += ''; + html += ''; + + document.getElementById('plugins_tab').style.display = ''; + + }); + + html += ''; + html += '
    ' + ed.getLang('advanced_dlg.about_plugin') + '' + ed.getLang('advanced_dlg.about_author') + '' + ed.getLang('advanced_dlg.about_version') + '
    ' + info.longname + '' + info.longname + '' + info.author + '' + info.author + '' + info.version + '
    '; + + tcont.innerHTML = html; + + tinyMCEPopup.dom.get('version').innerHTML = tinymce.majorVersion + "." + tinymce.minorVersion; + tinyMCEPopup.dom.get('date').innerHTML = tinymce.releaseDate; +} + +function insertHelpIFrame() { + var html; + + if (tinyMCEPopup.getParam('docs_url')) { + html = ''; + document.getElementById('iframecontainer').innerHTML = html; + document.getElementById('help_tab').style.display = 'block'; + } +} + +tinyMCEPopup.onInit.add(init); diff --git a/jscripts/tiny_mce/themes/advanced/js/anchor.js b/jscripts/tiny_mce/themes/advanced/js/anchor.js new file mode 100644 index 000000000..7fe781055 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/js/anchor.js @@ -0,0 +1,37 @@ +tinyMCEPopup.requireLangPack(); + +var AnchorDialog = { + init : function(ed) { + var action, elm, f = document.forms[0]; + + this.editor = ed; + elm = ed.dom.getParent(ed.selection.getNode(), 'A'); + v = ed.dom.getAttrib(elm, 'name'); + + if (v) { + this.action = 'update'; + f.anchorName.value = v; + } + + f.insert.value = ed.getLang(elm ? 'update' : 'insert'); + }, + + update : function() { + var ed = this.editor, elm, name = document.forms[0].anchorName.value; + + tinyMCEPopup.restoreSelection(); + + if (this.action != 'update') + ed.selection.collapse(1); + + elm = ed.dom.getParent(ed.selection.getNode(), 'A'); + if (elm) + elm.name = name; + else + ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('a', {name : name, 'class' : 'mceItemAnchor'}, '')); + + tinyMCEPopup.close(); + } +}; + +tinyMCEPopup.onInit.add(AnchorDialog.init, AnchorDialog); diff --git a/jscripts/tiny_mce/themes/advanced/js/charmap.js b/jscripts/tiny_mce/themes/advanced/js/charmap.js new file mode 100644 index 000000000..8c5aea172 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/js/charmap.js @@ -0,0 +1,335 @@ +/** + * charmap.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +tinyMCEPopup.requireLangPack(); + +var charmap = [ + [' ', ' ', true, 'no-break space'], + ['&', '&', true, 'ampersand'], + ['"', '"', true, 'quotation mark'], +// finance + ['¢', '¢', true, 'cent sign'], + ['€', '€', true, 'euro sign'], + ['£', '£', true, 'pound sign'], + ['¥', '¥', true, 'yen sign'], +// signs + ['©', '©', true, 'copyright sign'], + ['®', '®', true, 'registered sign'], + ['™', '™', true, 'trade mark sign'], + ['‰', '‰', true, 'per mille sign'], + ['µ', 'µ', true, 'micro sign'], + ['·', '·', true, 'middle dot'], + ['•', '•', true, 'bullet'], + ['…', '…', true, 'three dot leader'], + ['′', '′', true, 'minutes / feet'], + ['″', '″', true, 'seconds / inches'], + ['§', '§', true, 'section sign'], + ['¶', '¶', true, 'paragraph sign'], + ['ß', 'ß', true, 'sharp s / ess-zed'], +// quotations + ['‹', '‹', true, 'single left-pointing angle quotation mark'], + ['›', '›', true, 'single right-pointing angle quotation mark'], + ['«', '«', true, 'left pointing guillemet'], + ['»', '»', true, 'right pointing guillemet'], + ['‘', '‘', true, 'left single quotation mark'], + ['’', '’', true, 'right single quotation mark'], + ['“', '“', true, 'left double quotation mark'], + ['”', '”', true, 'right double quotation mark'], + ['‚', '‚', true, 'single low-9 quotation mark'], + ['„', '„', true, 'double low-9 quotation mark'], + ['<', '<', true, 'less-than sign'], + ['>', '>', true, 'greater-than sign'], + ['≤', '≤', true, 'less-than or equal to'], + ['≥', '≥', true, 'greater-than or equal to'], + ['–', '–', true, 'en dash'], + ['—', '—', true, 'em dash'], + ['¯', '¯', true, 'macron'], + ['‾', '‾', true, 'overline'], + ['¤', '¤', true, 'currency sign'], + ['¦', '¦', true, 'broken bar'], + ['¨', '¨', true, 'diaeresis'], + ['¡', '¡', true, 'inverted exclamation mark'], + ['¿', '¿', true, 'turned question mark'], + ['ˆ', 'ˆ', true, 'circumflex accent'], + ['˜', '˜', true, 'small tilde'], + ['°', '°', true, 'degree sign'], + ['−', '−', true, 'minus sign'], + ['±', '±', true, 'plus-minus sign'], + ['÷', '÷', true, 'division sign'], + ['⁄', '⁄', true, 'fraction slash'], + ['×', '×', true, 'multiplication sign'], + ['¹', '¹', true, 'superscript one'], + ['²', '²', true, 'superscript two'], + ['³', '³', true, 'superscript three'], + ['¼', '¼', true, 'fraction one quarter'], + ['½', '½', true, 'fraction one half'], + ['¾', '¾', true, 'fraction three quarters'], +// math / logical + ['ƒ', 'ƒ', true, 'function / florin'], + ['∫', '∫', true, 'integral'], + ['∑', '∑', true, 'n-ary sumation'], + ['∞', '∞', true, 'infinity'], + ['√', '√', true, 'square root'], + ['∼', '∼', false,'similar to'], + ['≅', '≅', false,'approximately equal to'], + ['≈', '≈', true, 'almost equal to'], + ['≠', '≠', true, 'not equal to'], + ['≡', '≡', true, 'identical to'], + ['∈', '∈', false,'element of'], + ['∉', '∉', false,'not an element of'], + ['∋', '∋', false,'contains as member'], + ['∏', '∏', true, 'n-ary product'], + ['∧', '∧', false,'logical and'], + ['∨', '∨', false,'logical or'], + ['¬', '¬', true, 'not sign'], + ['∩', '∩', true, 'intersection'], + ['∪', '∪', false,'union'], + ['∂', '∂', true, 'partial differential'], + ['∀', '∀', false,'for all'], + ['∃', '∃', false,'there exists'], + ['∅', '∅', false,'diameter'], + ['∇', '∇', false,'backward difference'], + ['∗', '∗', false,'asterisk operator'], + ['∝', '∝', false,'proportional to'], + ['∠', '∠', false,'angle'], +// undefined + ['´', '´', true, 'acute accent'], + ['¸', '¸', true, 'cedilla'], + ['ª', 'ª', true, 'feminine ordinal indicator'], + ['º', 'º', true, 'masculine ordinal indicator'], + ['†', '†', true, 'dagger'], + ['‡', '‡', true, 'double dagger'], +// alphabetical special chars + ['À', 'À', true, 'A - grave'], + ['Á', 'Á', true, 'A - acute'], + ['Â', 'Â', true, 'A - circumflex'], + ['Ã', 'Ã', true, 'A - tilde'], + ['Ä', 'Ä', true, 'A - diaeresis'], + ['Å', 'Å', true, 'A - ring above'], + ['Æ', 'Æ', true, 'ligature AE'], + ['Ç', 'Ç', true, 'C - cedilla'], + ['È', 'È', true, 'E - grave'], + ['É', 'É', true, 'E - acute'], + ['Ê', 'Ê', true, 'E - circumflex'], + ['Ë', 'Ë', true, 'E - diaeresis'], + ['Ì', 'Ì', true, 'I - grave'], + ['Í', 'Í', true, 'I - acute'], + ['Î', 'Î', true, 'I - circumflex'], + ['Ï', 'Ï', true, 'I - diaeresis'], + ['Ð', 'Ð', true, 'ETH'], + ['Ñ', 'Ñ', true, 'N - tilde'], + ['Ò', 'Ò', true, 'O - grave'], + ['Ó', 'Ó', true, 'O - acute'], + ['Ô', 'Ô', true, 'O - circumflex'], + ['Õ', 'Õ', true, 'O - tilde'], + ['Ö', 'Ö', true, 'O - diaeresis'], + ['Ø', 'Ø', true, 'O - slash'], + ['Œ', 'Œ', true, 'ligature OE'], + ['Š', 'Š', true, 'S - caron'], + ['Ù', 'Ù', true, 'U - grave'], + ['Ú', 'Ú', true, 'U - acute'], + ['Û', 'Û', true, 'U - circumflex'], + ['Ü', 'Ü', true, 'U - diaeresis'], + ['Ý', 'Ý', true, 'Y - acute'], + ['Ÿ', 'Ÿ', true, 'Y - diaeresis'], + ['Þ', 'Þ', true, 'THORN'], + ['à', 'à', true, 'a - grave'], + ['á', 'á', true, 'a - acute'], + ['â', 'â', true, 'a - circumflex'], + ['ã', 'ã', true, 'a - tilde'], + ['ä', 'ä', true, 'a - diaeresis'], + ['å', 'å', true, 'a - ring above'], + ['æ', 'æ', true, 'ligature ae'], + ['ç', 'ç', true, 'c - cedilla'], + ['è', 'è', true, 'e - grave'], + ['é', 'é', true, 'e - acute'], + ['ê', 'ê', true, 'e - circumflex'], + ['ë', 'ë', true, 'e - diaeresis'], + ['ì', 'ì', true, 'i - grave'], + ['í', 'í', true, 'i - acute'], + ['î', 'î', true, 'i - circumflex'], + ['ï', 'ï', true, 'i - diaeresis'], + ['ð', 'ð', true, 'eth'], + ['ñ', 'ñ', true, 'n - tilde'], + ['ò', 'ò', true, 'o - grave'], + ['ó', 'ó', true, 'o - acute'], + ['ô', 'ô', true, 'o - circumflex'], + ['õ', 'õ', true, 'o - tilde'], + ['ö', 'ö', true, 'o - diaeresis'], + ['ø', 'ø', true, 'o slash'], + ['œ', 'œ', true, 'ligature oe'], + ['š', 'š', true, 's - caron'], + ['ù', 'ù', true, 'u - grave'], + ['ú', 'ú', true, 'u - acute'], + ['û', 'û', true, 'u - circumflex'], + ['ü', 'ü', true, 'u - diaeresis'], + ['ý', 'ý', true, 'y - acute'], + ['þ', 'þ', true, 'thorn'], + ['ÿ', 'ÿ', true, 'y - diaeresis'], + ['Α', 'Α', true, 'Alpha'], + ['Β', 'Β', true, 'Beta'], + ['Γ', 'Γ', true, 'Gamma'], + ['Δ', 'Δ', true, 'Delta'], + ['Ε', 'Ε', true, 'Epsilon'], + ['Ζ', 'Ζ', true, 'Zeta'], + ['Η', 'Η', true, 'Eta'], + ['Θ', 'Θ', true, 'Theta'], + ['Ι', 'Ι', true, 'Iota'], + ['Κ', 'Κ', true, 'Kappa'], + ['Λ', 'Λ', true, 'Lambda'], + ['Μ', 'Μ', true, 'Mu'], + ['Ν', 'Ν', true, 'Nu'], + ['Ξ', 'Ξ', true, 'Xi'], + ['Ο', 'Ο', true, 'Omicron'], + ['Π', 'Π', true, 'Pi'], + ['Ρ', 'Ρ', true, 'Rho'], + ['Σ', 'Σ', true, 'Sigma'], + ['Τ', 'Τ', true, 'Tau'], + ['Υ', 'Υ', true, 'Upsilon'], + ['Φ', 'Φ', true, 'Phi'], + ['Χ', 'Χ', true, 'Chi'], + ['Ψ', 'Ψ', true, 'Psi'], + ['Ω', 'Ω', true, 'Omega'], + ['α', 'α', true, 'alpha'], + ['β', 'β', true, 'beta'], + ['γ', 'γ', true, 'gamma'], + ['δ', 'δ', true, 'delta'], + ['ε', 'ε', true, 'epsilon'], + ['ζ', 'ζ', true, 'zeta'], + ['η', 'η', true, 'eta'], + ['θ', 'θ', true, 'theta'], + ['ι', 'ι', true, 'iota'], + ['κ', 'κ', true, 'kappa'], + ['λ', 'λ', true, 'lambda'], + ['μ', 'μ', true, 'mu'], + ['ν', 'ν', true, 'nu'], + ['ξ', 'ξ', true, 'xi'], + ['ο', 'ο', true, 'omicron'], + ['π', 'π', true, 'pi'], + ['ρ', 'ρ', true, 'rho'], + ['ς', 'ς', true, 'final sigma'], + ['σ', 'σ', true, 'sigma'], + ['τ', 'τ', true, 'tau'], + ['υ', 'υ', true, 'upsilon'], + ['φ', 'φ', true, 'phi'], + ['χ', 'χ', true, 'chi'], + ['ψ', 'ψ', true, 'psi'], + ['ω', 'ω', true, 'omega'], +// symbols + ['ℵ', 'ℵ', false,'alef symbol'], + ['ϖ', 'ϖ', false,'pi symbol'], + ['ℜ', 'ℜ', false,'real part symbol'], + ['ϑ','ϑ', false,'theta symbol'], + ['ϒ', 'ϒ', false,'upsilon - hook symbol'], + ['℘', '℘', false,'Weierstrass p'], + ['ℑ', 'ℑ', false,'imaginary part'], +// arrows + ['←', '←', true, 'leftwards arrow'], + ['↑', '↑', true, 'upwards arrow'], + ['→', '→', true, 'rightwards arrow'], + ['↓', '↓', true, 'downwards arrow'], + ['↔', '↔', true, 'left right arrow'], + ['↵', '↵', false,'carriage return'], + ['⇐', '⇐', false,'leftwards double arrow'], + ['⇑', '⇑', false,'upwards double arrow'], + ['⇒', '⇒', false,'rightwards double arrow'], + ['⇓', '⇓', false,'downwards double arrow'], + ['⇔', '⇔', false,'left right double arrow'], + ['∴', '∴', false,'therefore'], + ['⊂', '⊂', false,'subset of'], + ['⊃', '⊃', false,'superset of'], + ['⊄', '⊄', false,'not a subset of'], + ['⊆', '⊆', false,'subset of or equal to'], + ['⊇', '⊇', false,'superset of or equal to'], + ['⊕', '⊕', false,'circled plus'], + ['⊗', '⊗', false,'circled times'], + ['⊥', '⊥', false,'perpendicular'], + ['⋅', '⋅', false,'dot operator'], + ['⌈', '⌈', false,'left ceiling'], + ['⌉', '⌉', false,'right ceiling'], + ['⌊', '⌊', false,'left floor'], + ['⌋', '⌋', false,'right floor'], + ['⟨', '〈', false,'left-pointing angle bracket'], + ['⟩', '〉', false,'right-pointing angle bracket'], + ['◊', '◊', true,'lozenge'], + ['♠', '♠', false,'black spade suit'], + ['♣', '♣', true, 'black club suit'], + ['♥', '♥', true, 'black heart suit'], + ['♦', '♦', true, 'black diamond suit'], + [' ', ' ', false,'en space'], + [' ', ' ', false,'em space'], + [' ', ' ', false,'thin space'], + ['‌', '‌', false,'zero width non-joiner'], + ['‍', '‍', false,'zero width joiner'], + ['‎', '‎', false,'left-to-right mark'], + ['‏', '‏', false,'right-to-left mark'], + ['­', '­', false,'soft hyphen'] +]; + +tinyMCEPopup.onInit.add(function() { + tinyMCEPopup.dom.setHTML('charmapView', renderCharMapHTML()); +}); + +function renderCharMapHTML() { + var charsPerRow = 20, tdWidth=20, tdHeight=20, i; + var html = ''; + var cols=-1; + + for (i=0; i' + + '' + + charmap[i][1] + + ''; + if ((cols+1) % charsPerRow == 0) + html += ''; + } + } + + if (cols % charsPerRow > 0) { + var padd = charsPerRow - (cols % charsPerRow); + for (var i=0; i '; + } + + html += '
    '; + + return html; +} + +function insertChar(chr) { + tinyMCEPopup.execCommand('mceInsertContent', false, '&#' + chr + ';'); + + // Refocus in window + if (tinyMCEPopup.isWindow) + window.focus(); + + tinyMCEPopup.editor.focus(); + tinyMCEPopup.close(); +} + +function previewChar(codeA, codeB, codeN) { + var elmA = document.getElementById('codeA'); + var elmB = document.getElementById('codeB'); + var elmV = document.getElementById('codeV'); + var elmN = document.getElementById('codeN'); + + if (codeA=='#160;') { + elmV.innerHTML = '__'; + } else { + elmV.innerHTML = '&' + codeA; + } + + elmB.innerHTML = '&' + codeA; + elmA.innerHTML = '&' + codeB; + elmN.innerHTML = codeN; +} diff --git a/jscripts/tiny_mce/themes/advanced/js/color_picker.js b/jscripts/tiny_mce/themes/advanced/js/color_picker.js new file mode 100644 index 000000000..fd9700f22 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/js/color_picker.js @@ -0,0 +1,253 @@ +tinyMCEPopup.requireLangPack(); + +var detail = 50, strhex = "0123456789abcdef", i, isMouseDown = false, isMouseOver = false; + +var colors = [ + "#000000","#000033","#000066","#000099","#0000cc","#0000ff","#330000","#330033", + "#330066","#330099","#3300cc","#3300ff","#660000","#660033","#660066","#660099", + "#6600cc","#6600ff","#990000","#990033","#990066","#990099","#9900cc","#9900ff", + "#cc0000","#cc0033","#cc0066","#cc0099","#cc00cc","#cc00ff","#ff0000","#ff0033", + "#ff0066","#ff0099","#ff00cc","#ff00ff","#003300","#003333","#003366","#003399", + "#0033cc","#0033ff","#333300","#333333","#333366","#333399","#3333cc","#3333ff", + "#663300","#663333","#663366","#663399","#6633cc","#6633ff","#993300","#993333", + "#993366","#993399","#9933cc","#9933ff","#cc3300","#cc3333","#cc3366","#cc3399", + "#cc33cc","#cc33ff","#ff3300","#ff3333","#ff3366","#ff3399","#ff33cc","#ff33ff", + "#006600","#006633","#006666","#006699","#0066cc","#0066ff","#336600","#336633", + "#336666","#336699","#3366cc","#3366ff","#666600","#666633","#666666","#666699", + "#6666cc","#6666ff","#996600","#996633","#996666","#996699","#9966cc","#9966ff", + "#cc6600","#cc6633","#cc6666","#cc6699","#cc66cc","#cc66ff","#ff6600","#ff6633", + "#ff6666","#ff6699","#ff66cc","#ff66ff","#009900","#009933","#009966","#009999", + "#0099cc","#0099ff","#339900","#339933","#339966","#339999","#3399cc","#3399ff", + "#669900","#669933","#669966","#669999","#6699cc","#6699ff","#999900","#999933", + "#999966","#999999","#9999cc","#9999ff","#cc9900","#cc9933","#cc9966","#cc9999", + "#cc99cc","#cc99ff","#ff9900","#ff9933","#ff9966","#ff9999","#ff99cc","#ff99ff", + "#00cc00","#00cc33","#00cc66","#00cc99","#00cccc","#00ccff","#33cc00","#33cc33", + "#33cc66","#33cc99","#33cccc","#33ccff","#66cc00","#66cc33","#66cc66","#66cc99", + "#66cccc","#66ccff","#99cc00","#99cc33","#99cc66","#99cc99","#99cccc","#99ccff", + "#cccc00","#cccc33","#cccc66","#cccc99","#cccccc","#ccccff","#ffcc00","#ffcc33", + "#ffcc66","#ffcc99","#ffcccc","#ffccff","#00ff00","#00ff33","#00ff66","#00ff99", + "#00ffcc","#00ffff","#33ff00","#33ff33","#33ff66","#33ff99","#33ffcc","#33ffff", + "#66ff00","#66ff33","#66ff66","#66ff99","#66ffcc","#66ffff","#99ff00","#99ff33", + "#99ff66","#99ff99","#99ffcc","#99ffff","#ccff00","#ccff33","#ccff66","#ccff99", + "#ccffcc","#ccffff","#ffff00","#ffff33","#ffff66","#ffff99","#ffffcc","#ffffff" +]; + +var named = { + '#F0F8FF':'AliceBlue','#FAEBD7':'AntiqueWhite','#00FFFF':'Aqua','#7FFFD4':'Aquamarine','#F0FFFF':'Azure','#F5F5DC':'Beige', + '#FFE4C4':'Bisque','#000000':'Black','#FFEBCD':'BlanchedAlmond','#0000FF':'Blue','#8A2BE2':'BlueViolet','#A52A2A':'Brown', + '#DEB887':'BurlyWood','#5F9EA0':'CadetBlue','#7FFF00':'Chartreuse','#D2691E':'Chocolate','#FF7F50':'Coral','#6495ED':'CornflowerBlue', + '#FFF8DC':'Cornsilk','#DC143C':'Crimson','#00FFFF':'Cyan','#00008B':'DarkBlue','#008B8B':'DarkCyan','#B8860B':'DarkGoldenRod', + '#A9A9A9':'DarkGray','#A9A9A9':'DarkGrey','#006400':'DarkGreen','#BDB76B':'DarkKhaki','#8B008B':'DarkMagenta','#556B2F':'DarkOliveGreen', + '#FF8C00':'Darkorange','#9932CC':'DarkOrchid','#8B0000':'DarkRed','#E9967A':'DarkSalmon','#8FBC8F':'DarkSeaGreen','#483D8B':'DarkSlateBlue', + '#2F4F4F':'DarkSlateGray','#2F4F4F':'DarkSlateGrey','#00CED1':'DarkTurquoise','#9400D3':'DarkViolet','#FF1493':'DeepPink','#00BFFF':'DeepSkyBlue', + '#696969':'DimGray','#696969':'DimGrey','#1E90FF':'DodgerBlue','#B22222':'FireBrick','#FFFAF0':'FloralWhite','#228B22':'ForestGreen', + '#FF00FF':'Fuchsia','#DCDCDC':'Gainsboro','#F8F8FF':'GhostWhite','#FFD700':'Gold','#DAA520':'GoldenRod','#808080':'Gray','#808080':'Grey', + '#008000':'Green','#ADFF2F':'GreenYellow','#F0FFF0':'HoneyDew','#FF69B4':'HotPink','#CD5C5C':'IndianRed','#4B0082':'Indigo','#FFFFF0':'Ivory', + '#F0E68C':'Khaki','#E6E6FA':'Lavender','#FFF0F5':'LavenderBlush','#7CFC00':'LawnGreen','#FFFACD':'LemonChiffon','#ADD8E6':'LightBlue', + '#F08080':'LightCoral','#E0FFFF':'LightCyan','#FAFAD2':'LightGoldenRodYellow','#D3D3D3':'LightGray','#D3D3D3':'LightGrey','#90EE90':'LightGreen', + '#FFB6C1':'LightPink','#FFA07A':'LightSalmon','#20B2AA':'LightSeaGreen','#87CEFA':'LightSkyBlue','#778899':'LightSlateGray','#778899':'LightSlateGrey', + '#B0C4DE':'LightSteelBlue','#FFFFE0':'LightYellow','#00FF00':'Lime','#32CD32':'LimeGreen','#FAF0E6':'Linen','#FF00FF':'Magenta','#800000':'Maroon', + '#66CDAA':'MediumAquaMarine','#0000CD':'MediumBlue','#BA55D3':'MediumOrchid','#9370D8':'MediumPurple','#3CB371':'MediumSeaGreen','#7B68EE':'MediumSlateBlue', + '#00FA9A':'MediumSpringGreen','#48D1CC':'MediumTurquoise','#C71585':'MediumVioletRed','#191970':'MidnightBlue','#F5FFFA':'MintCream','#FFE4E1':'MistyRose','#FFE4B5':'Moccasin', + '#FFDEAD':'NavajoWhite','#000080':'Navy','#FDF5E6':'OldLace','#808000':'Olive','#6B8E23':'OliveDrab','#FFA500':'Orange','#FF4500':'OrangeRed','#DA70D6':'Orchid', + '#EEE8AA':'PaleGoldenRod','#98FB98':'PaleGreen','#AFEEEE':'PaleTurquoise','#D87093':'PaleVioletRed','#FFEFD5':'PapayaWhip','#FFDAB9':'PeachPuff', + '#CD853F':'Peru','#FFC0CB':'Pink','#DDA0DD':'Plum','#B0E0E6':'PowderBlue','#800080':'Purple','#FF0000':'Red','#BC8F8F':'RosyBrown','#4169E1':'RoyalBlue', + '#8B4513':'SaddleBrown','#FA8072':'Salmon','#F4A460':'SandyBrown','#2E8B57':'SeaGreen','#FFF5EE':'SeaShell','#A0522D':'Sienna','#C0C0C0':'Silver', + '#87CEEB':'SkyBlue','#6A5ACD':'SlateBlue','#708090':'SlateGray','#708090':'SlateGrey','#FFFAFA':'Snow','#00FF7F':'SpringGreen', + '#4682B4':'SteelBlue','#D2B48C':'Tan','#008080':'Teal','#D8BFD8':'Thistle','#FF6347':'Tomato','#40E0D0':'Turquoise','#EE82EE':'Violet', + '#F5DEB3':'Wheat','#FFFFFF':'White','#F5F5F5':'WhiteSmoke','#FFFF00':'Yellow','#9ACD32':'YellowGreen' +}; + +function init() { + var inputColor = convertRGBToHex(tinyMCEPopup.getWindowArg('input_color')); + + tinyMCEPopup.resizeToInnerSize(); + + generatePicker(); + + if (inputColor) { + changeFinalColor(inputColor); + + col = convertHexToRGB(inputColor); + + if (col) + updateLight(col.r, col.g, col.b); + } +} + +function insertAction() { + var color = document.getElementById("color").value, f = tinyMCEPopup.getWindowArg('func'); + + tinyMCEPopup.restoreSelection(); + + if (f) + f(color); + + tinyMCEPopup.close(); +} + +function showColor(color, name) { + if (name) + document.getElementById("colorname").innerHTML = name; + + document.getElementById("preview").style.backgroundColor = color; + document.getElementById("color").value = color.toLowerCase(); +} + +function convertRGBToHex(col) { + var re = new RegExp("rgb\\s*\\(\\s*([0-9]+).*,\\s*([0-9]+).*,\\s*([0-9]+).*\\)", "gi"); + + if (!col) + return col; + + var rgb = col.replace(re, "$1,$2,$3").split(','); + if (rgb.length == 3) { + r = parseInt(rgb[0]).toString(16); + g = parseInt(rgb[1]).toString(16); + b = parseInt(rgb[2]).toString(16); + + r = r.length == 1 ? '0' + r : r; + g = g.length == 1 ? '0' + g : g; + b = b.length == 1 ? '0' + b : b; + + return "#" + r + g + b; + } + + return col; +} + +function convertHexToRGB(col) { + if (col.indexOf('#') != -1) { + col = col.replace(new RegExp('[^0-9A-F]', 'gi'), ''); + + r = parseInt(col.substring(0, 2), 16); + g = parseInt(col.substring(2, 4), 16); + b = parseInt(col.substring(4, 6), 16); + + return {r : r, g : g, b : b}; + } + + return null; +} + +function generatePicker() { + var el = document.getElementById('light'), h = '', i; + + for (i = 0; i < detail; i++){ + h += '
    '; + } + + el.innerHTML = h; +} + +function generateWebColors() { + var el = document.getElementById('webcolors'), h = '', i; + + if (el.className == 'generated') + return; + + h += '' + + ''; + + for (i=0; i' + + '' + + ''; + if ((i+1) % 18 == 0) + h += ''; + } + + h += '
    '; + + el.innerHTML = h; + el.className = 'generated'; +} + +function generateNamedColors() { + var el = document.getElementById('namedcolors'), h = '', n, v, i = 0; + + if (el.className == 'generated') + return; + + for (n in named) { + v = named[n]; + h += '' + } + + el.innerHTML = h; + el.className = 'generated'; +} + +function dechex(n) { + return strhex.charAt(Math.floor(n / 16)) + strhex.charAt(n % 16); +} + +function computeColor(e) { + var x, y, partWidth, partDetail, imHeight, r, g, b, coef, i, finalCoef, finalR, finalG, finalB; + + x = e.offsetX ? e.offsetX : (e.target ? e.clientX - e.target.x : 0); + y = e.offsetY ? e.offsetY : (e.target ? e.clientY - e.target.y : 0); + + partWidth = document.getElementById('colors').width / 6; + partDetail = detail / 2; + imHeight = document.getElementById('colors').height; + + r = (x >= 0)*(x < partWidth)*255 + (x >= partWidth)*(x < 2*partWidth)*(2*255 - x * 255 / partWidth) + (x >= 4*partWidth)*(x < 5*partWidth)*(-4*255 + x * 255 / partWidth) + (x >= 5*partWidth)*(x < 6*partWidth)*255; + g = (x >= 0)*(x < partWidth)*(x * 255 / partWidth) + (x >= partWidth)*(x < 3*partWidth)*255 + (x >= 3*partWidth)*(x < 4*partWidth)*(4*255 - x * 255 / partWidth); + b = (x >= 2*partWidth)*(x < 3*partWidth)*(-2*255 + x * 255 / partWidth) + (x >= 3*partWidth)*(x < 5*partWidth)*255 + (x >= 5*partWidth)*(x < 6*partWidth)*(6*255 - x * 255 / partWidth); + + coef = (imHeight - y) / imHeight; + r = 128 + (r - 128) * coef; + g = 128 + (g - 128) * coef; + b = 128 + (b - 128) * coef; + + changeFinalColor('#' + dechex(r) + dechex(g) + dechex(b)); + updateLight(r, g, b); +} + +function updateLight(r, g, b) { + var i, partDetail = detail / 2, finalCoef, finalR, finalG, finalB, color; + + for (i=0; i=0) && (i'); + }, + + init : function() { + var f = document.forms[0], ed = tinyMCEPopup.editor; + + // Setup browse button + document.getElementById('srcbrowsercontainer').innerHTML = getBrowserHTML('srcbrowser','src','image','theme_advanced_image'); + if (isVisible('srcbrowser')) + document.getElementById('src').style.width = '180px'; + + e = ed.selection.getNode(); + + this.fillFileList('image_list', 'tinyMCEImageList'); + + if (e.nodeName == 'IMG') { + f.src.value = ed.dom.getAttrib(e, 'src'); + f.alt.value = ed.dom.getAttrib(e, 'alt'); + f.border.value = this.getAttrib(e, 'border'); + f.vspace.value = this.getAttrib(e, 'vspace'); + f.hspace.value = this.getAttrib(e, 'hspace'); + f.width.value = ed.dom.getAttrib(e, 'width'); + f.height.value = ed.dom.getAttrib(e, 'height'); + f.insert.value = ed.getLang('update'); + this.styleVal = ed.dom.getAttrib(e, 'style'); + selectByValue(f, 'image_list', f.src.value); + selectByValue(f, 'align', this.getAttrib(e, 'align')); + this.updateStyle(); + } + }, + + fillFileList : function(id, l) { + var dom = tinyMCEPopup.dom, lst = dom.get(id), v, cl; + + l = window[l]; + + if (l && l.length > 0) { + lst.options[lst.options.length] = new Option('', ''); + + tinymce.each(l, function(o) { + lst.options[lst.options.length] = new Option(o[0], o[1]); + }); + } else + dom.remove(dom.getParent(id, 'tr')); + }, + + update : function() { + var f = document.forms[0], nl = f.elements, ed = tinyMCEPopup.editor, args = {}, el; + + tinyMCEPopup.restoreSelection(); + + if (f.src.value === '') { + if (ed.selection.getNode().nodeName == 'IMG') { + ed.dom.remove(ed.selection.getNode()); + ed.execCommand('mceRepaint'); + } + + tinyMCEPopup.close(); + return; + } + + if (!ed.settings.inline_styles) { + args = tinymce.extend(args, { + vspace : nl.vspace.value, + hspace : nl.hspace.value, + border : nl.border.value, + align : getSelectValue(f, 'align') + }); + } else + args.style = this.styleVal; + + tinymce.extend(args, { + src : f.src.value, + alt : f.alt.value, + width : f.width.value, + height : f.height.value + }); + + el = ed.selection.getNode(); + + if (el && el.nodeName == 'IMG') { + ed.dom.setAttribs(el, args); + } else { + ed.execCommand('mceInsertContent', false, '', {skip_undo : 1}); + ed.dom.setAttribs('__mce_tmp', args); + ed.dom.setAttrib('__mce_tmp', 'id', ''); + ed.undoManager.add(); + } + + tinyMCEPopup.close(); + }, + + updateStyle : function() { + var dom = tinyMCEPopup.dom, st, v, f = document.forms[0]; + + if (tinyMCEPopup.editor.settings.inline_styles) { + st = tinyMCEPopup.dom.parseStyle(this.styleVal); + + // Handle align + v = getSelectValue(f, 'align'); + if (v) { + if (v == 'left' || v == 'right') { + st['float'] = v; + delete st['vertical-align']; + } else { + st['vertical-align'] = v; + delete st['float']; + } + } else { + delete st['float']; + delete st['vertical-align']; + } + + // Handle border + v = f.border.value; + if (v || v == '0') { + if (v == '0') + st['border'] = '0'; + else + st['border'] = v + 'px solid black'; + } else + delete st['border']; + + // Handle hspace + v = f.hspace.value; + if (v) { + delete st['margin']; + st['margin-left'] = v + 'px'; + st['margin-right'] = v + 'px'; + } else { + delete st['margin-left']; + delete st['margin-right']; + } + + // Handle vspace + v = f.vspace.value; + if (v) { + delete st['margin']; + st['margin-top'] = v + 'px'; + st['margin-bottom'] = v + 'px'; + } else { + delete st['margin-top']; + delete st['margin-bottom']; + } + + // Merge + st = tinyMCEPopup.dom.parseStyle(dom.serializeStyle(st), 'img'); + this.styleVal = dom.serializeStyle(st, 'img'); + } + }, + + getAttrib : function(e, at) { + var ed = tinyMCEPopup.editor, dom = ed.dom, v, v2; + + if (ed.settings.inline_styles) { + switch (at) { + case 'align': + if (v = dom.getStyle(e, 'float')) + return v; + + if (v = dom.getStyle(e, 'vertical-align')) + return v; + + break; + + case 'hspace': + v = dom.getStyle(e, 'margin-left') + v2 = dom.getStyle(e, 'margin-right'); + if (v && v == v2) + return parseInt(v.replace(/[^0-9]/g, '')); + + break; + + case 'vspace': + v = dom.getStyle(e, 'margin-top') + v2 = dom.getStyle(e, 'margin-bottom'); + if (v && v == v2) + return parseInt(v.replace(/[^0-9]/g, '')); + + break; + + case 'border': + v = 0; + + tinymce.each(['top', 'right', 'bottom', 'left'], function(sv) { + sv = dom.getStyle(e, 'border-' + sv + '-width'); + + // False or not the same as prev + if (!sv || (sv != v && v !== 0)) { + v = 0; + return false; + } + + if (sv) + v = sv; + }); + + if (v) + return parseInt(v.replace(/[^0-9]/g, '')); + + break; + } + } + + if (v = dom.getAttrib(e, at)) + return v; + + return ''; + }, + + resetImageData : function() { + var f = document.forms[0]; + + f.width.value = f.height.value = ""; + }, + + updateImageData : function() { + var f = document.forms[0], t = ImageDialog; + + if (f.width.value == "") + f.width.value = t.preloadImg.width; + + if (f.height.value == "") + f.height.value = t.preloadImg.height; + }, + + getImageData : function() { + var f = document.forms[0]; + + this.preloadImg = new Image(); + this.preloadImg.onload = this.updateImageData; + this.preloadImg.onerror = this.resetImageData; + this.preloadImg.src = tinyMCEPopup.editor.documentBaseURI.toAbsolute(f.src.value); + } +}; + +ImageDialog.preInit(); +tinyMCEPopup.onInit.add(ImageDialog.init, ImageDialog); diff --git a/jscripts/tiny_mce/themes/advanced/js/link.js b/jscripts/tiny_mce/themes/advanced/js/link.js new file mode 100644 index 000000000..f67a5bc82 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/js/link.js @@ -0,0 +1,156 @@ +tinyMCEPopup.requireLangPack(); + +var LinkDialog = { + preInit : function() { + var url; + + if (url = tinyMCEPopup.getParam("external_link_list_url")) + document.write(''); + }, + + init : function() { + var f = document.forms[0], ed = tinyMCEPopup.editor; + + // Setup browse button + document.getElementById('hrefbrowsercontainer').innerHTML = getBrowserHTML('hrefbrowser', 'href', 'file', 'theme_advanced_link'); + if (isVisible('hrefbrowser')) + document.getElementById('href').style.width = '180px'; + + this.fillClassList('class_list'); + this.fillFileList('link_list', 'tinyMCELinkList'); + this.fillTargetList('target_list'); + + if (e = ed.dom.getParent(ed.selection.getNode(), 'A')) { + f.href.value = ed.dom.getAttrib(e, 'href'); + f.linktitle.value = ed.dom.getAttrib(e, 'title'); + f.insert.value = ed.getLang('update'); + selectByValue(f, 'link_list', f.href.value); + selectByValue(f, 'target_list', ed.dom.getAttrib(e, 'target')); + selectByValue(f, 'class_list', ed.dom.getAttrib(e, 'class')); + } + }, + + update : function() { + var f = document.forms[0], ed = tinyMCEPopup.editor, e, b; + + tinyMCEPopup.restoreSelection(); + e = ed.dom.getParent(ed.selection.getNode(), 'A'); + + // Remove element if there is no href + if (!f.href.value) { + if (e) { + tinyMCEPopup.execCommand("mceBeginUndoLevel"); + b = ed.selection.getBookmark(); + ed.dom.remove(e, 1); + ed.selection.moveToBookmark(b); + tinyMCEPopup.execCommand("mceEndUndoLevel"); + tinyMCEPopup.close(); + return; + } + } + + tinyMCEPopup.execCommand("mceBeginUndoLevel"); + + // Create new anchor elements + if (e == null) { + ed.getDoc().execCommand("unlink", false, null); + tinyMCEPopup.execCommand("CreateLink", false, "#mce_temp_url#", {skip_undo : 1}); + + tinymce.each(ed.dom.select("a"), function(n) { + if (ed.dom.getAttrib(n, 'href') == '#mce_temp_url#') { + e = n; + + ed.dom.setAttribs(e, { + href : f.href.value, + title : f.linktitle.value, + target : f.target_list ? getSelectValue(f, "target_list") : null, + 'class' : f.class_list ? getSelectValue(f, "class_list") : null + }); + } + }); + } else { + ed.dom.setAttribs(e, { + href : f.href.value, + title : f.linktitle.value, + target : f.target_list ? getSelectValue(f, "target_list") : null, + 'class' : f.class_list ? getSelectValue(f, "class_list") : null + }); + } + + // Don't move caret if selection was image + if (e.childNodes.length != 1 || e.firstChild.nodeName != 'IMG') { + ed.focus(); + ed.selection.select(e); + ed.selection.collapse(0); + tinyMCEPopup.storeSelection(); + } + + tinyMCEPopup.execCommand("mceEndUndoLevel"); + tinyMCEPopup.close(); + }, + + checkPrefix : function(n) { + if (n.value && Validator.isEmail(n) && !/^\s*mailto:/i.test(n.value) && confirm(tinyMCEPopup.getLang('advanced_dlg.link_is_email'))) + n.value = 'mailto:' + n.value; + + if (/^\s*www\./i.test(n.value) && confirm(tinyMCEPopup.getLang('advanced_dlg.link_is_external'))) + n.value = 'http://' + n.value; + }, + + fillFileList : function(id, l) { + var dom = tinyMCEPopup.dom, lst = dom.get(id), v, cl; + + l = window[l]; + + if (l && l.length > 0) { + lst.options[lst.options.length] = new Option('', ''); + + tinymce.each(l, function(o) { + lst.options[lst.options.length] = new Option(o[0], o[1]); + }); + } else + dom.remove(dom.getParent(id, 'tr')); + }, + + fillClassList : function(id) { + var dom = tinyMCEPopup.dom, lst = dom.get(id), v, cl; + + if (v = tinyMCEPopup.getParam('theme_advanced_styles')) { + cl = []; + + tinymce.each(v.split(';'), function(v) { + var p = v.split('='); + + cl.push({'title' : p[0], 'class' : p[1]}); + }); + } else + cl = tinyMCEPopup.editor.dom.getClasses(); + + if (cl.length > 0) { + lst.options[lst.options.length] = new Option(tinyMCEPopup.getLang('not_set'), ''); + + tinymce.each(cl, function(o) { + lst.options[lst.options.length] = new Option(o.title || o['class'], o['class']); + }); + } else + dom.remove(dom.getParent(id, 'tr')); + }, + + fillTargetList : function(id) { + var dom = tinyMCEPopup.dom, lst = dom.get(id), v; + + lst.options[lst.options.length] = new Option(tinyMCEPopup.getLang('not_set'), ''); + lst.options[lst.options.length] = new Option(tinyMCEPopup.getLang('advanced_dlg.link_target_same'), '_self'); + lst.options[lst.options.length] = new Option(tinyMCEPopup.getLang('advanced_dlg.link_target_blank'), '_blank'); + + if (v = tinyMCEPopup.getParam('theme_advanced_link_targets')) { + tinymce.each(v.split(','), function(v) { + v = v.split('='); + lst.options[lst.options.length] = new Option(v[0], v[1]); + }); + } + } +}; + +LinkDialog.preInit(); +tinyMCEPopup.onInit.add(LinkDialog.init, LinkDialog); diff --git a/jscripts/tiny_mce/themes/advanced/js/source_editor.js b/jscripts/tiny_mce/themes/advanced/js/source_editor.js new file mode 100644 index 000000000..279328614 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/js/source_editor.js @@ -0,0 +1,62 @@ +tinyMCEPopup.requireLangPack(); +tinyMCEPopup.onInit.add(onLoadInit); + +function saveContent() { + tinyMCEPopup.editor.setContent(document.getElementById('htmlSource').value, {source_view : true}); + tinyMCEPopup.close(); +} + +function onLoadInit() { + tinyMCEPopup.resizeToInnerSize(); + + // Remove Gecko spellchecking + if (tinymce.isGecko) + document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck"); + + document.getElementById('htmlSource').value = tinyMCEPopup.editor.getContent({source_view : true}); + + if (tinyMCEPopup.editor.getParam("theme_advanced_source_editor_wrap", true)) { + setWrap('soft'); + document.getElementById('wraped').checked = true; + } + + resizeInputs(); +} + +function setWrap(val) { + var v, n, s = document.getElementById('htmlSource'); + + s.wrap = val; + + if (!tinymce.isIE) { + v = s.value; + n = s.cloneNode(false); + n.setAttribute("wrap", val); + s.parentNode.replaceChild(n, s); + n.value = v; + } +} + +function toggleWordWrap(elm) { + if (elm.checked) + setWrap('soft'); + else + setWrap('off'); +} + +var wHeight=0, wWidth=0, owHeight=0, owWidth=0; + +function resizeInputs() { + var el = document.getElementById('htmlSource'); + + if (!tinymce.isIE) { + wHeight = self.innerHeight - 65; + wWidth = self.innerWidth - 16; + } else { + wHeight = document.body.clientHeight - 70; + wWidth = document.body.clientWidth - 16; + } + + el.style.height = Math.abs(wHeight) + 'px'; + el.style.width = Math.abs(wWidth) + 'px'; +} diff --git a/jscripts/tiny_mce/themes/advanced/langs/en.js b/jscripts/tiny_mce/themes/advanced/langs/en.js new file mode 100644 index 000000000..69694b1f9 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/langs/en.js @@ -0,0 +1,62 @@ +tinyMCE.addI18n('en.advanced',{ +style_select:"Styles", +font_size:"Font size", +fontdefault:"Font family", +block:"Format", +paragraph:"Paragraph", +div:"Div", +address:"Address", +pre:"Preformatted", +h1:"Heading 1", +h2:"Heading 2", +h3:"Heading 3", +h4:"Heading 4", +h5:"Heading 5", +h6:"Heading 6", +blockquote:"Blockquote", +code:"Code", +samp:"Code sample", +dt:"Definition term ", +dd:"Definition description", +bold_desc:"Bold (Ctrl+B)", +italic_desc:"Italic (Ctrl+I)", +underline_desc:"Underline (Ctrl+U)", +striketrough_desc:"Strikethrough", +justifyleft_desc:"Align left", +justifycenter_desc:"Align center", +justifyright_desc:"Align right", +justifyfull_desc:"Align full", +bullist_desc:"Unordered list", +numlist_desc:"Ordered list", +outdent_desc:"Outdent", +indent_desc:"Indent", +undo_desc:"Undo (Ctrl+Z)", +redo_desc:"Redo (Ctrl+Y)", +link_desc:"Insert/edit link", +unlink_desc:"Unlink", +image_desc:"Insert/edit image", +cleanup_desc:"Cleanup messy code", +code_desc:"Edit HTML Source", +sub_desc:"Subscript", +sup_desc:"Superscript", +hr_desc:"Insert horizontal ruler", +removeformat_desc:"Remove formatting", +custom1_desc:"Your custom description here", +forecolor_desc:"Select text color", +backcolor_desc:"Select background color", +charmap_desc:"Insert custom character", +visualaid_desc:"Toggle guidelines/invisible elements", +anchor_desc:"Insert/edit anchor", +cut_desc:"Cut", +copy_desc:"Copy", +paste_desc:"Paste", +image_props_desc:"Image properties", +newdocument_desc:"New document", +help_desc:"Help", +blockquote_desc:"Blockquote", +clipboard_msg:"Copy/Cut/Paste is not available in Mozilla and Firefox.\r\nDo you want more information about this issue?", +path:"Path", +newdocument:"Are you sure you want clear all contents?", +toolbar_focus:"Jump to tool buttons - Alt+Q, Jump to editor - Alt-Z, Jump to element path - Alt-X", +more_colors:"More colors" +}); \ No newline at end of file diff --git a/jscripts/tiny_mce/themes/advanced/langs/en_dlg.js b/jscripts/tiny_mce/themes/advanced/langs/en_dlg.js new file mode 100644 index 000000000..9d124d7db --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/langs/en_dlg.js @@ -0,0 +1,51 @@ +tinyMCE.addI18n('en.advanced_dlg',{ +about_title:"About TinyMCE", +about_general:"About", +about_help:"Help", +about_license:"License", +about_plugins:"Plugins", +about_plugin:"Plugin", +about_author:"Author", +about_version:"Version", +about_loaded:"Loaded plugins", +anchor_title:"Insert/edit anchor", +anchor_name:"Anchor name", +code_title:"HTML Source Editor", +code_wordwrap:"Word wrap", +colorpicker_title:"Select a color", +colorpicker_picker_tab:"Picker", +colorpicker_picker_title:"Color picker", +colorpicker_palette_tab:"Palette", +colorpicker_palette_title:"Palette colors", +colorpicker_named_tab:"Named", +colorpicker_named_title:"Named colors", +colorpicker_color:"Color:", +colorpicker_name:"Name:", +charmap_title:"Select custom character", +image_title:"Insert/edit image", +image_src:"Image URL", +image_alt:"Image description", +image_list:"Image list", +image_border:"Border", +image_dimensions:"Dimensions", +image_vspace:"Vertical space", +image_hspace:"Horizontal space", +image_align:"Alignment", +image_align_baseline:"Baseline", +image_align_top:"Top", +image_align_middle:"Middle", +image_align_bottom:"Bottom", +image_align_texttop:"Text top", +image_align_textbottom:"Text bottom", +image_align_left:"Left", +image_align_right:"Right", +link_title:"Insert/edit link", +link_url:"Link URL", +link_target:"Target", +link_target_same:"Open link in the same window", +link_target_blank:"Open link in a new window", +link_titlefield:"Title", +link_is_email:"The URL you entered seems to be an email address, do you want to add the required mailto: prefix?", +link_is_external:"The URL you entered seems to external link, do you want to add the required http:// prefix?", +link_list:"Link list" +}); \ No newline at end of file diff --git a/jscripts/tiny_mce/themes/advanced/link.htm b/jscripts/tiny_mce/themes/advanced/link.htm new file mode 100644 index 000000000..7565b9ae8 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/link.htm @@ -0,0 +1,58 @@ + + + + {#advanced_dlg.link_title} + + + + + + + +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
     
    +
    +
    + +
    + + +
    +
    + + diff --git a/jscripts/tiny_mce/themes/advanced/skins/default/content.css b/jscripts/tiny_mce/themes/advanced/skins/default/content.css new file mode 100644 index 000000000..36f38aba2 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/skins/default/content.css @@ -0,0 +1,35 @@ +body, td, pre {color:#000; font-family:Verdana, Arial, Helvetica, sans-serif; font-size:10px; margin:8px;} +body {background:#FFF;} +body.mceForceColors {background:#FFF; color:#000;} +h1 {font-size: 2em} +h2 {font-size: 1.5em} +h3 {font-size: 1.17em} +h4 {font-size: 1em} +h5 {font-size: .83em} +h6 {font-size: .75em} +.mceItemTable, .mceItemTable td, .mceItemTable th, .mceItemTable caption, .mceItemVisualAid {border: 1px dashed #BBB;} +a.mceItemAnchor {display:inline-block; width:11px !important; height:11px !important; background:url(img/items.gif) no-repeat 0 0;} +td.mceSelected, th.mceSelected {background-color:#3399ff !important} +img {border:0;} +table {cursor:default} +table td, table th {cursor:text} +ins {border-bottom:1px solid green; text-decoration: none; color:green} +del {color:red; text-decoration:line-through} +cite {border-bottom:1px dashed blue} +acronym {border-bottom:1px dotted #CCC; cursor:help} +abbr {border-bottom:1px dashed #CCC; cursor:help} + +/* IE */ +* html body { +scrollbar-3dlight-color:#F0F0EE; +scrollbar-arrow-color:#676662; +scrollbar-base-color:#F0F0EE; +scrollbar-darkshadow-color:#DDD; +scrollbar-face-color:#E0E0DD; +scrollbar-highlight-color:#F0F0EE; +scrollbar-shadow-color:#F0F0EE; +scrollbar-track-color:#F5F5F5; +} + +img:-moz-broken {-moz-force-broken-image-icon:1; width:24px; height:24px} +font[face=mceinline] {font-family:inherit !important} diff --git a/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css b/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css new file mode 100644 index 000000000..f01222650 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css @@ -0,0 +1,117 @@ +/* Generic */ +body { +font-family:Verdana, Arial, Helvetica, sans-serif; font-size:11px; +scrollbar-3dlight-color:#F0F0EE; +scrollbar-arrow-color:#676662; +scrollbar-base-color:#F0F0EE; +scrollbar-darkshadow-color:#DDDDDD; +scrollbar-face-color:#E0E0DD; +scrollbar-highlight-color:#F0F0EE; +scrollbar-shadow-color:#F0F0EE; +scrollbar-track-color:#F5F5F5; +background:#F0F0EE; +padding:0; +margin:8px 8px 0 8px; +} + +html {background:#F0F0EE;} +td {font-family:Verdana, Arial, Helvetica, sans-serif; font-size:10px;} +textarea {resize:none;outline:none;} +a:link, a:visited {color:black;} +a:hover {color:#2B6FB6;} +.nowrap {white-space: nowrap} + +/* Forms */ +fieldset {margin:0; padding:4px; border:1px solid #919B9C; font-family:Verdana, Arial; font-size:10px;} +legend {color:#2B6FB6; font-weight:bold;} +label.msg {display:none;} +label.invalid {color:#EE0000; display:inline;} +input.invalid {border:1px solid #EE0000;} +input {background:#FFF; border:1px solid #CCC;} +input, select, textarea {font-family:Verdana, Arial, Helvetica, sans-serif; font-size:10px;} +input, select, textarea {border:1px solid #808080;} +input.radio {border:1px none #000000; background:transparent; vertical-align:middle;} +input.checkbox {border:1px none #000000; background:transparent; vertical-align:middle;} +.input_noborder {border:0;} + +/* Buttons */ +#insert, #cancel, input.button, .updateButton { +border:0; margin:0; padding:0; +font-weight:bold; +width:94px; height:26px; +background:url(img/buttons.png) 0 -26px; +cursor:pointer; +padding-bottom:2px; +float:left; +} + +#insert {background:url(img/buttons.png) 0 -52px} +#cancel {background:url(img/buttons.png) 0 0; float:right} + +/* Browse */ +a.pickcolor, a.browse {text-decoration:none} +a.browse span {display:block; width:20px; height:18px; background:url(../../img/icons.gif) -860px 0; border:1px solid #FFF; margin-left:1px;} +.mceOldBoxModel a.browse span {width:22px; height:20px;} +a.browse:hover span {border:1px solid #0A246A; background-color:#B2BBD0;} +a.browse span.disabled {border:1px solid white; opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)} +a.browse:hover span.disabled {border:1px solid white; background-color:transparent;} +a.pickcolor span {display:block; width:20px; height:16px; background:url(../../img/icons.gif) -840px 0; margin-left:2px;} +.mceOldBoxModel a.pickcolor span {width:21px; height:17px;} +a.pickcolor:hover span {background-color:#B2BBD0;} +a.pickcolor:hover span.disabled {} + +/* Charmap */ +table.charmap {border:1px solid #AAA; text-align:center} +td.charmap, #charmap a {width:18px; height:18px; color:#000; border:1px solid #AAA; text-align:center; font-size:12px; vertical-align:middle; line-height: 18px;} +#charmap a {display:block; color:#000; text-decoration:none; border:0} +#charmap a:hover {background:#CCC;color:#2B6FB6} +#charmap #codeN {font-size:10px; font-family:Arial,Helvetica,sans-serif; text-align:center} +#charmap #codeV {font-size:40px; height:80px; border:1px solid #AAA; text-align:center} + +/* Source */ +.wordWrapCode {vertical-align:middle; border:1px none #000000; background:transparent;} +.mceActionPanel {margin-top:5px;} + +/* Tabs classes */ +.tabs {width:100%; height:18px; line-height:normal; background:url(img/tabs.gif) repeat-x 0 -72px;} +.tabs ul {margin:0; padding:0; list-style:none;} +.tabs li {float:left; background:url(img/tabs.gif) no-repeat 0 0; margin:0 2px 0 0; padding:0 0 0 10px; line-height:17px; height:18px; display:block;} +.tabs li.current {background:url(img/tabs.gif) no-repeat 0 -18px; margin-right:2px;} +.tabs span {float:left; display:block; background:url(img/tabs.gif) no-repeat right -36px; padding:0px 10px 0 0;} +.tabs .current span {background:url(img/tabs.gif) no-repeat right -54px;} +.tabs a {text-decoration:none; font-family:Verdana, Arial; font-size:10px;} +.tabs a:link, .tabs a:visited, .tabs a:hover {color:black;} + +/* Panels */ +.panel_wrapper div.panel {display:none;} +.panel_wrapper div.current {display:block; width:100%; height:300px; overflow:visible;} +.panel_wrapper {border:1px solid #919B9C; border-top:0px; padding:10px; padding-top:5px; clear:both; background:white;} + +/* Columns */ +.column {float:left;} +.properties {width:100%;} +.properties .column1 {} +.properties .column2 {text-align:left;} + +/* Titles */ +h1, h2, h3, h4 {color:#2B6FB6; margin:0; padding:0; padding-top:5px;} +h3 {font-size:14px;} +.title {font-size:12px; font-weight:bold; color:#2B6FB6;} + +/* Dialog specific */ +#link .panel_wrapper, #link div.current {height:125px;} +#image .panel_wrapper, #image div.current {height:200px;} +#plugintable thead {font-weight:bold; background:#DDD;} +#plugintable, #about #plugintable td {border:1px solid #919B9C;} +#plugintable {width:96%; margin-top:10px;} +#pluginscontainer {height:290px; overflow:auto;} +#colorpicker #preview {float:right; width:50px; height:14px;line-height:1px; border:1px solid black; margin-left:5px;} +#colorpicker #colors {float:left; border:1px solid gray; cursor:crosshair;} +#colorpicker #light {border:1px solid gray; margin-left:5px; float:left;width:15px; height:150px; cursor:crosshair;} +#colorpicker #light div {overflow:hidden;} +#colorpicker #previewblock {float:right; padding-left:10px; height:20px;} +#colorpicker .panel_wrapper div.current {height:175px;} +#colorpicker #namedcolors {width:150px;} +#colorpicker #namedcolors a {display:block; float:left; width:10px; height:10px; margin:1px 1px 0 0; overflow:hidden;} +#colorpicker #colornamecontainer {margin-top:5px;} +#colorpicker #picker_panel fieldset {margin:auto;width:325px;} diff --git a/jscripts/tiny_mce/themes/advanced/skins/default/img/buttons.png b/jscripts/tiny_mce/themes/advanced/skins/default/img/buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..7dd58418ba7cfe58ae7efdf174e0b223fe3aa6a0 GIT binary patch literal 3274 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 SLTQbO*E^TJI;F+RU=09Vu@yA{ literal 0 HcmV?d00001 diff --git a/jscripts/tiny_mce/themes/advanced/skins/default/img/menu_check.gif b/jscripts/tiny_mce/themes/advanced/skins/default/img/menu_check.gif new file mode 100644 index 0000000000000000000000000000000000000000..adfdddccd7cac62a17d68873fa53c248bff8351a GIT binary patch literal 70 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 R!Sp}^Mt9BhYjvGt{|88cricIl literal 0 HcmV?d00001 diff --git a/jscripts/tiny_mce/themes/advanced/skins/o2k7/img/button_bg_black.png b/jscripts/tiny_mce/themes/advanced/skins/o2k7/img/button_bg_black.png new file mode 100644 index 0000000000000000000000000000000000000000..8996c7493e8a58c9c40845cbe8abdc3e6730716d GIT binary patch literal 3736 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 M07*qoM6N<$f@g#ZbN~PV literal 0 HcmV?d00001 diff --git a/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui.css b/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui.css new file mode 100644 index 000000000..bb6b6138c --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui.css @@ -0,0 +1,216 @@ +/* Reset */ +.o2k7Skin table, .o2k7Skin tbody, .o2k7Skin a, .o2k7Skin img, .o2k7Skin tr, .o2k7Skin div, .o2k7Skin td, .o2k7Skin iframe, .o2k7Skin span, .o2k7Skin *, .o2k7Skin .mceText {border:0; margin:0; padding:0; background:transparent; white-space:nowrap; text-decoration:none; font-weight:normal; cursor:default; color:#000; vertical-align:baseline; width:auto; border-collapse:separate; text-align:left} +.o2k7Skin a:hover, .o2k7Skin a:link, .o2k7Skin a:visited, .o2k7Skin a:active {text-decoration:none; font-weight:normal; cursor:default; color:#000} +.o2k7Skin table td {vertical-align:middle} + +/* Containers */ +.o2k7Skin table {background:#E5EFFD} +.o2k7Skin iframe {display:block; background:#FFF} +.o2k7Skin .mceToolbar {height:26px} + +/* External */ +.o2k7Skin .mceExternalToolbar {position:absolute; border:1px solid #ABC6DD; border-bottom:0; display:none} +.o2k7Skin .mceExternalToolbar td.mceToolbar {padding-right:13px;} +.o2k7Skin .mceExternalClose {position:absolute; top:3px; right:3px; width:7px; height:7px; background:url(../../img/icons.gif) -820px 0} + +/* Layout */ +.o2k7Skin table.mceLayout {border:0; border-left:1px solid #ABC6DD; border-right:1px solid #ABC6DD} +.o2k7Skin table.mceLayout tr.mceFirst td {border-top:1px solid #ABC6DD} +.o2k7Skin table.mceLayout tr.mceLast td {border-bottom:1px solid #ABC6DD} +.o2k7Skin table.mceToolbar, .o2k7Skin tr.mceFirst .mceToolbar tr td, .o2k7Skin tr.mceLast .mceToolbar tr td {border:0; margin:0; padding:0} +.o2k7Skin .mceIframeContainer {border-top:1px solid #ABC6DD; border-bottom:1px solid #ABC6DD} +.o2k7Skin .mceStatusbar {display:block; font-family:'MS Sans Serif',sans-serif,Verdana,Arial; font-size:9pt; line-height:16px; overflow:visible; color:#000; height:20px} +.o2k7Skin .mceStatusbar div {float:left; padding:2px} +.o2k7Skin .mceStatusbar a.mceResize {display:block; float:right; background:url(../../img/icons.gif) -800px 0; width:20px; height:20px; cursor:se-resize} +.o2k7Skin .mceStatusbar a:hover {text-decoration:underline} +.o2k7Skin table.mceToolbar {margin-left:3px} +.o2k7Skin .mceToolbar .mceToolbarStart span {display:block; background:url(img/button_bg.png) -22px 0; width:1px; height:22px; margin-left:3px;} +.o2k7Skin .mceToolbar td.mceFirst span {margin:0} +.o2k7Skin .mceToolbar .mceToolbarEnd span {display:block; background:url(img/button_bg.png) -22px 0; width:1px; height:22px} +.o2k7Skin .mceToolbar .mceToolbarEndListBox span, .o2k7Skin .mceToolbar .mceToolbarStartListBox span {display:none} +.o2k7Skin span.mceIcon, .o2k7Skin img.mceIcon {display:block; width:20px; height:20px} +.o2k7Skin .mceIcon {background:url(../../img/icons.gif) no-repeat 20px 20px} +.o2k7Skin td.mceCenter {text-align:center;} +.o2k7Skin td.mceCenter table {margin:0 auto; text-align:left;} +.o2k7Skin td.mceRight table {margin:0 0 0 auto;} + +/* Button */ +.o2k7Skin .mceButton {display:block; background:url(img/button_bg.png); width:22px; height:22px} +.o2k7Skin a.mceButton span, .o2k7Skin a.mceButton img {margin-left:1px} +.o2k7Skin .mceOldBoxModel a.mceButton span, .o2k7Skin .mceOldBoxModel a.mceButton img {margin:0 0 0 1px} +.o2k7Skin a.mceButtonEnabled:hover {background-color:#B2BBD0; background-position:0 -22px} +.o2k7Skin a.mceButtonActive, .o2k7Skin a.mceButtonSelected {background-position:0 -44px} +.o2k7Skin .mceButtonDisabled .mceIcon {opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)} +.o2k7Skin .mceButtonLabeled {width:auto} +.o2k7Skin .mceButtonLabeled span.mceIcon {float:left} +.o2k7Skin span.mceButtonLabel {display:block; font-size:10px; padding:4px 6px 0 22px; font-family:Tahoma,Verdana,Arial,Helvetica} +.o2k7Skin .mceButtonDisabled .mceButtonLabel {color:#888} + +/* Separator */ +.o2k7Skin .mceSeparator {display:block; background:url(img/button_bg.png) -22px 0; width:5px; height:22px} + +/* ListBox */ +.o2k7Skin .mceListBox {margin-left:3px} +.o2k7Skin .mceListBox, .o2k7Skin .mceListBox a {display:block} +.o2k7Skin .mceListBox .mceText {padding-left:4px; text-align:left; width:70px; border:1px solid #b3c7e1; border-right:0; background:#eaf2fb; font-family:Tahoma,Verdana,Arial,Helvetica; font-size:11px; height:20px; line-height:20px; overflow:hidden} +.o2k7Skin .mceListBox .mceOpen {width:14px; height:22px; background:url(img/button_bg.png) -66px 0} +.o2k7Skin table.mceListBoxEnabled:hover .mceText, .o2k7Skin .mceListBoxHover .mceText, .o2k7Skin .mceListBoxSelected .mceText {background:#FFF} +.o2k7Skin table.mceListBoxEnabled:hover .mceOpen, .o2k7Skin .mceListBoxHover .mceOpen, .o2k7Skin .mceListBoxSelected .mceOpen {background-position:-66px -22px} +.o2k7Skin .mceListBoxDisabled .mceText {color:gray} +.o2k7Skin .mceListBoxMenu {overflow:auto; overflow-x:hidden} +.o2k7Skin .mceOldBoxModel .mceListBox .mceText {height:22px} +.o2k7Skin select.mceListBox {font-family:Tahoma,Verdana,Arial,Helvetica; font-size:12px; border:1px solid #b3c7e1; background:#FFF;} + +/* SplitButton */ +.o2k7Skin .mceSplitButton, .o2k7Skin .mceSplitButton a, .o2k7Skin .mceSplitButton span {display:block; height:22px} +.o2k7Skin .mceSplitButton {background:url(img/button_bg.png)} +.o2k7Skin .mceSplitButton a.mceAction {width:22px} +.o2k7Skin .mceSplitButton span.mceAction {width:22px; background-image:url(../../img/icons.gif)} +.o2k7Skin .mceSplitButton a.mceOpen {width:10px; background:url(img/button_bg.png) -44px 0} +.o2k7Skin .mceSplitButton span.mceOpen {display:none} +.o2k7Skin table.mceSplitButtonEnabled:hover a.mceAction, .o2k7Skin .mceSplitButtonHover a.mceAction, .o2k7Skin .mceSplitButtonSelected {background:url(img/button_bg.png) 0 -22px} +.o2k7Skin table.mceSplitButtonEnabled:hover a.mceOpen, .o2k7Skin .mceSplitButtonHover a.mceOpen, .o2k7Skin .mceSplitButtonSelected a.mceOpen {background-position:-44px -44px} +.o2k7Skin .mceSplitButtonDisabled .mceAction {opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)} +.o2k7Skin .mceSplitButtonActive {background-position:0 -44px} + +/* ColorSplitButton */ +.o2k7Skin div.mceColorSplitMenu table {background:#FFF; border:1px solid gray} +.o2k7Skin .mceColorSplitMenu td {padding:2px} +.o2k7Skin .mceColorSplitMenu a {display:block; width:9px; height:9px; overflow:hidden; border:1px solid #808080} +.o2k7Skin .mceColorSplitMenu td.mceMoreColors {padding:1px 3px 1px 1px} +.o2k7Skin .mceColorSplitMenu a.mceMoreColors {width:100%; height:auto; text-align:center; font-family:Tahoma,Verdana,Arial,Helvetica; font-size:11px; line-height:20px; border:1px solid #FFF} +.o2k7Skin .mceColorSplitMenu a.mceMoreColors:hover {border:1px solid #0A246A; background-color:#B6BDD2} +.o2k7Skin a.mceMoreColors:hover {border:1px solid #0A246A} +.o2k7Skin .mceColorPreview {margin-left:2px; width:16px; height:4px; overflow:hidden; background:#9a9b9a;overflow:hidden} +.o2k7Skin .mce_forecolor span.mceAction, .o2k7Skin .mce_backcolor span.mceAction {height:15px;overflow:hidden} + +/* Menu */ +.o2k7Skin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid #ABC6DD} +.o2k7Skin .mceNoIcons span.mceIcon {width:0;} +.o2k7Skin .mceNoIcons a .mceText {padding-left:10px} +.o2k7Skin .mceMenu table {background:#FFF} +.o2k7Skin .mceMenu a, .o2k7Skin .mceMenu span, .o2k7Skin .mceMenu {display:block} +.o2k7Skin .mceMenu td {height:20px} +.o2k7Skin .mceMenu a {position:relative;padding:3px 0 4px 0} +.o2k7Skin .mceMenu .mceText {position:relative; display:block; font-family:Tahoma,Verdana,Arial,Helvetica; color:#000; cursor:default; margin:0; padding:0 25px 0 25px; display:block} +.o2k7Skin .mceMenu span.mceText, .o2k7Skin .mceMenu .mcePreview {font-size:11px} +.o2k7Skin .mceMenu pre.mceText {font-family:Monospace} +.o2k7Skin .mceMenu .mceIcon {position:absolute; top:0; left:0; width:22px;} +.o2k7Skin .mceMenu .mceMenuItemEnabled a:hover, .o2k7Skin .mceMenu .mceMenuItemActive {background-color:#dbecf3} +.o2k7Skin td.mceMenuItemSeparator {background:#DDD; height:1px} +.o2k7Skin .mceMenuItemTitle a {border:0; background:#E5EFFD; border-bottom:1px solid #ABC6DD} +.o2k7Skin .mceMenuItemTitle span.mceText {color:#000; font-weight:bold; padding-left:4px} +.o2k7Skin .mceMenuItemDisabled .mceText {color:#888} +.o2k7Skin .mceMenuItemSelected .mceIcon {background:url(../default/img/menu_check.gif)} +.o2k7Skin .mceNoIcons .mceMenuItemSelected a {background:url(../default/img/menu_arrow.gif) no-repeat -6px center} +.o2k7Skin .mceMenu span.mceMenuLine {display:none} +.o2k7Skin .mceMenuItemSub a {background:url(../default/img/menu_arrow.gif) no-repeat top right;} + +/* Progress,Resize */ +.o2k7Skin .mceBlocker {position:absolute; left:0; top:0; z-index:1000; opacity:0.5; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=50); background:#FFF} +.o2k7Skin .mceProgress {position:absolute; left:0; top:0; z-index:1001; background:url(../default/img/progress.gif) no-repeat; width:32px; height:32px; margin:-16px 0 0 -16px} +.o2k7Skin .mcePlaceHolder {border:1px dotted gray} + +/* Formats */ +.o2k7Skin .mce_formatPreview a {font-size:10px} +.o2k7Skin .mce_p span.mceText {} +.o2k7Skin .mce_address span.mceText {font-style:italic} +.o2k7Skin .mce_pre span.mceText {font-family:monospace} +.o2k7Skin .mce_h1 span.mceText {font-weight:bolder; font-size: 2em} +.o2k7Skin .mce_h2 span.mceText {font-weight:bolder; font-size: 1.5em} +.o2k7Skin .mce_h3 span.mceText {font-weight:bolder; font-size: 1.17em} +.o2k7Skin .mce_h4 span.mceText {font-weight:bolder; font-size: 1em} +.o2k7Skin .mce_h5 span.mceText {font-weight:bolder; font-size: .83em} +.o2k7Skin .mce_h6 span.mceText {font-weight:bolder; font-size: .75em} + +/* Theme */ +.o2k7Skin span.mce_bold {background-position:0 0} +.o2k7Skin span.mce_italic {background-position:-60px 0} +.o2k7Skin span.mce_underline {background-position:-140px 0} +.o2k7Skin span.mce_strikethrough {background-position:-120px 0} +.o2k7Skin span.mce_undo {background-position:-160px 0} +.o2k7Skin span.mce_redo {background-position:-100px 0} +.o2k7Skin span.mce_cleanup {background-position:-40px 0} +.o2k7Skin span.mce_bullist {background-position:-20px 0} +.o2k7Skin span.mce_numlist {background-position:-80px 0} +.o2k7Skin span.mce_justifyleft {background-position:-460px 0} +.o2k7Skin span.mce_justifyright {background-position:-480px 0} +.o2k7Skin span.mce_justifycenter {background-position:-420px 0} +.o2k7Skin span.mce_justifyfull {background-position:-440px 0} +.o2k7Skin span.mce_anchor {background-position:-200px 0} +.o2k7Skin span.mce_indent {background-position:-400px 0} +.o2k7Skin span.mce_outdent {background-position:-540px 0} +.o2k7Skin span.mce_link {background-position:-500px 0} +.o2k7Skin span.mce_unlink {background-position:-640px 0} +.o2k7Skin span.mce_sub {background-position:-600px 0} +.o2k7Skin span.mce_sup {background-position:-620px 0} +.o2k7Skin span.mce_removeformat {background-position:-580px 0} +.o2k7Skin span.mce_newdocument {background-position:-520px 0} +.o2k7Skin span.mce_image {background-position:-380px 0} +.o2k7Skin span.mce_help {background-position:-340px 0} +.o2k7Skin span.mce_code {background-position:-260px 0} +.o2k7Skin span.mce_hr {background-position:-360px 0} +.o2k7Skin span.mce_visualaid {background-position:-660px 0} +.o2k7Skin span.mce_charmap {background-position:-240px 0} +.o2k7Skin span.mce_paste {background-position:-560px 0} +.o2k7Skin span.mce_copy {background-position:-700px 0} +.o2k7Skin span.mce_cut {background-position:-680px 0} +.o2k7Skin span.mce_blockquote {background-position:-220px 0} +.o2k7Skin .mce_forecolor span.mceAction {background-position:-720px 0} +.o2k7Skin .mce_backcolor span.mceAction {background-position:-760px 0} +.o2k7Skin span.mce_forecolorpicker {background-position:-720px 0} +.o2k7Skin span.mce_backcolorpicker {background-position:-760px 0} + +/* Plugins */ +.o2k7Skin span.mce_advhr {background-position:-0px -20px} +.o2k7Skin span.mce_ltr {background-position:-20px -20px} +.o2k7Skin span.mce_rtl {background-position:-40px -20px} +.o2k7Skin span.mce_emotions {background-position:-60px -20px} +.o2k7Skin span.mce_fullpage {background-position:-80px -20px} +.o2k7Skin span.mce_fullscreen {background-position:-100px -20px} +.o2k7Skin span.mce_iespell {background-position:-120px -20px} +.o2k7Skin span.mce_insertdate {background-position:-140px -20px} +.o2k7Skin span.mce_inserttime {background-position:-160px -20px} +.o2k7Skin span.mce_absolute {background-position:-180px -20px} +.o2k7Skin span.mce_backward {background-position:-200px -20px} +.o2k7Skin span.mce_forward {background-position:-220px -20px} +.o2k7Skin span.mce_insert_layer {background-position:-240px -20px} +.o2k7Skin span.mce_insertlayer {background-position:-260px -20px} +.o2k7Skin span.mce_movebackward {background-position:-280px -20px} +.o2k7Skin span.mce_moveforward {background-position:-300px -20px} +.o2k7Skin span.mce_media {background-position:-320px -20px} +.o2k7Skin span.mce_nonbreaking {background-position:-340px -20px} +.o2k7Skin span.mce_pastetext {background-position:-360px -20px} +.o2k7Skin span.mce_pasteword {background-position:-380px -20px} +.o2k7Skin span.mce_selectall {background-position:-400px -20px} +.o2k7Skin span.mce_preview {background-position:-420px -20px} +.o2k7Skin span.mce_print {background-position:-440px -20px} +.o2k7Skin span.mce_cancel {background-position:-460px -20px} +.o2k7Skin span.mce_save {background-position:-480px -20px} +.o2k7Skin span.mce_replace {background-position:-500px -20px} +.o2k7Skin span.mce_search {background-position:-520px -20px} +.o2k7Skin span.mce_styleprops {background-position:-560px -20px} +.o2k7Skin span.mce_table {background-position:-580px -20px} +.o2k7Skin span.mce_cell_props {background-position:-600px -20px} +.o2k7Skin span.mce_delete_table {background-position:-620px -20px} +.o2k7Skin span.mce_delete_col {background-position:-640px -20px} +.o2k7Skin span.mce_delete_row {background-position:-660px -20px} +.o2k7Skin span.mce_col_after {background-position:-680px -20px} +.o2k7Skin span.mce_col_before {background-position:-700px -20px} +.o2k7Skin span.mce_row_after {background-position:-720px -20px} +.o2k7Skin span.mce_row_before {background-position:-740px -20px} +.o2k7Skin span.mce_merge_cells {background-position:-760px -20px} +.o2k7Skin span.mce_table_props {background-position:-980px -20px} +.o2k7Skin span.mce_row_props {background-position:-780px -20px} +.o2k7Skin span.mce_split_cells {background-position:-800px -20px} +.o2k7Skin span.mce_template {background-position:-820px -20px} +.o2k7Skin span.mce_visualchars {background-position:-840px -20px} +.o2k7Skin span.mce_abbr {background-position:-860px -20px} +.o2k7Skin span.mce_acronym {background-position:-880px -20px} +.o2k7Skin span.mce_attribs {background-position:-900px -20px} +.o2k7Skin span.mce_cite {background-position:-920px -20px} +.o2k7Skin span.mce_del {background-position:-940px -20px} +.o2k7Skin span.mce_ins {background-position:-960px -20px} +.o2k7Skin span.mce_pagebreak {background-position:0 -40px} +.o2k7Skin span.mce_restoredraft {background-position:-20px -40px} +.o2k7Skin .mce_spellchecker span.mceAction {background-position:-540px -20px} diff --git a/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui_black.css b/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui_black.css new file mode 100644 index 000000000..153f0c38a --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui_black.css @@ -0,0 +1,8 @@ +/* Black */ +.o2k7SkinBlack .mceToolbar .mceToolbarStart span, .o2k7SkinBlack .mceToolbar .mceToolbarEnd span, .o2k7SkinBlack .mceButton, .o2k7SkinBlack .mceSplitButton, .o2k7SkinBlack .mceSeparator, .o2k7SkinBlack .mceSplitButton a.mceOpen, .o2k7SkinBlack .mceListBox a.mceOpen {background-image:url(img/button_bg_black.png)} +.o2k7SkinBlack table, .o2k7SkinBlack .mceMenuItemTitle a, .o2k7SkinBlack .mceMenuItemTitle span.mceText, .o2k7SkinBlack .mceStatusbar div, .o2k7SkinBlack .mceStatusbar span, .o2k7SkinBlack .mceStatusbar a {background:#535353; color:#FFF} +.o2k7SkinBlack table.mceListBoxEnabled .mceText, o2k7SkinBlack .mceListBox .mceText {background:#FFF; border:1px solid #CBCFD4; border-bottom-color:#989FA9; border-right:0} +.o2k7SkinBlack table.mceListBoxEnabled:hover .mceText, .o2k7SkinBlack .mceListBoxHover .mceText, .o2k7SkinBlack .mceListBoxSelected .mceText {background:#FFF; border:1px solid #FFBD69; border-right:0} +.o2k7SkinBlack .mceExternalToolbar, .o2k7SkinBlack .mceListBox .mceText, .o2k7SkinBlack div.mceMenu, .o2k7SkinBlack table.mceLayout, .o2k7SkinBlack .mceMenuItemTitle a, .o2k7SkinBlack table.mceLayout tr.mceFirst td, .o2k7SkinBlack table.mceLayout, .o2k7SkinBlack .mceMenuItemTitle a, .o2k7SkinBlack table.mceLayout tr.mceLast td, .o2k7SkinBlack .mceIframeContainer {border-color: #535353;} +.o2k7SkinBlack table.mceSplitButtonEnabled:hover a.mceAction, .o2k7SkinBlack .mceSplitButtonHover a.mceAction, .o2k7SkinBlack .mceSplitButtonSelected {background-image:url(img/button_bg_black.png)} +.o2k7SkinBlack .mceMenu .mceMenuItemEnabled a:hover, .o2k7SkinBlack .mceMenu .mceMenuItemActive {background-color:#FFE7A1} \ No newline at end of file diff --git a/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui_silver.css b/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui_silver.css new file mode 100644 index 000000000..7fe3b45e1 --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui_silver.css @@ -0,0 +1,5 @@ +/* Silver */ +.o2k7SkinSilver .mceToolbar .mceToolbarStart span, .o2k7SkinSilver .mceButton, .o2k7SkinSilver .mceSplitButton, .o2k7SkinSilver .mceSeparator, .o2k7SkinSilver .mceSplitButton a.mceOpen, .o2k7SkinSilver .mceListBox a.mceOpen {background-image:url(img/button_bg_silver.png)} +.o2k7SkinSilver table, .o2k7SkinSilver .mceMenuItemTitle a {background:#eee} +.o2k7SkinSilver .mceListBox .mceText {background:#FFF} +.o2k7SkinSilver .mceExternalToolbar, .o2k7SkinSilver .mceListBox .mceText, .o2k7SkinSilver div.mceMenu, .o2k7SkinSilver table.mceLayout, .o2k7SkinSilver .mceMenuItemTitle a, .o2k7SkinSilver table.mceLayout tr.mceFirst td, .o2k7SkinSilver table.mceLayout, .o2k7SkinSilver .mceMenuItemTitle a, .o2k7SkinSilver table.mceLayout tr.mceLast td, .o2k7SkinSilver .mceIframeContainer {border-color: #bbb} diff --git a/jscripts/tiny_mce/themes/advanced/source_editor.htm b/jscripts/tiny_mce/themes/advanced/source_editor.htm new file mode 100644 index 000000000..1c81d23ee --- /dev/null +++ b/jscripts/tiny_mce/themes/advanced/source_editor.htm @@ -0,0 +1,26 @@ + + + + {#advanced_dlg.code_title} + + + + +
    +
    {#advanced_dlg.code_title}
    + +
    + +
    + +
    + + + +
    + + +
    +
    + + diff --git a/jscripts/tiny_mce/themes/simple/editor_template.js b/jscripts/tiny_mce/themes/simple/editor_template.js new file mode 100644 index 000000000..ed89abc06 --- /dev/null +++ b/jscripts/tiny_mce/themes/simple/editor_template.js @@ -0,0 +1 @@ +(function(){var a=tinymce.DOM;tinymce.ThemeManager.requireLangPack("simple");tinymce.create("tinymce.themes.SimpleTheme",{init:function(c,d){var e=this,b=["Bold","Italic","Underline","Strikethrough","InsertUnorderedList","InsertOrderedList"],f=c.settings;e.editor=c;c.onInit.add(function(){c.onNodeChange.add(function(h,g){tinymce.each(b,function(i){g.get(i.toLowerCase()).setActive(h.queryCommandState(i))})});c.dom.loadCSS(d+"/skins/"+f.skin+"/content.css")});a.loadCSS((f.editor_css?c.documentBaseURI.toAbsolute(f.editor_css):"")||d+"/skins/"+f.skin+"/ui.css")},renderUI:function(h){var e=this,i=h.targetNode,b,c,d=e.editor,f=d.controlManager,g;i=a.insertAfter(a.create("span",{id:d.id+"_container","class":"mceEditor "+d.settings.skin+"SimpleSkin"}),i);i=g=a.add(i,"table",{cellPadding:0,cellSpacing:0,"class":"mceLayout"});i=c=a.add(i,"tbody");i=a.add(c,"tr");i=b=a.add(a.add(i,"td"),"div",{"class":"mceIframeContainer"});i=a.add(a.add(c,"tr",{"class":"last"}),"td",{"class":"mceToolbar mceLast",align:"center"});c=e.toolbar=f.createToolbar("tools1");c.add(f.createButton("bold",{title:"simple.bold_desc",cmd:"Bold"}));c.add(f.createButton("italic",{title:"simple.italic_desc",cmd:"Italic"}));c.add(f.createButton("underline",{title:"simple.underline_desc",cmd:"Underline"}));c.add(f.createButton("strikethrough",{title:"simple.striketrough_desc",cmd:"Strikethrough"}));c.add(f.createSeparator());c.add(f.createButton("undo",{title:"simple.undo_desc",cmd:"Undo"}));c.add(f.createButton("redo",{title:"simple.redo_desc",cmd:"Redo"}));c.add(f.createSeparator());c.add(f.createButton("cleanup",{title:"simple.cleanup_desc",cmd:"mceCleanup"}));c.add(f.createSeparator());c.add(f.createButton("insertunorderedlist",{title:"simple.bullist_desc",cmd:"InsertUnorderedList"}));c.add(f.createButton("insertorderedlist",{title:"simple.numlist_desc",cmd:"InsertOrderedList"}));c.renderTo(i);return{iframeContainer:b,editorContainer:d.id+"_container",sizeContainer:g,deltaHeight:-20}},getInfo:function(){return{longname:"Simple theme",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.ThemeManager.add("simple",tinymce.themes.SimpleTheme)})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/themes/simple/editor_template_src.js b/jscripts/tiny_mce/themes/simple/editor_template_src.js new file mode 100644 index 000000000..4b862d49d --- /dev/null +++ b/jscripts/tiny_mce/themes/simple/editor_template_src.js @@ -0,0 +1,85 @@ +/** + * editor_template_src.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +(function() { + var DOM = tinymce.DOM; + + // Tell it to load theme specific language pack(s) + tinymce.ThemeManager.requireLangPack('simple'); + + tinymce.create('tinymce.themes.SimpleTheme', { + init : function(ed, url) { + var t = this, states = ['Bold', 'Italic', 'Underline', 'Strikethrough', 'InsertUnorderedList', 'InsertOrderedList'], s = ed.settings; + + t.editor = ed; + + ed.onInit.add(function() { + ed.onNodeChange.add(function(ed, cm) { + tinymce.each(states, function(c) { + cm.get(c.toLowerCase()).setActive(ed.queryCommandState(c)); + }); + }); + + ed.dom.loadCSS(url + "/skins/" + s.skin + "/content.css"); + }); + + DOM.loadCSS((s.editor_css ? ed.documentBaseURI.toAbsolute(s.editor_css) : '') || url + "/skins/" + s.skin + "/ui.css"); + }, + + renderUI : function(o) { + var t = this, n = o.targetNode, ic, tb, ed = t.editor, cf = ed.controlManager, sc; + + n = DOM.insertAfter(DOM.create('span', {id : ed.id + '_container', 'class' : 'mceEditor ' + ed.settings.skin + 'SimpleSkin'}), n); + n = sc = DOM.add(n, 'table', {cellPadding : 0, cellSpacing : 0, 'class' : 'mceLayout'}); + n = tb = DOM.add(n, 'tbody'); + + // Create iframe container + n = DOM.add(tb, 'tr'); + n = ic = DOM.add(DOM.add(n, 'td'), 'div', {'class' : 'mceIframeContainer'}); + + // Create toolbar container + n = DOM.add(DOM.add(tb, 'tr', {'class' : 'last'}), 'td', {'class' : 'mceToolbar mceLast', align : 'center'}); + + // Create toolbar + tb = t.toolbar = cf.createToolbar("tools1"); + tb.add(cf.createButton('bold', {title : 'simple.bold_desc', cmd : 'Bold'})); + tb.add(cf.createButton('italic', {title : 'simple.italic_desc', cmd : 'Italic'})); + tb.add(cf.createButton('underline', {title : 'simple.underline_desc', cmd : 'Underline'})); + tb.add(cf.createButton('strikethrough', {title : 'simple.striketrough_desc', cmd : 'Strikethrough'})); + tb.add(cf.createSeparator()); + tb.add(cf.createButton('undo', {title : 'simple.undo_desc', cmd : 'Undo'})); + tb.add(cf.createButton('redo', {title : 'simple.redo_desc', cmd : 'Redo'})); + tb.add(cf.createSeparator()); + tb.add(cf.createButton('cleanup', {title : 'simple.cleanup_desc', cmd : 'mceCleanup'})); + tb.add(cf.createSeparator()); + tb.add(cf.createButton('insertunorderedlist', {title : 'simple.bullist_desc', cmd : 'InsertUnorderedList'})); + tb.add(cf.createButton('insertorderedlist', {title : 'simple.numlist_desc', cmd : 'InsertOrderedList'})); + tb.renderTo(n); + + return { + iframeContainer : ic, + editorContainer : ed.id + '_container', + sizeContainer : sc, + deltaHeight : -20 + }; + }, + + getInfo : function() { + return { + longname : 'Simple theme', + author : 'Moxiecode Systems AB', + authorurl : 'http://tinymce.moxiecode.com', + version : tinymce.majorVersion + "." + tinymce.minorVersion + } + } + }); + + tinymce.ThemeManager.add('simple', tinymce.themes.SimpleTheme); +})(); \ No newline at end of file diff --git a/jscripts/tiny_mce/themes/simple/img/icons.gif b/jscripts/tiny_mce/themes/simple/img/icons.gif new file mode 100644 index 0000000000000000000000000000000000000000..16af141ff0eea376a889b1e8d28e9c1cacaaab16 GIT binary patch literal 1440 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 VmjT$ODWe$zObpERYjs_s{s;8{A&me4 literal 0 HcmV?d00001 diff --git a/jscripts/tiny_mce/themes/simple/skins/o2k7/ui.css b/jscripts/tiny_mce/themes/simple/skins/o2k7/ui.css new file mode 100644 index 000000000..cf6c35d10 --- /dev/null +++ b/jscripts/tiny_mce/themes/simple/skins/o2k7/ui.css @@ -0,0 +1,35 @@ +/* Reset */ +.o2k7SimpleSkin table, .o2k7SimpleSkin tbody, .o2k7SimpleSkin a, .o2k7SimpleSkin img, .o2k7SimpleSkin tr, .o2k7SimpleSkin div, .o2k7SimpleSkin td, .o2k7SimpleSkin iframe, .o2k7SimpleSkin span, .o2k7SimpleSkin * {border:0; margin:0; padding:0; background:transparent; white-space:nowrap; text-decoration:none; font-weight:normal; cursor:default; color:#000} + +/* Containers */ +.o2k7SimpleSkin {position:relative} +.o2k7SimpleSkin table.mceLayout {background:#E5EFFD; border:1px solid #ABC6DD;} +.o2k7SimpleSkin iframe {display:block; background:#FFF; border-bottom:1px solid #ABC6DD;} +.o2k7SimpleSkin .mceToolbar {height:26px;} + +/* Layout */ +.o2k7SimpleSkin .mceToolbar .mceToolbarStart span {display:block; background:url(img/button_bg.png) -22px 0; width:1px; height:22px; } +.o2k7SimpleSkin .mceToolbar .mceToolbarEnd span {display:block; background:url(img/button_bg.png) -22px 0; width:1px; height:22px} +.o2k7SimpleSkin span.mceIcon, .o2k7SimpleSkin img.mceIcon {display:block; width:20px; height:20px} +.o2k7SimpleSkin .mceIcon {background:url(../../img/icons.gif) no-repeat 20px 20px} + +/* Button */ +.o2k7SimpleSkin .mceButton {display:block; background:url(img/button_bg.png); width:22px; height:22px} +.o2k7SimpleSkin a.mceButton span, .o2k7SimpleSkin a.mceButton img {margin:1px 0 0 1px} +.o2k7SimpleSkin a.mceButtonEnabled:hover {background-color:#B2BBD0; background-position:0 -22px} +.o2k7SimpleSkin a.mceButtonActive {background-position:0 -44px} +.o2k7SimpleSkin .mceButtonDisabled span {opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)} + +/* Separator */ +.o2k7SimpleSkin .mceSeparator {display:block; background:url(img/button_bg.png) -22px 0; width:5px; height:22px} + +/* Theme */ +.o2k7SimpleSkin span.mce_bold {background-position:0 0} +.o2k7SimpleSkin span.mce_italic {background-position:-60px 0} +.o2k7SimpleSkin span.mce_underline {background-position:-140px 0} +.o2k7SimpleSkin span.mce_strikethrough {background-position:-120px 0} +.o2k7SimpleSkin span.mce_undo {background-position:-160px 0} +.o2k7SimpleSkin span.mce_redo {background-position:-100px 0} +.o2k7SimpleSkin span.mce_cleanup {background-position:-40px 0} +.o2k7SimpleSkin span.mce_insertunorderedlist {background-position:-20px 0} +.o2k7SimpleSkin span.mce_insertorderedlist {background-position:-80px 0} diff --git a/jscripts/tiny_mce/tiny_mce.js b/jscripts/tiny_mce/tiny_mce.js new file mode 100644 index 000000000..045e0db7d --- /dev/null +++ b/jscripts/tiny_mce/tiny_mce.js @@ -0,0 +1 @@ +(function(c){var a=/^\s*|\s*$/g,d;var b={majorVersion:"3",minorVersion:"3",releaseDate:"2010-03-10",_init:function(){var r=this,o=document,m=navigator,f=m.userAgent,l,e,k,j,h,q;r.isOpera=c.opera&&opera.buildNumber;r.isWebKit=/WebKit/.test(f);r.isIE=!r.isWebKit&&!r.isOpera&&(/MSIE/gi).test(f)&&(/Explorer/gi).test(m.appName);r.isIE6=r.isIE&&/MSIE [56]/.test(f);r.isGecko=!r.isWebKit&&/Gecko/.test(f);r.isMac=f.indexOf("Mac")!=-1;r.isAir=/adobeair/i.test(f);if(c.tinyMCEPreInit){r.suffix=tinyMCEPreInit.suffix;r.baseURL=tinyMCEPreInit.base;r.query=tinyMCEPreInit.query;return}r.suffix="";e=o.getElementsByTagName("base");for(l=0;l=c.length){for(e=0,b=g.length;e=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e=0;c--){if(f[c].length==0||f[c]=="."){continue}if(f[c]==".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!=0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(e,b){var c=new Date();c.setTime(c.getTime()-1000);this.set(e,"",c,b,c)}})})();tinymce.create("static tinymce.util.JSON",{serialize:function(e){var c,a,d=tinymce.util.JSON.serialize,b;if(e==null){return"null"}b=typeof e;if(b=="string"){a="\bb\tt\nn\ff\rr\"\"''\\\\";return'"'+e.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g,function(g,f){c=a.indexOf(f);if(c+1){return"\\"+a.charAt(c+1)}g=f.charCodeAt().toString(16);return"\\u"+"0000".substring(g.length)+g})+'"'}if(b=="object"){if(e.hasOwnProperty&&e instanceof Array){for(c=0,a="[";c0?",":"")+d(e[c])}return a+"]"}a="{";for(c in e){a+=typeof e[c]!="function"?(a.length>1?',"':'"')+c+'":'+d(e[c]):""}return a+"}"}return""+e},parse:function(s){try{return eval("("+s+")")}catch(ex){}}});tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){e.call(f.error_scope||f.scope,h,g)};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(m){var k=m.each,j=m.is,i=m.isWebKit,d=m.isIE,a=/^(H[1-6R]|P|DIV|ADDRESS|PRE|FORM|T(ABLE|BODY|HEAD|FOOT|H|R|D)|LI|OL|UL|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|MENU|ISINDEX|SAMP)$/,e=g("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"),f=g("src,href,style,coords,shape"),c={"&":"&",'"':""","<":"<",">":">"},n=/[<>&\"]/g,b=/^([a-z0-9],?)+$/i,h=/<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g,l=/(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;function g(q){var p={},o;q=q.split(",");for(o=q.length;o>=0;o--){p[q[o]]=1}return p}m.create("tinymce.dom.DOMUtils",{doc:null,root:null,files:null,pixelStyles:/^(top|left|bottom|right|width|height|borderWidth)$/,props:{"for":"htmlFor","class":"className",className:"className",checked:"checked",disabled:"disabled",maxlength:"maxLength",readonly:"readOnly",selected:"selected",value:"value",id:"id",name:"name",type:"type"},DOMUtils:function(u,q){var p=this,o;p.doc=u;p.win=window;p.files={};p.cssFlicker=false;p.counter=0;p.boxModel=!m.isIE||u.compatMode=="CSS1Compat";p.stdMode=u.documentMode===8;p.settings=q=m.extend({keep_values:false,hex_colors:1,process_html:1},q);if(m.isIE6){try{u.execCommand("BackgroundImageCache",false,true)}catch(r){p.cssFlicker=true}}if(q.valid_styles){p._styles={};k(q.valid_styles,function(t,s){p._styles[s]=m.explode(t)})}m.addUnload(p.destroy,p)},getRoot:function(){var o=this,p=o.settings;return(p&&o.get(p.root_element))||o.doc.body},getViewPort:function(p){var q,o;p=!p?this.win:p;q=p.document;o=this.boxModel?q.documentElement:q.body;return{x:p.pageXOffset||o.scrollLeft,y:p.pageYOffset||o.scrollTop,w:p.innerWidth||o.clientWidth,h:p.innerHeight||o.clientHeight}},getRect:function(s){var r,o=this,q;s=o.get(s);r=o.getPos(s);q=o.getSize(s);return{x:r.x,y:r.y,w:q.w,h:q.h}},getSize:function(r){var p=this,o,q;r=p.get(r);o=p.getStyle(r,"width");q=p.getStyle(r,"height");if(o.indexOf("px")===-1){o=0}if(q.indexOf("px")===-1){q=0}return{w:parseInt(o)||r.offsetWidth||r.clientWidth,h:parseInt(q)||r.offsetHeight||r.clientHeight}},getParent:function(q,p,o){return this.getParents(q,p,o,false)},getParents:function(z,v,s,y){var q=this,p,u=q.settings,x=[];z=q.get(z);y=y===undefined;if(u.strict_root){s=s||q.getRoot()}if(j(v,"string")){p=v;if(v==="*"){v=function(o){return o.nodeType==1}}else{v=function(o){return q.is(o,p)}}}while(z){if(z==s||!z.nodeType||z.nodeType===9){break}if(!v||v(z)){if(y){x.push(z)}else{return z}}z=z.parentNode}return y?x:null},get:function(o){var p;if(o&&this.doc&&typeof(o)=="string"){p=o;o=this.doc.getElementById(o);if(o&&o.id!==p){return this.doc.getElementsByName(p)[1]}}return o},getNext:function(p,o){return this._findSib(p,o,"nextSibling")},getPrev:function(p,o){return this._findSib(p,o,"previousSibling")},select:function(q,p){var o=this;return m.dom.Sizzle(q,o.get(p)||o.get(o.settings.root_element)||o.doc,[])},is:function(q,o){var p;if(q.length===undefined){if(o==="*"){return q.nodeType==1}if(b.test(o)){o=o.toLowerCase().split(/,/);q=q.nodeName.toLowerCase();for(p=o.length-1;p>=0;p--){if(o[p]==q){return true}}return false}}return m.dom.Sizzle.matches(o,q.nodeType?[q]:q).length>0},add:function(s,v,o,r,u){var q=this;return this.run(s,function(y){var x,t;x=j(v,"string")?q.doc.createElement(v):v;q.setAttribs(x,o);if(r){if(r.nodeType){x.appendChild(r)}else{q.setHTML(x,r)}}return !u?y.appendChild(x):x})},create:function(q,o,p){return this.add(this.doc.createElement(q),q,o,p,1)},createHTML:function(v,p,s){var u="",r=this,q;u+="<"+v;for(q in p){if(p.hasOwnProperty(q)){u+=" "+q+'="'+r.encode(p[q])+'"'}}if(m.is(s)){return u+">"+s+""}return u+" />"},remove:function(o,p){return this.run(o,function(r){var q,s;q=r.parentNode;if(!q){return null}if(p){while(s=r.firstChild){if(s.nodeType!==3||s.nodeValue){q.insertBefore(s,r)}else{r.removeChild(s)}}}return q.removeChild(r)})},setStyle:function(r,o,p){var q=this;return q.run(r,function(v){var u,t;u=v.style;o=o.replace(/-(\D)/g,function(x,s){return s.toUpperCase()});if(q.pixelStyles.test(o)&&(m.is(p,"number")||/^[\-0-9\.]+$/.test(p))){p+="px"}switch(o){case"opacity":if(d){u.filter=p===""?"":"alpha(opacity="+(p*100)+")";if(!r.currentStyle||!r.currentStyle.hasLayout){u.display="inline-block"}}u[o]=u["-moz-opacity"]=u["-khtml-opacity"]=p||"";break;case"float":d?u.styleFloat=p:u.cssFloat=p;break;default:u[o]=p||""}if(q.settings.update_styles){q.setAttrib(v,"_mce_style")}})},getStyle:function(r,o,q){r=this.get(r);if(!r){return false}if(this.doc.defaultView&&q){o=o.replace(/[A-Z]/g,function(s){return"-"+s});try{return this.doc.defaultView.getComputedStyle(r,null).getPropertyValue(o)}catch(p){return null}}o=o.replace(/-(\D)/g,function(t,s){return s.toUpperCase()});if(o=="float"){o=d?"styleFloat":"cssFloat"}if(r.currentStyle&&q){return r.currentStyle[o]}return r.style[o]},setStyles:function(u,v){var q=this,r=q.settings,p;p=r.update_styles;r.update_styles=0;k(v,function(o,s){q.setStyle(u,s,o)});r.update_styles=p;if(r.update_styles){q.setAttrib(u,r.cssText)}},setAttrib:function(q,r,o){var p=this;if(!q||!r){return}if(p.settings.strict){r=r.toLowerCase()}return this.run(q,function(u){var t=p.settings;switch(r){case"style":if(!j(o,"string")){k(o,function(s,x){p.setStyle(u,x,s)});return}if(t.keep_values){if(o&&!p._isRes(o)){u.setAttribute("_mce_style",o,2)}else{u.removeAttribute("_mce_style",2)}}u.style.cssText=o;break;case"class":u.className=o||"";break;case"src":case"href":if(t.keep_values){if(t.url_converter){o=t.url_converter.call(t.url_converter_scope||p,o,r,u)}p.setAttrib(u,"_mce_"+r,o,2)}break;case"shape":u.setAttribute("_mce_style",o);break}if(j(o)&&o!==null&&o.length!==0){u.setAttribute(r,""+o,2)}else{u.removeAttribute(r,2)}})},setAttribs:function(q,r){var p=this;return this.run(q,function(o){k(r,function(s,t){p.setAttrib(o,t,s)})})},getAttrib:function(r,s,q){var o,p=this;r=p.get(r);if(!r||r.nodeType!==1){return false}if(!j(q)){q=""}if(/^(src|href|style|coords|shape)$/.test(s)){o=r.getAttribute("_mce_"+s);if(o){return o}}if(d&&p.props[s]){o=r[p.props[s]];o=o&&o.nodeValue?o.nodeValue:o}if(!o){o=r.getAttribute(s,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(s)){if(r[p.props[s]]===true&&o===""){return s}return o?s:""}if(r.nodeName==="FORM"&&r.getAttributeNode(s)){return r.getAttributeNode(s).nodeValue}if(s==="style"){o=o||r.style.cssText;if(o){o=p.serializeStyle(p.parseStyle(o),r.nodeName);if(p.settings.keep_values&&!p._isRes(o)){r.setAttribute("_mce_style",o)}}}if(i&&s==="class"&&o){o=o.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(d){switch(s){case"rowspan":case"colspan":if(o===1){o=""}break;case"size":if(o==="+0"||o===20||o===0){o=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(o===0){o=""}break;case"hspace":if(o===-1){o=""}break;case"maxlength":case"tabindex":if(o===32768||o===2147483647||o==="32768"){o=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(o===65535){return s}return q;case"shape":o=o.toLowerCase();break;default:if(s.indexOf("on")===0&&o){o=(""+o).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1")}}}return(o!==undefined&&o!==null&&o!=="")?""+o:q},getPos:function(A,s){var p=this,o=0,z=0,u,v=p.doc,q;A=p.get(A);s=s||v.body;if(A){if(d&&!p.stdMode){A=A.getBoundingClientRect();u=p.boxModel?v.documentElement:v.body;o=p.getStyle(p.select("html")[0],"borderWidth");o=(o=="medium"||p.boxModel&&!p.isIE6)&&2||o;A.top+=p.win.self!=p.win.top?2:0;return{x:A.left+u.scrollLeft-o,y:A.top+u.scrollTop-o}}q=A;while(q&&q!=s&&q.nodeType){o+=q.offsetLeft||0;z+=q.offsetTop||0;q=q.offsetParent}q=A.parentNode;while(q&&q!=s&&q.nodeType){o-=q.scrollLeft||0;z-=q.scrollTop||0;q=q.parentNode}}return{x:o,y:z}},parseStyle:function(r){var u=this,v=u.settings,x={};if(!r){return x}function p(D,A,C){var z,B,o,y;z=x[D+"-top"+A];if(!z){return}B=x[D+"-right"+A];if(z!=B){return}o=x[D+"-bottom"+A];if(B!=o){return}y=x[D+"-left"+A];if(o!=y){return}x[C]=y;delete x[D+"-top"+A];delete x[D+"-right"+A];delete x[D+"-bottom"+A];delete x[D+"-left"+A]}function q(y,s,o,A){var z;z=x[s];if(!z){return}z=x[o];if(!z){return}z=x[A];if(!z){return}x[y]=x[s]+" "+x[o]+" "+x[A];delete x[s];delete x[o];delete x[A]}r=r.replace(/&(#?[a-z0-9]+);/g,"&$1_MCE_SEMI_");k(r.split(";"),function(s){var o,t=[];if(s){s=s.replace(/_MCE_SEMI_/g,";");s=s.replace(/url\([^\)]+\)/g,function(y){t.push(y);return"url("+t.length+")"});s=s.split(":");o=m.trim(s[1]);o=o.replace(/url\(([^\)]+)\)/g,function(z,y){return t[parseInt(y)-1]});o=o.replace(/rgb\([^\)]+\)/g,function(y){return u.toHex(y)});if(v.url_converter){o=o.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g,function(y,z){return"url("+v.url_converter.call(v.url_converter_scope||u,u.decode(z),"style",null)+")"})}x[m.trim(s[0]).toLowerCase()]=o}});p("border","","border");p("border","-width","border-width");p("border","-color","border-color");p("border","-style","border-style");p("padding","","padding");p("margin","","margin");q("border","border-width","border-style","border-color");if(d){if(x.border=="medium none"){x.border=""}}return x},serializeStyle:function(v,p){var q=this,r="";function u(s,o){if(o&&s){if(o.indexOf("-")===0){return}switch(o){case"font-weight":if(s==700){s="bold"}break;case"color":case"background-color":s=s.toLowerCase();break}r+=(r?" ":"")+o+": "+s+";"}}if(p&&q._styles){k(q._styles["*"],function(o){u(v[o],o)});k(q._styles[p.toLowerCase()],function(o){u(v[o],o)})}else{k(v,u)}return r},loadCSS:function(o){var q=this,r=q.doc,p;if(!o){o=""}p=q.select("head")[0];k(o.split(","),function(s){var t;if(q.files[s]){return}q.files[s]=true;t=q.create("link",{rel:"stylesheet",href:m._addVer(s)});if(d&&r.documentMode){t.onload=function(){r.recalc();t.onload=null}}p.appendChild(t)})},addClass:function(o,p){return this.run(o,function(q){var r;if(!p){return 0}if(this.hasClass(q,p)){return q.className}r=this.removeClass(q,p);return q.className=(r!=""?(r+" "):"")+p})},removeClass:function(q,r){var o=this,p;return o.run(q,function(t){var s;if(o.hasClass(t,r)){if(!p){p=new RegExp("(^|\\s+)"+r+"(\\s+|$)","g")}s=t.className.replace(p," ");s=m.trim(s!=" "?s:"");t.className=s;if(!s){t.removeAttribute("class")}return s}return t.className})},hasClass:function(p,o){p=this.get(p);if(!p||!o){return false}return(" "+p.className+" ").indexOf(" "+o+" ")!==-1},show:function(o){return this.setStyle(o,"display","block")},hide:function(o){return this.setStyle(o,"display","none")},isHidden:function(o){o=this.get(o);return !o||o.style.display=="none"||this.getStyle(o,"display")=="none"},uniqueId:function(o){return(!o?"mce_":o)+(this.counter++)},setHTML:function(q,p){var o=this;return this.run(q,function(v){var r,t,s,z,u,r;p=o.processHTML(p);if(d){function y(){while(v.firstChild){v.firstChild.removeNode()}try{v.innerHTML="
    "+p;v.removeChild(v.firstChild)}catch(x){r=o.create("div");r.innerHTML="
    "+p;k(r.childNodes,function(B,A){if(A){v.appendChild(B)}})}}if(o.settings.fix_ie_paragraphs){p=p.replace(/

    <\/p>|]+)><\/p>|/gi,' 

    ')}y();if(o.settings.fix_ie_paragraphs){s=v.getElementsByTagName("p");for(t=s.length-1,r=0;t>=0;t--){z=s[t];if(!z.hasChildNodes()){if(!z._mce_keep){r=1;break}z.removeAttribute("_mce_keep")}}}if(r){p=p.replace(/

    ]+)>|

    /ig,'

    ');p=p.replace(/<\/p>/g,"
    ");y();if(o.settings.fix_ie_paragraphs){s=v.getElementsByTagName("DIV");for(t=s.length-1;t>=0;t--){z=s[t];if(z._mce_tmp){u=o.doc.createElement("p");z.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi,function(A,x){var B;if(x!=="_mce_tmp"){B=z.getAttribute(x);if(!B&&x==="class"){B=z.className}u.setAttribute(x,B)}});for(r=0;r]+)\/>|/gi,"");if(q.keep_values){if(/)/g,"\n");t=t.replace(/^[\r\n]*|[\r\n]*$/g,"");t=t.replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g,"");return t}r=r.replace(/]+|)>([\s\S]*?)<\/script>/gi,function(s,x,t){if(!x){x=' type="text/javascript"'}x=x.replace(/src=\"([^\"]+)\"?/i,function(y,z){if(q.url_converter){z=p.encode(q.url_converter.call(q.url_converter_scope||p,p.decode(z),"src","script"))}return'_mce_src="'+z+'"'});if(m.trim(t)){v.push(o(t));t=""}return""+t+""});r=r.replace(/]+|)>([\s\S]*?)<\/style>/gi,function(s,x,t){if(t){v.push(o(t));t=""}return""+t+""});r=r.replace(/]+|)>([\s\S]*?)<\/noscript>/g,function(s,x,t){return""})}r=r.replace(//g,"");function u(s){return s.replace(h,function(y,z,x,t){return"<"+z+x.replace(l,function(B,A,E,D,C){var F;A=A.toLowerCase();E=E||D||C||"";if(e[A]){if(E==="false"||E==="0"){return}return A+'="'+A+'"'}if(f[A]&&x.indexOf("_mce_"+A)==-1){F=p.decode(E);if(q.url_converter&&(A=="src"||A=="href")){F=q.url_converter.call(q.url_converter_scope||p,F,A,z)}if(A=="style"){F=p.serializeStyle(p.parseStyle(F),A)}return A+'="'+E+'" _mce_'+A+'="'+p.encode(F)+'"'}return B})+t+">"})}r=u(r);r=r.replace(/MCE_SCRIPT:([0-9]+)/g,function(t,s){return v[s]})}return r},getOuterHTML:function(o){var p;o=this.get(o);if(!o){return null}if(o.outerHTML!==undefined){return o.outerHTML}p=(o.ownerDocument||this.doc).createElement("body");p.appendChild(o.cloneNode(true));return p.innerHTML},setOuterHTML:function(r,p,s){var o=this;function q(u,t,x){var y,v;v=x.createElement("body");v.innerHTML=t;y=v.lastChild;while(y){o.insertAfter(y.cloneNode(true),u);y=y.previousSibling}o.remove(u)}return this.run(r,function(u){u=o.get(u);if(u.nodeType==1){s=s||u.ownerDocument||o.doc;if(d){try{if(d&&u.nodeType==1){u.outerHTML=p}else{q(u,p,s)}}catch(t){q(u,p,s)}}else{q(u,p,s)}}})},decode:function(p){var q,r,o;if(/&[\w#]+;/.test(p)){q=this.doc.createElement("div");q.innerHTML=p;r=q.firstChild;o="";if(r){do{o+=r.nodeValue}while(r=r.nextSibling)}return o||p}return p},encode:function(o){return(""+o).replace(n,function(p){return c[p]})},insertAfter:function(o,p){p=this.get(p);return this.run(o,function(r){var q,s;q=p.parentNode;s=p.nextSibling;if(s){q.insertBefore(r,s)}else{q.appendChild(r)}return r})},isBlock:function(o){if(o.nodeType&&o.nodeType!==1){return false}o=o.nodeName||o;return a.test(o)},replace:function(s,r,p){var q=this;if(j(r,"array")){s=s.cloneNode(true)}return q.run(r,function(t){if(p){k(m.grep(t.childNodes),function(o){s.appendChild(o)})}return t.parentNode.replaceChild(s,t)})},rename:function(r,o){var q=this,p;if(r.nodeName!=o.toUpperCase()){p=q.create(o);k(q.getAttribs(r),function(s){q.setAttrib(p,s.nodeName,q.getAttrib(r,s.nodeName))});q.replace(p,r,1)}return p||r},findCommonAncestor:function(q,o){var r=q,p;while(r){p=o;while(p&&r!=p){p=p.parentNode}if(r==p){break}r=r.parentNode}if(!r&&q.ownerDocument){return q.ownerDocument.documentElement}return r},toHex:function(o){var q=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(o);function p(r){r=parseInt(r).toString(16);return r.length>1?r:"0"+r}if(q){o="#"+p(q[1])+p(q[2])+p(q[3]);return o}return o},getClasses:function(){var s=this,o=[],r,u={},v=s.settings.class_filter,q;if(s.classes){return s.classes}function x(t){k(t.imports,function(y){x(y)});k(t.cssRules||t.rules,function(y){switch(y.type||1){case 1:if(y.selectorText){k(y.selectorText.split(","),function(z){z=z.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(z)||!/\.[\w\-]+$/.test(z)){return}q=z;z=z.replace(/.*\.([a-z0-9_\-]+).*/i,"$1");if(v&&!(z=v(z,q))){return}if(!u[z]){o.push({"class":z});u[z]=1}})}break;case 3:x(y.styleSheet);break}})}try{k(s.doc.styleSheets,x)}catch(p){}if(o.length>0){s.classes=o}return o},run:function(u,r,q){var p=this,v;if(p.doc&&typeof(u)==="string"){u=p.get(u)}if(!u){return false}q=q||this;if(!u.nodeType&&(u.length||u.length===0)){v=[];k(u,function(s,o){if(s){if(typeof(s)=="string"){s=p.doc.getElementById(s)}v.push(r.call(q,s,o))}});return v}return r.call(q,u)},getAttribs:function(q){var p;q=this.get(q);if(!q){return[]}if(d){p=[];if(q.nodeName=="OBJECT"){return q.attributes}if(q.nodeName==="OPTION"&&this.getAttrib(q,"selected")){p.push({specified:1,nodeName:"selected"})}q.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(o){p.push({specified:1,nodeName:o})});return p}return q.attributes},destroy:function(p){var o=this;if(o.events){o.events.destroy()}o.win=o.doc=o.root=o.events=null;if(!p){m.removeUnload(o.destroy)}},createRng:function(){var o=this.doc;return o.createRange?o.createRange():new m.dom.Range(this)},nodeIndex:function(r,s){var o=0,q,p;if(r){for(r=r.previousSibling,q=r;r;r=r.previousSibling){p=r.nodeType;if(s&&p==3){if(r.nodeValue.length>0&&(q.nodeType!=p||q.nodeValue.length===0)){o++}}else{o++}q=r}}return o},split:function(u,s,y){var z=this,o=z.createRng(),v,q,x;function p(A){var t,r=A.childNodes;if(A.nodeType==1&&A.getAttribute("_mce_type")=="bookmark"){return}for(t=r.length-1;t>=0;t--){p(r[t])}if(A.nodeType!=9){if(A.nodeType==3&&A.nodeValue.length>0){return}if(A.nodeType==1){r=A.childNodes;if(r.length==1&&r[0]&&r[0].nodeType==1&&r[0].getAttribute("_mce_type")=="bookmark"){A.parentNode.insertBefore(r[0],A)}if(r.length||/^(br|hr|input|img)$/i.test(A.nodeName)){return}}z.remove(A)}return A}if(u&&s){o.setStart(u.parentNode,z.nodeIndex(u));o.setEnd(s.parentNode,z.nodeIndex(s));v=o.extractContents();o=z.createRng();o.setStart(s.parentNode,z.nodeIndex(s)+1);o.setEnd(u.parentNode,z.nodeIndex(u)+1);q=o.extractContents();x=u.parentNode;x.insertBefore(p(v),u);if(y){x.replaceChild(y,s)}else{x.insertBefore(s,u)}x.insertBefore(p(q),u);z.remove(u);return y||s}},bind:function(s,o,r,q){var p=this;if(!p.events){p.events=new m.dom.EventUtils()}return p.events.add(s,o,r,q||this)},unbind:function(r,o,q){var p=this;if(!p.events){p.events=new m.dom.EventUtils()}return p.events.remove(r,o,q)},_findSib:function(r,o,p){var q=this,s=o;if(r){if(j(s,"string")){s=function(t){return q.is(t,o)}}for(r=r[p];r;r=r[p]){if(s(r)){return r}}}return null},_isRes:function(o){return/^(top|left|bottom|right|width|height)/i.test(o)||/;\s*(top|left|bottom|right|width|height)/i.test(o)}});m.DOM=new m.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var N=this,e=c.doc,S=0,E=1,j=2,D=true,R=false,U="startOffset",h="startContainer",P="endContainer",z="endOffset",k=tinymce.extend,n=c.nodeIndex;k(N,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:D,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:I,setEndBefore:J,setEndAfter:u,collapse:A,selectNode:x,selectNodeContents:F,compareBoundaryPoints:v,deleteContents:p,extractContents:H,cloneContents:d,insertNode:C,surroundContents:M,cloneRange:K});function q(V,t){B(D,V,t)}function s(V,t){B(R,V,t)}function g(t){q(t.parentNode,n(t))}function I(t){q(t.parentNode,n(t)+1)}function J(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function A(t){if(t){N[P]=N[h];N[z]=N[U]}else{N[h]=N[P];N[U]=N[z]}N.collapsed=D}function x(t){g(t);u(t)}function F(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(W,X){var Z=N[h],Y=N[U],V=N[P],t=N[z];if(W===0){return G(Z,Y,Z,Y)}if(W===1){return G(Z,Y,V,t)}if(W===2){return G(V,t,V,t)}if(W===3){return G(V,t,Z,Y)}}function p(){m(j)}function H(){return m(S)}function d(){return m(E)}function C(Y){var V=this[h],t=this[U],X,W;if((V.nodeType===3||V.nodeType===4)&&V.nodeValue){if(!t){V.parentNode.insertBefore(Y,V)}else{if(t>=V.nodeValue.length){c.insertAfter(Y,V)}else{X=V.splitText(t);V.parentNode.insertBefore(Y,X)}}}else{if(V.childNodes.length>0){W=V.childNodes[t]}if(W){V.insertBefore(Y,W)}else{V.appendChild(Y)}}}function M(V){var t=N.extractContents();N.insertNode(V);V.appendChild(t);N.selectNode(V)}function K(){return k(new b(c),{startContainer:N[h],startOffset:N[U],endContainer:N[P],endOffset:N[z],collapsed:N.collapsed,commonAncestorContainer:N.commonAncestorContainer})}function O(t,V){var W;if(t.nodeType==3){return t}if(V<0){return t}W=t.firstChild;while(W&&V>0){--V;W=W.nextSibling}if(W){return W}return t}function l(){return(N[h]==N[P]&&N[U]==N[z])}function G(X,Z,V,Y){var aa,W,t,ab,ad,ac;if(X==V){if(Z==Y){return 0}if(Z0){N.collapse(V)}}else{N.collapse(V)}N.collapsed=l();N.commonAncestorContainer=c.findCommonAncestor(N[h],N[P])}function m(ab){var aa,X=0,ad=0,V,Z,W,Y,t,ac;if(N[h]==N[P]){return f(ab)}for(aa=N[P],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[h]){return r(aa,ab)}++X}for(aa=N[h],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[P]){return T(aa,ab)}++ad}Z=ad-X;W=N[h];while(Z>0){W=W.parentNode;Z--}Y=N[P];while(Z<0){Y=Y.parentNode;Z++}for(t=W.parentNode,ac=Y.parentNode;t!=ac;t=t.parentNode,ac=ac.parentNode){W=t;Y=ac}return o(W,Y,ab)}function f(Z){var ab,Y,X,aa,t,W,V;if(Z!=j){ab=e.createDocumentFragment()}if(N[U]==N[z]){return ab}if(N[h].nodeType==3){Y=N[h].nodeValue;X=Y.substring(N[U],N[z]);if(Z!=E){N[h].deleteData(N[U],N[z]-N[U]);N.collapse(D)}if(Z==j){return}ab.appendChild(e.createTextNode(X));return ab}aa=O(N[h],N[U]);t=N[z]-N[U];while(t>0){W=aa.nextSibling;V=y(aa,Z);if(ab){ab.appendChild(V)}--t;aa=W}if(Z!=E){N.collapse(D)}return ab}function r(ab,Y){var aa,Z,V,t,X,W;if(Y!=j){aa=e.createDocumentFragment()}Z=i(ab,Y);if(aa){aa.appendChild(Z)}V=n(ab);t=V-N[U];if(t<=0){if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}Z=ab.previousSibling;while(t>0){X=Z.previousSibling;W=y(Z,Y);if(aa){aa.insertBefore(W,aa.firstChild)}--t;Z=X}if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}function T(Z,Y){var ab,V,aa,t,X,W;if(Y!=j){ab=e.createDocumentFragment()}aa=Q(Z,Y);if(ab){ab.appendChild(aa)}V=n(Z);++V;t=N[z]-V;aa=Z.nextSibling;while(t>0){X=aa.nextSibling;W=y(aa,Y);if(ab){ab.appendChild(W)}--t;aa=X}if(Y!=E){N.setStartAfter(Z);N.collapse(D)}return ab}function o(Z,t,ac){var W,ae,Y,aa,ab,V,ad,X;if(ac!=j){ae=e.createDocumentFragment()}W=Q(Z,ac);if(ae){ae.appendChild(W)}Y=Z.parentNode;aa=n(Z);ab=n(t);++aa;V=ab-aa;ad=Z.nextSibling;while(V>0){X=ad.nextSibling;W=y(ad,ac);if(ae){ae.appendChild(W)}ad=X;--V}W=i(t,ac);if(ae){ae.appendChild(W)}if(ac!=E){N.setStartAfter(Z);N.collapse(D)}return ae}function i(aa,ab){var W=O(N[P],N[z]-1),ac,Z,Y,t,V,X=W!=N[P];if(W==aa){return L(W,X,R,ab)}ac=W.parentNode;Z=L(ac,R,R,ab);while(ac){while(W){Y=W.previousSibling;t=L(W,X,R,ab);if(ab!=j){Z.insertBefore(t,Z.firstChild)}X=D;W=Y}if(ac==aa){return Z}W=ac.previousSibling;ac=ac.parentNode;V=L(ac,R,R,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function Q(aa,ab){var X=O(N[h],N[U]),Y=X!=N[h],ac,Z,W,t,V;if(X==aa){return L(X,Y,D,ab)}ac=X.parentNode;Z=L(ac,R,D,ab);while(ac){while(X){W=X.nextSibling;t=L(X,Y,D,ab);if(ab!=j){Z.appendChild(t)}Y=D;X=W}if(ac==aa){return Z}X=ac.nextSibling;ac=ac.parentNode;V=L(ac,R,D,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function L(t,Y,ab,ac){var X,W,Z,V,aa;if(Y){return y(t,ac)}if(t.nodeType==3){X=t.nodeValue;if(ab){V=N[U];W=X.substring(V);Z=X.substring(0,V)}else{V=N[z];W=X.substring(0,V);Z=X.substring(V)}if(ac!=E){t.nodeValue=Z}if(ac==j){return}aa=t.cloneNode(R);aa.nodeValue=W;return aa}if(ac==j){return}return t.cloneNode(R)}function y(V,t){if(t!=j){return t==E?V.cloneNode(D):V}V.parentNode.removeChild(V)}}a.Range=b})(tinymce.dom);(function(){function a(h){var j=this,k="\uFEFF",f,i,e=h.dom,d=true,g=false;function c(m,l){if(m&&l){if(m.item&&l.item&&m.item(0)===l.item(0)){return d}if(m.isEqual&&l.isEqual&&l.isEqual(m)){try{f.startContainer.nextSibling;return d}catch(n){}}}return g}function b(){var p=h.getRng(),l=e.createRng(),m,n,r,q;n=p.item?p.item(0):p.parentElement();if(n.ownerDocument!=e.doc){return l}if(p.item||!n.hasChildNodes()){l.setStart(n.parentNode,e.nodeIndex(n));l.setEnd(l.startContainer,l.startOffset+1);return l}m=p.duplicate();r=h.isCollapsed();p.collapse();p.pasteHTML('");if(!r){m.collapse(g);m.pasteHTML('")}function o(x){var t,v,s,u;s=e.get("_mce_"+(x?"start":"end"));u=s.previousSibling;if(u&&u.nodeType==3){t=u;v=t.nodeValue.length;e.remove(s);u=t.nextSibling;if(u&&u.nodeType==3){q=d;t.appendData(u.nodeValue);e.remove(u)}}else{u=s.nextSibling;if(u&&u.nodeType==3){t=u;v=0}else{if(u){v=e.nodeIndex(u)-1}else{v=e.nodeIndex(s)}t=s.parentNode}e.remove(s)}if(x){l.setStart(t,v)}if(!x||r){l.setEnd(t,v)}}o(d);if(!r){o(g)}if(q){j.addRange(l)}return l}this.addRange=function(m){var u,A,z=h.dom.doc,s=z.body,v,o,y,p,t,l,q,r,x,n;this.destroy();y=m.startContainer;p=m.startOffset;t=m.endContainer;l=m.endOffset;u=s.createTextRange();if(y==z||t==z){u=s.createTextRange();u.collapse();u.select();return}if(y.nodeType==1&&y.hasChildNodes()){r=y.childNodes.length-1;if(p>r){x=1;y=y.childNodes[r]}else{y=y.childNodes[p]}if(y.nodeType==3){p=0}}if(t.nodeType==1&&t.hasChildNodes()){r=t.childNodes.length-1;if(l==0){n=1;t=t.childNodes[0]}else{t=t.childNodes[Math.min(r,l-1)];if(t.nodeType==3){l=t.nodeValue.length}}}if(y==t&&y.nodeType==1){if(/^(IMG|TABLE)$/.test(y.nodeName)&&p!=l){u=s.createControlRange();u.addElement(y)}else{u=s.createTextRange();if(!y.hasChildNodes()&&y.canHaveHTML){y.innerHTML=k}u.moveToElementText(y);if(y.innerHTML==k){u.collapse(d);y.removeChild(y.firstChild)}}if(p==l){u.collapse(l<=m.endContainer.childNodes.length-1)}u.select();u.scrollIntoView();return}u=s.createTextRange();q=z.createElement("span");q.innerHTML=" ";if(y.nodeType==3){if(x){e.insertAfter(q,y)}else{y.parentNode.insertBefore(q,y)}u.moveToElementText(q);q.parentNode.removeChild(q);u.move("character",p)}else{u.moveToElementText(y);if(x){u.collapse(g)}}if(y==t&&y.nodeType==3){u.moveEnd("character",l-p);u.select();u.scrollIntoView();return}A=s.createTextRange();if(t.nodeType==3){t.parentNode.insertBefore(q,t);A.moveToElementText(q);q.parentNode.removeChild(q);A.move("character",l);u.setEndPoint("EndToStart",A)}else{A.moveToElementText(t);A.collapse(!!n);u.setEndPoint("EndToEnd",A)}u.select();u.scrollIntoView()};this.getRangeAt=function(){if(!f||!c(i,h.getRng())){f=b();i=h.getRng()}return f};this.destroy=function(){i=f=null};if(h.dom.boxModel){(function(){var r=e.doc,m=r.body,o,p;r.documentElement.unselectable=d;function q(s,v){var t=m.createTextRange();try{t.moveToPoint(s,v)}catch(u){t=null}return t}function n(t){var s;if(t.button){s=q(t.x,t.y);if(s){if(s.compareEndPoints("StartToStart",p)>0){s.setEndPoint("StartToStart",p)}else{s.setEndPoint("EndToEnd",p)}s.select()}}else{l()}}function l(){e.unbind(r,"mouseup",l);e.unbind(r,"mousemove",n);o=0}e.bind(r,"mousedown",function(s){if(s.target.nodeName==="HTML"){if(o){l()}o=1;p=q(s.x,s.y);if(p){e.bind(r,"mouseup",l);e.bind(r,"mousemove",n);p.select()}}})})()}}tinymce.dom.TridentSelection=a})();(function(){var p=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,i=0,d=Object.prototype.toString,n=false;var b=function(E,t,B,v){B=B||[];var e=t=t||document;if(t.nodeType!==1&&t.nodeType!==9){return[]}if(!E||typeof E!=="string"){return B}var C=[],D,z,H,G,A,s,r=true,x=o(t);p.lastIndex=0;while((D=p.exec(E))!==null){C.push(D[1]);if(D[2]){s=RegExp.rightContext;break}}if(C.length>1&&j.exec(E)){if(C.length===2&&f.relative[C[0]]){z=g(C[0]+C[1],t)}else{z=f.relative[C[0]]?[t]:b(C.shift(),t);while(C.length){E=C.shift();if(f.relative[E]){E+=C.shift()}z=g(E,z)}}}else{if(!v&&C.length>1&&t.nodeType===9&&!x&&f.match.ID.test(C[0])&&!f.match.ID.test(C[C.length-1])){var I=b.find(C.shift(),t,x);t=I.expr?b.filter(I.expr,I.set)[0]:I.set[0]}if(t){var I=v?{expr:C.pop(),set:a(v)}:b.find(C.pop(),C.length===1&&(C[0]==="~"||C[0]==="+")&&t.parentNode?t.parentNode:t,x);z=I.expr?b.filter(I.expr,I.set):I.set;if(C.length>0){H=a(z)}else{r=false}while(C.length){var u=C.pop(),y=u;if(!f.relative[u]){u=""}else{y=C.pop()}if(y==null){y=t}f.relative[u](H,y,x)}}else{H=C=[]}}if(!H){H=z}if(!H){throw"Syntax error, unrecognized expression: "+(u||E)}if(d.call(H)==="[object Array]"){if(!r){B.push.apply(B,H)}else{if(t&&t.nodeType===1){for(var F=0;H[F]!=null;F++){if(H[F]&&(H[F]===true||H[F].nodeType===1&&h(t,H[F]))){B.push(z[F])}}}else{for(var F=0;H[F]!=null;F++){if(H[F]&&H[F].nodeType===1){B.push(z[F])}}}}}else{a(H,B)}if(s){b(s,e,B,v);b.uniqueSort(B)}return B};b.uniqueSort=function(r){if(c){n=false;r.sort(c);if(n){for(var e=1;e":function(x,r,y){var u=typeof r==="string";if(u&&!/\W/.test(r)){r=y?r:r.toUpperCase();for(var s=0,e=x.length;s=0)){if(!s){e.push(v)}}else{if(s){r[u]=false}}}}return false},ID:function(e){return e[1].replace(/\\/g,"")},TAG:function(r,e){for(var s=0;e[s]===false;s++){}return e[s]&&o(e[s])?r[1]:r[1].toUpperCase()},CHILD:function(e){if(e[1]=="nth"){var r=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(e[2]=="even"&&"2n"||e[2]=="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(r[1]+(r[2]||1))-0;e[3]=r[3]-0}e[0]=i++;return e},ATTR:function(u,r,s,e,v,x){var t=u[1].replace(/\\/g,"");if(!x&&f.attrMap[t]){u[1]=f.attrMap[t]}if(u[2]==="~="){u[4]=" "+u[4]+" "}return u},PSEUDO:function(u,r,s,e,v){if(u[1]==="not"){if(u[3].match(p).length>1||/^\w/.test(u[3])){u[3]=b(u[3],null,null,r)}else{var t=b.filter(u[3],r,s,true^v);if(!s){e.push.apply(e,t)}return false}}else{if(f.match.POS.test(u[0])||f.match.CHILD.test(u[0])){return true}}return u},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){e.parentNode.selectedIndex;return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(s,r,e){return !!b(e[3],s).length},header:function(e){return/h\d/i.test(e.nodeName)},text:function(e){return"text"===e.type},radio:function(e){return"radio"===e.type},checkbox:function(e){return"checkbox"===e.type},file:function(e){return"file"===e.type},password:function(e){return"password"===e.type},submit:function(e){return"submit"===e.type},image:function(e){return"image"===e.type},reset:function(e){return"reset"===e.type},button:function(e){return"button"===e.type||e.nodeName.toUpperCase()==="BUTTON"},input:function(e){return/input|select|textarea|button/i.test(e.nodeName)}},setFilters:{first:function(r,e){return e===0},last:function(s,r,e,t){return r===t.length-1},even:function(r,e){return e%2===0},odd:function(r,e){return e%2===1},lt:function(s,r,e){return re[3]-0},nth:function(s,r,e){return e[3]-0==r},eq:function(s,r,e){return e[3]-0==r}},filter:{PSEUDO:function(x,s,t,y){var r=s[1],u=f.filters[r];if(u){return u(x,t,s,y)}else{if(r==="contains"){return(x.textContent||x.innerText||"").indexOf(s[3])>=0}else{if(r==="not"){var v=s[3];for(var t=0,e=v.length;t=0)}}},ID:function(r,e){return r.nodeType===1&&r.getAttribute("id")===e},TAG:function(r,e){return(e==="*"&&r.nodeType===1)||r.nodeName===e},CLASS:function(r,e){return(" "+(r.className||r.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(v,t){var s=t[1],e=f.attrHandle[s]?f.attrHandle[s](v):v[s]!=null?v[s]:v.getAttribute(s),x=e+"",u=t[2],r=t[4];return e==null?u==="!=":u==="="?x===r:u==="*="?x.indexOf(r)>=0:u==="~="?(" "+x+" ").indexOf(r)>=0:!r?x&&e!==false:u==="!="?x!=r:u==="^="?x.indexOf(r)===0:u==="$="?x.substr(x.length-r.length)===r:u==="|="?x===r||x.substr(0,r.length+1)===r+"-":false},POS:function(u,r,s,v){var e=r[2],t=f.setFilters[e];if(t){return t(u,s,r,v)}}}};var j=f.match.POS;for(var l in f.match){f.match[l]=new RegExp(f.match[l].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var a=function(r,e){r=Array.prototype.slice.call(r);if(e){e.push.apply(e,r);return e}return r};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(k){a=function(u,t){var r=t||[];if(d.call(u)==="[object Array]"){Array.prototype.push.apply(r,u)}else{if(typeof u.length==="number"){for(var s=0,e=u.length;s";var e=document.documentElement;e.insertBefore(r,e.firstChild);if(!!document.getElementById(s)){f.find.ID=function(u,v,x){if(typeof v.getElementById!=="undefined"&&!x){var t=v.getElementById(u[1]);return t?t.id===u[1]||typeof t.getAttributeNode!=="undefined"&&t.getAttributeNode("id").nodeValue===u[1]?[t]:undefined:[]}};f.filter.ID=function(v,t){var u=typeof v.getAttributeNode!=="undefined"&&v.getAttributeNode("id");return v.nodeType===1&&u&&u.nodeValue===t}}e.removeChild(r)})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){f.find.TAG=function(r,v){var u=v.getElementsByTagName(r[1]);if(r[1]==="*"){var t=[];for(var s=0;u[s];s++){if(u[s].nodeType===1){t.push(u[s])}}u=t}return u}}e.innerHTML="";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){f.attrHandle.href=function(r){return r.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var e=b,s=document.createElement("div");s.innerHTML="

    ";if(s.querySelectorAll&&s.querySelectorAll(".TEST").length===0){return}b=function(x,v,t,u){v=v||document;if(!u&&v.nodeType===9&&!o(v)){try{return a(v.querySelectorAll(x),t)}catch(y){}}return e(x,v,t,u)};for(var r in e){b[r]=e[r]}})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var e=document.createElement("div");e.innerHTML="
    ";if(e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}f.order.splice(1,0,"CLASS");f.find.CLASS=function(r,s,t){if(typeof s.getElementsByClassName!=="undefined"&&!t){return s.getElementsByClassName(r[1])}}})()}function m(r,x,v,B,y,A){var z=r=="previousSibling"&&!A;for(var t=0,s=B.length;t0){u=e;break}}}e=e[r]}B[t]=u}}}var h=document.compareDocumentPosition?function(r,e){return r.compareDocumentPosition(e)&16}:function(r,e){return r!==e&&(r.contains?r.contains(e):true)};var o=function(e){return e.nodeType===9&&e.documentElement.nodeName!=="HTML"||!!e.ownerDocument&&e.ownerDocument.documentElement.nodeName!=="HTML"};var g=function(e,y){var t=[],u="",v,s=y.nodeType?[y]:y;while((v=f.match.PSEUDO.exec(e))){u+=v[0];e=e.replace(f.match.PSEUDO,"")}e=f.relative[e]?e+"*":e;for(var x=0,r=s.length;x=0;h--){k=g[h];if(k.obj===l){j._remove(k.obj,k.name,k.cfunc);k.obj=k.cfunc=null;g.splice(h,1)}}}},cancel:function(g){if(!g){return false}this.stop(g);return this.prevent(g)},stop:function(g){if(g.stopPropagation){g.stopPropagation()}else{g.cancelBubble=true}return false},prevent:function(g){if(g.preventDefault){g.preventDefault()}else{g.returnValue=false}return false},destroy:function(){var g=this;f(g.events,function(j,h){g._remove(j.obj,j.name,j.cfunc);j.obj=j.cfunc=null});g.events=[];g=null},_add:function(h,i,g){if(h.attachEvent){h.attachEvent("on"+i,g)}else{if(h.addEventListener){h.addEventListener(i,g,false)}else{h["on"+i]=g}}},_remove:function(i,j,h){if(i){try{if(i.detachEvent){i.detachEvent("on"+j,h)}else{if(i.removeEventListener){i.removeEventListener(j,h,false)}else{i["on"+j]=null}}}catch(g){}}},_pageInit:function(h){var g=this;if(g.domLoaded){return}g.domLoaded=true;f(g.inits,function(i){i()});g.inits=[]},_wait:function(i){var g=this,h=i.document;if(i.tinyMCE_GZ&&tinyMCE_GZ.loaded){g.domLoaded=1;return}if(h.attachEvent){h.attachEvent("onreadystatechange",function(){if(h.readyState==="complete"){h.detachEvent("onreadystatechange",arguments.callee);g._pageInit(i)}});if(h.documentElement.doScroll&&i==i.top){(function(){if(g.domLoaded){return}try{h.documentElement.doScroll("left")}catch(j){setTimeout(arguments.callee,0);return}g._pageInit(i)})()}}else{if(h.addEventListener){g._add(i,"DOMContentLoaded",function(){g._pageInit(i)})}}g._add(i,"load",function(){g._pageInit(i)})},_stoppers:{preventDefault:function(){this.returnValue=false},stopPropagation:function(){this.cancelBubble=true}}});a=d.dom.Event=new d.dom.EventUtils();a._wait(window);d.addUnload(function(){a.destroy()})})(tinymce);(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j_';if(j.startContainer==k&&j.endContainer==k){k.body.innerHTML=i}else{j.deleteContents();j.insertNode(f.getRng().createContextualFragment(i))}l=f.dom.get("__caret");j=k.createRange();j.setStartBefore(l);j.setEndBefore(l);f.setRng(j);f.dom.remove("__caret")}else{if(j.item){k.execCommand("Delete",false,null);j=f.getRng()}j.pasteHTML(i)}f.onSetContent.dispatch(f,g)},getStart:function(){var f=this,g=f.getRng(),h;if(a){if(g.item){return g.item(0)}g=g.duplicate();g.collapse(1);h=g.parentElement();if(h&&h.nodeName=="BODY"){return h.firstChild||h}return h}else{h=g.startContainer;if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[Math.min(h.childNodes.length-1,g.startOffset)]}if(h&&h.nodeType==3){return h.parentNode}return h}},getEnd:function(){var g=this,h=g.getRng(),i,f;if(a){if(h.item){return h.item(0)}h=h.duplicate();h.collapse(0);i=h.parentElement();if(i&&i.nodeName=="BODY"){return i.lastChild||i}return i}else{i=h.endContainer;f=h.endOffset;if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[f>0?f-1:f]}if(i&&i.nodeType==3){return i.parentNode}return i}},getBookmark:function(q,r){var u=this,m=u.dom,g,j,i,n,h,o,p,l="\uFEFF",s;function f(v,x){var t=0;d(m.select(v),function(z,y){if(z==x){t=y}});return t}if(q==2){function k(){var v=u.getRng(true),t=m.getRoot(),x={};function y(z,B){var F=[],D,H,A=z[B?"startContainer":"endContainer"],E=z[B?"startOffset":"endOffset"],C,G={};if(A.nodeType==1&&A.hasChildNodes()){H=A.childNodes.length-1;G.exclude=(B&&E>H)||(!B&&E==0);if(!B&&E){E--}A=A.childNodes[E>H?H:E];if(A.nodeType==3){E=B?0:A.nodeValue.length}}if(A.nodeType==3){if(r){for(D=A.previousSibling;D&&D.nodeType==3;D=D.previousSibling){E+=D.nodeValue.length}}G.offset=E}for(;A&&A!=t;A=A.parentNode){F.push(u.dom.nodeIndex(A,r))}G.indexes=F;return G}x.start=y(v,true);if(!u.isCollapsed()){x.end=y(v)}return x}return k()}if(q){return{rng:u.getRng()}}g=u.getRng();i=m.uniqueId();n=tinyMCE.activeEditor.selection.isCollapsed();s="overflow:hidden;line-height:0px";if(g.duplicate||g.item){if(!g.item){j=g.duplicate();g.collapse();g.pasteHTML(''+l+"");if(!n){j.collapse(false);j.pasteHTML(''+l+"")}}else{o=g.item(0);h=o.nodeName;return{name:h,index:f(h,o)}}}else{o=u.getNode();h=o.nodeName;if(h=="IMG"){return{name:h,index:f(h,o)}}j=g.cloneRange();if(!n){j.collapse(false);j.insertNode(m.create("span",{_mce_type:"bookmark",id:i+"_end",style:s},l))}g.collapse(true);g.insertNode(m.create("span",{_mce_type:"bookmark",id:i+"_start",style:s},l))}u.moveToBookmark({id:i,keep:1});return{id:i}},moveToBookmark:function(l){var n=this,k=n.dom,i,h,f,m;if(n.tridentSel){n.tridentSel.destroy()}if(l){if(l.start){f=k.createRng();m=k.getRoot();function g(s){var o=l[s?"start":"end"],p,q,r;if(o){for(q=m,p=o.indexes.length-1;p>=0;p--){q=q.childNodes[o.indexes[p]]||q}if(s){if(q.nodeType==3&&o.offset){f.setStart(q,o.offset)}else{if(o.exclude){f.setStartAfter(q)}else{f.setStartBefore(q)}}}else{if(q.nodeType==3&&o.offset){f.setEnd(q,o.offset)}else{if(o.exclude){f.setEndBefore(q)}else{f.setEndAfter(q)}}}}}g(true);g();n.setRng(f)}else{if(l.id){f=k.createRng();function j(u){var p=k.get(l.id+"_"+u),t,o,r,s,q=l.keep;if(p){t=p.parentNode;if(u=="start"){if(!q){o=k.nodeIndex(p)}else{t=p;o=1}f.setStart(t,o);f.setEnd(t,o)}else{if(!q){o=k.nodeIndex(p)}else{t=p;o=1}f.setEnd(t,o)}if(!q){s=p.previousSibling;r=p.nextSibling;d(c.grep(p.childNodes),function(v){if(v.nodeType==3){v.nodeValue=v.nodeValue.replace(/\uFEFF/g,"")}});while(p=k.get(l.id+"_"+u)){k.remove(p,1)}if(s&&r&&s.nodeType==r.nodeType&&s.nodeType==3){o=s.nodeValue.length;s.appendData(r.nodeValue);k.remove(r);if(u=="start"){f.setStart(s,o);f.setEnd(s,o)}else{f.setEnd(s,o)}}}}}j("start");j("end");n.setRng(f)}else{if(l.name){n.select(k.select(l.name)[l.index])}else{if(l.rng){n.setRng(l.rng)}}}}}},select:function(k,j){var i=this,l=i.dom,g=l.createRng(),f;f=l.nodeIndex(k);g.setStart(k.parentNode,f);g.setEnd(k.parentNode,f+1);if(j){function h(m,o){var n=new c.dom.TreeWalker(m,m);do{if(m.nodeType==3&&c.trim(m.nodeValue).length!=0){if(o){g.setStart(m,0)}else{g.setEnd(m,m.nodeValue.length)}return}if(m.nodeName=="BR"){if(o){g.setStartBefore(m)}else{g.setEndBefore(m)}return}}while(m=(o?n.next():n.prev()))}h(k,1);h(k)}i.setRng(g);return k},isCollapsed:function(){var f=this,h=f.getRng(),g=f.getSel();if(!h||h.item){return false}if(h.compareEndPoints){return h.compareEndPoints("StartToEnd",h)===0}return !g||h.collapsed},collapse:function(f){var g=this,h=g.getRng(),i;if(h.item){i=h.item(0);h=this.win.document.body.createTextRange();h.moveToElementText(i)}h.collapse(!!f);g.setRng(h)},getSel:function(){var g=this,f=this.win;return f.getSelection?f.getSelection():f.document.selection},getRng:function(j){var g=this,h,i;if(j&&g.tridentSel){return g.tridentSel.getRangeAt(0)}try{if(h=g.getSel()){i=h.rangeCount>0?h.getRangeAt(0):(h.createRange?h.createRange():g.win.document.createRange())}}catch(f){}if(!i){i=a?g.win.document.body.createTextRange():g.win.document.createRange()}return i},setRng:function(i){var h,g=this;if(!g.tridentSel){h=g.getSel();if(h){h.removeAllRanges();h.addRange(i)}}else{if(i.cloneRange){g.tridentSel.addRange(i);return}try{i.select()}catch(f){}}},setNode:function(g){var f=this;f.setContent(f.dom.getOuterHTML(g));return g},getNode:function(){var g=this,f=g.getRng(),h=g.getSel(),i;if(!a){if(!f){return g.dom.getRoot()}i=f.commonAncestorContainer;if(!f.collapsed){if(f.startContainer==f.endContainer){if(f.startOffset-f.endOffset<2){if(f.startContainer.hasChildNodes()){i=f.startContainer.childNodes[f.startOffset]}}}if(c.isWebKit&&h.anchorNode&&h.anchorNode.nodeType==1){return h.anchorNode.childNodes[h.anchorOffset]}}if(i&&i.nodeType==3){return i.parentNode}return i}return f.item?f.item(0):f.parentElement()},getSelectedBlocks:function(g,f){var i=this,j=i.dom,m,h,l,k=[];m=j.getParent(g||i.getStart(),j.isBlock);h=j.getParent(f||i.getEnd(),j.isBlock);if(m){k.push(m)}if(m&&h&&m!=h){l=m;while((l=l.nextSibling)&&l!=h){if(j.isBlock(l)){k.push(l)}}}if(h&&m!=h){k.push(h)}return k},destroy:function(g){var f=this;f.win=null;if(f.tridentSel){f.tridentSel.destroy()}if(!g){c.removeUnload(f.destroy)}}})})(tinymce);(function(a){a.create("tinymce.dom.XMLWriter",{node:null,XMLWriter:function(c){function b(){var e=document.implementation;if(!e||!e.createDocument){try{return new ActiveXObject("MSXML2.DOMDocument")}catch(d){}try{return new ActiveXObject("Microsoft.XmlDom")}catch(d){}}else{return e.createDocument("","",null)}}this.doc=b();this.valid=a.isOpera||a.isWebKit;this.reset()},reset:function(){var b=this,c=b.doc;if(c.firstChild){c.removeChild(c.firstChild)}b.node=c.appendChild(c.createElement("html"))},writeStartElement:function(c){var b=this;b.node=b.node.appendChild(b.doc.createElement(c))},writeAttribute:function(c,b){if(this.valid){b=b.replace(/>/g,"%MCGT%")}this.node.setAttribute(c,b)},writeEndElement:function(){this.node=this.node.parentNode},writeFullEndElement:function(){var b=this,c=b.node;c.appendChild(b.doc.createTextNode(""));b.node=c.parentNode},writeText:function(b){if(this.valid){b=b.replace(/>/g,"%MCGT%")}this.node.appendChild(this.doc.createTextNode(b))},writeCDATA:function(b){this.node.appendChild(this.doc.createCDATASection(b))},writeComment:function(b){if(a.isIE){b=b.replace(/^\-|\-$/g," ")}this.node.appendChild(this.doc.createComment(b.replace(/\-\-/g," ")))},getContent:function(){var b;b=this.doc.xml||new XMLSerializer().serializeToString(this.doc);b=b.replace(/<\?[^?]+\?>||<\/html>||]+>/g,"");b=b.replace(/ ?\/>/g," />");if(this.valid){b=b.replace(/\%MCGT%/g,">")}return b}})})(tinymce);(function(a){a.create("tinymce.dom.StringWriter",{str:null,tags:null,count:0,settings:null,indent:null,StringWriter:function(b){this.settings=a.extend({indent_char:" ",indentation:0},b);this.reset()},reset:function(){this.indent="";this.str="";this.tags=[];this.count=0},writeStartElement:function(b){this._writeAttributesEnd();this.writeRaw("<"+b);this.tags.push(b);this.inAttr=true;this.count++;this.elementCount=this.count},writeAttribute:function(d,b){var c=this;c.writeRaw(" "+c.encode(d)+'="'+c.encode(b)+'"')},writeEndElement:function(){var b;if(this.tags.length>0){b=this.tags.pop();if(this._writeAttributesEnd(1)){this.writeRaw("")}if(this.settings.indentation>0){this.writeRaw("\n")}}},writeFullEndElement:function(){if(this.tags.length>0){this._writeAttributesEnd();this.writeRaw("");if(this.settings.indentation>0){this.writeRaw("\n")}}},writeText:function(b){this._writeAttributesEnd();this.writeRaw(this.encode(b));this.count++},writeCDATA:function(b){this._writeAttributesEnd();this.writeRaw("");this.count++},writeComment:function(b){this._writeAttributesEnd();this.writeRaw("");this.count++},writeRaw:function(b){this.str+=b},encode:function(b){return b.replace(/[<>&"]/g,function(c){switch(c){case"<":return"<";case">":return">";case"&":return"&";case'"':return"""}return c})},getContent:function(){return this.str},_writeAttributesEnd:function(b){if(!this.inAttr){return}this.inAttr=false;if(b&&this.elementCount==this.count){this.writeRaw(" />");return false}this.writeRaw(">");return true}})})(tinymce);(function(e){var g=e.extend,f=e.each,b=e.util.Dispatcher,d=e.isIE,a=e.isGecko;function c(h){return h.replace(/([?+*])/g,".$1")}e.create("tinymce.dom.Serializer",{Serializer:function(j){var i=this;i.key=0;i.onPreProcess=new b(i);i.onPostProcess=new b(i);try{i.writer=new e.dom.XMLWriter()}catch(h){i.writer=new e.dom.StringWriter()}i.settings=j=g({dom:e.DOM,valid_nodes:0,node_filter:0,attr_filter:0,invalid_attrs:/^(_mce_|_moz_|sizset|sizcache)/,closed:/^(br|hr|input|meta|img|link|param|area)$/,entity_encoding:"named",entities:"160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro",valid_elements:"*[*]",extended_valid_elements:0,invalid_elements:0,fix_table_elements:1,fix_list_elements:true,fix_content_duplication:true,convert_fonts_to_spans:false,font_size_classes:0,apply_source_formatting:0,indent_mode:"simple",indent_char:"\t",indent_levels:1,remove_linebreaks:1,remove_redundant_brs:1,element_format:"xhtml"},j);i.dom=j.dom;i.schema=j.schema;if(j.entity_encoding=="named"&&!j.entities){j.entity_encoding="raw"}if(j.remove_redundant_brs){i.onPostProcess.add(function(k,l){l.content=l.content.replace(/(
    \s*)+<\/(p|h[1-6]|div|li)>/gi,function(n,m,o){if(/^
    \s*<\//.test(n)){return""}return n})})}if(j.element_format=="html"){i.onPostProcess.add(function(k,l){l.content=l.content.replace(/<([^>]+) \/>/g,"<$1>")})}if(j.fix_list_elements){i.onPreProcess.add(function(v,s){var l,z,y=["ol","ul"],u,t,q,k=/^(OL|UL)$/,A;function m(r,x){var o=x.split(","),p;while((r=r.previousSibling)!=null){for(p=0;p=1767){f(i.dom.select("p table",l.node).reverse(),function(p){var o=i.dom.getParent(p.parentNode,"table,p");if(o.nodeName!="TABLE"){try{i.dom.split(o,p)}catch(m){}}})}})}},setEntities:function(o){var n=this,j,m,h={},k;if(n.entityLookup){return}j=o.split(",");for(m=0;m1){f(q[1].split("|"),function(u){var p={},t;k=k||[];u=u.replace(/::/g,"~");u=/^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(u);u[2]=u[2].replace(/~/g,":");if(u[1]=="!"){r=r||[];r.push(u[2])}if(u[1]=="-"){for(t=0;t=1767)){p=j.createHTMLDocument("");f(r.nodeName=="BODY"?r.childNodes:[r],function(h){p.body.appendChild(p.importNode(h,true))});if(r.nodeName!="BODY"){r=p.body.firstChild}else{r=p.body}i=k.dom.doc;k.dom.doc=p}k.key=""+(parseInt(k.key)+1);if(!q.no_events){q.node=r;k.onPreProcess.dispatch(k,q)}k.writer.reset();k._info=q;k._serializeNode(r,q.getInner);q.content=k.writer.getContent();if(i){k.dom.doc=i}if(!q.no_events){k.onPostProcess.dispatch(k,q)}k._postProcess(q);q.node=null;return e.trim(q.content)},_postProcess:function(n){var i=this,k=i.settings,j=n.content,m=[],l;if(n.format=="html"){l=i._protect({content:j,patterns:[{pattern:/(]*>)(.*?)(<\/script>)/g},{pattern:/(]*>)(.*?)(<\/noscript>)/g},{pattern:/(]*>)(.*?)(<\/style>)/g},{pattern:/(]*>)(.*?)(<\/pre>)/g,encode:1},{pattern:/()/g}]});j=l.content;if(k.entity_encoding!=="raw"){j=i._encode(j)}if(!n.set){j=j.replace(/

    \s+<\/p>|]+)>\s+<\/p>/g,k.entity_encoding=="numeric"?" 

    ":" 

    ");if(k.remove_linebreaks){j=j.replace(/\r?\n|\r/g," ");j=j.replace(/(<[^>]+>)\s+/g,"$1 ");j=j.replace(/\s+(<\/[^>]+>)/g," $1");j=j.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g,"<$1 $2>");j=j.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g,"<$1>");j=j.replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g,"")}if(k.apply_source_formatting&&k.indent_mode=="simple"){j=j.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g,"\n<$1$2$3>\n");j=j.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g,"\n<$1$2>");j=j.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g,"\n");j=j.replace(/\n\n/g,"\n")}}j=i._unprotect(j,l);j=j.replace(//g,"");if(k.entity_encoding=="raw"){j=j.replace(/

     <\/p>|]+)> <\/p>/g,"\u00a0

    ")}j=j.replace(/]+|)>([\s\S]*?)<\/noscript>/g,function(h,p,o){return""+i.dom.decode(o.replace(//g,""))+""})}n.content=j},_serializeNode:function(D,I){var z=this,A=z.settings,x=z.writer,q,j,u,F,E,H,B,h,y,k,r,C,p,m,G,o;if(!A.node_filter||A.node_filter(D)){switch(D.nodeType){case 1:if(D.hasAttribute?D.hasAttribute("_mce_bogus"):D.getAttribute("_mce_bogus")){return}p=G=false;q=D.hasChildNodes();k=D.getAttribute("_mce_name")||D.nodeName.toLowerCase();o=D.getAttribute("_mce_type");if(o){if(!z._info.cleanup){p=true;return}else{G=1}}if(d){if(D.scopeName!=="HTML"&&D.scopeName!=="html"){k=D.scopeName+":"+k}}if(k.indexOf("mce:")===0){k=k.substring(4)}if(!G){if(!z.validElementsRE||!z.validElementsRE.test(k)||(z.invalidElementsRE&&z.invalidElementsRE.test(k))||I){p=true;break}}if(d){if(A.fix_content_duplication){if(D._mce_serialized==z.key){return}D._mce_serialized=z.key}if(k.charAt(0)=="/"){k=k.substring(1)}}else{if(a){if(D.nodeName==="BR"&&D.getAttribute("type")=="_moz"){return}}}if(A.validate_children){if(z.elementName&&!z.schema.isValid(z.elementName,k)){p=true;break}z.elementName=k}r=z.findRule(k);k=r.name||k;m=A.closed.test(k);if((!q&&r.noEmpty)||(d&&!k)){p=true;break}if(r.requiredAttribs){H=r.requiredAttribs;for(F=H.length-1;F>=0;F--){if(this.dom.getAttrib(D,H[F])!==""){break}}if(F==-1){p=true;break}}x.writeStartElement(k);if(r.attribs){for(F=0,B=r.attribs,E=B.length;F-1;F--){h=B[F];if(h.specified){H=h.nodeName.toLowerCase();if(A.invalid_attrs.test(H)||!r.validAttribsRE.test(H)){continue}C=z.findAttribRule(r,H);y=z._getAttrib(D,C,H);if(y!==null){x.writeAttribute(H,y)}}}}if(o&&G){x.writeAttribute("_mce_type",o)}if(k==="script"&&e.trim(D.innerHTML)){x.writeText("// ");x.writeCDATA(D.innerHTML.replace(/|<\[CDATA\[|\]\]>/g,""));q=false;break}if(r.padd){if(q&&(u=D.firstChild)&&u.nodeType===1&&D.childNodes.length===1){if(u.hasAttribute?u.hasAttribute("_mce_bogus"):u.getAttribute("_mce_bogus")){x.writeText("\u00a0")}}else{if(!q){x.writeText("\u00a0")}}}break;case 3:if(A.validate_children&&z.elementName&&!z.schema.isValid(z.elementName,"#text")){return}return x.writeText(D.nodeValue);case 4:return x.writeCDATA(D.nodeValue);case 8:return x.writeComment(D.nodeValue)}}else{if(D.nodeType==1){q=D.hasChildNodes()}}if(q&&!m){u=D.firstChild;while(u){z._serializeNode(u);z.elementName=k;u=u.nextSibling}}if(!p){if(!m){x.writeFullEndElement()}else{x.writeEndElement()}}},_protect:function(j){var i=this;j.items=j.items||[];function h(l){return l.replace(/[\r\n\\]/g,function(m){if(m==="\n"){return"\\n"}else{if(m==="\\"){return"\\\\"}}return"\\r"})}function k(l){return l.replace(/\\[\\rn]/g,function(m){if(m==="\\n"){return"\n"}else{if(m==="\\\\"){return"\\"}}return"\r"})}f(j.patterns,function(l){j.content=k(h(j.content).replace(l.pattern,function(n,o,m,p){m=k(m);if(l.encode){m=i._encode(m)}j.items.push(m);return o+""+p}))});return j},_unprotect:function(i,j){i=i.replace(/\"))}if(a&&j.ListBox){if(a.Button||a.SplitButton){e+=b.createHTML("td",{"class":"mceToolbarEnd"},b.createHTML("span",null,""))}}if(b.stdMode){e+=''+j.renderHTML()+""}else{e+=""+j.renderHTML()+""}if(f&&j.ListBox){if(f.Button||f.SplitButton){e+=b.createHTML("td",{"class":"mceToolbarStart"},b.createHTML("span",null,""))}}}g="mceToolbarEnd";if(j.Button){g+=" mceToolbarEndButton"}else{if(j.SplitButton){g+=" mceToolbarEndSplitButton"}else{if(j.ListBox){g+=" mceToolbarEndListBox"}}}e+=b.createHTML("td",{"class":g},b.createHTML("span",null,""));return b.createHTML("table",{id:l.id,"class":"mceToolbar"+(m["class"]?" "+m["class"]:""),cellpadding:"0",cellspacing:"0",align:l.settings.align||""},""+e+"")}});(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{items:[],urls:{},lookup:{},onAdd:new a(this),get:function(d){return this.lookup[d]},requireLangPack:function(e){var d=b.settings;if(d&&d.language){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(e,d){this.items.push(d);this.lookup[e]=d;this.onAdd.dispatch(this,e,d);return d},load:function(h,e,d,g){var f=this;if(f.urls[h]){return}if(e.indexOf("/")!=0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}f.urls[h]=e.substring(0,e.lastIndexOf("/"));b.ScriptLoader.add(e,d,g)}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(q){var n=this,p,l=j.ScriptLoader,u,o=[],m;function r(x,y,t){var v=x[y];if(!v){return}if(j.is(v,"string")){t=v.replace(/\.\w+$/,"");t=t?j.resolve(t):0;v=j.resolve(v)}return v.apply(t||this,Array.prototype.slice.call(arguments,2))}q=d({theme:"simple",language:"en"},q);n.settings=q;i.add(document,"init",function(){var s,v;r(q,"onpageload");switch(q.mode){case"exact":s=q.elements||"";if(s.length>0){g(e(s),function(x){if(k.get(x)){m=new j.Editor(x,q);o.push(m);m.render(1)}else{g(document.forms,function(y){g(y.elements,function(z){if(z.name===x){x="mce_editor_"+c++;k.setAttrib(z,"id",x);m=new j.Editor(x,q);o.push(m);m.render(1)}})})}})}break;case"textareas":case"specific_textareas":function t(y,x){return x.constructor===RegExp?x.test(y.className):k.hasClass(y,x)}g(k.select("textarea"),function(x){if(q.editor_deselector&&t(x,q.editor_deselector)){return}if(!q.editor_selector||t(x,q.editor_selector)){u=k.get(x.name);if(!x.id&&!u){x.id=x.name}if(!x.id||n.get(x.id)){x.id=k.uniqueId()}m=new j.Editor(x.id,q);o.push(m);m.render(1)}});break}if(q.oninit){s=v=0;g(o,function(x){v++;if(!x.initialized){x.onInit.add(function(){s++;if(s==v){r(q,"oninit")}})}else{s++}if(s==v){r(q,"oninit")}})}})},get:function(l){if(l===a){return this.editors}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l':"",visual_table_class:"mceItemTable",visual:1,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",apply_source_formatting:1,directionality:"ltr",forced_root_block:"p",valid_elements:"@[id|class|style|title|dir';if(F.document_base_url!=m.documentBaseURL){E.iframeHTML+=''}E.iframeHTML+='';if(m.relaxedDomain){E.iframeHTML+=''; + + bi = s.body_id || 'tinymce'; + if (bi.indexOf('=') != -1) { + bi = t.getParam('body_id', '', 'hash'); + bi = bi[t.id] || bi; + } + + bc = s.body_class || ''; + if (bc.indexOf('=') != -1) { + bc = t.getParam('body_class', '', 'hash'); + bc = bc[t.id] || ''; + } + + t.iframeHTML += ''; + + // Domain relaxing enabled, then set document domain + if (tinymce.relaxedDomain) { + // We need to write the contents here in IE since multiple writes messes up refresh button and back button + if (isIE || (tinymce.isOpera && parseFloat(opera.version()) >= 9.5)) + u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'; + else if (tinymce.isOpera) + u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";document.close();ed.setupIframe();})()'; + } + + // Create iframe + n = DOM.add(o.iframeContainer, 'iframe', { + id : t.id + "_ifr", + src : u || 'javascript:""', // Workaround for HTTPS warning in IE6/7 + frameBorder : '0', + style : { + width : '100%', + height : h + } + }); + + t.contentAreaContainer = o.iframeContainer; + DOM.get(o.editorContainer).style.display = t.orgDisplay; + DOM.get(t.id).style.display = 'none'; + + if (!isIE || !tinymce.relaxedDomain) + t.setupIframe(); + + e = n = o = null; // Cleanup + }, + + setupIframe : function() { + var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b; + + // Setup iframe body + if (!isIE || !tinymce.relaxedDomain) { + d.open(); + d.write(t.iframeHTML); + d.close(); + } + + // Design mode needs to be added here Ctrl+A will fail otherwise + if (!isIE) { + try { + if (!s.readonly) + d.designMode = 'On'; + } catch (ex) { + // Will fail on Gecko if the editor is placed in an hidden container element + // The design mode will be set ones the editor is focused + } + } + + // IE needs to use contentEditable or it will display non secure items for HTTPS + if (isIE) { + // It will not steal focus if we hide it while setting contentEditable + b = t.getBody(); + DOM.hide(b); + + if (!s.readonly) + b.contentEditable = true; + + DOM.show(b); + } + + t.dom = new tinymce.dom.DOMUtils(t.getDoc(), { + keep_values : true, + url_converter : t.convertURL, + url_converter_scope : t, + hex_colors : s.force_hex_style_colors, + class_filter : s.class_filter, + update_styles : 1, + fix_ie_paragraphs : 1, + valid_styles : s.valid_styles + }); + + t.schema = new tinymce.dom.Schema(); + + t.serializer = new tinymce.dom.Serializer(extend(s, { + valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements, + dom : t.dom, + schema : t.schema + })); + + t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer); + + t.formatter = new tinymce.Formatter(this); + + // Register default formats + t.formatter.register({ + alignleft : [ + {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}}, + {selector : 'img,table', styles : {'float' : 'left'}} + ], + + aligncenter : [ + {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}}, + {selector : 'img', styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}}, + {selector : 'table', styles : {marginLeft : 'auto', marginRight : 'auto'}} + ], + + alignright : [ + {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}}, + {selector : 'img,table', styles : {'float' : 'right'}} + ], + + alignfull : [ + {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}} + ], + + bold : [ + {inline : 'strong'}, + {inline : 'span', styles : {fontWeight : 'bold'}}, + {inline : 'b'} + ], + + italic : [ + {inline : 'em'}, + {inline : 'span', styles : {fontStyle : 'italic'}}, + {inline : 'i'} + ], + + underline : [ + {inline : 'span', styles : {textDecoration : 'underline'}, exact : true}, + {inline : 'u'} + ], + + strikethrough : [ + {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true}, + {inline : 'u'} + ], + + forecolor : {inline : 'span', styles : {color : '%value'}}, + hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}}, + fontname : {inline : 'span', styles : {fontFamily : '%value'}}, + fontsize : {inline : 'span', styles : {fontSize : '%value'}}, + blockquote : {block : 'blockquote', wrapper : 1}, + + removeformat : [ + {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true}, + {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true}, + {selector : '*', attributes : ['style', 'class'], expand : false, deep : true} + ] + }); + + // Register default block formats + each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) { + t.formatter.register(name, {block : name}); + }); + + // Register user defined formats + t.formatter.register(t.settings.formats); + + t.undoManager = new tinymce.UndoManager(t); + + // Pass through + t.undoManager.onAdd.add(function(um, l) { + if (!l.initial) + return t.onChange.dispatch(t, l, um); + }); + + t.undoManager.onUndo.add(function(um, l) { + return t.onUndo.dispatch(t, l, um); + }); + + t.undoManager.onRedo.add(function(um, l) { + return t.onRedo.dispatch(t, l, um); + }); + + t.forceBlocks = new tinymce.ForceBlocks(t, { + forced_root_block : s.forced_root_block + }); + + t.editorCommands = new tinymce.EditorCommands(t); + + // Pass through + t.serializer.onPreProcess.add(function(se, o) { + return t.onPreProcess.dispatch(t, o, se); + }); + + t.serializer.onPostProcess.add(function(se, o) { + return t.onPostProcess.dispatch(t, o, se); + }); + + t.onPreInit.dispatch(t); + + if (!s.gecko_spellcheck) + t.getBody().spellcheck = 0; + + if (!s.readonly) + t._addEvents(); + + t.controlManager.onPostRender.dispatch(t, t.controlManager); + t.onPostRender.dispatch(t); + + if (s.directionality) + t.getBody().dir = s.directionality; + + if (s.nowrap) + t.getBody().style.whiteSpace = "nowrap"; + + if (s.custom_elements) { + function handleCustom(ed, o) { + each(explode(s.custom_elements), function(v) { + var n; + + if (v.indexOf('~') === 0) { + v = v.substring(1); + n = 'span'; + } else + n = 'div'; + + o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' _mce_name="$1"$2>'); + o.content = o.content.replace(new RegExp('', 'g'), ''); + }); + }; + + t.onBeforeSetContent.add(handleCustom); + t.onPostProcess.add(function(ed, o) { + if (o.set) + handleCustom(ed, o); + }); + } + + if (s.handle_node_change_callback) { + t.onNodeChange.add(function(ed, cm, n) { + t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed()); + }); + } + + if (s.save_callback) { + t.onSaveContent.add(function(ed, o) { + var h = t.execCallback('save_callback', t.id, o.content, t.getBody()); + + if (h) + o.content = h; + }); + } + + if (s.onchange_callback) { + t.onChange.add(function(ed, l) { + t.execCallback('onchange_callback', t, l); + }); + } + + if (s.convert_newlines_to_brs) { + t.onBeforeSetContent.add(function(ed, o) { + if (o.initial) + o.content = o.content.replace(/\r?\n/g, '
    '); + }); + } + + if (s.fix_nesting && isIE) { + t.onBeforeSetContent.add(function(ed, o) { + o.content = t._fixNesting(o.content); + }); + } + + if (s.preformatted) { + t.onPostProcess.add(function(ed, o) { + o.content = o.content.replace(/^\s*/, ''); + o.content = o.content.replace(/<\/pre>\s*$/, ''); + + if (o.set) + o.content = '
    ' + o.content + '
    '; + }); + } + + if (s.verify_css_classes) { + t.serializer.attribValueFilter = function(n, v) { + var s, cl; + + if (n == 'class') { + // Build regexp for classes + if (!t.classesRE) { + cl = t.dom.getClasses(); + + if (cl.length > 0) { + s = ''; + + each (cl, function(o) { + s += (s ? '|' : '') + o['class']; + }); + + t.classesRE = new RegExp('(' + s + ')', 'gi'); + } + } + + return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : ''; + } + + return v; + }; + } + + if (s.cleanup_callback) { + t.onBeforeSetContent.add(function(ed, o) { + o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o); + }); + + t.onPreProcess.add(function(ed, o) { + if (o.set) + t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o); + + if (o.get) + t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o); + }); + + t.onPostProcess.add(function(ed, o) { + if (o.set) + o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o); + + if (o.get) + o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o); + }); + } + + if (s.save_callback) { + t.onGetContent.add(function(ed, o) { + if (o.save) + o.content = t.execCallback('save_callback', t.id, o.content, t.getBody()); + }); + } + + if (s.handle_event_callback) { + t.onEvent.add(function(ed, e, o) { + if (t.execCallback('handle_event_callback', e, ed, o) === false) + Event.cancel(e); + }); + } + + // Add visual aids when new contents is added + t.onSetContent.add(function() { + t.addVisual(t.getBody()); + }); + + // Remove empty contents + if (s.padd_empty_editor) { + t.onPostProcess.add(function(ed, o) { + o.content = o.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
    [\r\n]*)$/, ''); + }); + } + + if (isGecko) { + // Fix gecko link bug, when a link is placed at the end of block elements there is + // no way to move the caret behind the link. This fix adds a bogus br element after the link + function fixLinks(ed, o) { + each(ed.dom.select('a'), function(n) { + var pn = n.parentNode; + + if (ed.dom.isBlock(pn) && pn.lastChild === n) + ed.dom.add(pn, 'br', {'_mce_bogus' : 1}); + }); + }; + + t.onExecCommand.add(function(ed, cmd) { + if (cmd === 'CreateLink') + fixLinks(ed); + }); + + t.onSetContent.add(t.selection.onSetContent.add(fixLinks)); + + if (!s.readonly) { + try { + // Design mode must be set here once again to fix a bug where + // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again + d.designMode = 'Off'; + d.designMode = 'On'; + } catch (ex) { + // Will fail on Gecko if the editor is placed in an hidden container element + // The design mode will be set ones the editor is focused + } + } + } + + // A small timeout was needed since firefox will remove. Bug: #1838304 + setTimeout(function () { + if (t.removed) + return; + + t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')}); + t.startContent = t.getContent({format : 'raw'}); + t.initialized = true; + + t.onInit.dispatch(t); + t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc()); + t.execCallback('init_instance_callback', t); + t.focus(true); + t.nodeChanged({initial : 1}); + + // Load specified content CSS last + if (s.content_css) { + tinymce.each(explode(s.content_css), function(u) { + t.dom.loadCSS(t.documentBaseURI.toAbsolute(u)); + }); + } + + // Handle auto focus + if (s.auto_focus) { + setTimeout(function () { + var ed = tinymce.get(s.auto_focus); + + ed.selection.select(ed.getBody(), 1); + ed.selection.collapse(1); + ed.getWin().focus(); + }, 100); + } + }, 1); + + e = null; + }, + + + focus : function(sf) { + var oed, t = this, ce = t.settings.content_editable; + + if (!sf) { + // Is not content editable or the selection is outside the area in IE + // the IE statement is needed to avoid bluring if element selections inside layers since + // the layer is like it's own document in IE + if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc())) + t.getWin().focus(); + + } + + if (tinymce.activeEditor != t) { + if ((oed = tinymce.activeEditor) != null) + oed.onDeactivate.dispatch(oed, t); + + t.onActivate.dispatch(t, oed); + } + + tinymce._setActive(t); + }, + + execCallback : function(n) { + var t = this, f = t.settings[n], s; + + if (!f) + return; + + // Look through lookup + if (t.callbackLookup && (s = t.callbackLookup[n])) { + f = s.func; + s = s.scope; + } + + if (is(f, 'string')) { + s = f.replace(/\.\w+$/, ''); + s = s ? tinymce.resolve(s) : 0; + f = tinymce.resolve(f); + t.callbackLookup = t.callbackLookup || {}; + t.callbackLookup[n] = {func : f, scope : s}; + } + + return f.apply(s || t, Array.prototype.slice.call(arguments, 1)); + }, + + translate : function(s) { + var c = this.settings.language || 'en', i18n = tinymce.i18n; + + if (!s) + return ''; + + return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) { + return i18n[c + '.' + b] || '{#' + b + '}'; + }); + }, + + getLang : function(n, dv) { + return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}'); + }, + + getParam : function(n, dv, ty) { + var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o; + + if (ty === 'hash') { + o = {}; + + if (is(v, 'string')) { + each(v.indexOf('=') > 0 ? v.split(/[;,](?![^=;,]*(?:[;,]|$))/) : v.split(','), function(v) { + v = v.split('='); + + if (v.length > 1) + o[tr(v[0])] = tr(v[1]); + else + o[tr(v[0])] = tr(v); + }); + } else + o = v; + + return o; + } + + return v; + }, + + nodeChanged : function(o) { + var t = this, s = t.selection, n = s.getNode() || t.getBody(); + + // Fix for bug #1896577 it seems that this can not be fired while the editor is loading + if (t.initialized) { + o = o || {}; + n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state + + // Get parents and add them to object + o.parents = []; + t.dom.getParent(n, function(node) { + if (node.nodeName == 'BODY') + return true; + + o.parents.push(node); + }); + + t.onNodeChange.dispatch( + t, + o ? o.controlManager || t.controlManager : t.controlManager, + n, + s.isCollapsed(), + o + ); + } + }, + + addButton : function(n, s) { + var t = this; + + t.buttons = t.buttons || {}; + t.buttons[n] = s; + }, + + addCommand : function(n, f, s) { + this.execCommands[n] = {func : f, scope : s || this}; + }, + + addQueryStateHandler : function(n, f, s) { + this.queryStateCommands[n] = {func : f, scope : s || this}; + }, + + addQueryValueHandler : function(n, f, s) { + this.queryValueCommands[n] = {func : f, scope : s || this}; + }, + + addShortcut : function(pa, desc, cmd_func, sc) { + var t = this, c; + + if (!t.settings.custom_shortcuts) + return false; + + t.shortcuts = t.shortcuts || {}; + + if (is(cmd_func, 'string')) { + c = cmd_func; + + cmd_func = function() { + t.execCommand(c, false, null); + }; + } + + if (is(cmd_func, 'object')) { + c = cmd_func; + + cmd_func = function() { + t.execCommand(c[0], c[1], c[2]); + }; + } + + each(explode(pa), function(pa) { + var o = { + func : cmd_func, + scope : sc || this, + desc : desc, + alt : false, + ctrl : false, + shift : false + }; + + each(explode(pa, '+'), function(v) { + switch (v) { + case 'alt': + case 'ctrl': + case 'shift': + o[v] = true; + break; + + default: + o.charCode = v.charCodeAt(0); + o.keyCode = v.toUpperCase().charCodeAt(0); + } + }); + + t.shortcuts[(o.ctrl ? 'ctrl' : '') + ',' + (o.alt ? 'alt' : '') + ',' + (o.shift ? 'shift' : '') + ',' + o.keyCode] = o; + }); + + return true; + }, + + execCommand : function(cmd, ui, val, a) { + var t = this, s = 0, o, st; + + if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus)) + t.focus(); + + o = {}; + t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o); + if (o.terminate) + return false; + + // Command callback + if (t.execCallback('execcommand_callback', t.id, t.selection.getNode(), cmd, ui, val)) { + t.onExecCommand.dispatch(t, cmd, ui, val, a); + return true; + } + + // Registred commands + if (o = t.execCommands[cmd]) { + st = o.func.call(o.scope, ui, val); + + // Fall through on true + if (st !== true) { + t.onExecCommand.dispatch(t, cmd, ui, val, a); + return st; + } + } + + // Plugin commands + each(t.plugins, function(p) { + if (p.execCommand && p.execCommand(cmd, ui, val)) { + t.onExecCommand.dispatch(t, cmd, ui, val, a); + s = 1; + return false; + } + }); + + if (s) + return true; + + // Theme commands + if (t.theme && t.theme.execCommand && t.theme.execCommand(cmd, ui, val)) { + t.onExecCommand.dispatch(t, cmd, ui, val, a); + return true; + } + + // Execute global commands + if (tinymce.GlobalCommands.execCommand(t, cmd, ui, val)) { + t.onExecCommand.dispatch(t, cmd, ui, val, a); + return true; + } + + // Editor commands + if (t.editorCommands.execCommand(cmd, ui, val)) { + t.onExecCommand.dispatch(t, cmd, ui, val, a); + return true; + } + + // Browser commands + t.getDoc().execCommand(cmd, ui, val); + t.onExecCommand.dispatch(t, cmd, ui, val, a); + }, + + queryCommandState : function(cmd) { + var t = this, o, s; + + // Is hidden then return undefined + if (t._isHidden()) + return; + + // Registred commands + if (o = t.queryStateCommands[cmd]) { + s = o.func.call(o.scope); + + // Fall though on true + if (s !== true) + return s; + } + + // Registred commands + o = t.editorCommands.queryCommandState(cmd); + if (o !== -1) + return o; + + // Browser commands + try { + return this.getDoc().queryCommandState(cmd); + } catch (ex) { + // Fails sometimes see bug: 1896577 + } + }, + + queryCommandValue : function(c) { + var t = this, o, s; + + // Is hidden then return undefined + if (t._isHidden()) + return; + + // Registred commands + if (o = t.queryValueCommands[c]) { + s = o.func.call(o.scope); + + // Fall though on true + if (s !== true) + return s; + } + + // Registred commands + o = t.editorCommands.queryCommandValue(c); + if (is(o)) + return o; + + // Browser commands + try { + return this.getDoc().queryCommandValue(c); + } catch (ex) { + // Fails sometimes see bug: 1896577 + } + }, + + show : function() { + var t = this; + + DOM.show(t.getContainer()); + DOM.hide(t.id); + t.load(); + }, + + hide : function() { + var t = this, d = t.getDoc(); + + // Fixed bug where IE has a blinking cursor left from the editor + if (isIE && d) + d.execCommand('SelectAll'); + + // We must save before we hide so Safari doesn't crash + t.save(); + DOM.hide(t.getContainer()); + DOM.setStyle(t.id, 'display', t.orgDisplay); + }, + + isHidden : function() { + return !DOM.isHidden(this.id); + }, + + setProgressState : function(b, ti, o) { + this.onSetProgressState.dispatch(this, b, ti, o); + + return b; + }, + + load : function(o) { + var t = this, e = t.getElement(), h; + + if (e) { + o = o || {}; + o.load = true; + + // Double encode existing entities in the value + h = t.setContent(is(e.value) ? e.value : e.innerHTML, o); + o.element = e; + + if (!o.no_events) + t.onLoadContent.dispatch(t, o); + + o.element = e = null; + + return h; + } + }, + + save : function(o) { + var t = this, e = t.getElement(), h, f; + + if (!e || !t.initialized) + return; + + o = o || {}; + o.save = true; + + // Add undo level will trigger onchange event + if (!o.no_events) { + t.undoManager.typing = 0; + t.undoManager.add(); + } + + o.element = e; + h = o.content = t.getContent(o); + + if (!o.no_events) + t.onSaveContent.dispatch(t, o); + + h = o.content; + + if (!/TEXTAREA|INPUT/i.test(e.nodeName)) { + e.innerHTML = h; + + // Update hidden form element + if (f = DOM.getParent(t.id, 'form')) { + each(f.elements, function(e) { + if (e.name == t.id) { + e.value = h; + return false; + } + }); + } + } else + e.value = h; + + o.element = e = null; + + return h; + }, + + setContent : function(h, o) { + var t = this; + + o = o || {}; + o.format = o.format || 'html'; + o.set = true; + o.content = h; + + if (!o.no_events) + t.onBeforeSetContent.dispatch(t, o); + + // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content + // It will also be impossible to place the caret in the editor unless there is a BR element present + if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) { + o.content = t.dom.setHTML(t.getBody(), '
    '); + o.format = 'raw'; + } + + o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content)); + + if (o.format != 'raw' && t.settings.cleanup) { + o.getInner = true; + o.content = t.dom.setHTML(t.getBody(), t.serializer.serialize(t.getBody(), o)); + } + + if (!o.no_events) + t.onSetContent.dispatch(t, o); + + return o.content; + }, + + getContent : function(o) { + var t = this, h; + + o = o || {}; + o.format = o.format || 'html'; + o.get = true; + + if (!o.no_events) + t.onBeforeGetContent.dispatch(t, o); + + if (o.format != 'raw' && t.settings.cleanup) { + o.getInner = true; + h = t.serializer.serialize(t.getBody(), o); + } else + h = t.getBody().innerHTML; + + h = h.replace(/^\s*|\s*$/g, ''); + o.content = h; + + if (!o.no_events) + t.onGetContent.dispatch(t, o); + + return o.content; + }, + + isDirty : function() { + var t = this; + + return tinymce.trim(t.startContent) != tinymce.trim(t.getContent({format : 'raw', no_events : 1})) && !t.isNotDirty; + }, + + getContainer : function() { + var t = this; + + if (!t.container) + t.container = DOM.get(t.editorContainer || t.id + '_parent'); + + return t.container; + }, + + getContentAreaContainer : function() { + return this.contentAreaContainer; + }, + + getElement : function() { + return DOM.get(this.settings.content_element || this.id); + }, + + getWin : function() { + var t = this, e; + + if (!t.contentWindow) { + e = DOM.get(t.id + "_ifr"); + + if (e) + t.contentWindow = e.contentWindow; + } + + return t.contentWindow; + }, + + getDoc : function() { + var t = this, w; + + if (!t.contentDocument) { + w = t.getWin(); + + if (w) + t.contentDocument = w.document; + } + + return t.contentDocument; + }, + + getBody : function() { + return this.bodyElement || this.getDoc().body; + }, + + convertURL : function(u, n, e) { + var t = this, s = t.settings; + + // Use callback instead + if (s.urlconverter_callback) + return t.execCallback('urlconverter_callback', u, e, true, n); + + // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs + if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0) + return u; + + // Convert to relative + if (s.relative_urls) + return t.documentBaseURI.toRelative(u); + + // Convert to absolute + u = t.documentBaseURI.toAbsolute(u, s.remove_script_host); + + return u; + }, + + addVisual : function(e) { + var t = this, s = t.settings; + + e = e || t.getBody(); + + if (!is(t.hasVisual)) + t.hasVisual = s.visual; + + each(t.dom.select('table,a', e), function(e) { + var v; + + switch (e.nodeName) { + case 'TABLE': + v = t.dom.getAttrib(e, 'border'); + + if (!v || v == '0') { + if (t.hasVisual) + t.dom.addClass(e, s.visual_table_class); + else + t.dom.removeClass(e, s.visual_table_class); + } + + return; + + case 'A': + v = t.dom.getAttrib(e, 'name'); + + if (v) { + if (t.hasVisual) + t.dom.addClass(e, 'mceItemAnchor'); + else + t.dom.removeClass(e, 'mceItemAnchor'); + } + + return; + } + }); + + t.onVisualAid.dispatch(t, e, t.hasVisual); + }, + + remove : function() { + var t = this, e = t.getContainer(); + + t.removed = 1; // Cancels post remove event execution + t.hide(); + + t.execCallback('remove_instance_callback', t); + t.onRemove.dispatch(t); + + // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command + t.onExecCommand.listeners = []; + + tinymce.remove(t); + DOM.remove(e); + }, + + destroy : function(s) { + var t = this; + + // One time is enough + if (t.destroyed) + return; + + if (!s) { + tinymce.removeUnload(t.destroy); + tinyMCE.onBeforeUnload.remove(t._beforeUnload); + + // Manual destroy + if (t.theme && t.theme.destroy) + t.theme.destroy(); + + // Destroy controls, selection and dom + t.controlManager.destroy(); + t.selection.destroy(); + t.dom.destroy(); + + // Remove all events + + // Don't clear the window or document if content editable + // is enabled since other instances might still be present + if (!t.settings.content_editable) { + Event.clear(t.getWin()); + Event.clear(t.getDoc()); + } + + Event.clear(t.getBody()); + Event.clear(t.formElement); + } + + if (t.formElement) { + t.formElement.submit = t.formElement._mceOldSubmit; + t.formElement._mceOldSubmit = null; + } + + t.contentAreaContainer = t.formElement = t.container = t.settings.content_element = t.bodyElement = t.contentDocument = t.contentWindow = null; + + if (t.selection) + t.selection = t.selection.win = t.selection.dom = t.selection.dom.doc = null; + + t.destroyed = 1; + }, + + // Internal functions + + _addEvents : function() { + // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset + var t = this, i, s = t.settings, lo = { + mouseup : 'onMouseUp', + mousedown : 'onMouseDown', + click : 'onClick', + keyup : 'onKeyUp', + keydown : 'onKeyDown', + keypress : 'onKeyPress', + submit : 'onSubmit', + reset : 'onReset', + contextmenu : 'onContextMenu', + dblclick : 'onDblClick', + paste : 'onPaste' // Doesn't work in all browsers yet + }; + + function eventHandler(e, o) { + var ty = e.type; + + // Don't fire events when it's removed + if (t.removed) + return; + + // Generic event handler + if (t.onEvent.dispatch(t, e, o) !== false) { + // Specific event handler + t[lo[e.fakeType || e.type]].dispatch(t, e, o); + } + }; + + // Add DOM events + each(lo, function(v, k) { + switch (k) { + case 'contextmenu': + if (tinymce.isOpera) { + // Fake contextmenu on Opera + t.dom.bind(t.getBody(), 'mousedown', function(e) { + if (e.ctrlKey) { + e.fakeType = 'contextmenu'; + eventHandler(e); + } + }); + } else + t.dom.bind(t.getBody(), k, eventHandler); + break; + + case 'paste': + t.dom.bind(t.getBody(), k, function(e) { + eventHandler(e); + }); + break; + + case 'submit': + case 'reset': + t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler); + break; + + default: + t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler); + } + }); + + t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) { + t.focus(true); + }); + + + // Fixes bug where a specified document_base_uri could result in broken images + // This will also fix drag drop of images in Gecko + if (tinymce.isGecko) { + // Convert all images to absolute URLs +/* t.onSetContent.add(function(ed, o) { + each(ed.dom.select('img'), function(e) { + var v; + + if (v = e.getAttribute('_mce_src')) + e.src = t.documentBaseURI.toAbsolute(v); + }) + });*/ + + t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) { + var v; + + e = e.target; + + if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('_mce_src'))) + e.src = t.documentBaseURI.toAbsolute(v); + }); + } + + // Set various midas options in Gecko + if (isGecko) { + function setOpts() { + var t = this, d = t.getDoc(), s = t.settings; + + if (isGecko && !s.readonly) { + if (t._isHidden()) { + try { + if (!s.content_editable) + d.designMode = 'On'; + } catch (ex) { + // Fails if it's hidden + } + } + + try { + // Try new Gecko method + d.execCommand("styleWithCSS", 0, false); + } catch (ex) { + // Use old method + if (!t._isHidden()) + try {d.execCommand("useCSS", 0, true);} catch (ex) {} + } + + if (!s.table_inline_editing) + try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {} + + if (!s.object_resizing) + try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {} + } + }; + + t.onBeforeExecCommand.add(setOpts); + t.onMouseDown.add(setOpts); + } + + // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 + // WebKit can't even do simple things like selecting an image + // This also fixes so it's possible to select mceItemAnchors + if (tinymce.isWebKit) { + t.onClick.add(function(ed, e) { + e = e.target; + + // Needs tobe the setBaseAndExtend or it will fail to select floated images + if (e.nodeName == 'IMG' || (e.nodeName == 'A' && t.dom.hasClass(e, 'mceItemAnchor'))) + t.selection.getSel().setBaseAndExtent(e, 0, e, 1); + }); + } + + // Add node change handlers + t.onMouseUp.add(t.nodeChanged); + t.onClick.add(t.nodeChanged); + t.onKeyUp.add(function(ed, e) { + var c = e.keyCode; + + if ((c >= 33 && c <= 36) || (c >= 37 && c <= 40) || c == 13 || c == 45 || c == 46 || c == 8 || (tinymce.isMac && (c == 91 || c == 93)) || e.ctrlKey) + t.nodeChanged(); + }); + + // Add reset handler + t.onReset.add(function() { + t.setContent(t.startContent, {format : 'raw'}); + }); + + // Add shortcuts + if (s.custom_shortcuts) { + if (s.custom_undo_redo_keyboard_shortcuts) { + t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo'); + t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo'); + } + + // Add default shortcuts for gecko + if (isGecko) { + t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold'); + t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic'); + t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline'); + } + + // BlockFormat shortcuts keys + for (i=1; i<=6; i++) + t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]); + + t.addShortcut('ctrl+7', '', ['FormatBlock', false, '

    ']); + t.addShortcut('ctrl+8', '', ['FormatBlock', false, '

    ']); + t.addShortcut('ctrl+9', '', ['FormatBlock', false, '
    ']); + + function find(e) { + var v = null; + + if (!e.altKey && !e.ctrlKey && !e.metaKey) + return v; + + each(t.shortcuts, function(o) { + if (tinymce.isMac && o.ctrl != e.metaKey) + return; + else if (!tinymce.isMac && o.ctrl != e.ctrlKey) + return; + + if (o.alt != e.altKey) + return; + + if (o.shift != e.shiftKey) + return; + + if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) { + v = o; + return false; + } + }); + + return v; + }; + + t.onKeyUp.add(function(ed, e) { + var o = find(e); + + if (o) + return Event.cancel(e); + }); + + t.onKeyPress.add(function(ed, e) { + var o = find(e); + + if (o) + return Event.cancel(e); + }); + + t.onKeyDown.add(function(ed, e) { + var o = find(e); + + if (o) { + o.func.call(o.scope); + return Event.cancel(e); + } + }); + } + + if (tinymce.isIE) { + // Fix so resize will only update the width and height attributes not the styles of an image + // It will also block mceItemNoResize items + t.dom.bind(t.getDoc(), 'controlselect', function(e) { + var re = t.resizeInfo, cb; + + e = e.target; + + // Don't do this action for non image elements + if (e.nodeName !== 'IMG') + return; + + if (re) + t.dom.unbind(re.node, re.ev, re.cb); + + if (!t.dom.hasClass(e, 'mceItemNoResize')) { + ev = 'resizeend'; + cb = t.dom.bind(e, ev, function(e) { + var v; + + e = e.target; + + if (v = t.dom.getStyle(e, 'width')) { + t.dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, '')); + t.dom.setStyle(e, 'width', ''); + } + + if (v = t.dom.getStyle(e, 'height')) { + t.dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, '')); + t.dom.setStyle(e, 'height', ''); + } + }); + } else { + ev = 'resizestart'; + cb = t.dom.bind(e, 'resizestart', Event.cancel, Event); + } + + re = t.resizeInfo = { + node : e, + ev : ev, + cb : cb + }; + }); + + t.onKeyDown.add(function(ed, e) { + switch (e.keyCode) { + case 8: + // Fix IE control + backspace browser bug + if (t.selection.getRng().item) { + ed.dom.remove(t.selection.getRng().item(0)); + return Event.cancel(e); + } + } + }); + + /*if (t.dom.boxModel) { + t.getBody().style.height = '100%'; + + Event.add(t.getWin(), 'resize', function(e) { + var docElm = t.getDoc().documentElement; + + docElm.style.height = (docElm.offsetHeight - 10) + 'px'; + }); + }*/ + } + + if (tinymce.isOpera) { + t.onClick.add(function(ed, e) { + Event.prevent(e); + }); + } + + // Add custom undo/redo handlers + if (s.custom_undo_redo) { + function addUndo() { + t.undoManager.typing = 0; + t.undoManager.add(); + }; + + t.dom.bind(t.getDoc(), 'focusout', function(e) { + if (!t.removed && t.undoManager.typing) + addUndo(); + }); + + t.onKeyUp.add(function(ed, e) { + if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) + addUndo(); + }); + + t.onKeyDown.add(function(ed, e) { + // Is caracter positon keys + if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) { + if (t.undoManager.typing) + addUndo(); + + return; + } + + if (!t.undoManager.typing) { + t.undoManager.add(); + t.undoManager.typing = 1; + } + }); + + t.onMouseDown.add(function() { + if (t.undoManager.typing) + addUndo(); + }); + } + }, + + _isHidden : function() { + var s; + + if (!isGecko) + return 0; + + // Weird, wheres that cursor selection? + s = this.selection.getSel(); + return (!s || !s.rangeCount || s.rangeCount == 0); + }, + + // Fix for bug #1867292 + _fixNesting : function(s) { + var d = [], i; + + s = s.replace(/<(\/)?([^\s>]+)[^>]*?>/g, function(a, b, c) { + var e; + + // Handle end element + if (b === '/') { + if (!d.length) + return ''; + + if (c !== d[d.length - 1].tag) { + for (i=d.length - 1; i>=0; i--) { + if (d[i].tag === c) { + d[i].close = 1; + break; + } + } + + return ''; + } else { + d.pop(); + + if (d.length && d[d.length - 1].close) { + a = a + ''; + d.pop(); + } + } + } else { + // Ignore these + if (/^(br|hr|input|meta|img|link|param)$/i.test(c)) + return a; + + // Ignore closed ones + if (/\/>$/.test(a)) + return a; + + d.push({tag : c}); // Push start element + } + + return a; + }); + + // End all open tags + for (i=d.length - 1; i>=0; i--) + s += ''; + + return s; + } + }); +})(tinymce); + +(function(tinymce) { + // Added for compression purposes + var each = tinymce.each, undefined, TRUE = true, FALSE = false; + + tinymce.EditorCommands = function(editor) { + var dom = editor.dom, + selection = editor.selection, + commands = {state: {}, exec : {}, value : {}}, + settings = editor.settings, + bookmark; + + function execCommand(command, ui, value) { + var func; + + command = command.toLowerCase(); + if (func = commands.exec[command]) { + func(command, ui, value); + return TRUE; + } + + return FALSE; + }; + + function queryCommandState(command) { + var func; + + command = command.toLowerCase(); + if (func = commands.state[command]) + return func(command); + + return -1; + }; + + function queryCommandValue(command) { + var func; + + command = command.toLowerCase(); + if (func = commands.value[command]) + return func(command); + + return FALSE; + }; + + function addCommands(command_list, type) { + type = type || 'exec'; + + each(command_list, function(callback, command) { + each(command.toLowerCase().split(','), function(command) { + commands[type][command] = callback; + }); + }); + }; + + // Expose public methods + tinymce.extend(this, { + execCommand : execCommand, + queryCommandState : queryCommandState, + queryCommandValue : queryCommandValue, + addCommands : addCommands + }); + + // Private methods + + function execNativeCommand(command, ui, value) { + if (ui === undefined) + ui = FALSE; + + if (value === undefined) + value = null; + + return editor.getDoc().execCommand(command, ui, value); + }; + + function isFormatMatch(name) { + return editor.formatter.match(name); + }; + + function toggleFormat(name, value) { + editor.formatter.toggle(name, value ? {value : value} : undefined); + }; + + function storeSelection(type) { + bookmark = selection.getBookmark(type); + }; + + function restoreSelection() { + selection.moveToBookmark(bookmark); + }; + + // Add execCommand overrides + addCommands({ + // Ignore these, added for compatibility + 'mceResetDesignMode,mceBeginUndoLevel' : function() {}, + + // Add undo manager logic + 'mceEndUndoLevel,mceAddUndoLevel' : function() { + editor.undoManager.add(); + }, + + 'Cut,Copy,Paste' : function(command) { + var doc = editor.getDoc(), failed; + + // Try executing the native command + try { + execNativeCommand(command); + } catch (ex) { + // Command failed + failed = TRUE; + } + + // Present alert message about clipboard access not being available + if (failed || !doc.queryCommandEnabled(command)) { + if (tinymce.isGecko) { + editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) { + if (state) + open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank'); + }); + } else + editor.windowManager.alert(editor.getLang('clipboard_no_support')); + } + }, + + // Override unlink command + unlink : function(command) { + if (selection.isCollapsed()) + selection.select(selection.getNode()); + + execNativeCommand(command); + selection.collapse(FALSE); + }, + + // Override justify commands to use the text formatter engine + 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) { + var align = command.substring(7); + + // Remove all other alignments first + each('left,center,right,full'.split(','), function(name) { + if (align != name) + editor.formatter.remove('align' + name); + }); + + toggleFormat('align' + align); + }, + + // Override list commands to fix WebKit bug + 'InsertUnorderedList,InsertOrderedList' : function(command) { + var listElm, listParent; + + execNativeCommand(command); + + // WebKit produces lists within block elements so we need to split them + // we will replace the native list creation logic to custom logic later on + // TODO: Remove this when the list creation logic is removed + listElm = dom.getParent(selection.getNode(), 'ol,ul'); + if (listElm) { + listParent = listElm.parentNode; + + // If list is within a text block then split that block + if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) { + storeSelection(); + dom.split(listParent, listElm); + restoreSelection(); + } + } + }, + + // Override commands to use the text formatter engine + 'Bold,Italic,Underline,Strikethrough' : function(command) { + toggleFormat(command); + }, + + // Override commands to use the text formatter engine + 'ForeColor,HiliteColor,FontName' : function(command, ui, value) { + toggleFormat(command, value); + }, + + FontSize : function(command, ui, value) { + var fontClasses, fontSizes; + + // Convert font size 1-7 to styles + if (value >= 1 && value <= 7) { + fontSizes = tinymce.explode(settings.font_size_style_values); + fontClasses = tinymce.explode(settings.font_size_classes); + + if (fontClasses) + value = fontClasses[value - 1] || value; + else + value = fontSizes[value - 1] || value; + } + + toggleFormat(command, value); + }, + + RemoveFormat : function(command) { + editor.formatter.remove(command); + }, + + mceBlockQuote : function(command) { + toggleFormat('blockquote'); + }, + + FormatBlock : function(command, ui, value) { + return toggleFormat(value); + }, + + mceCleanup : function() { + storeSelection(); + editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE}); + restoreSelection(); + }, + + mceRemoveNode : function(command, ui, value) { + var node = value || selection.getNode(); + + // Make sure that the body node isn't removed + if (node != ed.getBody()) { + storeSelection(); + editor.dom.remove(node, TRUE); + restoreSelection(); + } + }, + + mceSelectNodeDepth : function(command, ui, value) { + var counter = 0; + + dom.getParent(selection.getNode(), function(node) { + if (node.nodeType == 1 && counter++ == value) { + selection.select(node); + return FALSE; + } + }, editor.getBody()); + }, + + mceSelectNode : function(command, ui, value) { + selection.select(value); + }, + + mceInsertContent : function(command, ui, value) { + selection.setContent(value); + }, + + mceInsertRawHTML : function(command, ui, value) { + selection.setContent('tiny_mce_marker'); + editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, value)); + }, + + mceSetContent : function(command, ui, value) { + editor.setContent(value); + }, + + 'Indent,Outdent' : function(command) { + var intentValue, indentUnit, value; + + // Setup indent level + intentValue = settings.indentation; + indentUnit = /[a-z%]+$/i.exec(intentValue); + intentValue = parseInt(intentValue); + + if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) { + each(selection.getSelectedBlocks(), function(element) { + if (command == 'outdent') { + value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue); + dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : ''); + } else + dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit); + }); + } else + execNativeCommand(command); + }, + + mceRepaint : function() { + var bookmark; + + if (tinymce.isGecko) { + try { + storeSelection(TRUE); + + if (selection.getSel()) + selection.getSel().selectAllChildren(editor.getBody()); + + selection.collapse(TRUE); + restoreSelection(); + } catch (ex) { + // Ignore + } + } + }, + + InsertHorizontalRule : function() { + selection.setContent('
    '); + }, + + mceToggleVisualAid : function() { + editor.hasVisual = !editor.hasVisual; + editor.addVisual(); + }, + + mceReplaceContent : function(command, ui, value) { + selection.setContent(value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'}))); + }, + + mceInsertLink : function(command, ui, value) { + var link = dom.getParent(selection.getNode(), 'a'); + + if (tinymce.is(value, 'string')) + value = {href : value}; + + if (!link) { + execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);'); + each(dom.select('a[href=javascript:mctmp(0);]'), function(link) { + dom.setAttribs(link, value); + }); + } else { + if (value.href) + dom.setAttribs(link, value); + else + ed.dom.remove(link, TRUE); + } + } + }); + + // Add queryCommandState overrides + addCommands({ + // Override justify commands + 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) { + return isFormatMatch('align' + command.substring(7)); + }, + + 'Bold,Italic,Underline,Strikethrough' : function(command) { + return isFormatMatch(command); + }, + + mceBlockQuote : function() { + return isFormatMatch('blockquote'); + }, + + Outdent : function() { + var node; + + if (settings.inline_styles) { + if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0) + return TRUE; + + if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0) + return TRUE; + } + + return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE')); + }, + + 'InsertUnorderedList,InsertOrderedList' : function(command) { + return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL'); + } + }, 'state'); + + // Add queryCommandValue overrides + addCommands({ + 'FontSize,FontName' : function(command) { + var value = 0, parent; + + if (parent = dom.getParent(selection.getNode(), 'span')) { + if (command == 'fontsize') + value = parent.style.fontSize; + else + value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase(); + } + + return value; + } + }, 'value'); + + // Add undo manager logic + if (settings.custom_undo_redo) { + addCommands({ + Undo : function() { + editor.undoManager.undo(); + }, + + Redo : function() { + editor.undoManager.redo(); + } + }); + } + }; +})(tinymce); +(function(tinymce) { + tinymce.create('tinymce.UndoManager', { + index : 0, + data : null, + typing : 0, + + UndoManager : function(ed) { + var t = this, Dispatcher = tinymce.util.Dispatcher; + + t.editor = ed; + t.data = []; + t.onAdd = new Dispatcher(this); + t.onUndo = new Dispatcher(this); + t.onRedo = new Dispatcher(this); + }, + + add : function(l) { + var t = this, i, ed = t.editor, b, s = ed.settings, la; + + l = l || {}; + l.content = l.content || ed.getContent({format : 'raw', no_events : 1}); + l.content = l.content.replace(/^\s*|\s*$/g, ''); + + // Add undo level if needed + la = t.data[t.index]; + if (la && la.content == l.content) { + if (t.index > 0 || t.data.length == 1) + return null; + } + + // Time to compress + if (s.custom_undo_redo_levels) { + if (t.data.length > s.custom_undo_redo_levels) { + for (i = 0; i < t.data.length - 1; i++) + t.data[i] = t.data[i + 1]; + + t.data.length--; + t.index = t.data.length; + } + } + + if (s.custom_undo_redo_restore_selection) + l.bookmark = b = l.bookmark || ed.selection.getBookmark(2, true); + + // Crop array if needed + if (t.index < t.data.length - 1) { + // Treat first level as initial + if (t.index == 0) + t.data = []; + else + t.data.length = t.index + 1; + } + + t.data.push(l); + t.index = t.data.length - 1; + + t.onAdd.dispatch(t, l); + ed.isNotDirty = 0; + + //console.log(t.index); + //console.dir(t.data); + + return l; + }, + + undo : function() { + var t = this, ed = t.editor, l = l, i; + + if (t.typing) { + t.add(); + t.typing = 0; + } + + if (t.index > 0) { + l = t.data[--t.index]; + + ed.setContent(l.content, {format : 'raw'}); + ed.selection.moveToBookmark(l.bookmark); + + t.onUndo.dispatch(t, l); + } + + return l; + }, + + redo : function() { + var t = this, ed = t.editor, l = null; + + if (t.index < t.data.length - 1) { + l = t.data[++t.index]; + ed.setContent(l.content, {format : 'raw'}); + ed.selection.moveToBookmark(l.bookmark); + + t.onRedo.dispatch(t, l); + } + + return l; + }, + + clear : function() { + var t = this; + + t.data = []; + t.index = 0; + t.typing = 0; + }, + + hasUndo : function() { + return this.index > 0 || this.typing; + }, + + hasRedo : function() { + return this.index < this.data.length - 1; + } + }); +})(tinymce); + +(function(tinymce) { + // Shorten names + var Event = tinymce.dom.Event, + isIE = tinymce.isIE, + isGecko = tinymce.isGecko, + isOpera = tinymce.isOpera, + each = tinymce.each, + extend = tinymce.extend, + TRUE = true, + FALSE = false; + + // Checks if the selection/caret is at the end of the specified block element + function isAtEnd(rng, par) { + var rng2 = par.ownerDocument.createRange(); + + rng2.setStart(rng.endContainer, rng.endOffset); + rng2.setEndAfter(par); + + // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element + return rng2.cloneContents().textContent.length == 0; + }; + + function isEmpty(n) { + n = n.innerHTML; + + n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars + n = n.replace(/<[^>]+>/g, ''); // Remove all tags + + return n.replace(/[ \u00a0\t\r\n]+/g, '') == ''; + }; + + function splitList(selection, dom, li) { + var listBlock, block; + + if (isEmpty(li)) { + listBlock = dom.getParent(li, 'ul,ol'); + + if (!dom.getParent(listBlock.parentNode, 'ul,ol')) { + dom.split(listBlock, li); + block = dom.create('p', 0, '
    '); + dom.replace(block, li); + selection.select(block, 1); + } + + return FALSE; + } + + return TRUE; + }; + + tinymce.create('tinymce.ForceBlocks', { + ForceBlocks : function(ed) { + var t = this, s = ed.settings, elm; + + t.editor = ed; + t.dom = ed.dom; + elm = (s.forced_root_block || 'p').toLowerCase(); + s.element = elm.toUpperCase(); + + ed.onPreInit.add(t.setup, t); + + t.reOpera = new RegExp('(\\u00a0| | )<\/' + elm + '>', 'gi'); + t.rePadd = new RegExp(']+)><\\\/p>|]+)\\\/>|]+)>\\s+<\\\/p>|

    <\\\/p>||

    \\s+<\\\/p>'.replace(/p/g, elm), 'gi'); + t.reNbsp2BR1 = new RegExp(']+)>[\\s\\u00a0]+<\\\/p>|

    [\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi'); + t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>( | )<\\\/%p>|<%p>( | )<\\\/%p>'.replace(/%p/g, elm), 'gi'); + t.reBR2Nbsp = new RegExp(']+)>\\s*
    \\s*<\\\/p>|

    \\s*
    \\s*<\\\/p>'.replace(/p/g, elm), 'gi'); + + function padd(ed, o) { + if (isOpera) + o.content = o.content.replace(t.reOpera, ''); + + o.content = o.content.replace(t.rePadd, '<' + elm + '$1$2$3$4$5$6>\u00a0'); + + if (!isIE && !isOpera && o.set) { + // Use   instead of BR in padded paragraphs + o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2>
    '); + o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2>
    '); + } else + o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0'); + }; + + ed.onBeforeSetContent.add(padd); + ed.onPostProcess.add(padd); + + if (s.forced_root_block) { + ed.onInit.add(t.forceRoots, t); + ed.onSetContent.add(t.forceRoots, t); + ed.onBeforeGetContent.add(t.forceRoots, t); + } + }, + + setup : function() { + var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection; + + // Force root blocks when typing and when getting output + if (s.forced_root_block) { + ed.onBeforeExecCommand.add(t.forceRoots, t); + ed.onKeyUp.add(t.forceRoots, t); + ed.onPreProcess.add(t.forceRoots, t); + } + + if (s.force_br_newlines) { + // Force IE to produce BRs on enter + if (isIE) { + ed.onKeyPress.add(function(ed, e) { + var n; + + if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') { + selection.setContent('
    ', {format : 'raw'}); + n = dom.get('__'); + n.removeAttribute('id'); + selection.select(n); + selection.collapse(); + return Event.cancel(e); + } + }); + } + } + + if (!isIE && s.force_p_newlines) { + ed.onKeyPress.add(function(ed, e) { + if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e)) + Event.cancel(e); + }); + + if (isGecko) { + ed.onKeyDown.add(function(ed, e) { + if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) + t.backspaceDelete(e, e.keyCode == 8); + }); + } + } + + // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973 + if (tinymce.isWebKit) { + function insertBr(ed) { + var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h; + + // Insert BR element + rng.insertNode(br = dom.create('br')); + + // Place caret after BR + rng.setStartAfter(br); + rng.setEndAfter(br); + selection.setRng(rng); + + // Could not place caret after BR then insert an nbsp entity and move the caret + if (selection.getSel().focusNode == br.previousSibling) { + selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br)); + selection.collapse(TRUE); + } + + // Create a temporary DIV after the BR and get the position as it + // seems like getPos() returns 0 for text nodes and BR elements. + dom.insertAfter(div, br); + divYPos = dom.getPos(div).y; + dom.remove(div); + + // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117 + if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port. + ed.getWin().scrollTo(0, divYPos); + }; + + ed.onKeyPress.add(function(ed, e) { + if (e.keyCode == 13 && (e.shiftKey || s.force_br_newlines)) { + insertBr(ed); + Event.cancel(e); + } + }); + } + + // Padd empty inline elements within block elements + // For example:

    becomes

     

    + ed.onPreProcess.add(function(ed, o) { + each(dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) { + if (isEmpty(p)) { + each(dom.select('span,em,strong,b,i', o.node), function(n) { + if (!n.hasChildNodes()) { + n.appendChild(ed.getDoc().createTextNode('\u00a0')); + return FALSE; // Break the loop one padding is enough + } + }); + } + }); + }); + + // IE specific fixes + if (isIE) { + // Replaces IE:s auto generated paragraphs with the specified element name + if (s.element != 'P') { + ed.onKeyPress.add(function(ed, e) { + t.lastElm = selection.getNode().nodeName; + }); + + ed.onKeyUp.add(function(ed, e) { + var bl, n = selection.getNode(), b = ed.getBody(); + + if (b.childNodes.length === 1 && n.nodeName == 'P') { + n = dom.rename(n, s.element); + selection.select(n); + selection.collapse(); + ed.nodeChanged(); + } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') { + bl = dom.getParent(n, 'p'); + + if (bl) { + dom.rename(bl, s.element); + ed.nodeChanged(); + } + } + }); + } + } + }, + + find : function(n, t, s) { + var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, FALSE), c = -1; + + while (n = w.nextNode()) { + c++; + + // Index by node + if (t == 0 && n == s) + return c; + + // Node by index + if (t == 1 && c == s) + return n; + } + + return -1; + }, + + forceRoots : function(ed, e) { + var t = this, ed = t.editor, b = ed.getBody(), d = ed.getDoc(), se = ed.selection, s = se.getSel(), r = se.getRng(), si = -2, ei, so, eo, tr, c = -0xFFFFFF; + var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid; + + // Fix for bug #1863847 + //if (e && e.keyCode == 13) + // return TRUE; + + // Wrap non blocks into blocks + for (i = nl.length - 1; i >= 0; i--) { + nx = nl[i]; + + // Ignore internal elements + if (nx.nodeType === 1 && nx.getAttribute('_mce_type')) { + bl = null; + continue; + } + + // Is text or non block element + if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) { + if (!bl) { + // Create new block but ignore whitespace + if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) { + // Store selection + if (si == -2 && r) { + if (!isIE) { + // If selection is element then mark it + if (r.startContainer.nodeType == 1 && (n = r.startContainer.childNodes[r.startOffset]) && n.nodeType == 1) { + // Save the id of the selected element + eid = n.getAttribute("id"); + n.setAttribute("id", "__mce"); + } else { + // If element is inside body, might not be the case in contentEdiable mode + if (ed.dom.getParent(r.startContainer, function(e) {return e === b;})) { + so = r.startOffset; + eo = r.endOffset; + si = t.find(b, 0, r.startContainer); + ei = t.find(b, 0, r.endContainer); + } + } + } else { + tr = d.body.createTextRange(); + tr.moveToElementText(b); + tr.collapse(1); + bp = tr.move('character', c) * -1; + + tr = r.duplicate(); + tr.collapse(1); + sp = tr.move('character', c) * -1; + + tr = r.duplicate(); + tr.collapse(0); + le = (tr.move('character', c) * -1) - sp; + + si = sp - bp; + ei = le; + } + } + + // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE + // See: http://support.microsoft.com/kb/829907 + bl = ed.dom.create(ed.settings.forced_root_block); + nx.parentNode.replaceChild(bl, nx); + bl.appendChild(nx); + } + } else { + if (bl.hasChildNodes()) + bl.insertBefore(nx, bl.firstChild); + else + bl.appendChild(nx); + } + } else + bl = null; // Time to create new block + } + + // Restore selection + if (si != -2) { + if (!isIE) { + bl = b.getElementsByTagName(ed.settings.element)[0]; + r = d.createRange(); + + // Select last location or generated block + if (si != -1) + r.setStart(t.find(b, 1, si), so); + else + r.setStart(bl, 0); + + // Select last location or generated block + if (ei != -1) + r.setEnd(t.find(b, 1, ei), eo); + else + r.setEnd(bl, 0); + + if (s) { + s.removeAllRanges(); + s.addRange(r); + } + } else { + try { + r = s.createRange(); + r.moveToElementText(b); + r.collapse(1); + r.moveStart('character', si); + r.moveEnd('character', ei); + r.select(); + } catch (ex) { + // Ignore + } + } + } else if (!isIE && (n = ed.dom.get('__mce'))) { + // Restore the id of the selected element + if (eid) + n.setAttribute('id', eid); + else + n.removeAttribute('id'); + + // Move caret before selected element + r = d.createRange(); + r.setStartBefore(n); + r.setEndBefore(n); + se.setRng(r); + } + }, + + getParentBlock : function(n) { + var d = this.dom; + + return d.getParent(n, d.isBlock); + }, + + insertPara : function(e) { + var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body; + var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car; + + // If root blocks are forced then use Operas default behavior since it's really good +// Removed due to bug: #1853816 +// if (se.forced_root_block && isOpera) +// return TRUE; + + // Setup before range + rb = d.createRange(); + + // If is before the first block element and in body, then move it into first block element + rb.setStart(s.anchorNode, s.anchorOffset); + rb.collapse(TRUE); + + // Setup after range + ra = d.createRange(); + + // If is before the first block element and in body, then move it into first block element + ra.setStart(s.focusNode, s.focusOffset); + ra.collapse(TRUE); + + // Setup start/end points + dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0; + sn = dir ? s.anchorNode : s.focusNode; + so = dir ? s.anchorOffset : s.focusOffset; + en = dir ? s.focusNode : s.anchorNode; + eo = dir ? s.focusOffset : s.anchorOffset; + + // If selection is in empty table cell + if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) { + if (sn.firstChild.nodeName == 'BR') + dom.remove(sn.firstChild); // Remove BR + + // Create two new block elements + if (sn.childNodes.length == 0) { + ed.dom.add(sn, se.element, null, '
    '); + aft = ed.dom.add(sn, se.element, null, '
    '); + } else { + n = sn.innerHTML; + sn.innerHTML = ''; + ed.dom.add(sn, se.element, null, n); + aft = ed.dom.add(sn, se.element, null, '
    '); + } + + // Move caret into the last one + r = d.createRange(); + r.selectNodeContents(aft); + r.collapse(1); + ed.selection.setRng(r); + + return FALSE; + } + + // If the caret is in an invalid location in FF we need to move it into the first block + if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) { + sn = en = sn.firstChild; + so = eo = 0; + rb = d.createRange(); + rb.setStart(sn, 0); + ra = d.createRange(); + ra.setStart(en, 0); + } + + // Never use body as start or end node + sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes + sn = sn.nodeName == "BODY" ? sn.firstChild : sn; + en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes + en = en.nodeName == "BODY" ? en.firstChild : en; + + // Get start and end blocks + sb = t.getParentBlock(sn); + eb = t.getParentBlock(en); + bn = sb ? sb.nodeName : se.element; // Get block name to create + + // Return inside list use default browser behavior + if (n = t.dom.getParent(sb, 'li,pre')) { + if (n.nodeName == 'LI') + return splitList(ed.selection, t.dom, n); + + return TRUE; + } + + // If caption or absolute layers then always generate new blocks within + if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) { + bn = se.element; + sb = null; + } + + // If caption or absolute layers then always generate new blocks within + if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) { + bn = se.element; + eb = null; + } + + // Use P instead + if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) { + bn = se.element; + sb = eb = null; + } + + // Setup new before and after blocks + bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn); + aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn); + + // Remove id from after clone + aft.removeAttribute('id'); + + // Is header and cursor is at the end, then force paragraph under + if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb)) + aft = ed.dom.create(se.element); + + // Find start chop node + n = sc = sn; + do { + if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName)) + break; + + sc = n; + } while ((n = n.previousSibling ? n.previousSibling : n.parentNode)); + + // Find end chop node + n = ec = en; + do { + if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName)) + break; + + ec = n; + } while ((n = n.nextSibling ? n.nextSibling : n.parentNode)); + + // Place first chop part into before block element + if (sc.nodeName == bn) + rb.setStart(sc, 0); + else + rb.setStartBefore(sc); + + rb.setEnd(sn, so); + bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari + + // Place secnd chop part within new block element + try { + ra.setEndAfter(ec); + } catch(ex) { + //console.debug(s.focusNode, s.focusOffset); + } + + ra.setStart(en, eo); + aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari + + // Create range around everything + r = d.createRange(); + if (!sc.previousSibling && sc.parentNode.nodeName == bn) { + r.setStartBefore(sc.parentNode); + } else { + if (rb.startContainer.nodeName == bn && rb.startOffset == 0) + r.setStartBefore(rb.startContainer); + else + r.setStart(rb.startContainer, rb.startOffset); + } + + if (!ec.nextSibling && ec.parentNode.nodeName == bn) + r.setEndAfter(ec.parentNode); + else + r.setEnd(ra.endContainer, ra.endOffset); + + // Delete and replace it with new block elements + r.deleteContents(); + + if (isOpera) + ed.getWin().scrollTo(0, vp.y); + + // Never wrap blocks in blocks + if (bef.firstChild && bef.firstChild.nodeName == bn) + bef.innerHTML = bef.firstChild.innerHTML; + + if (aft.firstChild && aft.firstChild.nodeName == bn) + aft.innerHTML = aft.firstChild.innerHTML; + + // Padd empty blocks + if (isEmpty(bef)) + bef.innerHTML = '
    '; + + function appendStyles(e, en) { + var nl = [], nn, n, i; + + e.innerHTML = ''; + + // Make clones of style elements + if (se.keep_styles) { + n = en; + do { + // We only want style specific elements + if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) { + nn = n.cloneNode(FALSE); + dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique + nl.push(nn); + } + } while (n = n.parentNode); + } + + // Append style elements to aft + if (nl.length > 0) { + for (i = nl.length - 1, nn = e; i >= 0; i--) + nn = nn.appendChild(nl[i]); + + // Padd most inner style element + nl[0].innerHTML = isOpera ? ' ' : '
    '; // Extra space for Opera so that the caret can move there + return nl[0]; // Move caret to most inner element + } else + e.innerHTML = isOpera ? ' ' : '
    '; // Extra space for Opera so that the caret can move there + }; + + // Fill empty afterblook with current style + if (isEmpty(aft)) + car = appendStyles(aft, en); + + // Opera needs this one backwards for older versions + if (isOpera && parseFloat(opera.version()) < 9.5) { + r.insertNode(bef); + r.insertNode(aft); + } else { + r.insertNode(aft); + r.insertNode(bef); + } + + // Normalize + aft.normalize(); + bef.normalize(); + + function first(n) { + return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE).nextNode() || n; + }; + + // Move cursor and scroll into view + r = d.createRange(); + r.selectNodeContents(isGecko ? first(car || aft) : car || aft); + r.collapse(1); + s.removeAllRanges(); + s.addRange(r); + + // scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs + y = ed.dom.getPos(aft).y; + ch = aft.clientHeight; + + // Is element within viewport + if (y < vp.y || y + ch > vp.y + vp.h) { + ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks + //console.debug('SCROLL!', 'vp.y: ' + vp.y, 'y' + y, 'vp.h' + vp.h, 'clientHeight' + aft.clientHeight, 'yyy: ' + (y < vp.y ? y : y - vp.h + aft.clientHeight)); + } + + return FALSE; + }, + + backspaceDelete : function(e, bs) { + var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn; + + // The caret sometimes gets stuck in Gecko if you delete empty paragraphs + // This workaround removes the element by hand and moves the caret to the previous element + if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) { + if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) { + // Find previous block element + n = sc; + while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ; + + if (n) { + if (sc != b.firstChild) { + // Find last text node + w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE); + while (tn = w.nextNode()) + n = tn; + + // Place caret at the end of last text node + r = ed.getDoc().createRange(); + r.setStart(n, n.nodeValue ? n.nodeValue.length : 0); + r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0); + se.setRng(r); + + // Remove the target container + ed.dom.remove(sc); + } + + return Event.cancel(e); + } + } + } + + // Gecko generates BR elements here and there, we don't like those so lets remove them + function handler(e) { + var pr; + + e = e.target; + + // A new BR was created in a block element, remove it + if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) { + pr = e.previousSibling; + + Event.remove(b, 'DOMNodeInserted', handler); + + // Is there whitespace at the end of the node before then we might need the pesky BR + // to place the caret at a correct location see bug: #2013943 + if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue)) + return; + + // Only remove BR elements that got inserted in the middle of the text + if (e.previousSibling || e.nextSibling) + ed.dom.remove(e); + } + }; + + // Listen for new nodes + Event._add(b, 'DOMNodeInserted', handler); + + // Remove listener + window.setTimeout(function() { + Event._remove(b, 'DOMNodeInserted', handler); + }, 1); + } + }); +})(tinymce); + +(function(tinymce) { + // Shorten names + var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend; + + tinymce.create('tinymce.ControlManager', { + ControlManager : function(ed, s) { + var t = this, i; + + s = s || {}; + t.editor = ed; + t.controls = {}; + t.onAdd = new tinymce.util.Dispatcher(t); + t.onPostRender = new tinymce.util.Dispatcher(t); + t.prefix = s.prefix || ed.id + '_'; + t._cls = {}; + + t.onPostRender.add(function() { + each(t.controls, function(c) { + c.postRender(); + }); + }); + }, + + get : function(id) { + return this.controls[this.prefix + id] || this.controls[id]; + }, + + setActive : function(id, s) { + var c = null; + + if (c = this.get(id)) + c.setActive(s); + + return c; + }, + + setDisabled : function(id, s) { + var c = null; + + if (c = this.get(id)) + c.setDisabled(s); + + return c; + }, + + add : function(c) { + var t = this; + + if (c) { + t.controls[c.id] = c; + t.onAdd.dispatch(c, t); + } + + return c; + }, + + createControl : function(n) { + var c, t = this, ed = t.editor; + + each(ed.plugins, function(p) { + if (p.createControl) { + c = p.createControl(n, t); + + if (c) + return false; + } + }); + + switch (n) { + case "|": + case "separator": + return t.createSeparator(); + } + + if (!c && ed.buttons && (c = ed.buttons[n])) + return t.createButton(n, c); + + return t.add(c); + }, + + createDropMenu : function(id, s, cc) { + var t = this, ed = t.editor, c, bm, v, cls; + + s = extend({ + 'class' : 'mceDropDown', + constrain : ed.settings.constrain_menus + }, s); + + s['class'] = s['class'] + ' ' + ed.getParam('skin') + 'Skin'; + if (v = ed.getParam('skin_variant')) + s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1); + + id = t.prefix + id; + cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu; + c = t.controls[id] = new cls(id, s); + c.onAddItem.add(function(c, o) { + var s = o.settings; + + s.title = ed.getLang(s.title, s.title); + + if (!s.onclick) { + s.onclick = function(v) { + if (s.cmd) + ed.execCommand(s.cmd, s.ui || false, s.value); + }; + } + }); + + ed.onRemove.add(function() { + c.destroy(); + }); + + // Fix for bug #1897785, #1898007 + if (tinymce.isIE) { + c.onShowMenu.add(function() { + // IE 8 needs focus in order to store away a range with the current collapsed caret location + ed.focus(); + + bm = ed.selection.getBookmark(1); + }); + + c.onHideMenu.add(function() { + if (bm) { + ed.selection.moveToBookmark(bm); + bm = 0; + } + }); + } + + return t.add(c); + }, + + createListBox : function(id, s, cc) { + var t = this, ed = t.editor, cmd, c, cls; + + if (t.get(id)) + return null; + + s.title = ed.translate(s.title); + s.scope = s.scope || ed; + + if (!s.onselect) { + s.onselect = function(v) { + ed.execCommand(s.cmd, s.ui || false, v || s.value); + }; + } + + s = extend({ + title : s.title, + 'class' : 'mce_' + id, + scope : s.scope, + control_manager : t + }, s); + + id = t.prefix + id; + + if (ed.settings.use_native_selects) + c = new tinymce.ui.NativeListBox(id, s); + else { + cls = cc || t._cls.listbox || tinymce.ui.ListBox; + c = new cls(id, s); + } + + t.controls[id] = c; + + // Fix focus problem in Safari + if (tinymce.isWebKit) { + c.onPostRender.add(function(c, n) { + // Store bookmark on mousedown + Event.add(n, 'mousedown', function() { + ed.bookmark = ed.selection.getBookmark(1); + }); + + // Restore on focus, since it might be lost + Event.add(n, 'focus', function() { + ed.selection.moveToBookmark(ed.bookmark); + ed.bookmark = null; + }); + }); + } + + if (c.hideMenu) + ed.onMouseDown.add(c.hideMenu, c); + + return t.add(c); + }, + + createButton : function(id, s, cc) { + var t = this, ed = t.editor, o, c, cls; + + if (t.get(id)) + return null; + + s.title = ed.translate(s.title); + s.label = ed.translate(s.label); + s.scope = s.scope || ed; + + if (!s.onclick && !s.menu_button) { + s.onclick = function() { + ed.execCommand(s.cmd, s.ui || false, s.value); + }; + } + + s = extend({ + title : s.title, + 'class' : 'mce_' + id, + unavailable_prefix : ed.getLang('unavailable', ''), + scope : s.scope, + control_manager : t + }, s); + + id = t.prefix + id; + + if (s.menu_button) { + cls = cc || t._cls.menubutton || tinymce.ui.MenuButton; + c = new cls(id, s); + ed.onMouseDown.add(c.hideMenu, c); + } else { + cls = t._cls.button || tinymce.ui.Button; + c = new cls(id, s); + } + + return t.add(c); + }, + + createMenuButton : function(id, s, cc) { + s = s || {}; + s.menu_button = 1; + + return this.createButton(id, s, cc); + }, + + createSplitButton : function(id, s, cc) { + var t = this, ed = t.editor, cmd, c, cls; + + if (t.get(id)) + return null; + + s.title = ed.translate(s.title); + s.scope = s.scope || ed; + + if (!s.onclick) { + s.onclick = function(v) { + ed.execCommand(s.cmd, s.ui || false, v || s.value); + }; + } + + if (!s.onselect) { + s.onselect = function(v) { + ed.execCommand(s.cmd, s.ui || false, v || s.value); + }; + } + + s = extend({ + title : s.title, + 'class' : 'mce_' + id, + scope : s.scope, + control_manager : t + }, s); + + id = t.prefix + id; + cls = cc || t._cls.splitbutton || tinymce.ui.SplitButton; + c = t.add(new cls(id, s)); + ed.onMouseDown.add(c.hideMenu, c); + + return c; + }, + + createColorSplitButton : function(id, s, cc) { + var t = this, ed = t.editor, cmd, c, cls, bm; + + if (t.get(id)) + return null; + + s.title = ed.translate(s.title); + s.scope = s.scope || ed; + + if (!s.onclick) { + s.onclick = function(v) { + if (tinymce.isIE) + bm = ed.selection.getBookmark(1); + + ed.execCommand(s.cmd, s.ui || false, v || s.value); + }; + } + + if (!s.onselect) { + s.onselect = function(v) { + ed.execCommand(s.cmd, s.ui || false, v || s.value); + }; + } + + s = extend({ + title : s.title, + 'class' : 'mce_' + id, + 'menu_class' : ed.getParam('skin') + 'Skin', + scope : s.scope, + more_colors_title : ed.getLang('more_colors') + }, s); + + id = t.prefix + id; + cls = cc || t._cls.colorsplitbutton || tinymce.ui.ColorSplitButton; + c = new cls(id, s); + ed.onMouseDown.add(c.hideMenu, c); + + // Remove the menu element when the editor is removed + ed.onRemove.add(function() { + c.destroy(); + }); + + // Fix for bug #1897785, #1898007 + if (tinymce.isIE) { + c.onShowMenu.add(function() { + // IE 8 needs focus in order to store away a range with the current collapsed caret location + ed.focus(); + bm = ed.selection.getBookmark(1); + }); + + c.onHideMenu.add(function() { + if (bm) { + ed.selection.moveToBookmark(bm); + bm = 0; + } + }); + } + + return t.add(c); + }, + + createToolbar : function(id, s, cc) { + var c, t = this, cls; + + id = t.prefix + id; + cls = cc || t._cls.toolbar || tinymce.ui.Toolbar; + c = new cls(id, s); + + if (t.get(id)) + return null; + + return t.add(c); + }, + + createSeparator : function(cc) { + var cls = cc || this._cls.separator || tinymce.ui.Separator; + + return new cls(); + }, + + setControlType : function(n, c) { + return this._cls[n.toLowerCase()] = c; + }, + + destroy : function() { + each(this.controls, function(c) { + c.destroy(); + }); + + this.controls = null; + } + }); +})(tinymce); + +(function(tinymce) { + var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera; + + tinymce.create('tinymce.WindowManager', { + WindowManager : function(ed) { + var t = this; + + t.editor = ed; + t.onOpen = new Dispatcher(t); + t.onClose = new Dispatcher(t); + t.params = {}; + t.features = {}; + }, + + open : function(s, p) { + var t = this, f = '', x, y, mo = t.editor.settings.dialog_type == 'modal', w, sw, sh, vp = tinymce.DOM.getViewPort(), u; + + // Default some options + s = s || {}; + p = p || {}; + sw = isOpera ? vp.w : screen.width; // Opera uses windows inside the Opera window + sh = isOpera ? vp.h : screen.height; + s.name = s.name || 'mc_' + new Date().getTime(); + s.width = parseInt(s.width || 320); + s.height = parseInt(s.height || 240); + s.resizable = true; + s.left = s.left || parseInt(sw / 2.0) - (s.width / 2.0); + s.top = s.top || parseInt(sh / 2.0) - (s.height / 2.0); + p.inline = false; + p.mce_width = s.width; + p.mce_height = s.height; + p.mce_auto_focus = s.auto_focus; + + if (mo) { + if (isIE) { + s.center = true; + s.help = false; + s.dialogWidth = s.width + 'px'; + s.dialogHeight = s.height + 'px'; + s.scroll = s.scrollbars || false; + } + } + + // Build features string + each(s, function(v, k) { + if (tinymce.is(v, 'boolean')) + v = v ? 'yes' : 'no'; + + if (!/^(name|url)$/.test(k)) { + if (isIE && mo) + f += (f ? ';' : '') + k + ':' + v; + else + f += (f ? ',' : '') + k + '=' + v; + } + }); + + t.features = s; + t.params = p; + t.onOpen.dispatch(t, s, p); + + u = s.url || s.file; + u = tinymce._addVer(u); + + try { + if (isIE && mo) { + w = 1; + window.showModalDialog(u, window, f); + } else + w = window.open(u, s.name, f); + } catch (ex) { + // Ignore + } + + if (!w) + alert(t.editor.getLang('popup_blocked')); + }, + + close : function(w) { + w.close(); + this.onClose.dispatch(this); + }, + + createInstance : function(cl, a, b, c, d, e) { + var f = tinymce.resolve(cl); + + return new f(a, b, c, d, e); + }, + + confirm : function(t, cb, s, w) { + w = w || window; + + cb.call(s || this, w.confirm(this._decode(this.editor.getLang(t, t)))); + }, + + alert : function(tx, cb, s, w) { + var t = this; + + w = w || window; + w.alert(t._decode(t.editor.getLang(tx, tx))); + + if (cb) + cb.call(s || t); + }, + + resizeBy : function(dw, dh, win) { + win.resizeBy(dw, dh); + }, + + // Internal functions + + _decode : function(s) { + return tinymce.DOM.decode(s).replace(/\\n/g, '\n'); + } + }); +}(tinymce)); +(function(tinymce) { + function CommandManager() { + var execCommands = {}, queryStateCommands = {}, queryValueCommands = {}; + + function add(collection, cmd, func, scope) { + if (typeof(cmd) == 'string') + cmd = [cmd]; + + tinymce.each(cmd, function(cmd) { + collection[cmd.toLowerCase()] = {func : func, scope : scope}; + }); + }; + + tinymce.extend(this, { + add : function(cmd, func, scope) { + add(execCommands, cmd, func, scope); + }, + + addQueryStateHandler : function(cmd, func, scope) { + add(queryStateCommands, cmd, func, scope); + }, + + addQueryValueHandler : function(cmd, func, scope) { + add(queryValueCommands, cmd, func, scope); + }, + + execCommand : function(scope, cmd, ui, value, args) { + if (cmd = execCommands[cmd.toLowerCase()]) { + if (cmd.func.call(scope || cmd.scope, ui, value, args) !== false) + return true; + } + }, + + queryCommandValue : function() { + if (cmd = queryValueCommands[cmd.toLowerCase()]) + return cmd.func.call(scope || cmd.scope, ui, value, args); + }, + + queryCommandState : function() { + if (cmd = queryStateCommands[cmd.toLowerCase()]) + return cmd.func.call(scope || cmd.scope, ui, value, args); + } + }); + }; + + tinymce.GlobalCommands = new CommandManager(); +})(tinymce); +(function(tinymce) { + tinymce.Formatter = function(ed) { + var formats = {}, + each = tinymce.each, + dom = ed.dom, + selection = ed.selection, + TreeWalker = tinymce.dom.TreeWalker, + rangeUtils = new tinymce.dom.RangeUtils(dom), + isValid = ed.schema.isValid, + isBlock = dom.isBlock, + forcedRootBlock = ed.settings.forced_root_block, + nodeIndex = dom.nodeIndex, + INVISIBLE_CHAR = '\uFEFF', + MCE_ATTR_RE = /^(src|href|style)$/, + FALSE = false, + TRUE = true, + undefined, + caretHandler, + pendingFormats; + + function getParents(node, selector) { + return dom.getParents(node, selector, dom.getRoot()); + }; + + function resetPending() { + // Needs reset + if (!pendingFormats || pendingFormats.apply.length || pendingFormats.remove.length) + pendingFormats = {apply : [], remove : []}; + }; + + ed.onMouseUp.add(resetPending); + resetPending(); + + // Public functions + + function get(name) { + return name ? formats[name] : formats; + }; + + function register(name, format) { + if (name) { + if (typeof(name) !== 'string') { + each(name, function(format, name) { + register(name, format); + }); + } else { + // Force format into array and add it to internal collection + format = format.length ? format : [format]; + + each(format, function(format) { + // Set deep to false by default on selector formats this to avoid removing + // alignment on images inside paragraphs when alignment is changed on paragraphs + if (format.deep === undefined) + format.deep = !format.selector; + + // Default to true + if (format.split === undefined) + format.split = !format.selector; + + // Default to true + if (format.remove === undefined && format.selector) + format.remove = 'none'; + + // Split classes if needed + if (typeof(format.classes) === 'string') + format.classes = format.classes.split(/\s+/); + }); + + formats[name] = format; + } + } + }; + + function apply(name, vars, node) { + var formatList = get(name), format = formatList[0], bookmark, rng, i; + + function moveStart(rng) { + var container = rng.startContainer, + offset = rng.startOffset, + walker, node; + + // Move startContainer/startOffset in to a suitable node + if (container.nodeType == 1 || container.nodeValue === "") { + walker = new TreeWalker(container.childNodes[offset]); + for (node = walker.current(); node; node = walker.next()) { + if (node.nodeType == 3 && !isBlock(node.parentNode) && !isWhiteSpaceNode(node)) { + rng.setStart(node, 0); + break; + } + } + } + + return rng; + }; + + function setElementFormat(elm, fmt) { + fmt = fmt || format; + + if (elm) { + each(fmt.styles, function(value, name) { + dom.setStyle(elm, name, replaceVars(value, vars)); + }); + + each(fmt.attributes, function(value, name) { + dom.setAttrib(elm, name, replaceVars(value, vars)); + }); + + each(fmt.classes, function(value) { + value = replaceVars(value, vars); + + if (!dom.hasClass(elm, value)) + dom.addClass(elm, value); + }); + } + }; + + function applyRngStyle(rng) { + var newWrappers = [], wrapName, wrapElm; + + // Setup wrapper element + wrapName = format.inline || format.block; + wrapElm = dom.create(wrapName); + setElementFormat(wrapElm); + + rangeUtils.walk(rng, function(nodes) { + var currentWrapElm; + + function process(node) { + var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(); + + // Stop wrapping on br elements + if (isEq(nodeName, 'br')) { + currentWrapElm = 0; + + // Remove any br elements when we wrap things + if (format.block) + dom.remove(node); + + return; + } + + // If node is wrapper type + if (format.wrapper && matchNode(node, name, vars)) { + currentWrapElm = 0; + return; + } + + // Can we rename the block + if (format.block && !format.wrapper && isTextBlock(nodeName)) { + node = dom.rename(node, wrapName); + setElementFormat(node); + newWrappers.push(node); + currentWrapElm = 0; + return; + } + + // Handle selector patterns + if (format.selector) { + // Look for matching formats + each(formatList, function(format) { + if (dom.is(node, format.selector)) + setElementFormat(node, format); + }); + + return; + } + + // Is it valid to wrap this item + if (isValid(wrapName, nodeName) && isValid(parentName, wrapName)) { + // Start wrapping + if (!currentWrapElm) { + // Wrap the node + currentWrapElm = wrapElm.cloneNode(FALSE); + node.parentNode.insertBefore(currentWrapElm, node); + newWrappers.push(currentWrapElm); + } + + currentWrapElm.appendChild(node); + } else { + // Start a new wrapper for possible children + currentWrapElm = 0; + + each(tinymce.grep(node.childNodes), process); + + // End the last wrapper + currentWrapElm = 0; + } + }; + + // Process siblings from range + each(nodes, process); + }); + + // Cleanup + each(newWrappers, function(node) { + var childCount; + + function getChildCount(node) { + var count = 0; + + each(node.childNodes, function(node) { + if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) + count++; + }); + + return count; + }; + + function mergeStyles(node) { + var child, clone; + + each(node.childNodes, function(node) { + if (node.nodeType == 1 && !isBookmarkNode(node)) { + child = node; + return FALSE; // break loop + } + }); + + // If child was found and of the same type as the current node + if (child && matchName(child, format)) { + clone = child.cloneNode(FALSE); + setElementFormat(clone); + + dom.replace(clone, node, TRUE); + dom.remove(child, 1); + } + + return clone || node; + }; + + childCount = getChildCount(node); + + // Remove empty nodes + if (childCount === 0) { + dom.remove(node, 1); + return; + } + + if (format.inline || format.wrapper) { + // Merges the current node with it's children of similar type to reduce the number of elements + if (!format.exact && childCount === 1) + node = mergeStyles(node); + + // Remove/merge children + each(formatList, function(format) { + // Merge all children of similar type will move styles from child to parent + // this: text + // will become: text + each(dom.select(format.inline, node), function(child) { + removeFormat(format, vars, child, format.exact ? child : null); + }); + }); + + // Look for parent with similar style format + dom.getParent(node.parentNode, function(parent) { + if (matchNode(parent, name, vars)) { + dom.remove(node, 1); + node = 0; + return TRUE; + } + }); + + // Merge next and previous siblings if they are similar texttext becomes texttext + if (node) { + node = mergeSiblings(getNonWhiteSpaceSibling(node), node); + node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE)); + } + } + }); + }; + + if (format) { + if (node) { + rng = dom.createRng(); + + rng.setStartBefore(node); + rng.setEndAfter(node); + + applyRngStyle(rng); + } else { + if (!selection.isCollapsed() || !format.inline) { + // Apply formatting to selection + bookmark = selection.getBookmark(); + applyRngStyle(expandRng(selection.getRng(TRUE), formatList)); + + selection.moveToBookmark(bookmark); + selection.setRng(moveStart(selection.getRng(TRUE))); + ed.nodeChanged(); + } else + performCaretAction('apply', name, vars); + } + } + }; + + function remove(name, vars, node) { + var formatList = get(name), format = formatList[0], bookmark, i, rng; + + // Merges the styles for each node + function process(node) { + var children, i, l; + + // Grab the children first since the nodelist might be changed + children = tinymce.grep(node.childNodes); + + // Process current node + for (i = 0, l = formatList.length; i < l; i++) { + if (removeFormat(formatList[i], vars, node, node)) + break; + } + + // Process the children + if (format.deep) { + for (i = 0, l = children.length; i < l; i++) + process(children[i]); + } + }; + + function findFormatRoot(container) { + var formatRoot; + + // Find format root + each(getParents(container.parentNode).reverse(), function(parent) { + // Find format root element + if (!formatRoot && parent.id != '_start' && parent.id != '_end') { + // If the matched format has a remove none flag we shouldn't split it + if (!isBlock(parent) && matchNode(parent, name, vars)) + formatRoot = parent; + } + }); + + return formatRoot; + }; + + function wrapAndSplit(format_root, container, target, split) { + var parent, clone, lastClone, firstClone, i, formatRootParent; + + // Format root found then clone formats and split it + if (format_root) { + formatRootParent = format_root.parentNode; + + for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) { + clone = parent.cloneNode(FALSE); + + for (i = 0; i < formatList.length; i++) { + if (removeFormat(formatList[i], vars, clone, clone)) { + clone = 0; + break; + } + } + + // Build wrapper node + if (clone) { + if (lastClone) + clone.appendChild(lastClone); + + if (!firstClone) + firstClone = clone; + + lastClone = clone; + } + } + + if (split) + container = dom.split(format_root, container); + + // Wrap container in cloned formats + if (lastClone) { + target.parentNode.insertBefore(lastClone, target); + firstClone.appendChild(target); + } + } + + return container; + }; + + function splitToFormatRoot(container) { + return wrapAndSplit(findFormatRoot(container), container, container, true); + }; + + function unwrap(start) { + var node = dom.get(start ? '_start' : '_end'), + out = node[start ? 'firstChild' : 'lastChild']; + + dom.remove(node, 1); + + return out; + }; + + function removeRngStyle(rng) { + var startContainer, endContainer; + + rng = expandRng(rng, formatList, TRUE); + + if (format.split) { + startContainer = getContainer(rng, TRUE); + endContainer = getContainer(rng); + + if (startContainer != endContainer) { + // Wrap start/end nodes in span element since these might be cloned/moved + startContainer = wrap(startContainer, 'span', {id : '_start', _mce_type : 'bookmark'}); + endContainer = wrap(endContainer, 'span', {id : '_end', _mce_type : 'bookmark'}); + + // Split start/end + splitToFormatRoot(startContainer); + splitToFormatRoot(endContainer); + + // Unwrap start/end to get real elements again + startContainer = unwrap(TRUE); + endContainer = unwrap(); + } else + startContainer = endContainer = splitToFormatRoot(startContainer); + + // Update range positions since they might have changed after the split operations + rng.startContainer = startContainer.parentNode; + rng.startOffset = nodeIndex(startContainer); + rng.endContainer = endContainer.parentNode; + rng.endOffset = nodeIndex(endContainer) + 1; + } + + // Remove items between start/end + rangeUtils.walk(rng, function(nodes) { + each(nodes, function(node) { + process(node); + }); + }); + }; + + // Handle node + if (node) { + rng = dom.createRng(); + rng.setStartBefore(node); + rng.setEndAfter(node); + removeRngStyle(rng); + return; + } + + if (!selection.isCollapsed() || !format.inline) { + bookmark = selection.getBookmark(); + removeRngStyle(selection.getRng(TRUE)); + selection.moveToBookmark(bookmark); + ed.nodeChanged(); + } else + performCaretAction('remove', name, vars); + }; + + function toggle(name, vars, node) { + if (match(name, vars, node)) + remove(name, vars, node); + else + apply(name, vars, node); + }; + + function matchNode(node, name, vars) { + var formatList = get(name), format, i, classes; + + function matchItems(node, format, item_name) { + var key, value, items = format[item_name], i; + + // Check all items + if (items) { + // Non indexed object + if (items.length === undefined) { + for (key in items) { + if (items.hasOwnProperty(key)) { + if (item_name === 'attributes') + value = dom.getAttrib(node, key); + else + value = getStyle(node, key); + + if (!isEq(value, replaceVars(items[key], vars))) + return; + } + } + } else { + // Only one match needed for indexed arrays + for (i = 0; i < items.length; i++) { + if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) + return TRUE; + } + } + } + + return TRUE; + }; + + if (formatList && node) { + // Check each format in list + for (i = 0; i < formatList.length; i++) { + format = formatList[i]; + + // Name name, attributes, styles and classes + if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) { + // Match classes + if (classes = format.classes) { + for (i = 0; i < classes.length; i++) { + if (!dom.hasClass(node, classes[i])) + return; + } + } + + return TRUE; + } + } + } + }; + + function match(name, vars, node) { + var startNode, i; + + function matchParents(node) { + // Find first node with similar format settings + node = dom.getParent(node, function(node) { + return !!matchNode(node, name, vars); + }); + + // Do an exact check on the similar format element + return matchNode(node, name, vars); + }; + + // Check specified node + if (node) + return matchParents(node); + + // Check pending formats + if (selection.isCollapsed()) { + for (i = pendingFormats.apply.length - 1; i >= 0; i--) { + if (pendingFormats.apply[i].name == name) + return true; + } + + for (i = pendingFormats.remove.length - 1; i >= 0; i--) { + if (pendingFormats.remove[i].name == name) + return false; + } + + return matchParents(selection.getNode()); + } + + // Check selected node + node = selection.getNode(); + if (matchParents(node)) + return TRUE; + + // Check start node if it's different + startNode = selection.getStart(); + if (startNode != node) { + if (matchParents(startNode)) + return TRUE; + } + + return FALSE; + }; + + function canApply(name) { + var formatList = get(name), startNode, parents, i, x, selector; + + if (formatList) { + startNode = selection.getStart(); + parents = getParents(startNode); + + for (x = formatList.length - 1; x >= 0; x--) { + selector = formatList[x].selector; + + // Format is not selector based, then always return TRUE + if (!selector) + return TRUE; + + for (i = parents.length - 1; i >= 0; i--) { + if (dom.is(parents[i], selector)) + return TRUE; + } + } + } + + return FALSE; + }; + + // Expose to public + tinymce.extend(this, { + get : get, + register : register, + apply : apply, + remove : remove, + toggle : toggle, + match : match, + matchNode : matchNode, + canApply : canApply + }); + + // Private functions + + function matchName(node, format) { + // Check for inline match + if (isEq(node, format.inline)) + return TRUE; + + // Check for block match + if (isEq(node, format.block)) + return TRUE; + + // Check for selector match + if (format.selector) + return dom.is(node, format.selector); + }; + + function isEq(str1, str2) { + str1 = str1 || ''; + str2 = str2 || ''; + + str1 = str1.nodeName || str1; + str2 = str2.nodeName || str2; + + return str1.toLowerCase() == str2.toLowerCase(); + }; + + function getStyle(node, name) { + var styleVal = dom.getStyle(node, name); + + // Force the format to hex + if (name == 'color' || name == 'backgroundColor') + styleVal = dom.toHex(styleVal); + + // Opera will return bold as 700 + if (name == 'fontWeight' && styleVal == 700) + styleVal = 'bold'; + + return '' + styleVal; + }; + + function replaceVars(value, vars) { + if (typeof(value) != "string") + value = value(vars); + else if (vars) { + value = value.replace(/%(\w+)/g, function(str, name) { + return vars[name] || str; + }); + } + + return value; + }; + + function isWhiteSpaceNode(node) { + return node && node.nodeType === 3 && /^\s*$/.test(node.nodeValue); + }; + + function wrap(node, name, attrs) { + var wrapper = dom.create(name, attrs); + + node.parentNode.insertBefore(wrapper, node); + wrapper.appendChild(node); + + return wrapper; + }; + + function expandRng(rng, format, remove) { + var startContainer = rng.startContainer, + startOffset = rng.startOffset, + endContainer = rng.endContainer, + endOffset = rng.endOffset, sibling, lastIdx; + + // This function walks up the tree if there is no siblings before/after the node + function findParentContainer(container, child_name, sibling_name, root) { + var parent, child; + + root = root || dom.getRoot(); + + for (;;) { + // Check if we can move up are we at root level or body level + parent = container.parentNode; + + // Stop expanding on block elements or root depending on format + if (parent == root || (!format[0].block_expand && isBlock(parent))) + return container; + + for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) { + if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) + return container; + + if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling)) + return container; + } + + container = container.parentNode; + } + + return container; + }; + + // If index based start position then resolve it + if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) { + lastIdx = startContainer.childNodes.length - 1; + startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset]; + + if (startContainer.nodeType == 3) + startOffset = 0; + } + + // If index based end position then resolve it + if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) { + lastIdx = endContainer.childNodes.length - 1; + endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1]; + + if (endContainer.nodeType == 3) + endOffset = endContainer.nodeValue.length; + } + + // Exclude bookmark nodes if possible + if (isBookmarkNode(startContainer.parentNode)) + startContainer = startContainer.parentNode; + + if (isBookmarkNode(startContainer)) + startContainer = startContainer.nextSibling || startContainer; + + if (isBookmarkNode(endContainer.parentNode)) + endContainer = endContainer.parentNode; + + if (isBookmarkNode(endContainer)) + endContainer = endContainer.previousSibling || endContainer; + + // Move start/end point up the tree if the leaves are sharp and if we are in different containers + // Example * becomes !: !

    *texttext*

    ! + // This will reduce the number of wrapper elements that needs to be created + // Move start point up the tree + if (format[0].inline || format[0].block_expand) { + startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling'); + endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling'); + } + + // Expand start/end container to matching selector + if (format[0].selector && format[0].expand !== FALSE) { + function findSelectorEndPoint(container, sibling_name) { + var parents, i, y; + + if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name]) + container = container[sibling_name]; + + parents = getParents(container); + for (i = 0; i < parents.length; i++) { + for (y = 0; y < format.length; y++) { + if (dom.is(parents[i], format[y].selector)) + return parents[i]; + } + } + + return container; + }; + + // Find new startContainer/endContainer if there is better one + startContainer = findSelectorEndPoint(startContainer, 'previousSibling'); + endContainer = findSelectorEndPoint(endContainer, 'nextSibling'); + } + + // Expand start/end container to matching block element or text node + if (format[0].block || format[0].selector) { + function findBlockEndPoint(container, sibling_name, sibling_name2) { + var node; + + // Expand to block of similar type + if (!format[0].wrapper) + node = dom.getParent(container, format[0].block); + + // Expand to first wrappable block element or any block element + if (!node) + node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock); + + // Exclude inner lists from wrapping + if (node && format[0].wrapper) + node = getParents(node, 'ul,ol').reverse()[0] || node; + + // Didn't find a block element look for first/last wrappable element + if (!node) { + node = container; + + while (node[sibling_name] && !isBlock(node[sibling_name])) { + node = node[sibling_name]; + + // Break on BR but include it will be removed later on + // we can't remove it now since we need to check if it can be wrapped + if (isEq(node, 'br')) + break; + } + } + + return node || container; + }; + + // Find new startContainer/endContainer if there is better one + startContainer = findBlockEndPoint(startContainer, 'previousSibling'); + endContainer = findBlockEndPoint(endContainer, 'nextSibling'); + + // Non block element then try to expand up the leaf + if (format[0].block) { + if (!isBlock(startContainer)) + startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling'); + + if (!isBlock(endContainer)) + endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling'); + } + } + + // Setup index for startContainer + if (startContainer.nodeType == 1) { + startOffset = nodeIndex(startContainer); + startContainer = startContainer.parentNode; + } + + // Setup index for endContainer + if (endContainer.nodeType == 1) { + endOffset = nodeIndex(endContainer) + 1; + endContainer = endContainer.parentNode; + } + + // Return new range like object + return { + startContainer : startContainer, + startOffset : startOffset, + endContainer : endContainer, + endOffset : endOffset + }; + } + + function removeFormat(format, vars, node, compare_node) { + var i, attrs, stylesModified; + + // Check if node matches format + if (!matchName(node, format)) + return FALSE; + + // Should we compare with format attribs and styles + if (format.remove != 'all') { + // Remove styles + each(format.styles, function(value, name) { + value = replaceVars(value, vars); + + // Indexed array + if (typeof(name) === 'number') { + name = value; + compare_node = 0; + } + + if (!compare_node || isEq(getStyle(compare_node, name), value)) + dom.setStyle(node, name, ''); + + stylesModified = 1; + }); + + // Remove style attribute if it's empty + if (stylesModified && dom.getAttrib(node, 'style') == '') { + node.removeAttribute('style'); + node.removeAttribute('_mce_style'); + } + + // Remove attributes + each(format.attributes, function(value, name) { + var valueOut; + + value = replaceVars(value, vars); + + // Indexed array + if (typeof(name) === 'number') { + name = value; + compare_node = 0; + } + + if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) { + // Keep internal classes + if (name == 'class') { + value = dom.getAttrib(node, name); + if (value) { + // Build new class value where everything is removed except the internal prefixed classes + valueOut = ''; + each(value.split(/\s+/), function(cls) { + if (/mce\w+/.test(cls)) + valueOut += (valueOut ? ' ' : '') + cls; + }); + + // We got some internal classes left + if (valueOut) { + dom.setAttrib(node, name, valueOut); + return; + } + } + } + + // IE6 has a bug where the attribute doesn't get removed correctly + if (name == "class") + node.removeAttribute('className'); + + // Remove mce prefixed attributes + if (MCE_ATTR_RE.test(name)) + node.removeAttribute('_mce_' + name); + + node.removeAttribute(name); + } + }); + + // Remove classes + each(format.classes, function(value) { + value = replaceVars(value, vars); + + if (!compare_node || dom.hasClass(compare_node, value)) + dom.removeClass(node, value); + }); + + // Check for non internal attributes + attrs = dom.getAttribs(node); + for (i = 0; i < attrs.length; i++) { + if (attrs[i].nodeName.indexOf('_') !== 0) + return FALSE; + } + } + + // Remove the inline child if it's empty for example or + if (format.remove != 'none') { + removeNode(node, format); + return TRUE; + } + }; + + function removeNode(node, format) { + var parentNode = node.parentNode, rootBlockElm; + + if (format.block) { + if (!forcedRootBlock) { + function find(node, next, inc) { + node = getNonWhiteSpaceSibling(node, next, inc); + + return !node || (node.nodeName == 'BR' || isBlock(node)); + }; + + // Append BR elements if needed before we remove the block + if (isBlock(node) && !isBlock(parentNode)) { + if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) + node.insertBefore(dom.create('br'), node.firstChild); + + if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) + node.appendChild(dom.create('br')); + } + } else { + // Wrap the block in a forcedRootBlock if we are at the root of document + if (parentNode == dom.getRoot()) { + if (!format.list_block || !isEq(node, format.list_block)) { + each(tinymce.grep(node.childNodes), function(node) { + if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) { + if (!rootBlockElm) + rootBlockElm = wrap(node, forcedRootBlock); + else + rootBlockElm.appendChild(node); + } else + rootBlockElm = 0; + }); + } + } + } + } + + dom.remove(node, 1); + }; + + function getNonWhiteSpaceSibling(node, next, inc) { + if (node) { + next = next ? 'nextSibling' : 'previousSibling'; + + for (node = inc ? node : node[next]; node; node = node[next]) { + if (node.nodeType == 1 || !isWhiteSpaceNode(node)) + return node; + } + } + }; + + function isBookmarkNode(node) { + return node && node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark'; + }; + + function mergeSiblings(prev, next) { + var marker, sibling, tmpSibling; + + function compareElements(node1, node2) { + // Not the same name + if (node1.nodeName != node2.nodeName) + return FALSE; + + function getAttribs(node) { + var attribs = {}; + + each(dom.getAttribs(node), function(attr) { + var name = attr.nodeName.toLowerCase(); + + // Don't compare internal attributes or style + if (name.indexOf('_') !== 0 && name !== 'style') + attribs[name] = dom.getAttrib(node, name); + }); + + return attribs; + }; + + function compareObjects(obj1, obj2) { + var value, name; + + for (name in obj1) { + // Obj1 has item obj2 doesn't have + if (obj1.hasOwnProperty(name)) { + value = obj2[name]; + + // Obj2 doesn't have obj1 item + if (value === undefined) + return FALSE; + + // Obj2 item has a different value + if (obj1[name] != value) + return FALSE; + + // Delete similar value + delete obj2[name]; + } + } + + // Check if obj 2 has something obj 1 doesn't have + for (name in obj2) { + // Obj2 has item obj1 doesn't have + if (obj2.hasOwnProperty(name)) + return FALSE; + } + + return TRUE; + }; + + // Attribs are not the same + if (!compareObjects(getAttribs(node1), getAttribs(node2))) + return FALSE; + + // Styles are not the same + if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) + return FALSE; + + return TRUE; + }; + + // Check if next/prev exists and that they are elements + if (prev && next) { + function findElementSibling(node, sibling_name) { + for (sibling = node; sibling; sibling = sibling[sibling_name]) { + if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling)) + return node; + + if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) + return sibling; + } + + return node; + }; + + // If previous sibling is empty then jump over it + prev = findElementSibling(prev, 'previousSibling'); + next = findElementSibling(next, 'nextSibling'); + + // Compare next and previous nodes + if (compareElements(prev, next)) { + // Append nodes between + for (sibling = prev.nextSibling; sibling && sibling != next;) { + tmpSibling = sibling; + sibling = sibling.nextSibling; + prev.appendChild(tmpSibling); + } + + // Remove next node + dom.remove(next); + + // Move children into prev node + each(tinymce.grep(next.childNodes), function(node) { + prev.appendChild(node); + }); + + return prev; + } + } + + return next; + }; + + function isTextBlock(name) { + return /^(h[1-6]|p|div|pre|address)$/.test(name); + }; + + function getContainer(rng, start) { + var container, offset, lastIdx; + + container = rng[start ? 'startContainer' : 'endContainer']; + offset = rng[start ? 'startOffset' : 'endOffset']; + + if (container.nodeType == 1) { + lastIdx = container.childNodes.length - 1; + + if (!start && offset) + offset--; + + container = container.childNodes[offset > lastIdx ? lastIdx : offset]; + } + + return container; + }; + + function performCaretAction(type, name, vars) { + var i, rng, selectedNode = selection.getNode().parentNode, + doc = ed.getDoc(), marker = 'mceinline', + events = ['onKeyDown', 'onKeyUp', 'onKeyPress'], + currentPendingFormats = pendingFormats[type], + otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply']; + + // Check if it already exists + for (i = currentPendingFormats.length - 1; i >= 0; i--) { + if (currentPendingFormats[i].name == name) + return; + } + + currentPendingFormats.push({name : name, vars : vars}); + + // Check if it's in the oter type + for (i = otherPendingFormats.length - 1; i >= 0; i--) { + if (otherPendingFormats[i].name == name) + otherPendingFormats.splice(i, 1); + } + + function unbind() { + if (caretHandler) { + each(events, function(event) { + ed[event].remove(caretHandler); + }); + + caretHandler = 0; + } + }; + + function perform(caret_node) { + // Apply pending formats + each(pendingFormats.apply.reverse(), function(item) { + apply(item.name, item.vars, caret_node); + }); + + // Remove pending formats + each(pendingFormats.remove.reverse(), function(item) { + remove(item.name, item.vars, caret_node); + }); + + dom.remove(caret_node, 1); + resetPending(); + }; + + function isMarker(node) { + return node.face == marker || node.style.fontFamily == marker; + }; + + unbind(); + + doc.execCommand('FontName', false, marker); + + // IE will convert the current word + each(dom.select('font,span', selectedNode), function(node) { + var bookmark; + + if (isMarker(node)) { + bookmark = selection.getBookmark(); + perform(node); + selection.moveToBookmark(bookmark); + ed.nodeChanged(); + selectedNode = 0; + } + }); + + if (selectedNode) { + caretHandler = function(ed, e) { + each(dom.select('font,span', selectedNode), function(node) { + var bookmark, textNode; + + // Look for marker + if (node.face == marker || node.style.fontFamily == marker) { + textNode = node.firstChild; + + perform(node); + + rng = dom.createRng(); + rng.setStart(textNode, textNode.nodeValue.length); + rng.setEnd(textNode, textNode.nodeValue.length); + selection.setRng(rng); + ed.nodeChanged(); + + unbind(); + } + }); + + // Always unbind and clear pending styles on keyup + if (e.type == 'keyup') { + unbind(); + resetPending(); + } + }; + + each(events, function(event) { + ed[event].addToTop(caretHandler); + }); + } + } + }; +})(tinymce); + +tinymce.onAddEditor.add(function(tinymce, ed) { + var filters, fontSizes, dom, settings = ed.settings; + + if (settings.inline_styles) { + fontSizes = tinymce.explode(settings.font_size_style_values); + + function replaceWithSpan(node, styles) { + dom.replace(dom.create('span', { + style : styles + }), node, 1); + }; + + filters = { + font : function(dom, node) { + replaceWithSpan(node, { + backgroundColor : node.style.backgroundColor, + color : node.color, + fontFamily : node.face, + fontSize : fontSizes[parseInt(node.size) - 1] + }); + }, + + u : function(dom, node) { + replaceWithSpan(node, { + textDecoration : 'underline' + }); + }, + + strike : function(dom, node) { + replaceWithSpan(node, { + textDecoration : 'line-through' + }); + } + }; + + function convert(editor, params) { + dom = editor.dom; + + if (settings.convert_fonts_to_spans) { + tinymce.each(dom.select('font,u,strike', params.node), function(node) { + filters[node.nodeName.toLowerCase()](ed.dom, node); + }); + } + }; + + ed.onPreProcess.add(convert); + + ed.onInit.add(function() { + ed.selection.onSetContent.add(convert); + }); + } +}); + diff --git a/jscripts/tiny_mce/utils/editable_selects.js b/jscripts/tiny_mce/utils/editable_selects.js new file mode 100644 index 000000000..fd943c0f8 --- /dev/null +++ b/jscripts/tiny_mce/utils/editable_selects.js @@ -0,0 +1,70 @@ +/** + * editable_selects.js + * + * Copyright 2009, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://tinymce.moxiecode.com/license + * Contributing: http://tinymce.moxiecode.com/contributing + */ + +var TinyMCE_EditableSelects = { + editSelectElm : null, + + init : function() { + var nl = document.getElementsByTagName("select"), i, d = document, o; + + for (i=0; i'; + h += ' '; + + return h; +} + +function updateColor(img_id, form_element_id) { + document.getElementById(img_id).style.backgroundColor = document.forms[0].elements[form_element_id].value; +} + +function setBrowserDisabled(id, state) { + var img = document.getElementById(id); + var lnk = document.getElementById(id + "_link"); + + if (lnk) { + if (state) { + lnk.setAttribute("realhref", lnk.getAttribute("href")); + lnk.removeAttribute("href"); + tinyMCEPopup.dom.addClass(img, 'disabled'); + } else { + if (lnk.getAttribute("realhref")) + lnk.setAttribute("href", lnk.getAttribute("realhref")); + + tinyMCEPopup.dom.removeClass(img, 'disabled'); + } + } +} + +function getBrowserHTML(id, target_form_element, type, prefix) { + var option = prefix + "_" + type + "_browser_callback", cb, html; + + cb = tinyMCEPopup.getParam(option, tinyMCEPopup.getParam("file_browser_callback")); + + if (!cb) + return ""; + + html = ""; + html += ''; + html += ' '; + + return html; +} + +function openBrowser(img_id, target_form_element, type, option) { + var img = document.getElementById(img_id); + + if (img.className != "mceButtonDisabled") + tinyMCEPopup.openBrowser(target_form_element, type, option); +} + +function selectByValue(form_obj, field_name, value, add_custom, ignore_case) { + if (!form_obj || !form_obj.elements[field_name]) + return; + + var sel = form_obj.elements[field_name]; + + var found = false; + for (var i=0; i parseInt(v)) + st = this.mark(f, n); + } + } + + return st; + }, + + hasClass : function(n, c, d) { + return new RegExp('\\b' + c + (d ? '[0-9]+' : '') + '\\b', 'g').test(n.className); + }, + + getNum : function(n, c) { + c = n.className.match(new RegExp('\\b' + c + '([0-9]+)\\b', 'g'))[0]; + c = c.replace(/[^0-9]/g, ''); + + return c; + }, + + addClass : function(n, c, b) { + var o = this.removeClass(n, c); + n.className = b ? c + (o != '' ? (' ' + o) : '') : (o != '' ? (o + ' ') : '') + c; + }, + + removeClass : function(n, c) { + c = n.className.replace(new RegExp("(^|\\s+)" + c + "(\\s+|$)"), ' '); + return n.className = c != ' ' ? c : ''; + }, + + tags : function(f, s) { + return f.getElementsByTagName(s); + }, + + mark : function(f, n) { + var s = this.settings; + + this.addClass(n, s.invalid_cls); + this.markLabels(f, n, s.invalid_cls); + + return false; + }, + + markLabels : function(f, n, ic) { + var nl, i; + + nl = this.tags(f, "label"); + for (i=0; i<\/div>'; +} + + +function mkDivIe(x, y, w, h) +{ + this.htm += '%%'+this.color+';'+x+';'+y+';'+w+';'+h+';'; +} + + +function mkDivPrt(x, y, w, h) +{ + this.htm += '
    <\/div>'; +} + + +function mkLyr(x, y, w, h) +{ + this.htm += '<\/layer>\n'; +} + + +var regex = /%%([^;]+);([^;]+);([^;]+);([^;]+);([^;]+);/g; +function htmRpc() +{ + return this.htm.replace( + regex, + '
    \n'); +} + + +function htmPrtRpc() +{ + return this.htm.replace( + regex, + '
    \n'); +} + + +function mkLin(x1, y1, x2, y2) +{ + if (x1 > x2) + { + var _x2 = x2; + var _y2 = y2; + x2 = x1; + y2 = y1; + x1 = _x2; + y1 = _y2; + } + var dx = x2-x1, dy = Math.abs(y2-y1), + x = x1, y = y1, + yIncr = (y1 > y2)? -1 : 1; + + if (dx >= dy) + { + var pr = dy<<1, + pru = pr - (dx<<1), + p = pr-dx, + ox = x; + while ((dx--) > 0) + { + ++x; + if (p > 0) + { + this.mkDiv(ox, y, x-ox, 1); + y += yIncr; + p += pru; + ox = x; + } + else p += pr; + } + this.mkDiv(ox, y, x2-ox+1, 1); + } + + else + { + var pr = dx<<1, + pru = pr - (dy<<1), + p = pr-dy, + oy = y; + if (y2 <= y1) + { + while ((dy--) > 0) + { + if (p > 0) + { + this.mkDiv(x++, y, 1, oy-y+1); + y += yIncr; + p += pru; + oy = y; + } + else + { + y += yIncr; + p += pr; + } + } + this.mkDiv(x2, y2, 1, oy-y2+1); + } + else + { + while ((dy--) > 0) + { + y += yIncr; + if (p > 0) + { + this.mkDiv(x++, oy, 1, y-oy); + p += pru; + oy = y; + } + else p += pr; + } + this.mkDiv(x2, oy, 1, y2-oy+1); + } + } +} + + +function mkLin2D(x1, y1, x2, y2) +{ + if (x1 > x2) + { + var _x2 = x2; + var _y2 = y2; + x2 = x1; + y2 = y1; + x1 = _x2; + y1 = _y2; + } + var dx = x2-x1, dy = Math.abs(y2-y1), + x = x1, y = y1, + yIncr = (y1 > y2)? -1 : 1; + + var s = this.stroke; + if (dx >= dy) + { + if (dx > 0 && s-3 > 0) + { + var _s = (s*dx*Math.sqrt(1+dy*dy/(dx*dx))-dx-(s>>1)*dy) / dx; + _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1; + } + else var _s = s; + var ad = Math.ceil(s/2); + + var pr = dy<<1, + pru = pr - (dx<<1), + p = pr-dx, + ox = x; + while ((dx--) > 0) + { + ++x; + if (p > 0) + { + this.mkDiv(ox, y, x-ox+ad, _s); + y += yIncr; + p += pru; + ox = x; + } + else p += pr; + } + this.mkDiv(ox, y, x2-ox+ad+1, _s); + } + + else + { + if (s-3 > 0) + { + var _s = (s*dy*Math.sqrt(1+dx*dx/(dy*dy))-(s>>1)*dx-dy) / dy; + _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1; + } + else var _s = s; + var ad = Math.round(s/2); + + var pr = dx<<1, + pru = pr - (dy<<1), + p = pr-dy, + oy = y; + if (y2 <= y1) + { + ++ad; + while ((dy--) > 0) + { + if (p > 0) + { + this.mkDiv(x++, y, _s, oy-y+ad); + y += yIncr; + p += pru; + oy = y; + } + else + { + y += yIncr; + p += pr; + } + } + this.mkDiv(x2, y2, _s, oy-y2+ad); + } + else + { + while ((dy--) > 0) + { + y += yIncr; + if (p > 0) + { + this.mkDiv(x++, oy, _s, y-oy+ad); + p += pru; + oy = y; + } + else p += pr; + } + this.mkDiv(x2, oy, _s, y2-oy+ad+1); + } + } +} + + +function mkLinDott(x1, y1, x2, y2) +{ + if (x1 > x2) + { + var _x2 = x2; + var _y2 = y2; + x2 = x1; + y2 = y1; + x1 = _x2; + y1 = _y2; + } + var dx = x2-x1, dy = Math.abs(y2-y1), + x = x1, y = y1, + yIncr = (y1 > y2)? -1 : 1, + drw = true; + if (dx >= dy) + { + var pr = dy<<1, + pru = pr - (dx<<1), + p = pr-dx; + while ((dx--) > 0) + { + if (drw) this.mkDiv(x, y, 1, 1); + drw = !drw; + if (p > 0) + { + y += yIncr; + p += pru; + } + else p += pr; + ++x; + } + if (drw) this.mkDiv(x, y, 1, 1); + } + + else + { + var pr = dx<<1, + pru = pr - (dy<<1), + p = pr-dy; + while ((dy--) > 0) + { + if (drw) this.mkDiv(x, y, 1, 1); + drw = !drw; + y += yIncr; + if (p > 0) + { + ++x; + p += pru; + } + else p += pr; + } + if (drw) this.mkDiv(x, y, 1, 1); + } +} + + +function mkOv(left, top, width, height) +{ + var a = width>>1, b = height>>1, + wod = width&1, hod = (height&1)+1, + cx = left+a, cy = top+b, + x = 0, y = b, + ox = 0, oy = b, + aa = (a*a)<<1, bb = (b*b)<<1, + st = (aa>>1)*(1-(b<<1)) + bb, + tt = (bb>>1) - aa*((b<<1)-1), + w, h; + while (y > 0) + { + if (st < 0) + { + st += bb*((x<<1)+3); + tt += (bb<<1)*(++x); + } + else if (tt < 0) + { + st += bb*((x<<1)+3) - (aa<<1)*(y-1); + tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3); + w = x-ox; + h = oy-y; + if (w&2 && h&2) + { + this.mkOvQds(cx, cy, -x+2, ox+wod, -oy, oy-1+hod, 1, 1); + this.mkOvQds(cx, cy, -x+1, x-1+wod, -y-1, y+hod, 1, 1); + } + else this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, oy-h+hod, w, h); + ox = x; + oy = y; + } + else + { + tt -= aa*((y<<1)-3); + st -= (aa<<1)*(--y); + } + } + this.mkDiv(cx-a, cy-oy, a-ox+1, (oy<<1)+hod); + this.mkDiv(cx+ox+wod, cy-oy, a-ox+1, (oy<<1)+hod); +} + + +function mkOv2D(left, top, width, height) +{ + var s = this.stroke; + width += s-1; + height += s-1; + var a = width>>1, b = height>>1, + wod = width&1, hod = (height&1)+1, + cx = left+a, cy = top+b, + x = 0, y = b, + aa = (a*a)<<1, bb = (b*b)<<1, + st = (aa>>1)*(1-(b<<1)) + bb, + tt = (bb>>1) - aa*((b<<1)-1); + + if (s-4 < 0 && (!(s-2) || width-51 > 0 && height-51 > 0)) + { + var ox = 0, oy = b, + w, h, + pxl, pxr, pxt, pxb, pxw; + while (y > 0) + { + if (st < 0) + { + st += bb*((x<<1)+3); + tt += (bb<<1)*(++x); + } + else if (tt < 0) + { + st += bb*((x<<1)+3) - (aa<<1)*(y-1); + tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3); + w = x-ox; + h = oy-y; + + if (w-1) + { + pxw = w+1+(s&1); + h = s; + } + else if (h-1) + { + pxw = s; + h += 1+(s&1); + } + else pxw = h = s; + this.mkOvQds(cx, cy, -x+1, ox-pxw+w+wod, -oy, -h+oy+hod, pxw, h); + ox = x; + oy = y; + } + else + { + tt -= aa*((y<<1)-3); + st -= (aa<<1)*(--y); + } + } + this.mkDiv(cx-a, cy-oy, s, (oy<<1)+hod); + this.mkDiv(cx+a+wod-s+1, cy-oy, s, (oy<<1)+hod); + } + + else + { + var _a = (width-((s-1)<<1))>>1, + _b = (height-((s-1)<<1))>>1, + _x = 0, _y = _b, + _aa = (_a*_a)<<1, _bb = (_b*_b)<<1, + _st = (_aa>>1)*(1-(_b<<1)) + _bb, + _tt = (_bb>>1) - _aa*((_b<<1)-1), + + pxl = new Array(), + pxt = new Array(), + _pxb = new Array(); + pxl[0] = 0; + pxt[0] = b; + _pxb[0] = _b-1; + while (y > 0) + { + if (st < 0) + { + st += bb*((x<<1)+3); + tt += (bb<<1)*(++x); + pxl[pxl.length] = x; + pxt[pxt.length] = y; + } + else if (tt < 0) + { + st += bb*((x<<1)+3) - (aa<<1)*(y-1); + tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3); + pxl[pxl.length] = x; + pxt[pxt.length] = y; + } + else + { + tt -= aa*((y<<1)-3); + st -= (aa<<1)*(--y); + } + + if (_y > 0) + { + if (_st < 0) + { + _st += _bb*((_x<<1)+3); + _tt += (_bb<<1)*(++_x); + _pxb[_pxb.length] = _y-1; + } + else if (_tt < 0) + { + _st += _bb*((_x<<1)+3) - (_aa<<1)*(_y-1); + _tt += (_bb<<1)*(++_x) - _aa*(((_y--)<<1)-3); + _pxb[_pxb.length] = _y-1; + } + else + { + _tt -= _aa*((_y<<1)-3); + _st -= (_aa<<1)*(--_y); + _pxb[_pxb.length-1]--; + } + } + } + + var ox = 0, oy = b, + _oy = _pxb[0], + l = pxl.length, + w, h; + for (var i = 0; i < l; i++) + { + if (typeof _pxb[i] != "undefined") + { + if (_pxb[i] < _oy || pxt[i] < oy) + { + x = pxl[i]; + this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, _oy+hod, x-ox, oy-_oy); + ox = x; + oy = pxt[i]; + _oy = _pxb[i]; + } + } + else + { + x = pxl[i]; + this.mkDiv(cx-x+1, cy-oy, 1, (oy<<1)+hod); + this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod); + ox = x; + oy = pxt[i]; + } + } + this.mkDiv(cx-a, cy-oy, 1, (oy<<1)+hod); + this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod); + } +} + + +function mkOvDott(left, top, width, height) +{ + var a = width>>1, b = height>>1, + wod = width&1, hod = height&1, + cx = left+a, cy = top+b, + x = 0, y = b, + aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1, + st = (aa2>>1)*(1-(b<<1)) + bb, + tt = (bb>>1) - aa2*((b<<1)-1), + drw = true; + while (y > 0) + { + if (st < 0) + { + st += bb*((x<<1)+3); + tt += (bb<<1)*(++x); + } + else if (tt < 0) + { + st += bb*((x<<1)+3) - aa4*(y-1); + tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3); + } + else + { + tt -= aa2*((y<<1)-3); + st -= aa4*(--y); + } + if (drw) this.mkOvQds(cx, cy, -x, x+wod, -y, y+hod, 1, 1); + drw = !drw; + } +} + + +function mkRect(x, y, w, h) +{ + var s = this.stroke; + this.mkDiv(x, y, w, s); + this.mkDiv(x+w, y, s, h); + this.mkDiv(x, y+h, w+s, s); + this.mkDiv(x, y+s, s, h-s); +} + + +function mkRectDott(x, y, w, h) +{ + this.drawLine(x, y, x+w, y); + this.drawLine(x+w, y, x+w, y+h); + this.drawLine(x, y+h, x+w, y+h); + this.drawLine(x, y, x, y+h); +} + + +function jsgFont() +{ + this.PLAIN = 'font-weight:normal;'; + this.BOLD = 'font-weight:bold;'; + this.ITALIC = 'font-style:italic;'; + this.ITALIC_BOLD = this.ITALIC + this.BOLD; + this.BOLD_ITALIC = this.ITALIC_BOLD; +} +var Font = new jsgFont(); + + +function jsgStroke() +{ + this.DOTTED = -1; +} +var Stroke = new jsgStroke(); + + +function jsGraphics(id, wnd) +{ + this.setColor = new Function('arg', 'this.color = arg.toLowerCase();'); + + this.setStroke = function(x) + { + this.stroke = x; + if (!(x+1)) + { + this.drawLine = mkLinDott; + this.mkOv = mkOvDott; + this.drawRect = mkRectDott; + } + else if (x-1 > 0) + { + this.drawLine = mkLin2D; + this.mkOv = mkOv2D; + this.drawRect = mkRect; + } + else + { + this.drawLine = mkLin; + this.mkOv = mkOv; + this.drawRect = mkRect; + } + }; + + + this.setPrintable = function(arg) + { + this.printable = arg; + if (jg_fast) + { + this.mkDiv = mkDivIe; + this.htmRpc = arg? htmPrtRpc : htmRpc; + } + else this.mkDiv = jg_n4? mkLyr : arg? mkDivPrt : mkDiv; + }; + + + this.setFont = function(fam, sz, sty) + { + this.ftFam = fam; + this.ftSz = sz; + this.ftSty = sty || Font.PLAIN; + }; + + + this.drawPolyline = this.drawPolyLine = function(x, y, s) + { + for (var i=0 ; i>1, b = (h -= 1)>>1, + wod = (w&1)+1, hod = (h&1)+1, + cx = left+a, cy = top+b, + x = 0, y = b, + ox = 0, oy = b, + aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1, + st = (aa2>>1)*(1-(b<<1)) + bb, + tt = (bb>>1) - aa2*((b<<1)-1), + pxl, dw, dh; + if (w+1) while (y > 0) + { + if (st < 0) + { + st += bb*((x<<1)+3); + tt += (bb<<1)*(++x); + } + else if (tt < 0) + { + st += bb*((x<<1)+3) - aa4*(y-1); + pxl = cx-x; + dw = (x<<1)+wod; + tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3); + dh = oy-y; + this.mkDiv(pxl, cy-oy, dw, dh); + this.mkDiv(pxl, cy+y+hod, dw, dh); + ox = x; + oy = y; + } + else + { + tt -= aa2*((y<<1)-3); + st -= aa4*(--y); + } + } + this.mkDiv(cx-a, cy-oy, w+1, (oy<<1)+hod); + }; + + +/* fillPolygon method, implemented by Matthieu Haller. +This javascript function is an adaptation of the gdImageFilledPolygon for Walter Zorn lib. +C source of GD 1.8.4 found at http://www.boutell.com/gd/ + +THANKS to Kirsten Schulz for the polygon fixes! + +The intersection finding technique of this code could be improved +by remembering the previous intertersection, and by using the slope. +That could help to adjust intersections to produce a nice +interior_extrema. */ + this.fillPolygon = function(array_x, array_y) + { + var i; + var y; + var miny, maxy; + var x1, y1; + var x2, y2; + var ind1, ind2; + var ints; + + var n = array_x.length; + + if (!n) return; + + + miny = array_y[0]; + maxy = array_y[0]; + for (i = 1; i < n; i++) + { + if (array_y[i] < miny) + miny = array_y[i]; + + if (array_y[i] > maxy) + maxy = array_y[i]; + } + for (y = miny; y <= maxy; y++) + { + var polyInts = new Array(); + ints = 0; + for (i = 0; i < n; i++) + { + if (!i) + { + ind1 = n-1; + ind2 = 0; + } + else + { + ind1 = i-1; + ind2 = i; + } + y1 = array_y[ind1]; + y2 = array_y[ind2]; + if (y1 < y2) + { + x1 = array_x[ind1]; + x2 = array_x[ind2]; + } + else if (y1 > y2) + { + y2 = array_y[ind1]; + y1 = array_y[ind2]; + x2 = array_x[ind1]; + x1 = array_x[ind2]; + } + else continue; + + // modified 11. 2. 2004 Walter Zorn + if ((y >= y1) && (y < y2)) + polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1); + + else if ((y == maxy) && (y > y1) && (y <= y2)) + polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1); + } + polyInts.sort(integer_compare); + for (i = 0; i < ints; i+=2) + this.mkDiv(polyInts[i], y, polyInts[i+1]-polyInts[i]+1, 1); + } + }; + + + this.drawString = function(txt, x, y) + { + this.htm += '
    '+ + txt + + '<\/div>'; + }; + + +/* drawStringRect() added by Rick Blommers. +Allows to specify the size of the text rectangle and to align the +text both horizontally (e.g. right) and vertically within that rectangle */ + this.drawStringRect = function(txt, x, y, width, halign) + { + this.htm += '
    '+ + txt + + '<\/div>'; + }; + + + this.drawImage = function(imgSrc, x, y, w, h, a) + { + this.htm += '
    '+ + ''+ + '<\/div>'; + }; + + + this.clear = function() + { + this.htm = ""; + if (this.cnv) this.cnv.innerHTML = this.defhtm; + }; + + + this.mkOvQds = function(cx, cy, xl, xr, yt, yb, w, h) + { + this.mkDiv(xr+cx, yt+cy, w, h); + this.mkDiv(xr+cx, yb+cy, w, h); + this.mkDiv(xl+cx, yb+cy, w, h); + this.mkDiv(xl+cx, yt+cy, w, h); + }; + + this.setStroke(1); + this.setFont('verdana,geneva,helvetica,sans-serif', String.fromCharCode(0x31, 0x32, 0x70, 0x78), Font.PLAIN); + this.color = '#000000'; + this.htm = ''; + this.wnd = wnd || window; + + if (!(jg_ie || jg_dom || jg_ihtm)) chkDHTM(); + if (typeof id != 'string' || !id) this.paint = pntDoc; + else + { + this.cnv = document.all? (this.wnd.document.all[id] || null) + : document.getElementById? (this.wnd.document.getElementById(id) || null) + : null; + this.defhtm = (this.cnv && this.cnv.innerHTML)? this.cnv.innerHTML : ''; + this.paint = jg_dom? pntCnvDom : jg_ie? pntCnvIe : jg_ihtm? pntCnvIhtm : pntCnv; + } + + this.setPrintable(false); +} + + + +function integer_compare(x,y) +{ + return (x < y) ? -1 : ((x > y)*1); +} + diff --git a/license/gpl_licence.txt b/license/gpl_licence.txt new file mode 100644 index 000000000..a8ab76ef5 --- /dev/null +++ b/license/gpl_licence.txt @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/login.php b/login.php new file mode 100644 index 000000000..903375b9f --- /dev/null +++ b/login.php @@ -0,0 +1,249 @@ +containsFeedbacks()) { + if (isset($_COOKIE['ATLogin'])) { + $cookie_login = $_COOKIE['ATLogin']; + } + if (isset($_COOKIE['ATPass'])) { + $cookie_pass = $_COOKIE['ATPass']; + } +} + +//garbage collect for maximum login attempts table +if (rand(1, 100) == 1){ + $sql = 'DELETE FROM '.TABLE_PREFIX.'member_login_attempt WHERE expiry < '. time(); + mysql_query($sql, $db); +} + +if (isset($cookie_login, $cookie_pass) && !isset($_POST['submit'])) { + /* auto login */ + $this_login = $cookie_login; + $this_password = $cookie_pass; + $auto_login = 1; + $used_cookie = true; +} else if (isset($_POST['submit'])) { + /* form post login */ + $this_password = $_POST['form_password_hidden']; + $this_login = $_POST['form_login']; + $auto_login = isset($_POST['auto']) ? intval($_POST['auto']) : 0; + $used_cookie = false; +} + +if (isset($this_login, $this_password)) { + if (version_compare(PHP_VERSION, '5.1.0', '>=')) { + session_regenerate_id(TRUE); + } + + + if ($_GET['course']) { + $_POST['form_course_id'] = intval($_GET['course']); + } else { + $_POST['form_course_id'] = intval($_POST['form_course_id']); + } + $this_login = $addslashes($this_login); + $this_password = $addslashes($this_password); + + //Check if this account has exceeded maximum attempts +// $sql = 'SELECT a.login, b.attempt, b.expiry FROM (SELECT login FROM '.TABLE_PREFIX.'members UNION SELECT login FROM '.TABLE_PREFIX.'admins) AS a LEFT JOIN '.TABLE_PREFIX."member_login_attempt b ON a.login=b.login WHERE a.login='$this_login'"; + $sql = 'SELECT login, attempt, expiry FROM '.TABLE_PREFIX."member_login_attempt WHERE login='$this_login'"; + + $result = mysql_query($sql, $db); + if ($result && mysql_numrows($result) > 0){ + list($attempt_login_name, $attempt_login, $attempt_expiry) = mysql_fetch_array($result); + } else { + $attempt_login_name = ''; + $attempt_login = 0; + $attempt_expiry = 0; + } + if($attempt_expiry > 0 && $attempt_expiry < time()){ + //clear entry if it has expired + $sql = 'DELETE FROM '.TABLE_PREFIX."member_login_attempt WHERE login='$this_login'"; + mysql_query($sql, $db); + $attempt_login = 0; + $attempt_expiry = 0; + } + + if ($used_cookie) { + $sql = "SELECT member_id, login, first_name, second_name, last_name, preferences,password AS pass, language, status, last_login FROM ".TABLE_PREFIX."members WHERE login='$this_login' AND password='$this_password'"; + } else { + $sql = "SELECT member_id, login, first_name, second_name, last_name, preferences, language, status, password AS pass, last_login FROM ".TABLE_PREFIX."members WHERE (login='$this_login' OR email='$this_login') AND SHA1(CONCAT(password, '$_SESSION[token]'))='$this_password'"; + } + $result = mysql_query($sql, $db); + + if($_config['max_login'] > 0 && $attempt_login >= $_config['max_login']){ + $msg->addError('MAX_LOGIN_ATTEMPT'); + } else if (($row = mysql_fetch_assoc($result)) && ($row['status'] == AT_STATUS_UNCONFIRMED)) { + $msg->addError('NOT_CONFIRMED'); + } else if ($row && $row['status'] == AT_STATUS_DISABLED) { + $msg->addError('ACCOUNT_DISABLED'); + } else if ($row) { + $_SESSION['valid_user'] = true; + $_SESSION['member_id'] = intval($row['member_id']); + $_SESSION['login'] = $row['login']; + if ($row['preferences'] == "") + assign_session_prefs(unserialize(stripslashes($_config["pref_defaults"]))); + else + assign_session_prefs(unserialize(stripslashes($row['preferences']))); + $_SESSION['is_guest'] = 0; + $_SESSION['lang'] = $row['language']; + $_SESSION['course_id'] = 0; + + if ($auto_login == 1) { + $parts = parse_url($_base_href); + // update the cookie.. increment to another 2 days + $cookie_expire = time()+172800; + ATutor.setcookie('ATLogin', $this_login, $cookie_expire, $parts['path']); + ATutor.setcookie('ATPass', $row['pass'], $cookie_expire, $parts['path']); + } + + $_SESSION['first_login'] = false; + if ($row['last_login'] == null || $row['last_login'] == '' || $row['last_login'] == '0000-00-00 00:00:00') { + $_SESSION['first_login'] = true; + } + + $sql = "UPDATE ".TABLE_PREFIX."members SET creation_date=creation_date, last_login=NOW() WHERE member_id=$_SESSION[member_id]"; + mysql_query($sql, $db); + + //clear login attempt on successful login + $sql = 'DELETE FROM '.TABLE_PREFIX."member_login_attempt WHERE login='$this_login'"; + mysql_query($sql, $db); + + //if page variable is set, bring them there. + if (isset($_POST['p']) && $_POST['p']!=''){ + header ('Location: '.urldecode($_POST['p'])); + exit; + } + + $msg->addFeedback('LOGIN_SUCCESS'); + header('Location: bounce.php?course='.$_POST['form_course_id']); + exit; + } else { + // check if it's an admin login. + $sql = "SELECT login, `privileges`, language FROM ".TABLE_PREFIX."admins WHERE login='$this_login' AND SHA1(CONCAT(password, '$_SESSION[token]'))='$this_password' AND `privileges`>0"; + $result = mysql_query($sql, $db); + + if ($row = mysql_fetch_assoc($result)) { + $sql = "UPDATE ".TABLE_PREFIX."admins SET last_login=NOW() WHERE login='$this_login'"; + mysql_query($sql, $db); + + $_SESSION['login'] = $row['login']; + $_SESSION['valid_user'] = true; + $_SESSION['course_id'] = -1; + $_SESSION['privileges'] = intval($row['privileges']); + $_SESSION['lang'] = $row['language']; + + write_to_log(AT_ADMIN_LOG_UPDATE, 'admins', mysql_affected_rows($db), $sql); + //clear login attempt on successful login + $sql = 'DELETE FROM '.TABLE_PREFIX."member_login_attempt WHERE login='$this_login'"; + mysql_query($sql, $db); + + $msg->addFeedback('LOGIN_SUCCESS'); + + header('Location: admin/index.php'); + exit; + + } else { + //Only if the user exist in our database +// if ($attempt_login_name!=''){ + $expiry_stmt = ''; + $attempt_login++; + if ($attempt_expiry==0){ + $expiry_stmt = ', expiry='.(time() + LOGIN_ATTEMPT_LOCKED_TIME * 60); //an hour from now + } else { + $expiry_stmt = ', expiry='.$attempt_expiry; + } + $sql = 'REPLACE INTO '.TABLE_PREFIX.'member_login_attempt SET attempt='.$attempt_login . $expiry_stmt .", login='$this_login'"; + mysql_query($sql, $db); +// } + } + //Different error messages depend on the number of login failure. + if ($_config['max_login'] > 0 && ($_config['max_login']-$attempt_login)==2){ + $msg->addError('MAX_LOGIN_ATTEMPT_2'); + } elseif ($_config['max_login'] > 0 && ($_config['max_login']-$attempt_login)==1){ + $msg->addError('MAX_LOGIN_ATTEMPT_1'); + } elseif ($_config['max_login'] > 0 && ($_config['max_login']-$attempt_login)==0){ + $msg->addError('MAX_LOGIN_ATTEMPT'); + } else { + $msg->addError('INVALID_LOGIN'); + } + } +} + +$_SESSION['session_test'] = TRUE; + +if (isset($_SESSION['member_id'])) { + $sql = "DELETE FROM ".TABLE_PREFIX."users_online WHERE member_id=$_SESSION[member_id]"; + $result = @mysql_query($sql, $db); +} + +unset($_SESSION['login']); +unset($_SESSION['valid_user']); +unset($_SESSION['member_id']); +unset($_SESSION['is_admin']); +unset($_SESSION['course_id']); +unset($_SESSION['is_super_admin']); +unset($_SESSION['dd_question_ids']); + +$_SESSION['prefs']['PREF_FORM_FOCUS'] = 1; + +/*****************************/ +/* template starts down here */ + +$onload = 'document.form.form_login.focus();'; + +$savant->assign('form_course_id', $_GET['course']); + +if (isset($_GET['course']) && $_GET['course']) { + $savant->assign('title', ' '._AT('to1').' '.$system_courses[$_GET['course']]['title']); +} else { + $savant->assign('title', ' '); +} + +header('P3P: CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"'); +$savant->display('login.tmpl.php'); +?> diff --git a/logout.php b/logout.php new file mode 100644 index 000000000..77524f408 --- /dev/null +++ b/logout.php @@ -0,0 +1,35 @@ +addFeedback('LOGOUT'); +header('Location: login.php'); +exit; + +?> \ No newline at end of file diff --git a/mods/_core/backups/admin/create.php b/mods/_core/backups/admin/create.php new file mode 100644 index 000000000..bec3f1987 --- /dev/null +++ b/mods/_core/backups/admin/create.php @@ -0,0 +1,85 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + + $Backup->setCourseID($_POST['course']); + $error = $Backup->create($_POST['description']); + if ($error !== FALSE) { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + + +
    + +
    +
    +

    +
    + + + getNumAvailable() >= AT_COURSE_BACKUPS)): ?> +
    +

    +
    + +
    + *
    + +
    +
    +
    + +
    +
    + +
    + + +
    +

    +
    + +
    +
    + + \ No newline at end of file diff --git a/mods/_core/backups/admin/delete.php b/mods/_core/backups/admin/delete.php new file mode 100644 index 000000000..38fe49f1a --- /dev/null +++ b/mods/_core/backups/admin/delete.php @@ -0,0 +1,45 @@ +delete($_POST['backup_id']); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$hidden_vars['backup_id'] = $_GET['backup_id']; +$hidden_vars['course'] = $_GET['course']; +$msg->addConfirm('DELETE', $hidden_vars); +$msg->printConfirm(); + +require (AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/backups/admin/edit.php b/mods/_core/backups/admin/edit.php new file mode 100644 index 000000000..533113648 --- /dev/null +++ b/mods/_core/backups/admin/edit.php @@ -0,0 +1,68 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} + +$Backup = new Backup($db, $_REQUEST['course']); +$backup_row = $Backup->getRow($_REQUEST['backup_id']); + +if (isset($_POST['edit'])) { + $Backup->edit($_POST['backup_id'], $_POST['new_description']); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} + +//check for errors + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + +
    + + + +
    +
    +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    + \ No newline at end of file diff --git a/mods/_core/backups/admin/index.php b/mods/_core/backups/admin/index.php new file mode 100644 index 000000000..62159cdef --- /dev/null +++ b/mods/_core/backups/admin/index.php @@ -0,0 +1,120 @@ +download($backup_id); + exit; // never reached + +} else if (isset($_POST['delete'], $backup_id)) { + header('Location: delete.php?backup_id=' . $backup_id . SEP . 'course=' . $course); + exit; + +} else if (isset($_POST['edit'], $backup_id)) { + header('Location: edit.php?backup_id=' . $backup_id . SEP . 'course=' . $course); + exit; +} else if (!empty($_POST) && !$backup_id) { + $msg->addError('NO_ITEM_SELECTED'); +} + + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    + + + + + + + + + + + + + + + + + +setCourseID($course['course_id']); + $list = $Backup->getAvailableList(); + + echo ''; + + if (empty($list)) { ?> + + + '; + echo ''; + echo ''; + echo ''; + echo ''; + $num_backups ++; + } + } + } +?> + + + + + + +
    + + +
    '.$course['title'].'
    '; + echo ''.AT_date(_AT('filemanager_date_format'), $row['date_timestamp'], AT_DATE_UNIX_TIMESTAMP).''.get_human_size($row['file_size']).''.htmlentities_utf8($row['description']).'
    +
    + + \ No newline at end of file diff --git a/mods/_core/backups/admin/restore.php b/mods/_core/backups/admin/restore.php new file mode 100644 index 000000000..6c9858596 --- /dev/null +++ b/mods/_core/backups/admin/restore.php @@ -0,0 +1,124 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + if (!$_POST['material']) { + $msg->addError(array('EMPTY_FIELDS', _AT('material'))); + } + + if (!$msg->containsErrors()) { + $Backup = new Backup($db, $_POST['in_course']); + $Backup->restore($_POST['material'], $_POST['action'], $_POST['backup_id'], $_POST['course']); + + $msg->addFeedBack('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$Backup = new Backup($db, $_REQUEST['course']); + +$row = $Backup->getRow($_REQUEST['backup_id']); + +?> + +
    + + + +
    +
    +

    +
    +
    + *
    + +

    + + getModules(AT_MODULE_STATUS_ENABLED | AT_MODULE_STATUS_DISABLED, 0, TRUE); + $keys = array_keys($modules); + $i = 0; + ?> + + + isBackupable()): ?> +
    + + + +
    + +
    + *
    + + +
    + +
    +
    +
    + +
    +
    +
    + +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/mods/_core/backups/classes/Backup.class.php b/mods/_core/backups/classes/Backup.class.php new file mode 100644 index 000000000..371b64780 --- /dev/null +++ b/mods/_core/backups/classes/Backup.class.php @@ -0,0 +1,464 @@ +db = $db; + + $this->setCourseID($course_id); + } + + // public + // should be used by the admin section + function setCourseID($course_id) { + $this->course_id = $course_id; + $this->backup_dir = AT_BACKUP_DIR . $course_id . DIRECTORY_SEPARATOR; + } + + + // public + // call staticly + function generateFileName( ) { + global $system_courses; + $title = $system_courses[$this->course_id]['title']; + + $title = str_replace(' ', '_', $title); + $title = str_replace('%', '', $title); + $title = str_replace('\'', '', $title); + $title = str_replace('"', '', $title); + $title = str_replace('`', '', $title); + + $title .= '_' . date('d_M_y') . '.zip'; + + return $title; + } + + // public + // NOTE: should the create() deal with saving it to disk as well? or should it be general to just create it, and not actually + // responsible for where to save it? (write a diff method to save it after) + function create($description) { + global $addslashes, $moduleFactory; + + if ($this->getNumAvailable() >= AT_COURSE_BACKUPS) { + return FALSE; + } + + $timestamp = time(); + + $zipfile = new zipfile(); + + $package_identifier = VERSION."\n\n\n".'Do not change the first line of this file it contains the ATutor version this backup was created with.'; + $zipfile->add_file($package_identifier, 'atutor_backup_version', $timestamp); + + // backup course properties. ONLY BANNER FOR NOW. + require_once(AT_INCLUDE_PATH . 'classes/CSVExport.class.php'); + $CSVExport = new CSVExport(); + $now = time(); + + $sql = 'SELECT banner + FROM '.TABLE_PREFIX.'courses + WHERE course_id='.$this->course_id; + $properties = $CSVExport->export($sql, $course_id); + $zipfile->add_file($properties, 'properties.csv', $now); + + // backup modules + $modules = $moduleFactory->getModules(AT_MODULE_STATUS_ENABLED | AT_MODULE_STATUS_DISABLED); + $keys = array_keys($modules); + foreach($keys as $module_name) { + $module =& $modules[$module_name]; + $module->backup($this->course_id, $zipfile); + } + $zipfile->close(); + + $system_file_name = md5($timestamp); + + if (!is_dir(AT_BACKUP_DIR)) { + @mkdir(AT_BACKUP_DIR); + } + + if (!is_dir(AT_BACKUP_DIR . $this->course_id)) { + @mkdir(AT_BACKUP_DIR . $this->course_id); + } + + $zipfile->write_file(AT_BACKUP_DIR . $this->course_id . DIRECTORY_SEPARATOR . $system_file_name . '.zip'); + + $row['description'] = $addslashes($description); + $row['contents'] = addslashes(serialize($table_counters)); + $row['system_file_name'] = $system_file_name; + $row['file_size'] = $zipfile->get_size(); + $row['file_name'] = $this->generateFileName(); + + $this->add($row); + + return TRUE; + } + + // public + function upload($_FILES, $description) { + global $addslashes, $msg; + + $ext = pathinfo($_FILES['file']['name']); + $ext = $ext['extension']; + + if (!$_FILES['file']['name'] || !is_uploaded_file($_FILES['file']['tmp_name']) || ($ext != 'zip')) { + if ($_FILES['file']['error'] == 1) { // LEQ to UPLOAD_ERR_INI_SIZE + $errors = array('FILE_TOO_BIG', ini_get('upload_max_filesize')); + $msg->addError($errors); + } else { + $msg->addError('FILE_NOT_SELECTED'); + } + } + + if ($_FILES['file']['size'] == 0) { + $msg->addError('IMPORTFILE_EMPTY'); + } + + if($msg->containsErrors()) { + return; + } + + $row = array(); + $row['description'] = $addslashes($description); + $row['system_file_name'] = md5(time()); + $row['contents'] = ''; + $row['file_size'] = $_FILES['file']['size']; + $row['file_name'] = $addslashes($_FILES['file']['name']); + + if (!is_dir(AT_BACKUP_DIR)) { + @mkdir(AT_BACKUP_DIR); + } + + if (!is_dir(AT_BACKUP_DIR . $this->course_id)) { + @mkdir(AT_BACKUP_DIR . $this->course_id); + } + + $backup_path = AT_BACKUP_DIR . DIRECTORY_SEPARATOR . $this->course_id . DIRECTORY_SEPARATOR; + + move_uploaded_file($_FILES['file']['tmp_name'], $backup_path . $row['system_file_name'].'.zip'); + + $this->add($row); + + return; + } + + // private + // adds a backup to the database + function add($row) { + $sql = "INSERT INTO ".TABLE_PREFIX."backups VALUES (NULL, $this->course_id, NOW(), '$row[description]', '$row[file_size]', '$row[system_file_name]', '$row[file_name]', '$row[contents]')"; + mysql_query($sql, $this->db); + } + + // public + // get number of backups + function getNumAvailable() { + // use $num_backups, if not set then do a COUNT(*) on the table + if (isset($this->num_backups)) { + return $this->num_backups; + } + + $sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."backups WHERE course_id=$this->course_id"; + $result = mysql_query($sql, $this->db); + $row = mysql_fetch_assoc($result); + + $this->num_backups = $row['cnt']; + return $row['cnt']; + } + + // public + // get list of backups + function getAvailableList() { + $backup_list = array(); + + $sql = "SELECT *, UNIX_TIMESTAMP(date) AS date_timestamp FROM ".TABLE_PREFIX."backups WHERE course_id=$this->course_id ORDER BY date DESC"; + $result = mysql_query($sql, $this->db); + while ($row = mysql_fetch_assoc($result)) { + $backup_list[$row['backup_id']] = $row; + $backup_list[$row['backup_id']]['contents'] = unserialize($row['contents']); + } + + $this->num_backups = count($backup_list); + + return $backup_list; + } + + // public + function download($backup_id) { // or fetch() + $list = $this->getAvailableList($this->course_id); + if (!isset($list[$backup_id])) { + // catch the error + //debug('does not belong to us'); + exit; + } + + $my_backup = $list[$backup_id]; + $file_name = $my_backup['file_name']; + + header('Content-Type: application/zip'); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="'.htmlspecialchars($file_name).'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: '.$my_backup['file_size']); + + // see the note in get.php about the use of x-Sendfile + ob_end_clean(); + header("Content-Encoding: none"); + header('x-Sendfile: ' . AT_BACKUP_DIR . $this->course_id . DIRECTORY_SEPARATOR . $my_backup['system_file_name']. '.zip'); + header('x-Sendfile: ', TRUE); // if we get here then it didn't work + + readfile(AT_BACKUP_DIR . $this->course_id . DIRECTORY_SEPARATOR . $my_backup['system_file_name']. '.zip'); + exit; + } + + // public + function delete($backup_id) { + $list = $this->getAvailableList($this->course_id); + if (!isset($list[$backup_id])) { + // catch the error + //debug('does not belong to us'); + exit; + } + $my_backup = $list[$backup_id]; + + // delete the backup file: + @unlink(AT_BACKUP_DIR . $this->course_id . DIRECTORY_SEPARATOR . $my_backup['system_file_name']. '.zip'); + + // delete the row in the table: + $sql = "DELETE FROM ".TABLE_PREFIX."backups WHERE backup_id=$backup_id AND course_id=$this->course_id"; + $result = mysql_query($sql, $this->db); + } + + // public + function edit($backup_id, $description) { + global $addslashes; + + // sql safe input + $backup_id = abs($backup_id); + $description = $addslashes($description); + + // update description in the table: + $sql = "UPDATE ".TABLE_PREFIX."backups SET description='$description', date=date WHERE backup_id=$backup_id AND course_id=$this->course_id"; + $result = mysql_query($sql, $this->db); + + } + + // public + function getRow($backup_id, $course_id = 0) { + // sql safe input + $backup_id = abs($backup_id); + $course_id = abs($course_id); + + if ($course_id) { + $sql = "SELECT *, UNIX_TIMESTAMP(date) AS date_timestamp FROM ".TABLE_PREFIX."backups WHERE backup_id=$backup_id AND course_id=$course_id"; + } else { + $sql = "SELECT *, UNIX_TIMESTAMP(date) AS date_timestamp FROM ".TABLE_PREFIX."backups WHERE backup_id=$backup_id AND course_id=$this->course_id"; + } + + $result = mysql_query($sql, $this->db); + $row = mysql_fetch_assoc($result); + + if ($row) { + $row['contents'] = unserialize($row['contents']); + } + return $row; + } + + // public + function translate_whitespace($input) { + $input = str_replace('\n', "\n", $input); + $input = str_replace('\r', "\r", $input); + $input = str_replace('\x00', "\0", $input); + + return $input; + } + + // public + function getVersion() { + if ((file_exists($this->import_dir.'atutor_backup_version')) && ($version = file($this->import_dir.'atutor_backup_version'))) { + return trim($version[0]); + } else { + return false; + } + } + + // public + function restore($material, $action, $backup_id, $from_course_id = 0) { + global $moduleFactory; + require_once(AT_INCLUDE_PATH.'classes/pclzip.lib.php'); + require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + + if (!$from_course_id) { + $from_course_id = $this->course_id; + } + + // 1. get backup row/information + $my_backup = $this->getRow($backup_id, $from_course_id); + + @mkdir(AT_CONTENT_DIR . 'import/' . $this->course_id); + $this->import_dir = AT_CONTENT_DIR . 'import/' . $this->course_id . '/'; + + // 2. extract the backup + $archive = new PclZip(AT_BACKUP_DIR . $from_course_id. '/' . $my_backup['system_file_name']. '.zip'); + if ($archive->extract( PCLZIP_OPT_PATH, $this->import_dir, + PCLZIP_CB_PRE_EXTRACT, 'preImportCallBack') == 0) { + die("Error : ".$archive->errorInfo(true)); + } + + // 3. get the course's max_quota. if backup is too big AND we want to import files then abort/return FALSE + /* get the course's max_quota */ + // $this->getFilesSize(); + + // 4. figure out version number + $this->version = $this->getVersion(); + if (!$this->version) { + clr_dir($this->import_dir); + global $msg; + $msg->addError('BACKUP_RESTORE'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; + //exit('version not found. backups < 1.3 are not supported.'); + } + + if (version_compare($this->version, VERSION, '>') == 1) { + clr_dir($this->import_dir); + global $msg; + + $msg->addError('BACKUP_UNSUPPORTED_GREATER_VERSION'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; + } + if (version_compare($this_version, '1.5.3', '<')) { + if (file_exists($this->import_dir . 'resource_categories.csv')) { + @rename($this->import_dir . 'resource_categories.csv', $this->import_dir. 'links_categories.csv'); + } + if (file_exists($this->import_dir . 'resource_links.csv')) { + @rename($this->import_dir . 'resource_links.csv', $this->import_dir. 'links.csv'); + } + } + + // 5. if override is set then delete the content + if ($action == 'overwrite') { + require_once(AT_INCLUDE_PATH.'../mods/_core/properties/lib/delete_course.inc.php'); + delete_course($this->course_id, $material); + $_SESSION['s_cid'] = 0; + } // else: appending content + + if ($material === TRUE) { + // restore the entire backup (used when creating a new course) + $module_list = $moduleFactory->getModules(AT_MODULE_ENABLED | AT_MODULE_CORE); + $_POST['material'] = $module_list; + } + foreach ($_POST['material'] as $module_name => $garbage) { + // restore course properties, ONLY BANNER FOR NOW. + if ($module_name == 'properties' && file_exists($this->import_dir . "properties.csv")) + { + global $db; + + $fp = @fopen($this->import_dir . "properties.csv", 'rb'); + + if (($row = @fgetcsv($fp, 70000)) !== false) + { + //hack for http://www.atutor.ca/atutor/mantis/view.php?id=3839 + $row[0] = preg_replace('/\\\\r\\\\n/', "\r\n", $row[0]); + + $sql = "UPDATE ".TABLE_PREFIX."courses + SET banner = '". mysql_real_escape_string($row[0]). "' + WHERE course_id = ".$this->course_id; + $result = mysql_query($sql,$db) or die(mysql_error()); + } + } + + // restore modules + $module = $moduleFactory->getModule($module_name); + $module->restore($this->course_id, $this->version, $this->import_dir); + } + clr_dir($this->import_dir); + } + + // private + // no longer used + function restore_files() { + $sql = "SELECT max_quota FROM ".TABLE_PREFIX."courses WHERE course_id=$this->course_id"; + $result = mysql_query($sql, $this->db); + $row = mysql_fetch_assoc($result); + + if ($row['max_quota'] != AT_COURSESIZE_UNLIMITED) { + global $MaxCourseSize, $MaxCourseFloat; + + if ($row['max_quota'] == AT_COURSESIZE_DEFAULT) { + $row['max_quota'] = $MaxCourseSize; + } + + $totalBytes = dirsize($this->import_dir . 'content/'); + + $course_total = dirsize(AT_CONTENT_DIR . $this->course_id . '/'); + + $total_after = $row['max_quota'] - $course_total - $totalBytes + $MaxCourseFloat; + + if ($total_after < 0) { + //debug('not enough space. delete everything'); + // remove the content dir, since there's no space for it + clr_dir($this->import_dir); + return FALSE; + } + } + + copys($this->import_dir.'content/', AT_CONTENT_DIR . $this->course_id); + } +} + +?> \ No newline at end of file diff --git a/mods/_core/backups/classes/TableBackup.class.php b/mods/_core/backups/classes/TableBackup.class.php new file mode 100644 index 000000000..7247de461 --- /dev/null +++ b/mods/_core/backups/classes/TableBackup.class.php @@ -0,0 +1,1153 @@ +version = $version; + $this->db = $db; + $this->course_id = $course_id; + $this->import_dir = $import_dir; + } + + /** + * Create and return the specified AbstractTable Object. + * + * @access public + * @param string $table_name The name of the table to create an Object for. + * @return AbstractTable Object|NULL if $table_name does not match available Objects. + * @See AbstractTable + * + */ + function createTable($table_name) { + // static hash to keep track of new ID mappings: + static $id_map; + + switch ($table_name) { + case 'stats': + return new CourseStatsTable($this->version, $this->db, $this->course_id, $this->import_dir, $garbage); + break; + + /* + case 'polls': + return new PollsTable($this->version, $this->db, $this->course_id, $this->import_dir, $garbage); + break; + */ + + case 'tests': + return new TestsTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + case 'tests_questions_categories': + return new TestsQuestionsCategoriesTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + case 'tests_questions_assoc': + return new TestsQuestionsAssocTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + case 'tests_questions': + return new TestsQuestionsTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + case 'news': + return new NewsTable($this->version, $this->db, $this->course_id, $this->import_dir, $garbage); + break; + + case 'groups': + return new GroupsTable($this->version, $this->db, $this->course_id, $this->import_dir, $garbage); + break; + + case 'forums': + return new ForumsTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + case 'forums_courses': + return new ForumsCoursesTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + case 'glossary': + return new GlossaryTable($this->version, $this->db, $this->course_id, $this->import_dir, $garbage); + break; + + case 'resource_links': + return new ResourceLinksTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + case 'resource_categories': + return new ResourceCategoriesTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + case 'content': + return new ContentTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + case 'related_content': + return new RelatedContentTable($this->version, $this->db, $this->course_id, $this->import_dir, $id_map); + break; + + default: + if (class_exists($table_name . 'Table')) { + $table_name = $table_name . 'Table'; + return new $table_name($this->version, $this->db, $this->course_id, $this->import_dir, $garbage); + } + return NULL; + } + } +} + +/** +* AbstractTable +* Class for restoring backup tables +* @access public +* @author Joel Kronenberg +* @package Backup +*/ +class AbstractTable { + /** + * The ATutor version this backup was created with. + * + * @access protected + * @var string + */ + var $version; + + /** + * The database handler. + * + * @access private + * @var resource + */ + var $db; + + /** + * The CSV table file handler. + * + * @access private + * @var resource + */ + var $fp; + + /** + * The course ID we're restoring into. + * + * @access private + * @var int + */ + var $course_id; + + /** + * The directory unzip backup is found. + * + * @access private + * @var string + */ + var $import_dir; + + /** + * A hash table associated old ID's (key) with their new ID's (value). + * + * @access private + * @var array + */ + var $old_ids_to_new_ids; + + /** + * Constructor. + * + * @param string $version The backup version. + * @param resource $db The database handler. + * @param int $course_id The ID of this course. + * @param string $import_dir The directory where the backup was unzipped to. + * @param array $old_ids_to_new_ids Reference. + * + */ + function AbstractTable($version, $db, $course_id, $import_dir, &$old_ids_to_new_ids) { + global $db; + $this->db = $db; + $this->course_id = $course_id; + $this->version = $version; + $this->import_dir = $import_dir; + + $this->old_ids_to_new_ids = $old_ids_to_new_ids; + + if (!isset($this->old_ids_to_new_ids[$this->tableName])) { + $this->old_ids_to_new_ids[$this->tableName] = array(); + } + } + + // -- public methods below: + + /** + * Restores the table defined in the CSV file, one row at a time. + * + * @access public + * @return void + * + * @See getRows() + * @See insertRow() + */ + function restore() { + global $db; + + // skipLock is used specificially with the `forums_courses` table + if (!isset($this->skipLock)) { + $this->lockTable(); + } + + $this->getRows(); + + if ($this->rows) { + foreach ($this->rows as $row) { + + $sql = $this->generateSQL($row); + mysql_query($sql, $db); + //debug($sql); + //debug(mysql_error($this->db)); + } + } + if (!isset($this->skipLock)) { + $this->unlockTable(); + } + } + + // -- protected methods below: + + /** + * Converts escaped white space characters to their correct representation. + * + * @access protected + * @param string $input The string to convert. + * @return string The converted string. + * @See Backup::quoteCSV() + */ + function translateWhitespace($input) { + $input = str_replace('\n', "\n", $input); + $input = str_replace('\r', "\r", $input); + $input = str_replace('\x00', "\0", $input); + + $input = addslashes($input); + return $input; + } + + // protected + // find the index offset + function findOffset($id) { + return $this->rows[$id]['index_offset']; + } + + // -- private methods below: + function getNextID() { + global $db; + + $sql = 'SELECT MAX(' . $this->primaryIDField . ') AS next_id FROM ' . TABLE_PREFIX . $this->tableName; + $result = mysql_query($sql, $db); + $next_index = mysql_fetch_assoc($result); + return ($next_index['next_id'] + 1); + } + + /** + * Gets the member_id of the instructor who owns a course in the context of a backup restore + * + * @param int $id The backup course to query on + * @access protected + * @return int The member_id who owns the corresponding backup course + */ + function resolveBkpOwner($id) { + global $db; + + $sql = 'SELECT member_id FROM ' . TABLE_PREFIX . 'courses WHERE course_id = '. $id; + + $result = mysql_query($sql, $db); + + if (!$result) { + echo 'Fatal SQL error occured in TableBackup:resolveBkpOwner: ' . mysql_error() . + ' ' . mysql_error($db) . + ' Check that the course your are restoring to exists.'; + return; + } + + $row = mysql_fetch_assoc($result); + + if (!$row) { + echo 'Fatal SQL error occured in TableBackup:resolveBkpOwner: ' . mysql_error() . + ' ' . mysql_error($db) . + ' Check that the course your are restoring to exists.'; + return; + } + + return $row['member_id']; + } + + /** + * Reads the CSV table file into array $this->rows. + * + * @access private + * @return void + * + * @See openTable() + * @See closeTable() + * @See getOldID() + */ + function getRows() { + $this->openFile(); + $i = 0; + + $next_id = $this->getNextID(); + + while ($row = @fgetcsv($this->fp, 70000)) { + if (count($row)) { + $row[0] = trim($row[0]); + if ($row[0] == '') { + continue; + } + } + $row = $this->translateText($row); + $row = $this->convert($row); + + $row['index_offset'] = $i; + $row['new_id'] = $next_id++; + if ($this->getOldID($row) === FALSE) { + $this->rows[] = $row; + } else { + $this->rows[$this->getOldID($row)] = $row; + $this->old_ids_to_new_ids[$this->tableName][$this->getOldID($row)] = $row['new_id']; + } + + $i++; + } + $this->closeFile(); + } + + /** + * Converts $row to be ready for inserting into the db. + * + * @param array $row The row to convert. + * @access private + * @return array The converted row. + * + * @see translateWhitespace() + */ + function translateText($row) { + return $row; + global $backup_tables; + $count = 0; + + foreach ($backup_tables[$this->tableName]['fields'] as $field) { + if ($field[1] == TEXT) { + $row[$count] = $this->translateWhitespace($row[$count]); + } + $count++; + } + return $row; + } + + /** + * Locks the database table for writing or/and also lock the courses table for resolving restore issues + * in the admin context + * + * @access private + * @return void + * + * @See unlockTable() + */ + function lockTable() { + global $db; + $lock_sql; + + if ($_SESSION['member_id']) + $lock_sql = 'LOCK TABLES ' . TABLE_PREFIX . $this->tableName. ' WRITE'; + else // admin context + $lock_sql = 'LOCK TABLES ' . TABLE_PREFIX . $this->tableName. ', ' . TABLE_PREFIX . 'courses WRITE'; + + $result = mysql_query($lock_sql, $db); + } + + /** + * UnLocks the database table. + * + * @access private + * @return void + * + * @See lockTable() + */ + function unlockTable() { + global $db; + $lock_sql = 'UNLOCK TABLES'; + $result = mysql_query($lock_sql, $db); + } + + /** + * Opens the CSV table file for reading. + * + * @access private + * @return void + * + * @See closeFile() + */ + function openFile() { + $this->fp = @fopen($this->import_dir . $this->tableName . '.csv', 'rb'); + } + + /** + * Closes the CSV table file. + * + * @access private + * @return void + * + * @See openFile() + */ + function closeFile() { + @fclose($this->fp); + } + + /** + * Gets the entry/row's new ID based on it's old entry ID. + * + * @param int $id The old entry ID. + * @access protected + * @return int The new entry ID + * + */ + function getNewID($id) { + return $this->rows[$id]['new_id']; + } + + // -- abstract methods below: + /** + * Gets the entry/row ID as it appears in the CSV file, or FALSE if n/a. + * + * @param array $row The old entry row from the CSV file. + * @access private + * @return boolean|int The old ID or FALSE if not applicable. + * + */ + function getOldID($row) { /* abstract */ } + + /** + * Convert the entry/row to the current ATutor version. + * + * @param array $row The old entry row from the CSV file. + * @access private + * @return array The converted row. + * + */ + function convert($row) { /* abstract */ } + + /** + * Generate the SQL for this table. + * + * Precondition: $row has passed through convert() and + * translateText(). + * + * @param array $row The old entry row from the CSV file. + * @access private + * @return string The SQL query. + * + * @see insertRow() + */ + function generateSQL($row) { /* abstract */ } + +} +//--------------------------------------------------------------------- + +/** +* ForumsTable +* Extends AbstractTable and provides table specific methods and members. +* @access public +* @author Joel Kronenberg +* @author Heidi Hazelton +* @package Backup +*/ +class ForumsTable extends AbstractTable { + /** + * The ATutor database table name (w/o prefix). + * Also the CSV file name (w/o extension). + * + * @access private + * @var const string + */ + var $tableName = 'forums'; + + /** + * The ATutor database table primary ID field. + * + * @access private + * @var const string + */ + var $primaryIDField = 'forum_id'; + + // -- private methods below: + function getOldID($row) { + return $row[0]; + } + + function convert($row) { + if (version_compare($this->version, '1.4.3', '<')) { + // previous versions didn't have a forum_id field + static $count; + $count++; + for($i=5; $i>0; $i--) { + $row[$i] = $row[$i-1]; + } + $row[0] = $count; + } + return $row; + } + + function generateSQL($row) { + $sql = 'INSERT INTO '.TABLE_PREFIX.'forums VALUES '; + $sql .= '('.$row['new_id']. ','; + $sql .= "'".$row[1]."',"; // title + $sql .= "'".$row[2]."',"; // description + $sql .= "$row[3],"; // num_topics + $sql .= "$row[4],"; // num_posts + $sql .= "'".$row[5]."')"; // last_post + + return $sql; + } +} +//--------------------------------------------------------------------- +/** +* ForumsCoursesTable +* Extends AbstractTable and provides table specific methods and members. +* @access public +* @author Heidi Hazelton +* @package Backup +*/ +class ForumsCoursesTable extends AbstractTable { + + /** + * The ATutor database table name (w/o prefix). + * Also the CSV file name (w/o extension). + * + * @access private + * @var const string + */ + var $tableName = 'forums'; + + /** + * The ATutor database table primary ID field. + * + * @access private + * @var const string + */ + var $primaryIDField = 'forum_id'; + + /** + * Whether or not lock this table. + * This is a special case, b/c we read from the `forums` table + * but insert into the `forums_courses` table. Hence, we lock a different + * table than we actually insert into (ie. why we don't need/want a lock). + * + * @access private + * @var const boolean + */ + var $skipLock = TRUE; + + // -- private methods below: + function getOldID($row) { + return FALSE; + } + + function convert($row) { + return $row; + } + + function generateSQL($row) { + $this->count++; + if (version_compare($this->version, '1.4.3', '<')) { + $id = $this->count; + } else { + $id = $row[0]; + } + $sql = 'INSERT INTO '.TABLE_PREFIX.'forums_courses VALUES '; + $sql .= '('.$this->old_ids_to_new_ids['forums'][$id] . ','; // forum_id + $sql .= $this->course_id .")"; // course_id + + return $sql; + } +} +//--------------------------------------------------------------------- +class GlossaryTable extends AbstractTable { + var $tableName = 'glossary'; + var $primaryIDField = 'word_id'; + + function getOldID($row) { + return $row[0]; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + // insert row + $sql = 'INSERT INTO '.TABLE_PREFIX.'glossary VALUES '; + $sql .= '('.$row['new_id'].','; // word_id + $sql .= $this->course_id . ','; // course_id + $sql .= "'".$row[1]."',"; // word + $sql .= "'".$row[2]."',"; // definition + if ($row[3] == 0) { + $sql .= 0; + } else { + $sql .= $this->getNewID($row[3]); // related word + } + $sql .= ')'; + + return $sql; + } +} +//--------------------------------------------------------------------- +class ResourceCategoriesTable extends AbstractTable { + var $tableName = 'resource_categories'; + + var $primaryIDField = 'CatID'; + + function getOldID($row) { + return $row[0]; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + $sql = 'INSERT INTO '.TABLE_PREFIX.'resource_categories VALUES '; + $sql .= '('.$row['new_id'].','; + $sql .= $this->course_id .','; + // CatName + $sql .= "'".$row[1]."',"; + + // CatParent + if ($row[2] == 0) { + $sql .= 'NULL'; + } else { + $sql .= $this->getNewID($row[2]); // category parent + } + $sql .= ')'; + + return $sql; + } +} + +//--------------------------------------------------------------------- +class ResourceLinksTable extends AbstractTable { + var $tableName = 'resource_links'; + + var $primaryIDField = 'LinkID'; + + function getOldID($row) { + return FALSE; + } + + // private + function convert($row) { + // handle the white space issue as well + return $row; + } + + // private + function generateSQL($row) { + // insert row + $sql = 'INSERT INTO '.TABLE_PREFIX.'resource_links VALUES '; + $sql .= '('.$row['new_id'].', '; + $sql .= $this->old_ids_to_new_ids['resource_categories'][$row[0]] . ','; + + $sql .= "'".$row[1]."',"; // URL + $sql .= "'".$row[2]."',"; // LinkName + $sql .= "'".$row[3]."',"; // Description + $sql .= $row[4].','; // Approved + $sql .= "'".$row[5]."',"; // SubmitName + $sql .= "'".$row[6]."',"; // SubmitEmail + $sql .= "'".$row[7]."',"; // SubmitDate + $sql .= $row[8]. ')'; + + return $sql; + } +} +//--------------------------------------------------------------------- +class NewsTable extends AbstractTable { + var $tableName = 'news'; + var $primaryIDField = 'news_id'; + + function getOldID($row) { + return FALSE; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + // insert row + $sql = 'INSERT INTO '.TABLE_PREFIX.'news VALUES '; + $sql .= '('.$row['new_id'].','; + $sql .= $this->course_id.','; + + /** + * Admin user does not possess a member_id, assign ownership to instructor of restored course + */ + if (isset($_SESSION['member_id'])) { + $sql .= $_SESSION['member_id'] . ','; + } else { // admin context + $sql .= $this->resolveBkpOwner($this->course_id) . ','; + } + + $sql .= "'".$row[0]."',"; // date + $sql .= "'".$row[1]."',"; // formatting + $sql .= "'".$row[2]."',"; // title + $sql .= "'".$row[3]."')"; // body + + return $sql; + } + +} +//--------------------------------------------------------------------- +// -- tests (`tests`, `tests_questions`, `tests_categories`, `tests_questions_assoc`, `content_test_assco`) +class TestsTable extends AbstractTable { + var $tableName = 'tests'; + var $primaryIDField = 'test_id'; + + function getOldID($row) { + return $row[0]; + } + + // private + function convert($row) { + // handle the white space issue as well + if (version_compare($this->version, '1.4', '<')) { + $row[8] = 0; + $row[9] = 0; + $row[10] = 0; + $row[11] = 0; + } + + if (version_compare($this->version, '1.4.2', '<')) { + $row[12] = 0; + $row[13] = 0; + } + if (version_compare($this->version, '1.4.3', '<')) { + $row[9] = 0; + $row[14] = 0; + } + return $row; + } + + // private + function generateSQL($row) { + $sql = ''; + $sql = 'INSERT INTO '.TABLE_PREFIX.'tests VALUES '; + $sql .= '('.$row['new_id'].','; + $sql .= $this->course_id.','; + + $sql .= "'".$row[1]."',"; // title + $sql .= "'".$row[2]."',"; // format + $sql .= "'".$row[3]."',"; // start_date + $sql .= "'".$row[4]."',"; // end_date + $sql .= "'".$row[5]."',"; // randomize_order + $sql .= "'".$row[6]."',"; // num_questions + $sql .= "'".$row[7]."',"; // instructions + $sql .= '0,'; // content_id + $sql .= $row[9] . ','; // release_result + $sql .= $row[10] . ','; // random + $sql .= $row[11] . ','; // difficulty + $sql .= $row[12] . ','; // num_takes + $sql .= $row[13] . ','; // anonymous + $sql .= "'".$row[14]."'"; // out_of + $sql .= ')'; + + return $sql; + } +} +//--------------------------------------------------------------------- +class TestsQuestionsTable extends AbstractTable { + var $tableName = 'tests_questions'; + var $primaryIDField = 'question_id'; + + function getOldID($row) { + return $row[0]; + } + + // private + function convert($row) { + if (version_compare($this->version, '1.4.3', '<')) { + // basically, rework the fields then recreate the `tests_questions_assoc.csv` file. + // create the tests_questions_assoc file using $row[0] as the `test_id` and $row['new_id'] as the new question ID + static $count; + $test_id = $row[0]; + $order = $row[1]; + $weight = $row[3]; + $required = $row[4]; + + $count++; + $row[0] = $count; // question id + $row[1] = 0; // category id + $row[2] = $row[2]; // type + + for($i = 3; $i < 27; $i++) { + $row[$i] = $row[$i+2]; + } + + $assoc_data = "$test_id,$count,$weight,$order,$required\n"; + $fp = fopen($this->import_dir . 'tests_questions_assoc.csv', 'ab'); + fwrite($fp, $assoc_data); + fclose($fp); + } + + return $row; + } + + // private + function generateSQL($row) { + // insert row + + if (!isset($this->old_ids_to_new_ids['tests_questions_categories'][$row[1]])) { + $this->old_ids_to_new_ids['tests_questions_categories'][$row[1]] = 0; + } + + $sql = 'INSERT INTO '.TABLE_PREFIX.'tests_questions VALUES '; + $sql .= '('.$row['new_id'].',' . $this->old_ids_to_new_ids['tests_questions_categories'][$row[1]] . ','; + $sql .= $this->course_id; + + for ($i=2; $i <= 26; $i++) { + $sql .= ",'".$row[$i]."'"; + } + + $sql .= ')'; + + return $sql; + } +} +//--------------------------------------------------------------------- +class TestsQuestionsAssocTable extends AbstractTable { + var $tableName = 'tests_questions_assoc'; + var $primaryIDField = 'question_id'; + + function getOldID($row) { + return FALSE; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + // insert row + $sql = 'INSERT INTO '.TABLE_PREFIX.'tests_questions_assoc VALUES '; + $sql .= '(' . $this->old_ids_to_new_ids['tests'][$row[0]].',' . $this->old_ids_to_new_ids['tests_questions'][$row[1]]; + $sql .= ",'$row[2]','$row[3]','$row[4]')"; + + return $sql; + } +} +//--------------------------------------------------------------------- +class TestsQuestionsCategoriesTable extends AbstractTable { + var $tableName = 'tests_questions_categories'; + var $primaryIDField = 'category_id'; + + function getOldID($row) { + return $row[0]; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + // insert row + $sql = 'INSERT INTO '.TABLE_PREFIX.'tests_questions_categories VALUES '; + $sql .= '('.$row['new_id'].','; + $sql .= $this->course_id; + $sql .= ',"'.$row[1].'"'; + $sql .= ')'; + + return $sql; + } +} +//--------------------------------------------------------------------- +/* +class PollsTable extends AbstractTable { + var $tableName = 'polls'; + var $primaryIDField = 'poll_id'; + + function getOldID($row) { + return FALSE; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + // insert row + $sql = 'INSERT INTO '.TABLE_PREFIX.'polls VALUES '; + $sql .= '('.$row['new_id'].','; + $sql .= $this->course_id.','; + $sql .= "'$row[0]',"; // question + $sql .= "'$row[1]',"; // created date + $sql .= "0,"; // total + + for ($i=2; $i<=8; $i++) { + $sql .= "'".$row[$i]."',0,"; + } + + $sql = substr($sql, 0, -1); + $sql .= ')'; + + return $sql; + } +} +*/ +//--------------------------------------------------------------------- +class ContentTable extends AbstractTable { + var $tableName = 'content'; + + var $primaryIDField = 'content_id'; + + var $ordering; + + /** + * Constructor. + * + * @param string $version The backup version. + * @param resource $db The database handler. + * @param int $course_id The ID of this course. + * @param string $import_dir The directory where the backup was unzipped to. + * @param array $old_id_to_new_id Reference to either the parent ID's or to store current ID's. + * + */ + function ContentTable($version, $db, $course_id, $import_dir, &$old_id_to_new_id) { + // special case for `content` -- we need the max ordering + + $sql = 'SELECT MAX(ordering) AS ordering FROM '.TABLE_PREFIX.'content WHERE content_parent_id=0 AND course_id='.$course_id; + $result = mysql_query($sql, $db); + $ordering = mysql_fetch_assoc($result); + $this->ordering = $ordering['ordering'] +1; + + parent::AbstractTable($version, $db, $course_id, $import_dir, $old_id_to_new_id); + } + + function getOldID($row) { + return $row[0]; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + $sql = 'INSERT INTO '.TABLE_PREFIX.'content VALUES '; + $sql .= '('.$row['new_id'].','; // content_id + $sql .= $this->course_id .','; // course_id + if ($row[1] == 0) { // content_parent_id + $sql .= 0; + } else { + $sql .= $this->getNewID($row[1]); + } + $sql .= ','; + + if ($row[1] == 0) { + // find the new ordering: + $sql .= $this->ordering . ','; + $this->ordering ++; + } else { + $sql .= $row[2].','; + } + + $sql .= "'".$row[3]."',"; // last_modified + $sql .= $row[4] . ','; // revision + $sql .= $row[5] . ','; // formatting + $sql .= "'".$row[6]."',"; // release_date + $sql .= "'".$row[7]."',"; // keywords + $sql .= "'".$row[8]."',"; // content_path + $sql .= "'".$row[9]."',"; // title + $sql .= "'".$row[10]."',0)"; // text + + return $sql; + } +} + +//--------------------------------------------------------------------- +class ContentTestsAssocTable extends AbstractTable { + var $tableName = 'content_tests_assoc'; + var $primaryIDField = 'test_id'; + + function getOldID($row) { + return FALSE; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + // insert row + $sql = 'INSERT INTO '.TABLE_PREFIX.'content_tests_assoc VALUES '; + $sql .= '(' . $this->old_ids_to_new_ids['content'][$row[0]].',' . $this->old_ids_to_new_ids['tests'][$row[1]].')'; + + return $sql; + } +} +//--------------------------------------------------------------------- +class RelatedContentTable extends AbstractTable { + var $tableName = 'related_content'; + + var $primaryIDField = 'content_id'; + + function getOldID($row) { + return FALSE; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + $sql = 'INSERT INTO '.TABLE_PREFIX.'related_content VALUES '; + $sql .= '('.$this->old_ids_to_new_ids['content'][$row['0']].','. $this->old_ids_to_new_ids['content'][$row[1]].')'; + + return $sql; + } +} + +//--------------------------------------------------------------------- +class CourseStatsTable extends AbstractTable { + var $tableName = 'course_stats'; + var $primaryIDField = 'login_date'; // never actually used + + function getOldID($row) { + return FALSE; + } + + function getParentID($row) { + return FALSE; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + // insert row + $sql = 'INSERT INTO '.TABLE_PREFIX.'course_stats VALUES '; + $sql .= '('.$this->course_id.","; + $sql .= "'".$row[0]."',"; // login_date + $sql .= "'".$row[1]."',"; // guests + $sql .= "'".$row[2]."'"; // members + $sql .= ')'; + + return $sql; + } +} + +//--------------------------------------------------------------------- +class GroupsTable extends AbstractTable { + var $tableName = 'groups'; + var $primaryIDField = 'group_id'; + + function getOldID($row) { + return FALSE; + } + + function getParentID($row) { + return FALSE; + } + + // private + function convert($row) { + return $row; + } + + // private + function generateSQL($row) { + // insert row + $sql = 'INSERT INTO '.TABLE_PREFIX.'groups VALUES '; + $sql .= '('.$row['new_id'] . ',' . $this->course_id.","; + $sql .= "'".$row[0]."'"; // title + $sql .= ')'; + + return $sql; + } +} + +?> \ No newline at end of file diff --git a/mods/_core/backups/create.php b/mods/_core/backups/create.php new file mode 100644 index 000000000..f6cac2fcf --- /dev/null +++ b/mods/_core/backups/create.php @@ -0,0 +1,64 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + //make backup of current course + $Backup->create($_POST['description']); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    + +
    +
    +
    + +
    + + getNumAvailable() >= AT_COURSE_BACKUPS): ?> +
    +

    +
    + +
    + + +
    + +
    + + +
    + +
    +
    + + \ No newline at end of file diff --git a/mods/_core/backups/delete.php b/mods/_core/backups/delete.php new file mode 100644 index 000000000..c0150fe9c --- /dev/null +++ b/mods/_core/backups/delete.php @@ -0,0 +1,51 @@ +delete($_POST['backup_id']); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} + +else if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $delete_backup = intval($_REQUEST['backup_id']); + $sql = "SELECT * from ".TABLE_PREFIX."backups WHERE backup_id = '$delete_backup'"; + $result = mysql_query($sql, $db); + + +while ($row = mysql_fetch_assoc($result)){ + $title = $row['file_name']; +} + $index['backup_id'] = $_GET['backup_id']; + $msg->addConfirm(array('DELETE', htmlentities_utf8($title)), $index); + $msg->printConfirm(); + +require (AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_core/backups/edit.php b/mods/_core/backups/edit.php new file mode 100644 index 000000000..ac3846dd4 --- /dev/null +++ b/mods/_core/backups/edit.php @@ -0,0 +1,59 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} + +$Backup = new Backup($db, $_SESSION['course_id']); + +if (isset($_POST['edit'])) { + $Backup->edit($_POST['backup_id'], $_POST['new_description']); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$row = $Backup->getRow($_REQUEST['backup_id']); +//check for errors + + +?> +
    + +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    + \ No newline at end of file diff --git a/mods/_core/backups/index.php b/mods/_core/backups/index.php new file mode 100644 index 000000000..e51dc3067 --- /dev/null +++ b/mods/_core/backups/index.php @@ -0,0 +1,93 @@ +download($_POST['backup_id']); + exit; // never reached +} else if (isset($_POST['delete'], $_POST['backup_id'])) { + header('Location: delete.php?backup_id=' . $_POST['backup_id']); + exit; +} else if (isset($_POST['edit'], $_POST['backup_id'])) { + header('Location: edit.php?backup_id=' . $_POST['backup_id']); + exit; +} else if (!empty($_POST)) { + $msg->addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$Backup = new Backup($db, $_SESSION['course_id']); +$list = $Backup->getAvailableList(); + +?> + +
    + + + + + + + + + + + + + + + + + + + + + '; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } +?> + + +
    + + +
    '.AT_date(_AT('filemanager_date_format'), $row['date'], AT_DATE_MYSQL_DATETIME).''.get_human_size($row['file_size']).''.AT_Print(htmlentities_utf8($row['description']), 'backups.description').'
    +
    + + diff --git a/mods/_core/backups/module.php b/mods/_core/backups/module.php new file mode 100644 index 000000000..354519c0a --- /dev/null +++ b/mods/_core/backups/module.php @@ -0,0 +1,62 @@ +getAdminPrivilege()); + +if (admin_authenticate(AT_ADMIN_PRIV_BACKUPS, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + if (admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + $this->_pages['mods/_core/courses/admin/courses.php']['children'] = array('mods/_core/backups/admin/index.php'); + $this->_pages['mods/_core/backups/admin/index.php']['parent'] = 'mods/_core/coruses/admin/courses.php'; + } else { + $this->_pages[AT_NAV_ADMIN] = array('mods/_core/backups/admin/index.php'); + $this->_pages['mods/_core/backups/admin/index.php']['parent'] = AT_NAV_ADMIN; + } + + $this->_pages['mods/_core/backups/admin/index.php']['title_var'] = 'backups'; + $this->_pages['mods/_core/backups/admin/index.php']['guide'] = 'mods/_core/backups/admin/?p=backups.php'; + $this->_pages['mods/_core/backups/admin/index.php']['children'] = array('mods/_core/backups/admin/create.php'); + $this->_pages['mods/_core/backups/admin/index.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + + $this->_pages['mods/_core/backups/admin/create.php']['title_var'] = 'create_backup'; + $this->_pages['mods/_core/backups/admin/create.php']['parent'] = 'mods/_core/backups/admin/index.php'; + $this->_pages['mods/_core/backups/admin/create.php']['guide'] = 'mods/_core/backups/admin/?p=backups.php'; + + // this item is a bit iffy: + $this->_pages['mods/_core/backups/admin/restore.php']['title_var'] = 'restore'; + $this->_pages['mods/_core/backups/admin/restore.php']['parent'] = 'mods/_core/backups/admin/index.php'; + $this->_pages['mods/_core/backups/admin/restore.php']['guide'] = 'mods/_core/backups/admin/?p=backups.php'; + + $this->_pages['mods/_core/backups/admin/delete.php']['title_var'] = 'delete'; + $this->_pages['mods/_core/backups/admin/delete.php']['parent'] = 'mods/_core/backups/admin/index.php'; + + $this->_pages['mods/_core/backups/admin/edit.php']['title_var'] = 'edit'; + $this->_pages['mods/_core/backups/admin/edit.php']['parent'] = 'mods/_core/backups/admin/index.php'; +} + //instructor pages + $this->_pages['mods/_core/backups/index.php']['title_var'] = 'backups'; + $this->_pages['mods/_core/backups/index.php']['guide'] = 'instructor/?p=backups.php'; + $this->_pages['mods/_core/backups/index.php']['parent'] = 'tools/index.php'; + $this->_pages['mods/_core/backups/index.php']['children'] = array('mods/_core/backups/create.php', 'mods/_core/backups/upload.php'); + + $this->_pages['mods/_core/backups/create.php']['title_var'] = 'create'; + $this->_pages['mods/_core/backups/create.php']['parent'] = 'mods/_core/backups/index.php'; + $this->_pages['mods/_core/backups/create.php']['guide'] = 'instructor/?p=creating_restoring.php'; + + $this->_pages['mods/_core/backups/upload.php']['title_var'] = 'upload'; + $this->_pages['mods/_core/backups/upload.php']['parent'] = 'mods/_core/backups/index.php'; + $this->_pages['mods/_core/backups/upload.php']['guide'] = 'instructor/?p=downloading_uploading.php'; + + $this->_pages['mods/_core/backups/restore.php']['title_var'] = 'restore'; + $this->_pages['mods/_core/backups/restore.php']['parent'] = 'mods/_core/backups/index.php'; + $this->_pages['mods/_core/backups/restore.php']['guide'] = 'instructor/?p=creating_restoring.php'; + + $this->_pages['mods/_core/backups/edit.php']['title_var'] = 'edit'; + $this->_pages['mods/_core/backups/edit.php']['parent'] = 'mods/_core/backups/index.php'; + $this->_pages['mods/_core/backups/edit.php']['guide'] = 'instructor/?p=editing_deleting.php'; + + $this->_pages['mods/_core/backups/delete.php']['title_var'] = 'delete'; + $this->_pages['mods/_core/backups/delete.php']['parent'] = 'mods/_core/backups/index.php'; + $this->_pages['mods/_core/backups/delete.php']['guide'] = 'instructor/?p=editing_deleting.php'; + +?> \ No newline at end of file diff --git a/mods/_core/backups/module.xml b/mods/_core/backups/module.xml new file mode 100644 index 000000000..1dd9c80bc --- /dev/null +++ b/mods/_core/backups/module.xml @@ -0,0 +1,23 @@ + + + Backups + Allows instructors and administrators to create and restore course backups. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + existing + + + 2005-08-22 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/backups/module_delete.php b/mods/_core/backups/module_delete.php new file mode 100644 index 000000000..a7029843a --- /dev/null +++ b/mods/_core/backups/module_delete.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/mods/_core/backups/restore.php b/mods/_core/backups/restore.php new file mode 100644 index 000000000..6f54660bb --- /dev/null +++ b/mods/_core/backups/restore.php @@ -0,0 +1,108 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + if (!$_POST['material']) { + $msg->addError(array('EMPTY_FIELDS', _AT('material'))); + } else { + $Backup->restore($_POST['material'], $_POST['action'], $_POST['backup_id']); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$row = $Backup->getRow($_REQUEST['backup_id']); + +?> + +
    + + +
    +
    +
    + *
    + +

    + +
    + getModules(AT_MODULE_STATUS_ENABLED, 0, TRUE); + $keys = array_keys($modules); + ?> + + + isBackupable()): ?> +
    + + +
    + +
    +
    +
    + +
    +
    + +
    + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/mods/_core/backups/upload.php b/mods/_core/backups/upload.php new file mode 100644 index 000000000..7a044d3a5 --- /dev/null +++ b/mods/_core/backups/upload.php @@ -0,0 +1,85 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['upload']) && ($Backup->getNumAvailable() < AT_COURSE_BACKUPS)) { + $Backup->upload($_FILES, $_POST['description']); + + $_SESSION['done'] = 1; + + if($msg->containsErrors()) { + header('Location: upload.php'); + exit; + } else { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + +
    +
    +
    +
    +

    +
    + + getNumAvailable() >= AT_COURSE_BACKUPS): ?> +
    +

    +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + + +
    + +
    +
    +
    + + + + \ No newline at end of file diff --git a/mods/_core/cats_categories/admin/course_categories.php b/mods/_core/cats_categories/admin/course_categories.php new file mode 100644 index 000000000..cda7dbc0d --- /dev/null +++ b/mods/_core/cats_categories/admin/course_categories.php @@ -0,0 +1,91 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    +
    + +
    +
    + +
    + + + \ No newline at end of file diff --git a/mods/_core/cats_categories/admin/create_category.php b/mods/_core/cats_categories/admin/create_category.php new file mode 100644 index 000000000..7cada1f81 --- /dev/null +++ b/mods/_core/cats_categories/admin/create_category.php @@ -0,0 +1,138 @@ +addError(array('EMPTY_FIELDS', _AT('title'))); + } + $cat_name = validate_length($cat_name, 100); + + if ($_POST['theme_parent']) { + $sql = "SELECT theme FROM ".TABLE_PREFIX."course_cats WHERE cat_id=$cat_parent_id"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $cat_theme = $row['theme']; + } + } + + if (!$msg->containsErrors()) { + + $sql = "INSERT INTO ".TABLE_PREFIX."course_cats VALUES (NULL, '$cat_name', $cat_parent_id, '$cat_theme')"; + $result = mysql_query($sql, $db); + $cat_id = mysql_insert_id($db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + write_to_log(AT_ADMIN_LOG_INSERT, 'course_cats', mysql_affected_rows($db), $sql); + + header('Location: course_categories.php'); + exit; + } +} else if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: course_categories.php'); + exit; +} + +/* $categories[category_id] = array(cat_name, cat_parent, num_courses, [array(children)]) */ +$categories = get_categories(); + +require(AT_INCLUDE_PATH.'header.inc.php'); +$msg->printAll(); + +?> + +
    + + + +
    +
    + *
    + +
    + +
    + *
    + +
    + + +
    +
    + + +
    + + + +
    + + +

    +
    + + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/cats_categories/admin/delete_category.php b/mods/_core/cats_categories/admin/delete_category.php new file mode 100644 index 000000000..94979e891 --- /dev/null +++ b/mods/_core/cats_categories/admin/delete_category.php @@ -0,0 +1,67 @@ +addFeedback('CANCELLED'); + header('Location: course_categories.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + /* delete has been confirmed, delete this category */ + $cat_id = intval($_POST['cat_id']); + + if (!is_array($categories[$cat_id]['children'])) { + $sql = "DELETE FROM ".TABLE_PREFIX."course_cats WHERE cat_id=$cat_id"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_DELETE, 'course_cats', mysql_affected_rows($db), $sql); + + $sql = "UPDATE ".TABLE_PREFIX."courses SET cat_id=0 WHERE cat_id=$cat_id"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_DELETE, 'courses', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: course_categories.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $_GET['cat_id'] = intval($_GET['cat_id']); + + $sql = "SELECT * FROM ".TABLE_PREFIX."course_cats WHERE cat_id=$_GET[cat_id]"; + $result = mysql_query($sql,$db); + + if (mysql_num_rows($result) == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); + } else { + $row = mysql_fetch_assoc($result); + + $hidden_vars['cat_name']= $row['cat_name']; + $hidden_vars['cat_id'] = $row['cat_id']; + + $confirm = array('DELETE_CATEGORY', AT_print($row['cat_name'], 'course_cats.cat_name')); + $msg->addConfirm($confirm, $hidden_vars); + + $msg->printConfirm(); + } + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_core/cats_categories/admin/edit_category.php b/mods/_core/cats_categories/admin/edit_category.php new file mode 100644 index 000000000..d843a6d21 --- /dev/null +++ b/mods/_core/cats_categories/admin/edit_category.php @@ -0,0 +1,146 @@ +addError(array('EMPTY_FIELDS', _AT('title'))); + } + + if (!$msg->containsErrors()) { + + if ($_POST['theme_children']) { + // apply this theme to all the sub-categories recursively. + $children = recursive_get_subcategories($cat_id); + $children = implode(',', $children); + + if ($children) { + $sql = "UPDATE ".TABLE_PREFIX."course_cats SET theme='$cat_theme' WHERE cat_id IN ($children)"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_UPDATE, 'course_cats', mysql_affected_rows($db), $sql); + } + } + + $sql = "UPDATE ".TABLE_PREFIX."course_cats SET cat_parent=$cat_parent_id, cat_name='$cat_name', theme='$cat_theme' WHERE cat_id=$cat_id"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_UPDATE, 'course_cats', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: course_categories.php'); + exit; + } +} else if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: course_categories.php'); + exit; +} + +/* get all the categories: */ +/* $categories[category_id] = array(cat_name, cat_parent, num_courses, [array(children)]) */ +$categories = get_categories(); + +require(AT_INCLUDE_PATH.'header.inc.php'); +$msg->printAll(); + +?> + +
    + + + +
    +
    + *
    + +
    + +
    + *
    + +
    + + + +
    +
    + + +
    + + + +
    + + +

    +
    + + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/cats_categories/lib/admin_categories.inc.php b/mods/_core/cats_categories/lib/admin_categories.inc.php new file mode 100644 index 000000000..fae4ba1ea --- /dev/null +++ b/mods/_core/cats_categories/lib/admin_categories.inc.php @@ -0,0 +1,150 @@ +'; + foreach($categories[0] as $child_cat_id) { + print_categories($categories, $child_cat_id); + } + echo ''; + } else { + echo '
  • '; + if ($cat_id == $_REQUEST['cat_id']) { + echo ''.$categories[$cat_id]['cat_name'].''; + } else if ($cat_id == $_REQUEST['pcat_id']) { + echo ''.$categories[$cat_id]['cat_name'].''; + } else { + echo ''.$categories[$cat_id]['cat_name'].''; + } + echo ' ('.$categories[$cat_id]['num_courses'].' '; + if ($categories[$cat_id]['num_courses'] == 1) { + echo _AT('course'); + } else { + echo _AT('courses'); + } + + echo ')'; + if (is_array($categories[$cat_id]['children'])) { + echo '
      '; + foreach($categories[$cat_id]['children'] as $child_cat_id) { + print_categories($categories, $child_cat_id); + } + echo '
    '; + } + echo '
  • '; + } +} + +/* generates a + + + +
    + +
    + +
    +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     #
    + + + + + +
    +
    + \ No newline at end of file diff --git a/mods/_core/content/menu_inline_editor_submit.php b/mods/_core/content/menu_inline_editor_submit.php new file mode 100644 index 000000000..906aef53d --- /dev/null +++ b/mods/_core/content/menu_inline_editor_submit.php @@ -0,0 +1,32 @@ + "" && trim($_POST['value']) <> "") +{ + $fields = explode('-', $_POST['field']); + $content_id = intval($fields[1]); + + if ($content_id > 0) + { + $sql = "UPDATE ".TABLE_PREFIX."content SET title='".$addslashes($_POST['value'])."' WHERE content_id=$content_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_array($result); + } +} +?> \ No newline at end of file diff --git a/mods/_core/content/module.php b/mods/_core/content/module.php new file mode 100644 index 000000000..ca8b3df5b --- /dev/null +++ b/mods/_core/content/module.php @@ -0,0 +1,65 @@ +getPrivilege()); +} +$_student_tool = 'mods/_core/imscp/export.php'; +global $_custom_css; +$_custom_css = AT_BASE_HREF."jscripts/infusion/components/inlineEdit/css/InlineEdit.css"; + +//side menu dropdowns +$this->_stacks['menu_menu'] = array('title_var'=>'menu_menu', 'file'=>AT_INCLUDE_PATH.'html/dropdowns/menu_menu.inc.php'); +$this->_stacks['related_topics'] = array('title_var'=>'related_topics', 'file'=>AT_INCLUDE_PATH.'html/dropdowns/related_topics.inc.php'); +$this->_stacks['search'] = array('title_var'=>'search', 'file'=>AT_INCLUDE_PATH.'html/dropdowns/search.inc.php'); + + +$this->_pages['search.php']['title_var'] = 'search'; + +$this->_pages['mods/_core/content/index.php']['title_var'] = 'content'; +$this->_pages['mods/_core/content/index.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_core/content/index.php']['guide'] = 'instructor/?p=content.php'; +$this->_pages['mods/_core/content/index.php']['children'] = array('mods/_core/editor/add_content.php', 'mods/_core/editor/arrange_content.php', 'mods/_core/imscp/index.php'); + +$this->_pages['mods/_core/editor/add_content.php']['title_var'] = 'add_content'; +$this->_pages['mods/_core/editor/add_content.php']['parent'] = 'mods/_core/content/index.php'; +$this->_pages['mods/_core/editor/add_content.php']['guide'] = 'instructor/?p=creating_editing_content.php'; + +$this->_pages['mods/_core/editor/arrange_content.php']['title_var'] = 'arrange_content'; +$this->_pages['mods/_core/editor/arrange_content.php']['parent'] = 'mods/_core/content/index.php'; +$this->_pages['mods/_core/editor/arrange_content.php']['guide'] = 'instructor/?p=arrange_content.php'; + +$this->_pages['mods/_core/editor/edit_content.php']['title_var'] = 'edit_content'; +$this->_pages['mods/_core/editor/edit_content.php']['parent'] = 'mods/_core/content/index.php'; +$this->_pages['mods/_core/editor/edit_content.php']['guide'] = 'instructor/?p=content_edit.php'; + +$this->_pages['mods/_core/editor/preview.php']['title_var'] = 'preview'; +$this->_pages['mods/_core/editor/preview.php']['parent'] = 'mods/_core/content/edit_content.php'; + +$this->_pages['mods/_core/editor/accessibility.php']['title_var'] = 'accessibility'; +$this->_pages['mods/_core/editor/accessibility.php']['parent'] = 'mods/_core/content/edit_content.php'; + +//instructors +$this->_pages['mods/_core/imscp/index.php']['title_var'] = 'content_packaging'; +$this->_pages['mods/_core/imscp/index.php']['parent'] = 'mods/_core/content/index.php'; +$this->_pages['mods/_core/imscp/index.php']['guide'] = 'instructor/?p=content_packages.php'; + +//students +$this->_pages['mods/_core/imscp/export.php']['title_var'] = 'export_content'; +$this->_pages['mods/_core/imscp/export.php']['img'] = 'images/home-export_content.png'; +$this->_pages['mods/_core/imscp/export.php']['text'] = _AT('export_content_text'); +$this->_pages['mods/_core/imscp/export.php']['guide'] = 'general/?p=export_content.php'; + + +if (!isset($_GET['cid']) && !isset($_POST['cid'])) + $this->_pages['mods/_core/editor/edit_content_folder.php']['title_var'] = 'add_content_folder'; +else + $this->_pages['mods/_core/editor/edit_content_folder.php']['title_var'] = 'edit_content_folder'; +$this->_pages['mods/_core/editor/edit_content_folder.php']['parent'] = 'mods/_core/content/index.php'; +$this->_pages['mods/_core/editor/edit_content_folder.php']['guide'] = 'instructor/?p=creating_editing_content_folder.php'; + +$this->_pages['mods/_core/editor/delete_content.php']['title_var'] = 'delete_content'; +$this->_pages['mods/_core/editor/delete_content.php']['parent'] = 'mods/_core/content/index.php'; + +?> \ No newline at end of file diff --git a/mods/_core/content/module.xml b/mods/_core/content/module.xml new file mode 100644 index 000000000..750bcc530 --- /dev/null +++ b/mods/_core/content/module.xml @@ -0,0 +1,23 @@ + + + Content Pages + Displays the course material via content pages, created and editable by the instructor. Instructors and students are also able to export specific content pages. Statistics of which content pages students have viewed are also avaiable in "My Tracker". + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + + + 2005-08-25 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/content/module_backup.php b/mods/_core/content/module_backup.php new file mode 100644 index 000000000..856cea14b --- /dev/null +++ b/mods/_core/content/module_backup.php @@ -0,0 +1,99 @@ + \ No newline at end of file diff --git a/mods/_core/content/module_delete.php b/mods/_core/content/module_delete.php new file mode 100644 index 000000000..8f23d845b --- /dev/null +++ b/mods/_core/content/module_delete.php @@ -0,0 +1,35 @@ +deleteA4a(); + } + + $sql = "DELETE FROM ".TABLE_PREFIX."content WHERE course_id=$course"; + $result = mysql_query($sql,$db); + + $sql = "OPTIMIZE TABLE ".TABLE_PREFIX."content"; + $result = @mysql_query($sql, $db); + +} + +?> \ No newline at end of file diff --git a/mods/_core/content/refresh_content_nav.php b/mods/_core/content/refresh_content_nav.php new file mode 100644 index 000000000..d435c9759 --- /dev/null +++ b/mods/_core/content/refresh_content_nav.php @@ -0,0 +1,21 @@ +printMainMenu(); +?> \ No newline at end of file diff --git a/mods/_core/courses/admin/auto_enroll.php b/mods/_core/courses/admin/auto_enroll.php new file mode 100644 index 000000000..43632e0f2 --- /dev/null +++ b/mods/_core/courses/admin/auto_enroll.php @@ -0,0 +1,91 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    + + + + + + + + + + + + + + + + + + "; + ?> + + + + + + + + + + + + + +
     
    +
    + +
    +
    + +
    + + + \ No newline at end of file diff --git a/mods/_core/courses/admin/auto_enroll_delete.php b/mods/_core/courses/admin/auto_enroll_delete.php new file mode 100644 index 000000000..8e10521a2 --- /dev/null +++ b/mods/_core/courses/admin/auto_enroll_delete.php @@ -0,0 +1,66 @@ +addFeedback('CANCELLED'); + header('Location: auto_enroll.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + /* delete has been confirmed, delete this category */ + $auto_enroll_id = intval($_POST['auto_enroll_id']); + + $sql = "DELETE FROM ".TABLE_PREFIX."auto_enroll WHERE auto_enroll_id=$auto_enroll_id"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_DELETE, 'auto_enroll', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."auto_enroll_courses WHERE auto_enroll_id=$auto_enroll_id"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_DELETE, 'auto_enroll_courses', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: auto_enroll.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $_GET['auto_enroll_id'] = intval($_GET['auto_enroll_id']); + + $sql = "SELECT * FROM ".TABLE_PREFIX."auto_enroll WHERE auto_enroll_id=$_GET[auto_enroll_id]"; + $result = mysql_query($sql,$db) or die(mysql_error()); + + if (mysql_num_rows($result) == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); + } else { + $row = mysql_fetch_assoc($result); + + $hidden_vars['name']= $row['name']; + $hidden_vars['auto_enroll_id'] = $row['auto_enroll_id']; + +// $confirm = array('DELETE_AUTO_ENROLL', AT_print($row['name'], 'auto_enroll.name')); + $confirm = array('DELETE_AUTO_ENROLL', $row['name']); + $msg->addConfirm($confirm, $hidden_vars); + + $msg->printConfirm(); + } + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_core/courses/admin/auto_enroll_edit.php b/mods/_core/courses/admin/auto_enroll_edit.php new file mode 100644 index 000000000..85b28ced5 --- /dev/null +++ b/mods/_core/courses/admin/auto_enroll_edit.php @@ -0,0 +1,252 @@ + $maxlength) + $length = mt_rand ($maxlength, $minlength); + else + $length = mt_rand ($minlength, $maxlength); + + for ($i=0; $i<$length; $i++) + $key .= $charset[(mt_rand(0,(strlen($charset)-1)))]; + + return $key; +} + +// Main process +if (isset($_REQUEST['auto_enroll_id'])) $auto_enroll_id = $_REQUEST['auto_enroll_id']; +else $auto_enroll_id = 0; + +if (isset($_POST['save']) || isset($_POST['add'])) +{ + /* insert or update a category */ +// $cat_parent_id = intval($_POST['cat_parent_id']); + $name = trim($_POST['name']); + + $name = $addslashes($name); + + $name = validate_length($name, 50); + + if (isset($_POST['add']) && !$_POST['add_ids']) + $msg->addError('NO_ITEM_SELECTED'); + + if (!$msg->containsErrors()) + { + if ($auto_enroll_id == 0) + { + $sql = "INSERT INTO ".TABLE_PREFIX."auto_enroll(associate_string, name) + VALUES ('". get_random_string(6, 10) ."', '". $name ."')"; + $result = mysql_query($sql, $db) or die(mysql_error()); + $auto_enroll_id = mysql_insert_id($db); + write_to_log(AT_ADMIN_LOG_INSERT, 'auto_enroll', mysql_affected_rows($db), $sql); + } + else + { + $sql = "UPDATE ".TABLE_PREFIX."auto_enroll + SET name = '". $name ."' + WHERE auto_enroll_id = ".$auto_enroll_id; + + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_UPDATE, 'auto_enroll', mysql_affected_rows($db), $sql); + } + + if (isset($_POST['add'])) + { + foreach ($_POST['add_ids'] as $elem) + { + $sql = "SELECT count(*) cnt FROM ".TABLE_PREFIX."auto_enroll_courses + WHERE auto_enroll_id = ".$auto_enroll_id ." + AND course_id = ". $elem; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + if ($row["cnt"] == 0) + { + $sql = "INSERT INTO ".TABLE_PREFIX."auto_enroll_courses (auto_enroll_id, course_id) + VALUES (" . $auto_enroll_id .", " . $elem . ")"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + write_to_log(AT_ADMIN_LOG_INSERT, 'auto_enroll_courses', mysql_affected_rows($db), $sql); + } + } + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + if (isset($_POST["save"])) + { + header('Location: auto_enroll.php'); + exit; + } + } +} +else if (isset($_POST['delete'])) +{ + if (!$_POST['delete_ids']) + $msg->addError('NO_ITEM_SELECTED'); + + if (!$msg->containsErrors()) + { + foreach ($_POST['delete_ids'] as $elem) + { + $sql = "DELETE FROM ".TABLE_PREFIX."auto_enroll_courses + WHERE auto_enroll_courses_id = " . $elem; +// print $sql."
    "; + $result = mysql_query($sql, $db) or die(mysql_error()); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + write_to_log(AT_ADMIN_LOG_DELETE, 'auto_enroll_courses', mysql_affected_rows($db), $sql); + } +} +else if (isset($_POST['cancel'])) +{ + $msg->addFeedback('CANCELLED'); + header('Location: auto_enroll.php'); + exit; +} + +/* $categories[category_id] = array(cat_name, cat_parent, num_courses, [array(children)]) */ +require(AT_INCLUDE_PATH.'header.inc.php'); +$msg->printAll(); + +// existing auto enrollment +if ($auto_enroll_id > 0) +{ + $sql = "SELECT * FROM ".TABLE_PREFIX."auto_enroll + WHERE auto_enroll_id = " . $auto_enroll_id; + + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); +} +?> + +
    + + +
    +
    +


    + +
    + + +
    +


    +
    + +
    + + + + + + + + + + + + + + + + + + 0) +{ + $sql_courses = "SELECT auto_enroll_courses.auto_enroll_courses_id auto_enroll_courses_id, + auto_enroll_courses.course_id, + courses.cat_id, + courses.title title + FROM " . TABLE_PREFIX."auto_enroll_courses auto_enroll_courses, " . TABLE_PREFIX ."courses courses + where auto_enroll_courses.auto_enroll_id=".$auto_enroll_id . + " and auto_enroll_courses.course_id = courses.course_id"; + + $result_courses = mysql_query($sql_courses, $db) or die(mysql_error()); + + $num_of_rows = mysql_num_rows($result_courses); + + if ($row_courses = mysql_fetch_assoc($result_courses)) + do { + $existing_courses[] = $row_courses["course_id"]; + ?> + + + + + + + + + + + + +
    +
    + +
    +
    +
    + +
    +   +
    + +
    + + +
    +
    + + + +
    + + diff --git a/mods/_core/courses/admin/auto_enroll_filter_courses.php b/mods/_core/courses/admin/auto_enroll_filter_courses.php new file mode 100644 index 000000000..d94eb73c4 --- /dev/null +++ b/mods/_core/courses/admin/auto_enroll_filter_courses.php @@ -0,0 +1,216 @@ +-1'; + $_POST['access'] = ''; +} + +if (isset($_POST['category']) && ($_POST['category'] > -1)) { + $_POST['category'] = intval($_POST['category']); + $page_string .= SEP.'category='.$_POST['category']; + $sql_category = '='.$_POST['category']; +} else { + $sql_category = '<>-1'; + $_POST['category'] = -1; // all (because 0 = uncategorized) +} + +if (isset($_POST['include']) && $_POST['include'] == 'one') { + $checked_include_one = ' checked="checked"'; + $page_string .= SEP.'include=one'; +} else { + $_POST['include'] = 'all'; + $checked_include_all = ' checked="checked"'; + $page_string .= SEP.'include=all'; +} + +if (!empty($_POST['search'])) { + $page_string .= SEP.'search='.urlencode($stripslashes($_POST['search'])); + $search = $addslashes($_POST['search']); + $search = explode(' ', $search); + + if ($_POST['include'] == 'all') { + $predicate = 'AND '; + } else { + $predicate = 'OR '; + } + + $sql_search = ''; + foreach ($search as $term) { + $term = trim($term); + $term = str_replace(array('%','_'), array('\%', '\_'), $term); + if ($term) { + $term = '%'.$term.'%'; + $sql_search .= "((title LIKE '$term') OR (description LIKE '$term')) $predicate"; + } + } + $sql_search = '('.substr($sql_search, 0, -strlen($predicate)).')'; +} else { + $sql_search = '1'; +} + +$sql = "SELECT * FROM ".TABLE_PREFIX."courses WHERE access $sql_access AND cat_id $sql_category AND $sql_search AND hide=0 ORDER BY title"; +$courses_result = mysql_query($sql, $db); + +// calculate number of results found +$num_results = mysql_num_rows($courses_result); + +while ($row = mysql_fetch_assoc($courses_result)) + if (in_array($row['course_id'], $existing_courses)) $num_results--; + +if ($num_results > 0) mysql_data_seek($courses_result, 0); + +// get the categories /> + + /> + + /> + + /> +
    + + +
    +
    + +
    + + +
    +
    + + +
    + : + /> + /> +
    + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    +
    + +
    +   +
    +
    +
    + + diff --git a/mods/_core/courses/admin/courses.php b/mods/_core/courses/admin/courses.php new file mode 100644 index 000000000..70bf562c2 --- /dev/null +++ b/mods/_core/courses/admin/courses.php @@ -0,0 +1,209 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$page_string = ''; + +if ($_GET['reset_filter']) { + unset($_GET); +} + +$orders = array('asc' => 'desc', 'desc' => 'asc'); +$cols = array('title' => 1, 'login' => 1, 'access' => 1, 'created_date' => 1, 'cat_name' => 1); +$_access = array('public', 'protected', 'private'); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'title'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'title'; +} else { + // no order set + $order = 'asc'; + $col = 'title'; +} + +if (isset($_GET['access']) && ($_GET['access'] != '') && isset($_access[$_GET['access']])) { + $access = 'C.access = \'' . $_access[$_GET['access']].'\''; + $page_string .= SEP.'access='.$_GET['access']; +} else { + $access = '1'; +} + +if ($_GET['search']) { + $page_string .= SEP.'search='.urlencode($_GET['search']); + $search = $addslashes($_GET['search']); + $search = str_replace(array('%','_'), array('\%', '\_'), $search); + $search = '%'.$search.'%'; + $search = "((C.title LIKE '$search') OR (C.description LIKE '$search'))"; +} else { + $search = '1'; +} + +// get number of courses on the system +$sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."courses C WHERE 1 AND $access AND $search"; +$result = mysql_query($sql, $db); +$row = mysql_fetch_assoc($result); +$num_results = $row['cnt']; + +$results_per_page = 100; +$num_pages = max(ceil($num_results / $results_per_page), 1); +$page = intval($_GET['p']); +if (!$page) { + $page = 1; +} +$count = (($page-1) * $results_per_page) + 1; +$offset = ($page-1)*$results_per_page; + +${'highlight_'.$col} = ' style="background-color: #fff;"'; + +$sql = "SELECT COUNT(*) AS cnt, approved, course_id FROM ".TABLE_PREFIX."course_enrollment WHERE approved='y' OR approved='a' GROUP BY course_id, approved"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + if ($row['approved'] == 'y') { + $row['cnt']--; // remove the instructor + } + $enrolled[$row['course_id']][$row['approved']] = $row['cnt']; +} + +$sql = "SELECT C.*, M.login, T.cat_name FROM ".TABLE_PREFIX."members M INNER JOIN ".TABLE_PREFIX."courses C USING (member_id) LEFT JOIN ".TABLE_PREFIX."course_cats T USING (cat_id) WHERE 1 AND $access AND $search ORDER BY $col $order LIMIT $offset, $results_per_page"; +$result = mysql_query($sql, $db); + +$num_rows = mysql_num_rows($result); +?> + +
    +
    +
    +

    +
    + +
    +
    + + /> + + /> + + /> + + /> +
    + +
    +
    + +
    + +
    + + +
    +
    +
    + + + +
    + + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    + + +
    +
    + \ No newline at end of file diff --git a/mods/_core/courses/admin/create_course.php b/mods/_core/courses/admin/create_course.php new file mode 100644 index 000000000..f191f6aad --- /dev/null +++ b/mods/_core/courses/admin/create_course.php @@ -0,0 +1,48 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/courses/admin/courses.php'); + exit; +} else if (isset($_POST['form_course'])) { + $errors = add_update_course($_POST, TRUE); +//debug($_POST); + if ($errors !== FALSE) { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_core/courses/admin/courses.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$msg->printAll(); + +$course = 0; +$isadmin = TRUE; + +require(AT_INCLUDE_PATH.'../mods/_core/courses/html/course_properties.inc.php'); + + + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_core/courses/admin/default_mods.php b/mods/_core/courses/admin/default_mods.php new file mode 100644 index 000000000..89deb5842 --- /dev/null +++ b/mods/_core/courses/admin/default_mods.php @@ -0,0 +1,256 @@ +addFeedback('CANCELLED'); + header('Location: courses.php'); + exit; +} + +if (isset($_POST['up'])) { + $up = key($_POST['up']); + $_new_modules = array(); + if (isset($_POST['main'])) { + foreach ($_POST['main'] as $m) { + if ($m == $up) { + $last_m = array_pop($_new_modules); + $_new_modules[] = $m; + $_new_modules[] = $last_m; + } else { + $_new_modules[] = $m; + } + } + + $_POST['main'] = $_new_modules; + } + if (isset($_POST['home'])) { + $_new_modules = array(); + foreach ($_POST['home'] as $m) { + if ($m == $up) { + $last_m = array_pop($_new_modules); + $_new_modules[] = $m; + $_new_modules[] = $last_m; + } else { + $_new_modules[] = $m; + } + } + $_POST['home'] = $_new_modules; + } + + $_POST['submit'] = TRUE; +} else if (isset($_POST['down'])) { + $_new_modules = array(); + + $down = key($_POST['down']); + + if (isset($_POST['main'])) { + foreach ($_POST['main'] as $m) { + if ($m == $down) { + $found = TRUE; + continue; + } + $_new_modules[] = $m; + if ($found) { + $_new_modules[] = $down; + $found = FALSE; + } + } + + $_POST['main'] = $_new_modules; + } + + if (isset($_POST['home'])) { + $_new_modules = array(); + foreach ($_POST['home'] as $m) { + if ($m == $down) { + $found = TRUE; + continue; + } + $_new_modules[] = $m; + if ($found) { + $_new_modules[] = $down; + $found = FALSE; + } + } + + $_POST['home'] = $_new_modules; + } + + $_POST['submit'] = TRUE; +} + +if (isset($_POST['submit'])) { + if (isset($_POST['main'])) { + $_POST['main'] = array_unique($_POST['main']); + $_POST['main'] = array_filter($_POST['main']); // remove empties + $main_defaults = implode('|', $_POST['main']); + + } else { + $main_defaults = ''; + } + + if (isset($_POST['home'])) { + $_POST['home'] = array_unique($_POST['home']); + $_POST['home'] = array_filter($_POST['home']); // remove empties + $home_defaults = implode('|', $_POST['home']); + } else { + $home_defaults = ''; + } + + if (!($_config_defaults['main_defaults'] == $main_defaults) && (strlen($main_defaults) < 256)) { + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('main_defaults', '$main_defaults')"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='main_defaults_2'"; + } else if (!($_config_defaults['main_defaults'] == $main_defaults) && (strlen($main_defaults) > 255)) { + // we don't have to worry about chopping in the middle since they'll be combined anyway + $main_defaults_1 = substr($main_defaults, 0, 255); + $main_defaults_2 = substr($main_defaults, 255); + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('main_defaults', '$main_defaults_1')"; + $result = mysql_query($sql, $db); + + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('main_defaults_2', '$main_defaults_2')"; + } else if ($_config_defaults['main_defaults'] == $main_defaults) { + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='main_defaults' OR name='name_defaults_2'"; + } + $result = mysql_query($sql, $db); + + + if (!($_config_defaults['home_defaults'] == $home_defaults) && (strlen($home_defaults) < 256)) { + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('home_defaults', '$home_defaults')"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='home_defaults_2'"; + + } else if (!($_config_defaults['home_defaults'] == $home_defaults) && (strlen($home_defaults) > 255)) { + // we don't have to worry about chopping in the middle since they'll be combined anyway + $home_defaults_1 = substr($home_defaults, 0, 255); + $home_defaults_2 = substr($home_defaults, 255); + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('home_defaults', '$home_defaults_1')"; + $result = mysql_query($sql, $db); + + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('home_defaults_2', '$home_defaults_2')"; + + } else if ($_config_defaults['home_defaults'] == $home_defaults) { + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='home_defaults' OR name='home_defaults_2'"; + } + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} + + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$main_defaults = explode('|', $_config['main_defaults']); +$home_defaults = explode('|', $_config['home_defaults']); + +$main_defaults = array_filter($main_defaults); // remove empties +$home_defaults = array_filter($home_defaults); // remove empties +?> +
    + + + + + + + + + + + + + + +getModules(AT_MODULE_STATUS_ENABLED); +$keys = array_keys($module_list); + +foreach ($keys as $dir_name) { + $module =& $module_list[$dir_name]; + + if ($module->getStudentTools()) { + $student_tools[] = $module->getStudentTools(); + } +} + +$count = 0; + +//main mods +$_current_modules = $main_defaults; +$num_main = count($_current_modules); +//main and home merged +$_current_modules = array_merge($_current_modules, array_diff($home_defaults, $main_defaults)); +$num_modules = count($_current_modules); +//all other mods +$_current_modules = array_merge($_current_modules, array_diff($student_tools, $_current_modules)); + + +foreach ($_current_modules as $tool) : + $count++; +?> + + + + + + + +
    + + +
    + + + + + + + + + + + + + +   + + 1)): ?> + + + + + + + + + + +
    +
    + + \ No newline at end of file diff --git a/mods/_core/courses/admin/default_side.php b/mods/_core/courses/admin/default_side.php new file mode 100644 index 000000000..330f86647 --- /dev/null +++ b/mods/_core/courses/admin/default_side.php @@ -0,0 +1,99 @@ +addFeedback('CANCELLED'); + header('Location: courses.php'); + exit; +} + +if (isset($_POST['submit'])) { + + $side_menu = ''; + $_stack_names = array(); + + foreach($_stacks as $name=>$file) { + $_stack_names[] = $name; + } + + $_POST['stack'] = array_unique($_POST['stack']); + $_POST['stack'] = array_intersect($_POST['stack'], $_stack_names); + + foreach($_POST['stack'] as $dropdown) { + if($dropdown != '') { + $side_menu .= $dropdown . '|'; + } + } + $side_menu = substr($side_menu, 0, -1); + + if (!($_config_defaults['side_defaults'] == $side_menu) && (strlen($side_menu) < 256)) { + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('side_defaults', '$side_menu')"; + } else if ($_config_defaults['side_defaults'] == $side_menu) { + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='side_defaults'"; + } + + $result = mysql_query($sql, $db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location:'. $_SERVER[PHP_SELF]); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    +
    +
    +

    +
    + +
    + '; + echo ''; + foreach ($_stacks as $name=>$info) { + if (isset($info['title'])) { + $title = $info['title']; + } else { + $title = _AT($info['title_var']); + } + echo ''; + } + echo ''; + echo '
    '; + } ?> +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/courses/admin/instructor_login.php b/mods/_core/courses/admin/instructor_login.php new file mode 100644 index 000000000..ee25b12b5 --- /dev/null +++ b/mods/_core/courses/admin/instructor_login.php @@ -0,0 +1,63 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/courses/admin/courses.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $sql = "SELECT * FROM ".TABLE_PREFIX."courses WHERE course_id=".$_REQUEST['course']; + $result = mysql_query($sql, $db); + $row = mysql_fetch_array($result); + + $hidden_vars['course'] = $_GET['course']; + + $msg->addConfirm(array('LOGIN_INSTRUCTOR', SITE_NAME, $row['title']), $hidden_vars); + $msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/courses/html/course_icon.inc.php b/mods/_core/courses/html/course_icon.inc.php new file mode 100644 index 000000000..ac40122d1 --- /dev/null +++ b/mods/_core/courses/html/course_icon.inc.php @@ -0,0 +1,52 @@ +addInfo('FEATURE_NOT_AVAILABLE'); +}else{ + + $gd_info = gd_info(); + $supported_images = array(); + + if ($gd_info['GIF Create Support']) { + $supported_images[] = 'gif'; + } + if ($gd_info['JPG Support']) { + $supported_images[] = 'jpg'; + } + if ($gd_info['PNG Support']) { + $supported_images[] = 'png'; + } + + if (!$supported_images) { + $msg->addInfo('FEATURE_NOT_AVAILABLE'); + } +} +?> + + +
    +

    + printInfos(); + }else{ + + ?> + () + +
    diff --git a/mods/_core/courses/html/course_properties.inc.php b/mods/_core/courses/html/course_properties.inc.php new file mode 100644 index 000000000..b3c47b542 --- /dev/null +++ b/mods/_core/courses/html/course_properties.inc.php @@ -0,0 +1,678 @@ + + +
    + + + + + + + + + +
    +
    + +
    + *
    + '; + do { + if ($instructor_row['member_id'] == $row['member_id']) { + echo ''; + } else { + echo ''; + } + } while($instructor_row = mysql_fetch_assoc($result)); + echo ''; + } else { + echo ''._AT('none_found').''; + } + ?> +
    + + +
    + *
    + +
    + +
    +
    + printDropdown($row['primary_language'], 'pri_lang', 'pri_lang'); ?> +
    + +
    +
    + +
    + + +
    +
    + +
    + + + + +
    +
    + +
    + + +
    +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + + />

    + + />

    + + />
    + /> +
    + />. +
    + +
    +
    + + + />
    + + + /> + +
    + +
    +
    + + + />
    + + /> + +
    + +
    + '; + echo ''; + } else { + echo ''; + } + ?> +
    +
    + +
    + +
    + + +
    +
    + +
    + + + +
    +
    + 0) { + $course_size = dirsize(AT_CONTENT_DIR . $course.'/'); + } else { + $course_size = 0; + } + + if ($course) { + echo _AT('current_course_size') .': '.get_human_size($course_size).'
    '; + } + ?> + + />
    + />
    + /> - + value="" size="4" /> +
    + +
    +
    + + />
    + />
    + /> - + value="" size="4" /> +
    + + + + + + + +
    +
    + +
    +
    + "; + + //include(AT_INCLUDE_PATH.'html/course_icon.inc.php'); + ?> + <?php echo $row['icon']; ?> + + + + + +
    +
    + +
    + + + + +
    + +
    + +
    + + + +
    + "; + echo ""; + ?> + + + +
    +
    +
    + +
    +document.getElementById('uploadform').focus();"; +} +?> + + \ No newline at end of file diff --git a/mods/_core/courses/lib/course.inc.php b/mods/_core/courses/lib/course.inc.php new file mode 100644 index 000000000..569b42fc7 --- /dev/null +++ b/mods/_core/courses/lib/course.inc.php @@ -0,0 +1,430 @@ +addError(array('EMPTY_FIELDS', $missing_fields)); + } + + $_POST['access'] = $addslashes($_POST['access']); + $_POST['title'] = $addslashes($_POST['title']); + $_POST['description'] = $addslashes($_POST['description']); + $_POST['hide'] = $addslashes($_POST['hide']); + $_POST['pri_lang'] = $addslashes($_POST['pri_lang']); + $_POST['created_date'] = $addslashes($_POST['created_date']); + $_POST['copyright'] = $addslashes($_POST['copyright']); + $_POST['icon'] = $addslashes($_POST['icon']); + $_POST['banner'] = $addslashes($_POST['banner']); + $_POST['course_dir_name'] = $addslashes($_POST['course_dir_name']); + + $_POST['course'] = intval($_POST['course']); + $_POST['notify'] = intval($_POST['notify']); + $_POST['hide'] = intval($_POST['hide']); + $_POST['instructor']= intval($_POST['instructor']); + $_POST['category_parent'] = intval($_POST['category_parent']); + $_POST['rss'] = intval($_POST['rss']); + + // Course directory name (aka course slug) + if ($_POST['course_dir_name'] != ''){ + //validate the course_dir_name, allow only alphanumeric, dash, underscore. + if (preg_match('/^[\w][\w\d\-\_]+$/', $_POST['course_dir_name'])==0){ + $msg->addError('COURSE_DIR_NAME_INVALID'); + } + + //check if the course_dir_name is already being used + $sql = 'SELECT COUNT(course_id) as cnt FROM '.TABLE_PREFIX."courses WHERE course_id!=$_POST[course] AND course_dir_name='$_POST[course_dir_name]'"; + $result = mysql_query($sql); + $num_of_dir = mysql_fetch_assoc($result); + if (intval($num_of_dir['cnt']) > 0){ + $msg->addError('COURSE_DIR_NAME_IN_USE'); + } + } + + // Custom icon + if ($_FILES['customicon']['name'] != ''){ + // Use custom icon instead if it exists + $_POST['icon'] = $addslashes($_FILES['customicon']['name']); + } + if ($_FILES['customicon']['error'] == UPLOAD_ERR_FORM_SIZE){ + // Check if filesize is too large for a POST + $msg->addError(array('FILE_MAX_SIZE', $_config['prof_pic_max_file_size'] . ' ' . _AT('bytes'))); + } + if ($_POST['release_date']) { + $day_release = intval($_POST['day_release']); + $month_release = intval($_POST['month_release']); + $year_release = intval($_POST['year_release']); + $hour_release = intval($_POST['hour_release']); + $min_release = intval($_POST['min_release']); + + if (!checkdate($month_release, $day_release, $year_release)) { //or date is in the past + $msg->addError('RELEASE_DATE_INVALID'); + } + + if (strlen($month_release) == 1){ + $month_release = "0$month_release"; + } + if (strlen($day_release) == 1){ + $day_release = "0$day_release"; + } + if (strlen($hour_release) == 1){ + $hour_release = "0$hour_release"; + } + if (strlen($min_release) == 1){ + $min_release = "0$min_release"; + } + $release_date = "$year_release-$month_release-$day_release $hour_release:$min_release:00"; + } else { + $release_date = "0000-00-00 00:00:00"; + } + + if ($_POST['end_date']) { + $day_end = intval($_POST['day_end']); + $month_end = intval($_POST['month_end']); + $year_end = intval($_POST['year_end']); + $hour_end = intval($_POST['hour_end']); + $min_end = intval($_POST['min_end']); + + if (!checkdate($month_end, $day_end, $year_end)) { //or date is in the past + $msg->addError('END_DATE_INVALID'); + } + + if (strlen($month_end) == 1){ + $month_end = "0$month_end"; + } + if (strlen($day_end) == 1){ + $day_end = "0$day_end"; + } + if (strlen($hour_end) == 1){ + $hour_end = "0$hour_end"; + } + if (strlen($min_end) == 1){ + $min_end = "0$min_end"; + } + $end_date = "$year_end-$month_end-$day_end $hour_end:$min_end:00"; + } else { + $end_date = "0000-00-00 00:00:00"; + } + + $initial_content_info = explode('_', $_POST['initial_content'], 2); + //admin + $course_quotas = ''; + if ($isadmin) { + $instructor = $_POST['instructor']; + $quota = intval($_POST['quota']); + $quota_entered = intval($_POST['quota_entered']); + $filesize = intval($_POST['filesize']); + $filesize_entered= intval($_POST['filesize_entered']); + + //if they checked 'other', set quota=entered value, if it is empty or negative, set to default (-2) + if ($quota == '2') { + if ($quota_entered=='' || empty($quota_entered) || $quota_entered<0 ) { + $quota = AT_COURSESIZE_DEFAULT; + } else { + $quota = floatval($quota_entered); + $quota = megabytes_to_bytes($quota); + } + } + + //if they checked 'other', set filesize=entered value, if it is empty or negative, set to default + if ($filesize=='2') { + if ($filesize_entered=='' || empty($filesize_entered) || $filesize_entered<0 ) { + $filesize = AT_FILESIZE_DEFAULT; + $msg->addFeedback('COURSE_DEFAULT_FSIZE'); + } else { + $filesize = floatval($filesize_entered); + $filesize = megabytes_to_bytes($filesize); + } + } + + $course_quotas = "max_quota='$quota', max_file_size='$filesize',"; + + } else { + $instructor = $_SESSION['member_id']; + if (!$_POST['course']) { + $course_quotas = "max_quota=".AT_COURSESIZE_DEFAULT.", max_file_size=".AT_FILESIZE_DEFAULT.","; + $row = $Backup->getRow($initial_content_info[0], $initial_content_info[1]); + + if ((count($initial_content_info) == 2) + && ($system_courses[$initial_content_info[1]]['member_id'] == $_SESSION['member_id'])) { + + if ($MaxCourseSize < $row['contents']['file_manager']) { + $msg->addError('RESTORE_TOO_BIG'); + } + } else { + $initial_content_info = intval($_POST['initial_content']); + } + + } else { + unset($initial_content_info); + $course_quotas = "max_quota='{$system_courses[$_POST[course]][max_quota]}', max_file_size='{$system_courses[$_POST[course]][max_file_size]}',"; + } + } + + if ($msg->containsErrors()) { + return FALSE; + } + + //display defaults + if (!$_POST['course']) { + $menu_defaults = ",home_links='$_config[home_defaults]', main_links='$_config[main_defaults]', side_menu='$_config[side_defaults]'"; + } else { + $menu_defaults = ',home_links=\''.$system_courses[$_POST['course']]['home_links'].'\', main_links=\''.$system_courses[$_POST['course']]['main_links'].'\', side_menu=\''.$system_courses[$_POST['course']]['side_menu'].'\''; + } + + $sql = "REPLACE INTO ".TABLE_PREFIX."courses SET course_id=$_POST[course], member_id='$_POST[instructor]', access='$_POST[access]', title='$_POST[title]', description='$_POST[description]', course_dir_name='$_POST[course_dir_name]', cat_id='$_POST[category_parent]', content_packaging='$_POST[content_packaging]', notify=$_POST[notify], hide=$_POST[hide], $course_quotas primary_language='$_POST[pri_lang]', created_date='$_POST[created_date]', rss=$_POST[rss], copyright='$_POST[copyright]', icon='$_POST[icon]', banner='$_POST[banner]', release_date='$release_date', end_date='$end_date' $menu_defaults"; + + $result = mysql_query($sql, $db); + if (!$result) { + echo mysql_error($db); + echo 'DB Error'; + exit; + } + $_SESSION['is_admin'] = 1; + $new_course_id = $_SESSION['course_id'] = mysql_insert_id($db); + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_REPLACE, 'courses', mysql_affected_rows($db), $sql); + } + + if ($isadmin) { + //get current instructor and unenroll from course if different from POST instructor + $old_instructor = $system_courses[$_POST['course']]['member_id']; + + if ($old_instructor != $_POST['instructor']) { + //remove old from course enrollment + $sql = "DELETE FROM ".TABLE_PREFIX."course_enrollment WHERE course_id=".$_POST['course']." AND member_id=".$old_instructor; + $result = mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'course_enrollment', mysql_affected_rows($db), $sql); + } + } + + //enroll new instructor + $sql = "INSERT INTO ".TABLE_PREFIX."course_enrollment VALUES ($_POST[instructor], $new_course_id, 'y', 0, '"._AT('instructor')."', 0)"; + $result = mysql_query($sql, $db); + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_REPLACE, 'course_enrollment', mysql_affected_rows($db), $sql); + } + + // create the course content directory + $path = AT_CONTENT_DIR . $new_course_id . '/'; + @mkdir($path, 0700); + @copy(AT_CONTENT_DIR . 'index.html', AT_CONTENT_DIR . $new_course_id . '/index.html'); + + // create the course backup directory + $path = AT_BACKUP_DIR . $new_course_id . '/'; + @mkdir($path, 0700); + @copy(AT_CONTENT_DIR . 'index.html', AT_BACKUP_DIR . $new_course_id . '/index.html'); + + /* insert some default content: */ + + if (!$_POST['course_id'] && ($_POST['initial_content'] == '1')) { + $contentManager = new ContentManager($db, $new_course_id); + $contentManager->initContent( ); + + $cid = $contentManager->addContent($new_course_id, 0, 1,_AT('welcome_to_atutor'), + addslashes(_AT('this_is_content')), + '', '', 1, date('Y-m-d H:00:00')); + + $announcement = _AT('default_announcement'); + + $sql = "INSERT INTO ".TABLE_PREFIX."news VALUES (NULL, $new_course_id, $instructor, NOW(), 1, '"._AT('welcome_to_atutor')."', '$announcement')"; + $result = mysql_query($sql,$db); + + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_INSERT, 'news', mysql_affected_rows($db), $sql); + } + + /** + * removed - #3098 + // create forum for Welcome Course + $sql = "INSERT INTO ".TABLE_PREFIX."forums VALUES (NULL, '"._AT('forum_general_discussion')."', '', 0, 0, NOW())"; + $result = mysql_query($sql,$db); + + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_INSERT, 'forums', mysql_affected_rows($db), $sql); + } + + $sql = "INSERT INTO ".TABLE_PREFIX."forums_courses VALUES (LAST_INSERT_ID(), $new_course_id)"; + $result = mysql_query($sql,$db); + + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_INSERT, 'forums_courses', mysql_affected_rows($db), $sql); + } + ***/ + + } else if (!$_POST['course'] && (count($initial_content_info) == 2)){ + + $Backup->setCourseID($new_course_id); + $Backup->restore($material = TRUE, 'append', $initial_content_info[0], $initial_content_info[1]); + } + + // custom icon, have to be after directory is created +// $_FILES['customicon'] = $_POST['customicon']; //copy to $_FILES. + if($_FILES['customicon']['tmp_name'] != ''){ + $_POST['comments'] = trim($_POST['comments']); + + $owner_id = $_SESSION['course_id']; + $owner_type = "1"; + if ($_FILES['customicon']['error'] == UPLOAD_ERR_INI_SIZE) { + $msg->addError(array('FILE_TOO_BIG', get_human_size(megabytes_to_bytes(substr(ini_get('upload_max_filesize'), 0, -1))))); + } else if (!isset($_FILES['customicon']['name']) || ($_FILES['customicon']['error'] == UPLOAD_ERR_NO_FILE) || ($_FILES['customicon']['size'] == 0)) { + $msg->addError('FILE_NOT_SELECTED'); + + } else if ($_FILES['customicon']['error'] || !is_uploaded_file($_FILES['customicon']['tmp_name'])) { + $msg->addError('FILE_NOT_SAVED'); + } + + if (!$msg->containsErrors()) { + $_POST['description'] = $addslashes(trim($_POST['description'])); + $_FILES['customicon']['name'] = addslashes($_FILES['customicon']['name']); + + if ($_POST['comments']) { + $num_comments = 1; + } else { + $num_comments = 0; + } + + $path = AT_CONTENT_DIR.$owner_id."/custom_icons/"; + + if (!is_dir($path)) { + @mkdir($path); + } + + // if we can upload custom course icon, it means GD is enabled, no need to check extension again. + $gd_info = gd_info(); + $supported_images = array(); + if ($gd_info['GIF Create Support']) { + $supported_images[] = 'gif'; + } + if ($gd_info['JPG Support']) { + $supported_images[] = 'jpg'; + } + if ($gd_info['PNG Support']) { + $supported_images[] = 'png'; + } + + // check if this is a supported file type + $filename = $stripslashes($_FILES['customicon']['name']); + $path_parts = pathinfo($filename); + $extension = strtolower($path_parts['extension']); + $image_attributes = getimagesize($_FILES['customicon']['tmp_name']); + + if ($extension == 'jpeg') { + $extension = 'jpg'; + } + + // resize the original but don't backup a copy. + $width = $image_attributes[0]; + $height = $image_attributes[1]; + $original_img = $_FILES['customicon']['tmp_name']; + $thumbnail_img = $path . $_FILES['customicon']['name']; + + if ($width > $height && $width>79) { + $thumbnail_height = intval(79 * $height / $width); + $thumbnail_width = 79; + if (!resize_image($original_img, $thumbnail_img, $height, $width, $thumbnail_height, $thumbnail_width, $extension)){ + $msg->addError('FILE_NOT_SAVED'); + } + } else if ($width <= $height && $height > 79) { + $thumbnail_height= 100; + $thumbnail_width = intval(100 * $width / $height); + if (!resize_image($original_img, $thumbnail_img, $height, $width, $thumbnail_height, $thumbnail_width, $extension)){ + $msg->addError('FILE_NOT_SAVED'); + } + } else { + // no resizing, just copy the image. + // it's too small to resize. + copy($original_img, $thumbnail_img); + } + + } else { + $msg->addError('FILE_NOT_SAVED'); + + } + //header('Location: index.php'.$owner_arg_prefix.'folder='.$parent_folder_id); + //exit; + } + //---------------------------------------- + + /* delete the RSS feeds just in case: */ + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $new_course_id . '/RSS1.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $_POST['course'] . '/RSS1.0.xml'); + } + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $new_course_id . '/RSS2.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $new_course_id . '/RSS2.0.xml'); + } + + if ($isadmin) { + $_SESSION['course_id'] = -1; + } + + $_SESSION['course_title'] = $stripslashes($_POST['title']); + return $new_course_id; +} + +?> diff --git a/mods/_core/courses/module.php b/mods/_core/courses/module.php new file mode 100644 index 000000000..b4b92d878 --- /dev/null +++ b/mods/_core/courses/module.php @@ -0,0 +1,56 @@ +getPrivilege()); +} +if (!defined('AT_ADMIN_PRIV_COURSES')) { + define('AT_ADMIN_PRIV_COURSES', $this->getAdminPrivilege()); +} + + +// for admin +if (admin_authenticate(AT_ADMIN_PRIV_COURSES, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + + $this->_pages[AT_NAV_ADMIN] = array('mods/_core/courses/admin/courses.php'); + + $this->_pages['mods/_core/courses/admin/courses.php']['title_var'] = 'courses'; + $this->_pages['mods/_core/courses/admin/courses.php']['parent'] = AT_NAV_ADMIN; + $this->_pages['mods/_core/courses/admin/courses.php']['guide'] = 'mods/_core/courses/admin/?p=courses.php'; + $this->_pages['mods/_core/courses/admin/courses.php']['children'] = array('mods/_core/courses/admin/create_course.php','mods/_core/enrolment/admin/index.php', 'mods/_core/courses/admin/default_mods.php', 'mods/_core/courses/admin/default_side.php','mods/_core/courses/admin/auto_enroll.php'); + + $this->_pages['mods/_core/courses/admin/instructor_login.php']['title_var'] = 'view'; + $this->_pages['mods/_core/courses/admin/instructor_login.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + + $this->_pages['mods/_core/courses/admin/create_course.php']['title_var'] = 'create_course'; + $this->_pages['mods/_core/courses/admin/create_course.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + $this->_pages['mods/_core/courses/admin/create_course.php']['guide'] = 'mods/_core/courses/admin/?p=creating_courses.php'; + + $this->_pages['mods/_core/courses/admin/default_mods.php']['title_var'] = 'default_modules'; + $this->_pages['mods/_core/courses/admin/default_mods.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + $this->_pages['mods/_core/courses/admin/default_mods.php']['guide'] = 'mods/_core/courses/admin/?p=default_student_tools.php'; + + $this->_pages['mods/_core/courses/admin/default_side.php']['title_var'] = 'default_side_menu'; + $this->_pages['mods/_core/courses/admin/default_side.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + $this->_pages['mods/_core/courses/admin/default_side.php']['guide'] = 'mods/_core/courses/admin/?p=default_side_menu.php'; + + + $this->_pages['mods/_core/courses/admin/auto_enroll.php']['title_var'] = 'auto_enroll'; + $this->_pages['mods/_core/courses/admin/auto_enroll.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + $this->_pages['mods/_core/courses/admin/auto_enroll.php']['guide'] = 'admin/?p=auto_enroll.php'; + $this->_pages['mods/_core/courses/admin/auto_enroll.php']['children'] = array_merge(array('mods/_core/courses/admin/auto_enroll_edit.php')); + $this->_pages['admin/config_edit.php']['children'] = array_merge((array) $this->_pages['admin/config_edit.php']['children']); + + + + $this->_pages['mods/_core/courses/admin/auto_enroll_edit.php']['title_var'] = 'auto_enroll_edit'; + $this->_pages['mods/_core/courses/admin/auto_enroll_edit.php']['parent'] = 'mods/_core/courses/admin/auto_enroll.php'; + $this->_pages['mods/_core/courses/admin/auto_enroll_edit.php']['guide'] = 'admin/?p=auto_enroll.php'; + + $this->_pages['mods/_core/courses/admin/auto_enroll_delete.php']['title_var'] = 'auto_enroll_delete'; + $this->_pages['mods/_core/courses/admin/auto_enroll_delete.php']['parent'] = 'mods/_core/courses/admin/auto_enroll.php'; + +} + +?> \ No newline at end of file diff --git a/mods/_core/courses/module.xml b/mods/_core/courses/module.xml new file mode 100644 index 000000000..5fb37357e --- /dev/null +++ b/mods/_core/courses/module.xml @@ -0,0 +1,23 @@ + + + Courses Admin + Allows administrators to manage the courses on a system. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + + create + + 2005-09-12 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/courses/users/create_course.php b/mods/_core/courses/users/create_course.php new file mode 100644 index 000000000..64980d4da --- /dev/null +++ b/mods/_core/courses/users/create_course.php @@ -0,0 +1,86 @@ + +
    + +
    +
    +

    +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    + +
    +
    +

    +
    +
    + +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +}else if (isset($_POST['form_course']) && $_POST['submit'] != '') { + $_POST['instructor'] = $_SESSION['member_id']; + + $errors = add_update_course($_POST); + + if ($errors !== FALSE) { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'bounce.php?course='.$addslashes($errors).SEP.'p='.urlencode('index.php')); + exit; + } + +} + +$onload = 'document.course_form.title.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +require(AT_INCLUDE_PATH.'../mods/_core/courses/html/course_properties.inc.php'); +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/courses/users/request_instructor.php b/mods/_core/courses/users/request_instructor.php new file mode 100644 index 000000000..652a2814d --- /dev/null +++ b/mods/_core/courses/users/request_instructor.php @@ -0,0 +1,107 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'users/index.php'); + exit; + +} else if ($_POST['description'] == ''){ + $msg->addError(array('EMPTY_FIELDS', _AT('description'))); + header('Location: '.AT_BASE_HREF.'mods/_core/courses/users/create_course.php'); + exit; +} else if (isset($_POST['form_request_instructor'])) { + if (defined('AUTO_APPROVE_INSTRUCTORS') && AUTO_APPROVE_INSTRUCTORS) { + $sql = "UPDATE ".TABLE_PREFIX."members SET status=".AT_STATUS_INSTRUCTOR.", creation_date=creation_date, last_login=last_login WHERE member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACCOUNT_APPROVED'); + + } else { + + $_POST['description'] = $addslashes($_POST['description']); + + $sql = "INSERT INTO ".TABLE_PREFIX."instructor_approvals VALUES ($_SESSION[member_id], NOW(), '$_POST[description]')"; + $result = mysql_query($sql, $db); + /* email notification send to admin upon instructor request */ + + if (EMAIL_NOTIFY && ($_config['contact_email'] != '')) { + + $sql = "SELECT login, email FROM ".TABLE_PREFIX."members WHERE member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $email = $row['email']; + } + $tmp_message = _AT('req_message_instructor', get_display_name($_SESSION['member_id']), $_POST['description'], AT_BASE_HREF); + + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + $mail = new ATutorMailer; + + $mail->From = $email; + $mail->AddAddress($_config['contact_email']); + $mail->Subject = _AT('req_message9'); + $mail->Body = stripslashes($tmp_message); + + if(!$mail->Send()) { + //echo 'There was an error sending the message'; + $msg->printErrors('SENDING_ERROR'); + exit; + } + + unset($mail); + + } + $msg->addFeedback('APPROVAL_PENDING'); + } + + header('Location: ../../../../users/index.php'); + exit; +} + +$title = _AT('request_instructor_account'); +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($msg->containsErrors()) { $msg->printErrors(); } + +if (ALLOW_INSTRUCTOR_REQUESTS && ($row['status'] != AT_STATUS_INSTRUCTOR) ) { + $sql = "SELECT * FROM ".TABLE_PREFIX."instructor_approvals WHERE member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + if (!($row = mysql_fetch_array($result))) { + $msg->printInfos('REQUEST_ACCOUNT'); +?> +
    +

    + +

    +

    + +

    +
    +printInfos('APPROVAL_PENDING'); + } +} + + require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_core/editor/accessibility.php b/mods/_core/editor/accessibility.php new file mode 100644 index 000000000..3b7a3f8bc --- /dev/null +++ b/mods/_core/editor/accessibility.php @@ -0,0 +1,122 @@ +addError(array('EMPTY_FIELDS', $missing_fields)); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$result = $contentManager->getContentPage($cid); + +if (!($content_row = @mysql_fetch_assoc($result))) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('PAGE_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$course_base_href = ''; +$content_base_href = ''; + +//make decisions +if ($_POST['make_decision']) +{ + //get list of decisions + $desc_query = ''; + if (is_array($_POST['d'])) { + foreach ($_POST['d'] as $sequenceID => $decision) { + $desc_query .= '&'.$sequenceID.'='.$decision; + } + } + + $checker_url = AT_ACHECKER_URL. 'decisions.php?' + .'uri='.urlencode($_POST['pg_url']).'&id='.AT_ACHECKER_WEB_SERVICE_ID + .'&session='.$_POST['sessionid'].'&output=html'.$desc_query; + + if (@file_get_contents($checker_url) === false) { + $msg->addInfo('DECISION_NOT_SAVED'); + } +} +else if (isset($_POST['reverse'])) +{ + $reverse_url = AT_ACHECKER_URL. 'decisions.php?' + .'uri='.urlencode($_POST['pg_url']).'&id='.AT_ACHECKER_WEB_SERVICE_ID + .'&session='.$_POST['sessionid'].'&output=html&reverse=true&'.key($_POST['reverse']).'=N'; + + if (@file_get_contents($reverse_url) === false) { + $msg->addInfo('DECISION_NOT_REVERSED'); + } else { + $msg->addInfo('DECISION_REVERSED'); + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> +
    +
    +'; + echo ' '; + + if (!$cid) { + $msg->printInfos('SAVE_CONTENT'); + + echo '
    '; + + return; + } + +$msg->printInfos(); +if ($_POST['body_text'] != '') { + //save temp file + $_POST['content_path'] = $content_row['content_path']; + write_temp_file(); + + $pg_url = AT_BASE_HREF.'get_acheck.php/'.$_POST['cid'] . '.html'; + $checker_url = AT_ACHECKER_URL.'checkacc.php?uri='.urlencode($pg_url).'&id='.AT_ACHECKER_WEB_SERVICE_ID + . '&guide=WCAG2-L2&output=html'; + + $report = @file_get_contents($checker_url); + + if (stristr($report, '
    ')) { + $msg->printErrors('INVALID_URL'); + } else if ($report === false) { + $msg->printInfos('SERVICE_UNAVAILABLE'); + } else { + echo ' '; + echo $report; + + echo '

    '._AT('access_credit').'

    '; + } + //delete file + @unlink(AT_CONTENT_DIR . $_POST['cid'] . '.html'); + +} else { + $msg->printInfos('NO_PAGE_CONTENT'); +} +?> +
    +
    + \ No newline at end of file diff --git a/mods/_core/editor/add_content.php b/mods/_core/editor/add_content.php new file mode 100644 index 000000000..db02014ea --- /dev/null +++ b/mods/_core/editor/add_content.php @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/mods/_core/editor/arrange_content.php b/mods/_core/editor/arrange_content.php new file mode 100644 index 000000000..d0f635b20 --- /dev/null +++ b/mods/_core/editor/arrange_content.php @@ -0,0 +1,35 @@ +moveContent($_POST['moved_cid'], $new_pid, $new_ordering); + header('Location: '.AT_BASE_HREF.'mods/_core/editor/arrange_content.php'); + exit; +} + +if (!defined('AT_INCLUDE_PATH')) { exit; } + +$savant->assign('languageManager', $languageManager); + +$savant->display('editor/arrange_content.tmpl.php'); + +?> diff --git a/mods/_core/editor/delete_content.php b/mods/_core/editor/delete_content.php new file mode 100644 index 000000000..5c7d62fe0 --- /dev/null +++ b/mods/_core/editor/delete_content.php @@ -0,0 +1,68 @@ +deleteContent($_POST['cid']); + + unset($_SESSION['s_cid']); + unset($_SESSION['from_cid']); + + $msg->addFeedback('CONTENT_DELETED'); + header('Location: '.AT_BASE_HREF.'mods/_core/content/index.php'); + exit; +} else if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/content/index.php'); + exit; +} + +$_GET['cid'] = intval($_REQUEST['cid']); + +$path = $contentManager->getContentPath($cid); +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($_GET['cid'] == 0) { + $msg->printErrors('ID_ZERO'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$children = $contentManager->getContent($_GET['cid']); + +$hidden_vars['cid'] = $_GET['cid']; + +if (is_array($children) && (count($children)>0) ) { + $msg->addConfirm('SUB_CONTENT_DELETE', $hidden_vars); + $msg->addConfirm('GLOSSARY_REMAINS', $hidden_vars); +} else { + $msg->addConfirm('GLOSSARY_REMAINS', $hidden_vars); +} + +$sql = "SELECT * from ".TABLE_PREFIX."content WHERE content_id = '$hidden_vars[cid]'"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)){ + $title = $row['title']; +} + +$msg->addConfirm(array('DELETE', $title), $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/editor/edit_content.php b/mods/_core/editor/edit_content.php new file mode 100644 index 000000000..85e49c663 --- /dev/null +++ b/mods/_core/editor/edit_content.php @@ -0,0 +1,425 @@ +addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } else { + $msg->addFeedback('CLOSED'); + if ($cid == 0) { + header('Location: '.AT_BASE_HREF.'mods/_core/content/index.php'); + exit; + } + } + + if ($_REQUEST['cid'] == 0) { + header('Location: '.AT_BASE_HREF.'mods/_core/content/index.php'); + exit; + } + header('Location: '.$_base_path.url_rewrite('content.php?cid='.intval($_REQUEST['cid']))); + exit; +} + +$tabs = get_tabs(); +$num_tabs = count($tabs); +for ($i=0; $i < $num_tabs; $i++) { + if (isset($_POST['button_'.$i]) && ($_POST['button_'.$i] != -1)) { + $current_tab = $i; + $_POST['current_tab'] = $i; + break; + } +} + +if (isset($_GET['tab'])) { + $current_tab = intval($_GET['tab']); +} +if (isset($_POST['current_tab'])) { + $current_tab = intval($_POST['current_tab']); +} + +if (isset($_POST['submit_file'])) { + paste_from_file(body_text); +} else if (isset($_POST['submit']) && ($_POST['submit'] != 'submit1')) { + /* we're saving. redirects if successful. */ + save_changes(true, $current_tab); +} + +if (isset($_POST['submit_file_alt'])) { + paste_from_file(body_text_alt); +} else if (isset($_POST['submit']) && ($_POST['submit'] != 'submit1')) { + /* we're saving. redirects if successful. */ + save_changes(true, $current_tab); +} + +if (isset($_POST['submit'])) { + /* we're saving. redirects if successful. */ + save_changes(true, $current_tab); +} + +if (!isset($current_tab) && isset($_POST['button_1']) && ($_POST['button_1'] == -1) && !isset($_POST['submit'])) { + $current_tab = 1; +} else if (!isset($current_tab)) { + $current_tab = 0; +} + +if ($cid) { + $_section[0][0] = _AT('edit_content'); +} else { + $_section[0][0] = _AT('add_content'); +} + +if($current_tab == 0) { + $_custom_head .= ' + + + + '; +} + +if ($cid) { + $result = $contentManager->getContentPage($cid); + + if (!($content_row = @mysql_fetch_assoc($result))) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('PAGE_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + + $path = $contentManager->getContentPath($cid); + $content_test = $contentManager->getContentTestsAssoc($cid); + + if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $course_base_href = 'get.php/'; + } else { + $course_base_href = 'content/' . $_SESSION['course_id'] . '/'; + } + + if ($content_row['content_path']) { + $content_base_href .= $content_row['content_path'].'/'; + } +} else { + if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $content_base_href = 'get.php/'; + } else { + $content_base_href = 'content/' . $_SESSION['course_id'] . '/'; + } +} + +if (($current_tab == 0) || ($_current_tab == 3)) { + if ($_POST['formatting'] == null){ + // this is a fresh load from just logged in + if ($_SESSION['prefs']['PREF_CONTENT_EDITOR'] == 0) { + $_POST['formatting'] = 0; + } else { + $_POST['formatting'] = 1; + } + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($current_tab == 0 || $current_tab == 3) +{ + $simple = true; + if ($_POST['complexeditor'] == '1') { + $simple = false; + } + load_editor($simple, false, "none"); +} + +//TODO*************BOLOGNA****************REMOVE ME**************/ +//loading toolbar for insert discussion topic or web link into the content +if ($current_tab == 0){ + if(authenticate(AT_PRIV_CONTENT,AT_PRIV_RETURN)){ + $home_links = get_home_navigation(); //vengono lette le caratteristiche di ogni modulo attivato nella home page. + $main_links = get_main_navigation($current_page); //vengono lette le caratteristiche di ogni modulo attivo nel main navigation + + $num = count($main_links); //necessario elminare il primo e l'utlimo elemento poichè sono rispettivamente "Home" e "Manage" + unset($main_links[0]); //"Home" label + unset($main_links[$num-1]); //"Manage" label + + $all_tools = $home_links; //$all_tools represent a merge between $home_links and main_links without repetitions. + $check=false; + foreach($main_links as $main) { + foreach($home_links as $home) { + if($home['title'] == $main['title']) { + $check=true; + break; + } + } + if(!$check) + $all_tools[]=$main; + else + $check=false; + } + } +} + + +$cid = intval($_REQUEST['cid']); +$pid = intval($_REQUEST['pid']); +?> +
    +getRelatedContent($cid); + + $_POST['pid'] = $pid = $content_row['content_parent_id']; + + $_POST['related_term'] = $glossary_ids_related; + } + + } else { + $cid = 0; + if (!isset($_POST['current_tab'])) { + $_POST['day'] = date('d'); + $_POST['month'] = date('m'); + $_POST['year'] = date('Y'); + $_POST['hour'] = date('H'); + $_POST['min'] = 0; + + if (isset($_GET['pid'])) { + $pid = $_POST['pid'] = intval($_GET['pid']); + $_POST['ordering'] = count($contentManager->getContent($pid))+1; + } else { + $_POST['pid'] = 0; + $_POST['ordering'] = count($contentManager->getContent(0))+1; + } + } + } + + echo ''; + echo ''; + if ($_REQUEST['sub'] == 1) + { + echo ''; + echo ''; + } + echo ''; + if (($current_tab != 0) && (($_current_tab != 3))) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo ''; + echo ''; + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + echo ''; + + echo ''; + + if (is_array($_POST['related']) && ($current_tab != 1)) { + foreach($_POST['related'] as $r_id) { + echo ''; + } + } + echo ''; + + //content test association + echo ''; + + /* get glossary terms */ + $matches = find_terms(stripslashes($_POST['body_text'])); + $num_terms = count($matches[0]); + $matches = $matches[0]; + $word = str_replace(array('[?]', '[/?]'), '', $matches); + + if (is_array($word)) { + /* update $_POST['glossary_defs'] with any new/changed terms */ + for($i=0; $i<$num_terms; $i++) { + $word[$i] = htmlentities_utf8($word[$i]); + if (!isset($_POST['glossary_defs'][$word[$i]])) { + $_POST['glossary_defs'][$word[$i]] = $glossary[$word[$i]]; + } + } + } + + if (is_array($_POST['glossary_defs']) && ($current_tab != 2)) { + foreach($_POST['glossary_defs'] as $w => $d) { + + /* this term still exists in the content */ + if (!in_array($w, $word)) { + unset($_POST['glossary_defs'][$w]); + continue; + } + echo ''; + } + if (isset($_POST['related_term'])) { + foreach($_POST['related_term'] as $w => $d) { + echo ''; + } + } + } + + // adapted content + $sql = "SELECT pr.primary_resource_id, prt.type_id + FROM ".TABLE_PREFIX."primary_resources pr, ". + TABLE_PREFIX."primary_resources_types prt + WHERE pr.content_id = ".$cid." + AND pr.language_code = '".$_SESSION['lang']."' + AND pr.primary_resource_id = prt.primary_resource_id"; + $all_types_result = mysql_query($sql, $db); + + $i = 0; + while ($type = mysql_fetch_assoc($all_types_result)) { + $row_alternatives['alt_'.$type['primary_resource_id'].'_'.$type['type_id']] = 1; + } + + if ($current_tab != 3 && isset($_POST['use_post_for_alt'])) + { + echo ''; + if (is_array($_POST)) { + foreach ($_POST as $alt_id => $alt_value) { + if (substr($alt_id, 0 ,4) == 'alt_'){ + echo ''; + } + } + } + } + + //tests + if ($current_tab != 4){ + // set content associated tests + if (is_array($_POST['tid'])) { + foreach ($_POST['tid'] as $i=>$tid){ + echo ''; + } + } + else + { + $i = 0; + if ($content_test){ + while ($content_test_row = mysql_fetch_assoc($content_test)){ + echo ''; + } + } + } + + // set pre-tests + if (is_array($_POST['pre_tid'])) { + foreach ($_POST['pre_tid'] as $i=>$pre_tid){ + echo ''; + } + } + else + { + $i = 0; + $sql = 'SELECT * FROM '.TABLE_PREFIX."content_prerequisites WHERE content_id=$cid AND type='".CONTENT_PRE_TEST."'"; + $pretests_result = mysql_query($sql, $db); + while ($pretest_row = mysql_fetch_assoc($pretests_result)) { + echo ''; + } + } + } + if (!isset($_POST['allow_test_export']) && $current_tab != 4) { + //export flag handling. + $sql = "SELECT `allow_test_export` FROM ".TABLE_PREFIX."content WHERE content_id=$_REQUEST[cid]"; + $result2 = mysql_query($sql, $db); + if ($result2){ + $c_row = mysql_fetch_assoc($result2); + } + if (intval($c_row['allow_test_export'])==1){ + echo ''; + } else { + echo ''; + } + } else { + echo ''; + } + + if ($do_check) { + $changes_made = check_for_changes($content_row, $row_alternatives); + } +?> + +
    + +
    + +
    +
    + + +
    + + + /> + +
    + + +
    + /> +
    + + +
    +
    + + diff --git a/mods/_core/editor/edit_content_folder.php b/mods/_core/editor/edit_content_folder.php new file mode 100644 index 000000000..abf5912f6 --- /dev/null +++ b/mods/_core/editor/edit_content_folder.php @@ -0,0 +1,301 @@ + 0) +{ + $result = $contentManager->getContentPage($cid); + $content_row = mysql_fetch_assoc($result); +} + +if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $course_base_href = 'get.php/'; +} else { + $course_base_href = 'content/' . $_SESSION['course_id'] . '/'; +} + +// save changes +if ($_POST['submit']) +{ + if ($_POST['title'] == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('title'))); + } + + if (!($release_date = generate_release_date())) { + $msg->addError('BAD_DATE'); + } + + if (!$msg->containsErrors()) + { + $_POST['title'] = $content_row['title'] = $addslashes($_POST['title']); + + if ($cid > 0) + { // edit existing content + $err = $contentManager->editContent($cid, + $_POST['title'], + '', + '', + '', + $content_row['formatting'], + $release_date, + '', + $content_row['use_customized_head'], + '', + $content_row['allow_test_export']); + } + else + { // add new content + // find out ordering and content_parent_id + if ($pid) + { // insert sub content folder + $ordering = count($contentManager->getContent($pid))+1; + } + else + { // insert a top content folder + $ordering = count($contentManager->getContent(0)) + 1; + $pid = 0; + } + + $cid = $contentManager->addContent($_SESSION['course_id'], + $pid, + $ordering, + $_POST['title'], + '', + '', + '', + 0, + $release_date, + '', + 0, + '', + 1, + CONTENT_TYPE_FOLDER); + } + + // save pre-tests + $sql = "DELETE FROM ". TABLE_PREFIX . "content_prerequisites + WHERE content_id=".$cid." AND type='".CONTENT_PRE_TEST."'"; + $result = mysql_query($sql, $db); + + if (is_array($_POST['tid']) && sizeof($_POST['tid']) > 0) + { + foreach ($_POST['tid'] as $i => $tid){ + $tid = intval($tid); + $sql = "INSERT INTO ". TABLE_PREFIX . "content_prerequisites + SET content_id=".$cid.", type='".CONTENT_PRE_TEST."', item_id=$tid"; + $result = mysql_query($sql, $db); + + if ($result===false) $msg->addError('MYSQL_FAILED'); + } + } + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_base_path.'mods/_core/editor/edit_content_folder.php?cid='.$cid); + exit; + } +} + +if ($cid > 0) +{ // edit existing content folder + if (!$content_row) { + $_pages['mods/_core/editor/edit_content_folder.php']['title_var'] = 'missing_content'; + $_pages['mods/_core/editor/edit_content_folder.php']['parent'] = 'index.php'; + $_pages['mods/_core/editor/edit_content_folder.php']['ignore'] = true; + + require(AT_INCLUDE_PATH.'header.inc.php'); + + $msg->addError('PAGE_NOT_FOUND'); + $msg->printAll(); + + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } /* else: */ + + /* the "heading navigation": */ + $path = $contentManager->getContentPath($cid); + + if ($content_row['content_path']) { + $content_base_href = $content_row['content_path'].'/'; + } + + $parent_headings = ''; + $num_in_path = count($path); + + /* the page title: */ + $page_title = ''; + $page_title .= $content_row['title']; + + for ($i=0; $i<$num_in_path; $i++) { + $content_info = $path[$i]; + if ($_SESSION['prefs']['PREF_NUMBERING']) { + if ($contentManager->_menu_info[$content_info['content_id']]['content_parent_id'] == 0) { + $top_num = $contentManager->_menu_info[$content_info['content_id']]['ordering']; + $parent_headings .= $top_num; + } else { + $top_num = $top_num.'.'.$contentManager->_menu_info[$content_info['content_id']]['ordering']; + $parent_headings .= $top_num; + } + if ($_SESSION['prefs']['PREF_NUMBERING']) { + $path[$i]['content_number'] = $top_num . ' '; + } + $parent_headings .= ' '; + } + } + + if ($_SESSION['prefs']['PREF_NUMBERING']) { + if ($top_num != '') { + $top_num = $top_num.'.'.$content_row['ordering']; + $page_title .= $top_num.' '; + } else { + $top_num = $content_row['ordering']; + $page_title .= $top_num.' '; + } + } + + $parent = 0; +// foreach ($path as $i=>$page) { +// if (!$parent) { +// $_pages['editor/edit_content_folder.php?cid='.$page['content_id']]['title'] = $page['content_number'] . $page['title']; +// $_pages['editor/edit_content_folder.php?cid='.$page['content_id']]['parent'] = 'index.php'; +// } else { +// $_pages['editor/edit_content_folder.php?cid='.$page['content_id']]['title'] = $page['content_number'] . $page['title']; +// $_pages['editor/edit_content_folder.php?cid='.$page['content_id']]['parent'] = 'editor/edit_content_folder.php?cid='.$parent; +// } +// +// $_pages['editor/edit_content_folder.php?cid='.$page['content_id']]['ignore'] = true; +// $parent = $page['content_id']; +// } +// $last_page = array_pop($_pages); +// $_pages['editor/edit_content_folder.php'] = $last_page; + + reset($path); + $first_page = current($path); + + save_last_cid($cid); + + if (isset($top_num) && $top_num != (int) $top_num) { + $top_num = substr($top_num, 0, strpos($top_num, '.')); + } + + // used by header.inc.php + $_tool_shortcuts = $contentManager->getToolShortcuts($content_row); + $release_date = $content_row['release_date']; + + // display pre-tests + $sql = 'SELECT * FROM '.TABLE_PREFIX."content_prerequisites WHERE content_id=$_REQUEST[cid] AND type='".CONTENT_PRE_TEST."'"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $_POST['pre_tid'][] = $row['item_id']; + } + + $savant->assign('ftitle', $content_row['title']); + $savant->assign('shortcuts', $shortcuts); + $savant->assign('cid', $cid); +} + +// display pre-tests +// get a list of all the tests we have, and links to create, edit, delete, preview +$sql = "SELECT *, UNIX_TIMESTAMP(start_date) AS us, UNIX_TIMESTAMP(end_date) AS ue + FROM ".TABLE_PREFIX."tests + WHERE course_id=$_SESSION[course_id] + ORDER BY start_date DESC"; +$result = mysql_query($sql, $db); +$num_tests = mysql_num_rows($result); + +$i = 0; +while($row = mysql_fetch_assoc($result)) +{ + $results[$i]['test_id'] = $row['test_id']; + $results[$i]['title'] = $row['title']; + + if ( ($row['us'] <= time()) && ($row['ue'] >= time() ) ) { + $results[$i]['status'] = ''._AT('ongoing').''; + } else if ($row['ue'] < time() ) { + $results[$i]['status'] = ''._AT('expired').''; + } else if ($row['us'] > time() ) { + $results[$i]['status'] = ''._AT('pending').''; + } + + $startend_date_format=_AT('startend_date_format'); + + $results[$i]['availability'] = AT_date($startend_date_format, $row['start_date'], AT_DATE_MYSQL_DATETIME). ' ' ._AT('to_2').' '; + $results[$i]['availability'] .= AT_date($startend_date_format, $row['end_date'], AT_DATE_MYSQL_DATETIME); + + // get result release + if ($row['result_release'] == AT_RELEASE_IMMEDIATE) + $results[$i]['result_release'] = _AT('release_immediate'); + else if ($row['result_release'] == AT_RELEASE_MARKED) + $results[$i]['result_release'] = _AT('release_marked'); + else if ($row['result_release'] == AT_RELEASE_NEVER) + $results[$i]['result_release'] = _AT('release_never'); + + //get # marked submissions + $sql_sub = "SELECT COUNT(*) AS sub_cnt FROM ".TABLE_PREFIX."tests_results WHERE status=1 AND test_id=".$row['test_id']; + $result_sub = mysql_query($sql_sub, $db); + $row_sub = mysql_fetch_assoc($result_sub); + $results[$i]['submissions'] = $row_sub['sub_cnt'].' '._AT('submissions').', '; + + //get # submissions + $sql_sub = "SELECT COUNT(*) AS marked_cnt FROM ".TABLE_PREFIX."tests_results WHERE status=1 AND test_id=".$row['test_id']." AND final_score=''"; + $result_sub = mysql_query($sql_sub, $db); + $row_sub = mysql_fetch_assoc($result_sub); + $results[$i]['submissions'] .= $row_sub['marked_cnt'].' '._AT('unmarked'); + + //get assigned groups + $sql_sub = "SELECT G.title FROM ".TABLE_PREFIX."groups G INNER JOIN ".TABLE_PREFIX."tests_groups T USING (group_id) WHERE T.test_id=".$row['test_id']; + $result_sub = mysql_query($sql_sub, $db); + if (mysql_num_rows($result_sub) == 0) { + $results[$i]['assign_to'] = _AT('everyone'); + } else { + $row_sub = mysql_fetch_assoc($result_sub); + $results[$i]['assign_to'] = $row_sub['title']; + do { + $results[$i]['assign_to'] .= ', '.$row_sub['title']; + } while ($row_sub = mysql_fetch_assoc($result_sub)); + } + + if ($row['passscore'] == 0 && $row['passpercent'] == 0) + $results[$i]['pass_score'] = _AT('no_pass_score'); + else if ($row['passscore'] <> 0) + $results[$i]['pass_score'] = $row['passscore']; + else if ($row['passpercent'] <> 0) + $results[$i]['pass_score'] = $row['passpercent'].'%'; + + $i++; +} + +if (isset($results)) $savant->assign('pretests', $results); + +// set release date +if (!isset($release_date)) $release_date = date('Y-m-d H:i:s'); + +$_POST['day'] = substr($release_date, 8, 2); +$_POST['month'] = substr($release_date, 5, 2); +$_POST['year'] = substr($release_date, 0, 4); +$_POST['hour'] = substr($release_date, 11, 2); +$_POST['min']= substr($release_date, 14, 2); + +if ($pid > 0) $savant->assign('pid', $pid); + +require(AT_INCLUDE_PATH.'header.inc.php'); +$savant->display('editor/edit_content_folder.tmpl.php'); +require(AT_INCLUDE_PATH.'footer.inc.php'); + +//save last visit page. +$_SESSION['last_visited_page'] = $server_protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; +?> \ No newline at end of file diff --git a/mods/_core/editor/editor_tab_functions.inc.php b/mods/_core/editor/editor_tab_functions.inc.php new file mode 100644 index 000000000..4d6ac6cf2 --- /dev/null +++ b/mods/_core/editor/editor_tab_functions.inc.php @@ -0,0 +1,572 @@ + $strValue) + { + if (strtoupper($strItem) == strtoupper($strValue)) + { + return $key; + } + } + return false; +} + + +function get_tabs() { + //these are the _AT(x) variable names and their include file + /* tabs[tab_id] = array(tab_name, file_name, accesskey) */ + $tabs[0] = array('content', 'edit.inc.php', 'n'); + $tabs[1] = array('properties', 'properties.inc.php', 'p'); + $tabs[2] = array('glossary_terms', 'glossary.inc.php', 'g'); + //Silvia: Added to declare alternative resources + $tabs[3] = array('alternative_content', 'alternatives.inc.php', 'l'); + //Harris: Extended test functionality into content export + $tabs[4] = array('tests', 'tests.inc.php', 't'); + + return $tabs; +} + + +function output_tabs($current_tab, $changes) { + global $_base_path; + $tabs = get_tabs(); + $num_tabs = count($tabs); +?> +

    + + + + + + + + + + + +
    + + <?php echo _AT('usaved_changes_made'); ?> + + +   + + <?php echo _AT('usaved_changes_made'); ?> + + + '; ?> +   
    +addError(array('INVALID_INPUT', _AT('weblink'))); + } else { + $_POST['body_text'] = $url; + $content_type_pref = CONTENT_TYPE_WEBLINK; + } + } else { + $content_type_pref = CONTENT_TYPE_CONTENT; + } + + if (!($release_date = generate_release_date())) { + $msg->addError('BAD_DATE'); + } + + if ($_POST['title'] == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('title'))); + } + + if (!$msg->containsErrors()) { + $_POST['title'] = $addslashes($_POST['title']); + $_POST['body_text'] = $addslashes($_POST['body_text']); + $_POST['head'] = $addslashes($_POST['head']); + $_POST['keywords'] = $addslashes($_POST['keywords']); + $_POST['test_message'] = $addslashes($_POST['test_message']); + + // add or edit content + if ($_POST['cid']) { + /* editing an existing page */ + $err = $contentManager->editContent($_POST['cid'], $_POST['title'], $_POST['body_text'], + $_POST['keywords'], $_POST['related'], $_POST['formatting'], + $release_date, $_POST['head'], $_POST['use_customized_head'], + $_POST['test_message'], $_POST['allow_test_export']); + $cid = $_POST['cid']; + } else { + /* insert new */ + + $cid = $contentManager->addContent($_SESSION['course_id'], + $_POST['pid'], + $_POST['ordering'], + $_POST['title'], + $_POST['body_text'], + $_POST['keywords'], + $_POST['related'], + $_POST['formatting'], + $release_date, + $_POST['head'], + $_POST['use_customized_head'], + $_POST['test_message'], + $_POST['allow_test_export'], + $content_type_pref); + $_POST['cid'] = $cid; + $_REQUEST['cid'] = $cid; + } + } + else return; + + /* insert glossary terms */ + if (is_array($_POST['glossary_defs']) && ($num_terms = count($_POST['glossary_defs']))) { + global $glossary, $glossary_ids, $msg; + + foreach($_POST['glossary_defs'] as $w => $d) { + $old_w = $w; + $key = in_array_cin($w, $glossary_ids); + $w = urldecode($w); + $d = $addslashes($d); + + if (($key !== false) && (($glossary[$old_w] != $d) || isset($_POST['related_term'][$old_w])) ) { + $w = addslashes($w); + $related_id = intval($_POST['related_term'][$old_w]); + $sql = "UPDATE ".TABLE_PREFIX."glossary SET definition='$d', related_word_id=$related_id WHERE word_id=$key AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + $glossary[$old_w] = $d; + } else if ($key === false && ($d != '')) { + $w = addslashes($w); + $related_id = intval($_POST['related_term'][$old_w]); + $sql = "INSERT INTO ".TABLE_PREFIX."glossary VALUES (NULL, $_SESSION[course_id], '$w', '$d', $related_id)"; + + $result = mysql_query($sql, $db); + $glossary[$old_w] = $d; + } + } + } + if (isset($_GET['tab'])) { + $current_tab = intval($_GET['tab']); + } + if (isset($_POST['current_tab'])) { + $current_tab = intval($_POST['current_tab']); + } + + // adapted content: save primary content type + if (isset($_POST['use_post_for_alt'])) + { + // 1. delete old primary content type + $sql = "DELETE FROM ".TABLE_PREFIX."primary_resources_types + WHERE primary_resource_id in + (SELECT DISTINCT primary_resource_id + FROM ".TABLE_PREFIX."primary_resources + WHERE content_id=".$cid." + AND language_code='".$_SESSION['lang']."')"; + $result = mysql_query($sql, $db); + + // 2. insert the new primary content type + $sql = "SELECT pr.primary_resource_id, rt.type_id + FROM ".TABLE_PREFIX."primary_resources pr, ". + TABLE_PREFIX."resource_types rt + WHERE pr.content_id = ".$cid." + AND pr.language_code = '".$_SESSION['lang']."'"; + $all_types_result = mysql_query($sql, $db); + + while ($type = mysql_fetch_assoc($all_types_result)) { + if (isset($_POST['alt_'.$type['primary_resource_id'].'_'.$type['type_id']])) + { + $sql = "INSERT INTO ".TABLE_PREFIX."primary_resources_types (primary_resource_id, type_id) + VALUES (".$type['primary_resource_id'].", ".$type['type_id'].")"; + $result = mysql_query($sql, $db); + } + } + } + + //Add test to this content - @harris + $sql = 'SELECT * FROM '.TABLE_PREFIX."content_tests_assoc WHERE content_id=$_POST[cid]"; + $result = mysql_query($sql, $db); + $db_test_array = array(); + while ($row = mysql_fetch_assoc($result)) { + $db_test_array[] = $row['test_id']; + } + + if (is_array($_POST['tid']) && sizeof($_POST['tid']) > 0){ + $toBeDeleted = array_diff($db_test_array, $_POST['tid']); + $toBeAdded = array_diff($_POST['tid'], $db_test_array); + //Delete entries + if (!empty($toBeDeleted)){ + $tids = implode(",", $toBeDeleted); + $sql = 'DELETE FROM '. TABLE_PREFIX . "content_tests_assoc WHERE content_id=$_POST[cid] AND test_id IN ($tids)"; + $result = mysql_query($sql, $db); + } + + //Add entries + if (!empty($toBeAdded)){ + foreach ($toBeAdded as $i => $tid){ + $tid = intval($tid); + $sql = 'INSERT INTO '. TABLE_PREFIX . "content_tests_assoc SET content_id=$_POST[cid], test_id=$tid"; + $result = mysql_query($sql, $db); + if ($result===false){ + $msg->addError('MYSQL_FAILED'); + } + } + } + } else { + //All tests has been removed. + $sql = 'DELETE FROM '. TABLE_PREFIX . "content_tests_assoc WHERE content_id=$_POST[cid]"; + $result = mysql_query($sql, $db); + } + //End Add test + + // add pre-tests + $sql = "DELETE FROM ". TABLE_PREFIX . "content_prerequisites + WHERE content_id=".$_POST[cid]." AND type='".CONTENT_PRE_TEST."'"; + $result = mysql_query($sql, $db); + + if (is_array($_POST['pre_tid']) && sizeof($_POST['pre_tid']) > 0) + { + foreach ($_POST['pre_tid'] as $i => $tid){ + $tid = intval($tid); + $sql = "INSERT INTO ". TABLE_PREFIX . "content_prerequisites + SET content_id=".$_POST[cid].", type='".CONTENT_PRE_TEST."', item_id=$tid"; + $result = mysql_query($sql, $db); + + if ($result===false) $msg->addError('MYSQL_FAILED'); + } + } + + //TODO*******************BOLOGNA****************REMOVE ME**************/ + if(isset($_SESSION['associated_forum']) && !$msg->containsErrors()){ + if($_SESSION['associated_forum']=='none'){ + $sql = "DELETE FROM ".TABLE_PREFIX."content_forums_assoc WHERE content_id='$_POST[cid]'"; + mysql_query($sql,$db); + } else { + $sql = "DELETE FROM ".TABLE_PREFIX."content_forums_assoc WHERE content_id='$_POST[cid]'"; + mysql_query($sql,$db); + $associated_forum = $_SESSION['associated_forum']; + for($i=0; $icontainsErrors() && $redir) { + $_SESSION['save_n_close'] = $_POST['save_n_close']; + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.basename($_SERVER['PHP_SELF']).'?cid='.$cid.SEP.'close='.$addslashes($_POST['save_n_close']).SEP.'tab='.$addslashes($_POST['current_tab']).SEP.'displayhead='.$addslashes($_POST['displayhead']).SEP.'alternatives='.$addslashes($_POST['alternatives'])); + exit; + } else { + return; + } +} + +function generate_release_date($now = false) { + if ($now) { + $day = date('d'); + $month= date('m'); + $year = date('Y'); + $hour = date('H'); + $min = 0; + } else { + $day = intval($_POST['day']); + $month = intval($_POST['month']); + $year = intval($_POST['year']); + $hour = intval($_POST['hour']); + $min = intval($_POST['min']); + } + + if (!checkdate($month, $day, $year)) { + return false; + } + + if (strlen($month) == 1){ + $month = "0$month"; + } + if (strlen($day) == 1){ + $day = "0$day"; + } + if (strlen($hour) == 1){ + $hour = "0$hour"; + } + if (strlen($min) == 1){ + $min = "0$min"; + } + $release_date = "$year-$month-$day $hour:$min:00"; + + return $release_date; +} + +function check_for_changes($row, $row_alternatives) { + global $contentManager, $cid, $glossary, $glossary_ids_related, $addslashes; + + $changes = array(); + + if ($row && strcmp(trim($addslashes($_POST['title'])), addslashes($row['title']))) { + $changes[0] = true; + } else if (!$row && $_POST['title']) { + $changes[0] = true; + } + + if ($row && strcmp($addslashes(trim($_POST['head'])), trim(addslashes($row['head'])))) { + $changes[0] = true; + } else if (!$row && $_POST['head']) { + $changes[0] = true; + } + + if ($row && strcmp($addslashes(trim($_POST['body_text'])), trim(addslashes($row['text'])))) { + $changes[0] = true; + } else if (!$row && $_POST['body_text']) { + $changes[0] = true; + } + + if ($row && strcmp($addslashes(trim($_POST['weblink_text'])), trim(addslashes($row['text'])))) { + $changes[0] = true; + } else if (!$row && $_POST['weblink_text']) { + $changes[0] = true; + } + + /* use customized head: */ + if ($row && isset($_POST['use_customized_head']) && ($_POST['use_customized_head'] != $row['use_customized_head'])) { + $changes[0] = true; + } + + /* formatting: */ + if ($row && strcmp(trim($_POST['formatting']), $row['formatting'])) { + $changes[0] = true; + } else if (!$row && $_POST['formatting']) { + $changes[0] = true; + } + + /* release date: */ + if ($row && strcmp(substr(generate_release_date(), 0, -2), substr($row['release_date'], 0, -2))) { + /* the substr was added because sometimes the release_date in the db has the seconds field set, which we dont use */ + /* so it would show a difference, even though it should actually be the same, so we ignore the seconds with the -2 */ + /* the seconds gets added if the course was created during the installation process. */ + $changes[1] = true; + } else if (!$row && strcmp(generate_release_date(), generate_release_date(true))) { + $changes[1] = true; + } + + /* related content: */ + $row_related = $contentManager->getRelatedContent($cid); + + if (is_array($_POST['related']) && is_array($row_related)) { + $sum = array_sum(array_diff($_POST['related'], $row_related)); + $sum += array_sum(array_diff($row_related, $_POST['related'])); + if ($sum > 0) { + $changes[1] = true; + } + } else if (!is_array($_POST['related']) && !empty($row_related)) { + $changes[1] = true; + } + + /* keywords */ + if ($row && strcmp(trim($_POST['keywords']), $row['keywords'])) { + $changes[1] = true; + } else if (!$row && $_POST['keywords']) { + $changes[1] = true; + } + + + /* glossary */ + if (is_array($_POST['glossary_defs'])) { + global $glossary_ids; + foreach ($_POST['glossary_defs'] as $w => $d) { + + $key = in_array_cin($w, $glossary_ids); + if ($key === false) { + /* new term */ + $changes[2] = true; + break; + } else if ($cid && ($d &&($d != $glossary[$glossary_ids[$key]]))) { + /* changed term */ + $changes[2] = true; + break; + } + } + + if (is_array($_POST['related_term'])) { + foreach($_POST['related_term'] as $term => $r_id) { + if ($glossary_ids_related[$term] != $r_id) { + $changes[2] = true; + break; + } + } + } + } + + /* adapted content */ + if (isset($_POST['use_post_for_alt'])) + { + foreach ($_POST as $alt_id => $alt_value) { + if (substr($alt_id, 0 ,4) == 'alt_' && $alt_value != $row_alternatives[$alt_id]){ + $changes[3] = true; + break; + } + } + } + + /* test & survey */ + if ($row && isset($_POST['test_message']) && $_POST['test_message'] != $row['test_message']){ + $changes[4] = true; + } + if ($row && isset($_POST['allow_test_export']) && $_POST['allow_test_export'] != $row['allow_test_export']){ + $changes[4] = true; + } + + return $changes; +} + +function paste_from_file() { + global $msg; + if ($_FILES['uploadedfile_paste']['name'] == '') { + $msg->addError('FILE_NOT_SELECTED'); + return; + } + if ($_FILES['uploadedfile_paste']['name'] + && (($_FILES['uploadedfile_paste']['type'] == 'text/plain') + || ($_FILES['uploadedfile_paste']['type'] == 'text/html')) ) + { + + $path_parts = pathinfo($_FILES['uploadedfile_paste']['name']); + $ext = strtolower($path_parts['extension']); + + if (in_array($ext, array('html', 'htm'))) { + $_POST['body_text'] = file_get_contents($_FILES['uploadedfile_paste']['tmp_name']); + + /* get the of this page */ + + $start_pos = strpos(strtolower($_POST['body_text']), ''); + $end_pos = strpos(strtolower($_POST['body_text']), ''); + + if (($start_pos !== false) && ($end_pos !== false)) { + $start_pos += strlen(''); + $_POST['title'] = trim(substr($_POST['body_text'], $start_pos, $end_pos-$start_pos)); + } + unset($start_pos); + unset($end_pos); + + $_POST['head'] = get_html_head_by_tag($_POST['body_text'], array("link", "style", "script")); + if (strlen(trim($_POST['head'])) > 0) + $_POST['use_customized_head'] = 1; + else + $_POST['use_customized_head'] = 0; + + $_POST['body_text'] = get_html_body($_POST['body_text']); + + $msg->addFeedback('FILE_PASTED'); + } else if ($ext == 'txt') { + $_POST['body_text'] = file_get_contents($_FILES['uploadedfile_paste']['tmp_name']); + //LAW +// debug($_POST); + $msg->addFeedback('FILE_PASTED'); + + } + } else { + $msg->addError('BAD_FILE_TYPE'); + } + + return; +} + +//for accessibility checker +function write_temp_file() { + global $_POST, $msg; + + if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $content_base = 'get.php/'; + } else { + $content_base = 'content/' . $_SESSION['course_id'] . '/'; + } + + if ($_POST['content_path']) { + $content_base .= $_POST['content_path'] . '/'; + } + + $file_name = $_POST['cid'].'.html'; + + if ($handle = fopen(AT_CONTENT_DIR . $file_name, 'wb+')) { +// $temp_content = '<h2>'.AT_print(stripslashes($_POST['title']), 'content.title').'</h2>'; +// +// if ($_POST['body_text'] != '') { +// $temp_content .= format_content(stripslashes($_POST['body_text']), $_POST['formatting'], $_POST['glossary_defs']); +// } +// $temp_title = $_POST['title']; +// +// $html_template = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" +// "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +// <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +// <head> +// <base href="{BASE_HREF}" /> +// <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +// <title>{TITLE} +// +// +// +// {CONTENT} +// +// '; +// +// $page_html = str_replace( array('{BASE_HREF}', '{TITLE}', '{CONTENT}'), +// array($content_base, $temp_title, $temp_content), +// $html_template); + + if (!@fwrite($handle, stripslashes($_POST['body_text']))) { + $msg->addError('FILE_NOT_SAVED'); + } + } else { + $msg->addError('FILE_NOT_SAVED'); + } + $msg->printErrors(); +} +?> diff --git a/mods/_core/editor/editor_tabs/alternatives.inc.php b/mods/_core/editor/editor_tabs/alternatives.inc.php new file mode 100644 index 000000000..fb272d6c3 --- /dev/null +++ b/mods/_core/editor/editor_tabs/alternatives.inc.php @@ -0,0 +1,267 @@ + "adapted content" + */ + +if (!defined('AT_INCLUDE_PATH')) { exit; } + +if ($cid == 0) { + $msg->printErrors('SAVE_BEFORE_PROCEED'); + require_once(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +/** + * This function returns the preview link of the given file + * @param $file the file location in "file manager" + * @return the relative URL to preview the file + */ +function get_preview_link($file) +{ + global $content_row; + + if (substr($file, 0 , 7) == 'http://' || substr($file, 0 , 8) == 'https://') { + return $file; + } else { + if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $get_file = 'get.php/'; + } else { + $get_file = 'content/' . $_SESSION['course_id'] . '/'; + } + + return $get_file.$content_row['content_path'].'/'.$file; + } +} + +/** + * When the file name is a remote URL, this function reduces the full URL + * @param $filename + * @return the reduced name + */ +function get_display_filename($filename) +{ + if (substr($filename, 0 , 7) == 'http://' || substr($filename, 0 , 8) == 'https://') { + if (substr($filename, 0 , 7) == 'http://') $prefix = 'http://'; + if (substr($filename, 0 , 8) == 'https://') $prefix = 'https://'; + $name = substr($filename, strrpos($filename, '/')); + $filename = $prefix.'...'.$name; + } + return $filename; +} + +/** + * Display alternative table cell + * @param $secondary_result mysql result of all secondary alternatives + * $alternative type the resource type of the alternative to display. Must be one of the values in resource_types.type_id + * $content_id used to pass into file_manager/index.php + * $ps used to pass into file_manager/index.php + * @return html of the table cell "..." + */ +function display_alternative_cell($secondary_result, $alternative_type, $content_id, $pid, $td_header_id) +{ + global $content_row; + + $found_alternative = false; + + echo ' '."\n"; + + if (mysql_num_rows($secondary_result) > 0) + { + mysql_data_seek($secondary_result, 0); // move the mysql result cursor back to the first row + while ($secondary_resource = mysql_fetch_assoc($secondary_result)) + { + if ($secondary_resource['type_id'] == $alternative_type) + { + echo ' '."\n"; + $found_alternative = true; + break; + } + } + } + if (!$found_alternative) + { + echo '
    '."\n"; + echo ' '."\n"; + echo '
    '."\n"; + } + echo ' '."\n"; +} + +// Main program +global $db, $content_row; +require(AT_INCLUDE_PATH.'../mods/_core/imsafa/html/resources_parser.inc.php'); + +if (count($resources)==0) +{ + echo '

    '. _AT('No_resources'). '

    '; +} +else +{ + $is_post_indicator_set = false; + // get all resource types + $sql = "SELECT * FROM ".TABLE_PREFIX."resource_types"; + $resource_types_result = mysql_query($sql, $db); + + echo ''."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + + echo ' '; + foreach($resources as $primary_resource) + { + // check whether the primary resource is in the table + $sql = "SELECT * FROM ".TABLE_PREFIX."primary_resources + WHERE content_id = ".$cid." + AND language_code = '".$_SESSION['lang']."' + AND resource='".$primary_resource."'"; + $primary_result = mysql_query($sql, $db); + + // insert primary resource if it's not in db + if (mysql_num_rows($primary_result) == 0) + { + $sql = "INSERT INTO ".TABLE_PREFIX."primary_resources (content_id, resource, language_code) + VALUES (".$cid.", '".$primary_resource."', '".$_SESSION['lang']."')"; + $result = mysql_query($sql, $db); + $primary_resource_id = mysql_insert_id(); + } + else + { + // get primary resource id + $primary_resource_row = mysql_fetch_assoc($primary_result); + $primary_resource_id = $primary_resource_row['primary_resource_id']; + } + $sql = "SELECT prt.type_id, rt.type + FROM ".TABLE_PREFIX."primary_resources pr, ". + TABLE_PREFIX."primary_resources_types prt, ". + TABLE_PREFIX."resource_types rt + WHERE pr.content_id = ".$cid." + AND pr.language_code = '".$_SESSION['lang']."' + AND pr.resource='".$primary_resource."' + AND pr.primary_resource_id = prt.primary_resource_id + AND prt.type_id = rt.type_id"; + $primary_type_result = mysql_query($sql, $db); + + if (!$is_post_indicator_set) + { + echo ' '."\n"; + $is_post_indicator_set = true; + } + + // get secondary resources for the current primary resource + $sql = "SELECT pr.primary_resource_id, sr.secondary_resource, srt.type_id + FROM ".TABLE_PREFIX."primary_resources pr, ". + TABLE_PREFIX."secondary_resources sr, ". + TABLE_PREFIX."secondary_resources_types srt + WHERE pr.content_id = ".$cid." + AND pr.language_code = '".$_SESSION['lang']."' + AND pr.resource='".$primary_resource."' + AND pr.primary_resource_id = sr.primary_resource_id + AND sr.secondary_resource_id = srt.secondary_resource_id"; + $secondary_result = mysql_query($sql, $db); + + echo ' '."\n"; + + // table cell "original resource" + echo ' '."\n"; + + // table cell "original resource type" + echo ' '."\n"; + + // table cell "text alternative" + display_alternative_cell($secondary_result, 3, $cid, $primary_resource_id, "header3"); + + // table cell "audio" + display_alternative_cell($secondary_result, 1, $cid, $primary_resource_id, "header4"); + + // table cell "visual" + display_alternative_cell($secondary_result, 4, $cid, $primary_resource_id, "header5"); + + // table cell "sign language" + display_alternative_cell($secondary_result, 2, $cid, $primary_resource_id, "header6"); + + echo ' '."\n"; + } + echo ' '."\n"; + echo '
    '._AT('original_resource').''._AT('resource_type').''._AT('alternatives').'
    '._AT('text').''._AT('audio').''._AT('visual').''._AT('sign_lang').'
    '."\n"; + echo ' '.get_display_filename($primary_resource).''."\n"; + echo ' '."\n"; + + mysql_data_seek($resource_types_result, 0); // move the mysql result cursor back to the first row + while ($resource_type = mysql_fetch_assoc($resource_types_result)) + { + if ($resource_type['type'] == 'sign_language') + continue; + else + { + echo ' 0) mysql_data_seek($primary_type_result, 0); + while ($primary_resource_type = mysql_fetch_assoc($primary_type_result)) { + if ($primary_resource_type['type_id'] == $resource_type['type_id']){ + echo 'checked="checked"'; + break; + } + } + } + echo '/>'."\n"; + echo '
    '."\n"; + } + } + echo '
    '."\n"; +} +?> + + \ No newline at end of file diff --git a/mods/_core/editor/editor_tabs/edit.inc.php b/mods/_core/editor/editor_tabs/edit.inc.php new file mode 100644 index 000000000..5ea674ee6 --- /dev/null +++ b/mods/_core/editor/editor_tabs/edit.inc.php @@ -0,0 +1,149 @@ + + + + + + +
    + + * + + + + + + /> + + + /> + + + /> + + + +
    + +'._AT('packaged_in').':  '.$content_row['content_path'].'
    '; + } + if (trim($_POST['head']) == '
    ') { + $_POST['head'] = ''; + } + if ($do_check) { + $_POST['head'] = $stripslashes($_POST['head']); + } +?> + + +
    + + + +
    + + + + + +
    +
    ()
    + + +
    + + ') { + $_POST['body_text'] = ''; + } + if ($do_check) { + $_POST['body_text'] = $stripslashes($_POST['body_text']); + } + ?> + +
    + + + + + + + + +
    + diff --git a/mods/_core/editor/editor_tabs/glossary.inc.php b/mods/_core/editor/editor_tabs/glossary.inc.php new file mode 100644 index 000000000..4ba0c4eb8 --- /dev/null +++ b/mods/_core/editor/editor_tabs/glossary.inc.php @@ -0,0 +1,98 @@ + + +
    + + + + + 60): ?> + + + + + + + + + + + + + + +
    '._AT('new').' '; + $current_word = $word[$i]; + $current_defn = $_POST['glossary_defs'][$word[$i]]; + } else { + $current_word = $glossary_ids[$key]; + if (!$_POST['glossary_defs'][$word[$i]]) { + $current_defn = $glossary[$glossary_ids[$key]]; + } else { + $current_defn = $_POST['glossary_defs'][$word[$i]]; + } + } + + echo _AT('glossary_term'); ?>:
     
    +
    1) { + echo ''; + } else { + echo _AT('none_available'); + } + + ?>

    + + +
    \ No newline at end of file diff --git a/mods/_core/editor/editor_tabs/pastefromfile.php b/mods/_core/editor/editor_tabs/pastefromfile.php new file mode 100644 index 000000000..1943c2bfa --- /dev/null +++ b/mods/_core/editor/editor_tabs/pastefromfile.php @@ -0,0 +1,166 @@ + + + + + Paste from file tool + + + + +title; + } + + public function setTitle($value) { + $this->title = $value; + } + + public function getHead() { + return $this->head; + } + + public function setHead($value) { + $this->head = $value; + } + + public function getBody() { + return $this->body; + } + + public function setBody($value) { + $this->body = $value; + } + + public function getErrorMsg() { + return $this->errorMsg; + } + + public function setErrorMsg($value) { + $this->errorMsg = $value; + } + +} + +/** + * Paste_from_file + * Parses a named uploaded file of html or txt type + * The function identifies title, head and body for html files, + * or body for text files. + * + * @return FileData object + */ +function paste_from_file() { + $fileData = new FileData(); + if ($_FILES['uploadedfile_paste']['name'] == '') { + $fileData->setErrorMsg(_AT('AT_ERROR_FILE_NOT_SELECTED')); + } elseif (($_FILES['uploadedfile_paste']['type'] == 'text/plain') + || ($_FILES['uploadedfile_paste']['type'] == 'text/html') ) { + + $path_parts = pathinfo($_FILES['uploadedfile_paste']['name']); + $ext = strtolower($path_parts['extension']); + + if (in_array($ext, array('html', 'htm'))) { + $contents = file_get_contents($_FILES['uploadedfile_paste']['tmp_name']); + + /* get the of this page */ + $start_pos = strpos(strtolower($contents), ''); + $end_pos = strpos(strtolower($contents), ''); + + if (($start_pos !== false) && ($end_pos !== false)) { + $start_pos += strlen(''); + $fileData->setTitle(trim(substr($contents, $start_pos, $end_pos-$start_pos))); + } + unset($start_pos); + unset($end_pos); + + $fileData->setHead(trim(get_html_head_by_tag($contents, array("link", "style", "script")))); + $fileData->setBody(trim(get_html_body($contents))); + } else if ($ext == 'txt') { + $fileData->setBody(trim(file_get_contents($_FILES['uploadedfile_paste']['tmp_name']))); + } + } else { + $fileData->setErrorMsg(_AT('AT_ERROR_BAD_FILE_TYPE')); + } + return $fileData; +} + +if (isset($_POST['submit_file'])) +{ + echo '<script type="text/javascript">'; + echo 'ATutor.mods.editor.removeErrorMsg();'; + $fileData = paste_from_file(); + $errorMessage = $fileData->getErrorMsg(); + if ($errorMessage == "") { + echo 'ATutor.mods.editor.pasteFromFile('.json_encode($fileData->getBody()).','.json_encode($fileData->getTitle()).','.json_encode($fileData->getHead()).');'; + } else { + echo 'ATutor.mods.editor.insertErrorMsg("'.$errorMessage.'");'; + } + echo "window.close();"; + echo '</script>'; +} + +?> + <body> + <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" name="form" enctype="multipart/form-data"> + <input type="file" name="uploadedfile_paste" id="uploadedfile" class="formfield" size="20" /> + <input type="submit" name="submit_file" id="submit_file" value="<?php echo _AT('paste'); ?>" class="button" /> + </form> + </body> +</html> diff --git a/mods/_core/editor/editor_tabs/properties.inc.php b/mods/_core/editor/editor_tabs/properties.inc.php new file mode 100644 index 000000000..369bf7667 --- /dev/null +++ b/mods/_core/editor/editor_tabs/properties.inc.php @@ -0,0 +1,54 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: properties.inc.php 8794 2009-09-16 16:06:54Z cindy $ + +if (!defined('AT_INCLUDE_PATH')) { exit; } + +?> + <div class="row"> + <?php echo _AT('release_date'); ?><br /> + <?php if ($_POST['day']) { ?> + <?php + $today_day = $_POST['day']; + $today_mon = $_POST['month']; + $today_year = $_POST['year']; + + $today_hour = $_POST['hour']; + $today_min = $_POST['min']; + }?> + <?php require(AT_INCLUDE_PATH.'html/release_date.inc.php'); ?> + <?php echo _AT('applies_to_all_sub_pages'); ?> + </div> + + <div class="row"> + <label for="keys"><?php echo _AT('keywords'); ?></label><br /> + <textarea name="keywords" class="formfield" cols="73" rows="2" id="keys"><?php echo ContentManager::cleanOutput($_POST['keywords']); ?></textarea> + </div> + + <div class="row"> + <input type="hidden" name="button_1" value="-1" /> + <?php + if ($contentManager->getNumSections() > (1 - (bool)(!$cid))) { + echo '<p>' , _AT('editor_properties_insturctions_related') , '</p>'; + } + ?><br /> + <table border="0"> + <tr> + <th><?php echo _AT('related_topics'); ?></th> + </tr> + <tr> + <td><?php echo _AT('home'); ?></td> + </tr> + <?php $contentManager->printActionMenu($contentManager->_menu, 0, 0, '', array(), "related_content"); ?> + </table> + </div> \ No newline at end of file diff --git a/mods/_core/editor/editor_tabs/tests.inc.php b/mods/_core/editor/editor_tabs/tests.inc.php new file mode 100644 index 000000000..e28e795b3 --- /dev/null +++ b/mods/_core/editor/editor_tabs/tests.inc.php @@ -0,0 +1,207 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: tests.inc.php 7208 2008-01-09 16:07:24Z harris $ +if (!defined('AT_INCLUDE_PATH')) { exit; } +?> + +<?php +/* Get the list of associated tests with this content on page load */ + +$_REQUEST['cid'] = intval($_REQUEST['cid']); //uses request 'cause after 'saved', the cid will become $_GET. +$sql = 'SELECT * FROM '.TABLE_PREFIX."content_tests_assoc WHERE content_id=$_REQUEST[cid]"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $_POST['tid'][] = $row['test_id']; +} + +/* get a list of all the tests we have, and links to create, edit, delete, preview */ +$sql = "SELECT *, UNIX_TIMESTAMP(start_date) AS us, UNIX_TIMESTAMP(end_date) AS ue + FROM ".TABLE_PREFIX."tests + WHERE course_id=$_SESSION[course_id] + ORDER BY start_date DESC"; +$result = mysql_query($sql, $db); +$num_tests = mysql_num_rows($result); + +//If there are no tests, don't display anything except a message +if ($num_tests == 0){ + $msg->addInfo('NO_TESTS'); + $msg->printInfos(); + return; +} + +$i = 0; +while($row = mysql_fetch_assoc($result)) +{ + $results[$i]['test_id'] = $row['test_id']; + $results[$i]['title'] = $row['title']; + + if ( ($row['us'] <= time()) && ($row['ue'] >= time() ) ) { + $results[$i]['status'] = '<em>'._AT('ongoing').'</em>'; + } else if ($row['ue'] < time() ) { + $results[$i]['status'] = '<em>'._AT('expired').'</em>'; + } else if ($row['us'] > time() ) { + $results[$i]['status'] = '<em>'._AT('pending').'</em>'; + } + + $startend_date_format=_AT('startend_date_format'); + + $results[$i]['availability'] = AT_date($startend_date_format, $row['start_date'], AT_DATE_MYSQL_DATETIME). ' ' ._AT('to_2').' '; + $results[$i]['availability'] .= AT_date($startend_date_format, $row['end_date'], AT_DATE_MYSQL_DATETIME); + + // get result release + if ($row['result_release'] == AT_RELEASE_IMMEDIATE) + $results[$i]['result_release'] = _AT('release_immediate'); + else if ($row['result_release'] == AT_RELEASE_MARKED) + $results[$i]['result_release'] = _AT('release_marked'); + else if ($row['result_release'] == AT_RELEASE_NEVER) + $results[$i]['result_release'] = _AT('release_never'); + + //get # marked submissions + $sql_sub = "SELECT COUNT(*) AS sub_cnt FROM ".TABLE_PREFIX."tests_results WHERE status=1 AND test_id=".$row['test_id']; + $result_sub = mysql_query($sql_sub, $db); + $row_sub = mysql_fetch_assoc($result_sub); + $results[$i]['submissions'] = $row_sub['sub_cnt'].' '._AT('submissions').', '; + + //get # submissions + $sql_sub = "SELECT COUNT(*) AS marked_cnt FROM ".TABLE_PREFIX."tests_results WHERE status=1 AND test_id=".$row['test_id']." AND final_score=''"; + $result_sub = mysql_query($sql_sub, $db); + $row_sub = mysql_fetch_assoc($result_sub); + $results[$i]['submissions'] .= $row_sub['marked_cnt'].' '._AT('unmarked'); + + //get assigned groups + $sql_sub = "SELECT G.title FROM ".TABLE_PREFIX."groups G INNER JOIN ".TABLE_PREFIX."tests_groups T USING (group_id) WHERE T.test_id=".$row['test_id']; + $result_sub = mysql_query($sql_sub, $db); + if (mysql_num_rows($result_sub) == 0) { + $results[$i]['assign_to'] = _AT('everyone'); + } else { + $row_sub = mysql_fetch_assoc($result_sub); + $results[$i]['assign_to'] = $row_sub['title']; + do { + $results[$i]['assign_to'] .= ', '.$row_sub['title']; + } while ($row_sub = mysql_fetch_assoc($result_sub)); + } + + if ($row['passscore'] == 0 && $row['passpercent'] == 0) + $results[$i]['pass_score'] = _AT('no_pass_score'); + else if ($row['passscore'] <> 0) + $results[$i]['pass_score'] = $row['passscore']; + else if ($row['passpercent'] <> 0) + $results[$i]['pass_score'] = $row['passpercent'].'%'; + + $i++; +} +?> + + +<div class="row"> + <span style="font-weight:bold"><?php echo _AT('about_content_tests'); ?></span> +</div> + +<div class="row"> + <?php + //Need radio button 'cause one checkbox makes the states indeterministic + //@harris + $test_export_y_checked = ''; + $test_export_n_checked = ''; + if ($_POST['allow_test_export'] == 1){ + $test_export_y_checked = ' checked="checked"'; + } else { + $test_export_n_checked = ' checked="checked"'; + } + + echo _AT('allow_test_export'); +?> + + <input type="radio" name="allow_test_export" id="allow_test_export" value="1" <?php echo $test_export_y_checked; ?>/> + <label for="allow_test_export"><?php echo _AT('yes'); ?></label> + <input type="radio" name="allow_test_export" id="disallow_test_export" value="0" <?php echo $test_export_n_checked; ?>/> + <label for="disallow_test_export"><?php echo _AT('no'); ?></label> +</div> + + +<div class="row"> + <p><?php echo _AT('custom_test_message'); ?></p> + <textarea name="test_message"><?php echo $_POST['test_message']; ?></textarea> +</div> + +<?php print_test_table($results, $_POST['tid']);?> + +<?php +// display pre-tests +$sql = 'SELECT * FROM '.TABLE_PREFIX."content_prerequisites WHERE content_id=$_REQUEST[cid] AND type='".CONTENT_PRE_TEST."'"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $_POST['pre_tid'][] = $row['item_id']; +} + +?> +<div class="row"> + <span style="font-weight:bold"><?php echo _AT('define_pretest'); ?></span><br /> + <small>· <?php echo _AT('about_pretest'); ?></small><br /> + <?php echo _AT('applies_to_all_sub_pages');?> +</div> + +<?php print_test_table($results, $_POST['pre_tid'], 'pre_');?> + +<?php function print_test_table($results, $post_tids, $id_prefix='') {?> + <div> + <table class="data" summary="" style="width: 90%" rules="cols"> + <thead> + <tr> + <th scope="col"> </th> + <th scope="col"><?php echo _AT('title'); ?></th> + <th scope="col"><?php echo _AT('status'); ?></th> + <th scope="col"><?php echo _AT('availability'); ?></th> + <th scope="col"><?php echo _AT('result_release'); ?></th> + <th scope="col"><?php echo _AT('submissions'); ?></th> + <th scope="col"><?php echo _AT('pass_score'); ?></th> + <th scope="col"><?php echo _AT('assigned_to'); ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($results as $row) { ?> + <?php + $checkMe = ''; + if (is_array($post_tids) && in_array($row['test_id'], $post_tids)){ + $checkMe = ' checked="checked"'; + } + ?> + <tr onmousedown="toggleTestSelect('<?php echo $id_prefix; ?>r_<?php echo $row['test_id']; ?>');rowselect(this);" id="<?php echo $id_prefix; ?>r_<?php echo $row['test_id']; ?>"> + <td><input type="checkbox" name="<?php echo $id_prefix; ?>tid[]" value="<?php echo $row['test_id']; ?>" id="<?php echo $id_prefix; ?>t<?php echo $row['test_id']; ?>" <?php echo $checkMe; ?> onmouseup="this.checked=!this.checked" /></td> + <td><?php echo $row['title']; ?></td> + <td><?php echo $row['status']; ?></td> + <td><?php echo $row['availability']; ?></td> + <td><?php echo $row['result_release']; ?></td> + <td><?php echo $row['submissions']; ?></td> + <td><?php echo $row['pass_score']; ?></td> + <td><?php echo $row['assign_to']; ?></td> + </tr> + <?php } ?> + </tbody> + </table> + </div> + <br /> +<?php }?> + +<script language="javascript" type="text/javascript"> + function toggleTestSelect(r_id){ + var row = document.getElementById(r_id); + var checkBox = row.cells[0].firstChild; + + if (checkBox.checked == true){ + checkBox.checked = false; + } else { + checkBox.checked = true; + } + } +</script> diff --git a/mods/_core/editor/index.php b/mods/_core/editor/index.php new file mode 100644 index 000000000..8ac4f6dbc --- /dev/null +++ b/mods/_core/editor/index.php @@ -0,0 +1,26 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ + + +define('AT_INCLUDE_PATH', '../../../include/'); +require (AT_INCLUDE_PATH.'vitals.inc.php'); + +$_section[0][0] = 'Blank Page'; + +require (AT_INCLUDE_PATH.'header.inc.php'); + +?> +blank page +<?php +require (AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/editor/js/edit.js b/mods/_core/editor/js/edit.js new file mode 100644 index 000000000..535c1ba8d --- /dev/null +++ b/mods/_core/editor/js/edit.js @@ -0,0 +1,242 @@ +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2010 by Laurel Williams */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/************************************************************************/ +// $Id: $ + +/*global jQuery*/ +/*global ATutor */ +/*global tinyMCE */ +/*global window */ + +ATutor = ATutor || {}; +ATutor.mods = ATutor.mods || {}; +ATutor.mods.editor = ATutor.mods.editor || {}; + +(function () { + var hiddenClass = "hidden"; + var enabledClass = "clickable"; + + var hideIt = function (theElement, hiddenElement) { + theElement.addClass(hiddenClass); + hiddenElement.val("0"); + }; + + var showIt = function (theElement, hiddenElement) { + theElement.removeClass(hiddenClass); + hiddenElement.val("1"); + }; + + //hides or shows tool (toggle) and sets hidden input value appropriately. + var doToggle = function (theElement, hiddenElement) { + if (theElement.hasClass(hiddenClass)) { + showIt(theElement, hiddenElement); + } else { + hideIt(theElement, hiddenElement); + } + }; + + //click function to launch accessibility validation window + var launchAChecker = function () { + var theCode = '<html><body onLoad="document.accessform.submit();"> \n'; + theCode += '<h1>'+ATutor.mods.editor.processing_text+' .....</h1>\n'; + theCode += '<form action="'+ATutor.base_href+'mods/_core/editor/accessibility.php?popup=1" name="accessform" method="post"> \n'; + theCode += '<input type="hidden" name="cid" value="'+jQuery("input[name=cid]").val()+'" /> \n'; + theCode += '<textarea name="body_text" style="display:none">' + tinyMCE.activeEditor.getContent() + '</textarea>\n'; + theCode += '<input type="submit" style="display:none" /></form> \n'; + theCode += '</body></html> \n'; + accessWin = window.open('', 'accessibilityWin', 'menubar=0,scrollbars=1,resizable=1,width=600,height=600'); + accessWin.document.writeln(theCode); + accessWin.document.close(); + return false; + }; + + //AChecker variables + var accessibilityTool = { + toolId: "#accessibilitytool", + enabledClass: enabledClass, + enabledImage: "images/achecker.png", + clickFunction: function () { + launchAChecker(); + }, + disabledImage: "images/achecker_disabled.png" + }; + + //customized head variables + var headId = "#head"; + var displayheadId = "#displayhead"; + var headTool = { + toolId: "#headtool", + enabledClass: enabledClass, + enabledImage: "images/custom_head.png", + clickFunction: function () { + doToggle(jQuery(headId), jQuery(displayheadId)); + }, + disabledImage: "images/custom_head_disabled.png" + }; + + //paste from file variables + var pasteId = "#paste"; + var displaypasteId = "#displaypaste"; + var pasteTool = { + toolId: "#pastetool", + enabledClass: enabledClass, + enabledImage: "images/paste.png", + clickFunction: function () { + doToggle(jQuery(pasteId), jQuery(displaypasteId)); + }, + disabledImage: "images/paste_disabled.png" + }; + + //click function to launch file manager window + var launchFileManager = function () { + window.open(ATutor.base_href + 'mods/_core/file_manager/index.php?framed=1&popup=1&cp=' + ATutor.mods.editor.content_path, 'newWin1', 'menubar=0,scrollbars=1,resizable=1,width=640,height=490'); + return false; + }; + + //file manager variables + var filemanTool = { + toolId: "#filemantool", + enabledClass: enabledClass, + enabledImage: "images/file-manager.png", + clickFunction: function () { + launchFileManager(); + }, + disabledImage: "images/file-manager_disabled.png" + }; + + //checks hidden variable and shows/hides element accordingly + var setDisplay = function (theElement, hiddenElement) { + if (hiddenElement.val() === '0') { + theElement.addClass(hiddenClass); + } else { + theElement.removeClass(hiddenClass); + } + }; + + var disableTool = function (theTool) { + var theToolElement = jQuery(theTool.toolId); + theToolElement.removeClass(theTool.enabledClass); + theToolElement.attr("src", ATutor.base_href + theTool.disabledImage); + theToolElement.attr("title", theTool.disabledTitle); + theToolElement.attr("alt", theTool.disabledTitle); + theToolElement.unbind("click"); + }; + + var enableTool = function (theTool) { + var theToolElement = jQuery(theTool.toolId); + theToolElement.addClass(theTool.enabledClass); + theToolElement.attr("src", ATutor.base_href + theTool.enabledImage); + theToolElement.attr("title", theTool.enabledTitle); + theToolElement.attr("alt", theTool.enabledTitle); + theToolElement.click(theTool.clickFunction); + }; + + //initialises values to show or hide them + var setupPage = function () { + var head = jQuery(headId); + var displayhead = jQuery(displayheadId); + var paste = jQuery(pasteId); + var displaypaste = jQuery(displaypasteId); + var textArea = jQuery("#textSpan"); + var weblink = jQuery("#weblinkSpan"); + var textAreaId = "body_text"; + if (jQuery("#weblink").attr("checked")) { + disableTool(accessibilityTool); + disableTool(headTool); + disableTool(pasteTool); + disableTool(filemanTool); + + hideIt(head, displayhead); + hideIt(paste, displaypaste); + if (tinyMCE.get(textAreaId)) { + tinyMCE.execCommand('mceRemoveControl', false, textAreaId); + } + textArea.hide(); + weblink.show(); + } else if (jQuery("#html").attr("checked")) { + enableTool(accessibilityTool); + enableTool(headTool); + enableTool(pasteTool); + enableTool(filemanTool); + + setDisplay(head, displayhead); + setDisplay(paste, displaypaste); + if (ATutor.mods.editor.editor_pref !== '1' && !tinyMCE.get(textAreaId)) { + tinyMCE.execCommand('mceAddControl', false, textAreaId); + } + weblink.hide(); + textArea.show(); + } else { + disableTool(accessibilityTool); + disableTool(headTool); + enableTool(pasteTool); + enableTool(filemanTool); + + hideIt(head, displayhead); + setDisplay(paste, displaypaste); + weblink.hide(); + if (tinyMCE.get(textAreaId)) { + tinyMCE.execCommand('mceRemoveControl', false, textAreaId); + } + textArea.show(); + } + }; + + //click function to launch preview window + var previewTool = function () { + var theCode = '<html><body onLoad="document.accessform.submit();"> \n'; + theCode += '<h1>'+ATutor.mods.editor.processing_text+' .....</h1>\n'; + theCode += '<form action="'+ATutor.base_href+'mods/_core/editor/preview.php?popup=1" name="accessform" method="post"> \n'; + theCode += '<input type="hidden" name="title" value="'+jQuery("input[name=title]").val()+'" /> \n'; + theCode += '<input type="hidden" name="cid" value="'+jQuery("input[name=cid]").val()+'" /> \n'; + theCode += '<input type="hidden" name="formatting" value="'+jQuery("#formatting_radios input:radio:checked").val()+'" /> \n'; + jQuery("input[name*='glossary_defs[']").each(function() { + theCode += '<input type="hidden" name="'+this.name+'" value="'+jQuery(this).val()+'" /> \n'; + }); + if (jQuery("#weblink").attr("checked")) { + theCode += '<input type="hidden" name="weblink_text" value="'+jQuery("#weblink_text").val()+'" /> \n'; + } else if (jQuery("#html").attr("checked")) { + theCode += '<textarea name="body_text" style="display:none">' + tinyMCE.activeEditor.getContent() + '</textarea>\n'; + } else { + theCode += '<textarea name="body_text" style="display:none">' + jQuery("#body_text").val() + '</textarea>\n'; + } + theCode += '<input type="submit" style="display:none" /></form> \n'; + theCode += '</body></html> \n'; + accessWin = window.open('', 'previewWin', 'menubar=0,scrollbars=1,resizable=1,width=600,height=600'); + accessWin.document.writeln(theCode); + accessWin.document.close(); + return false; + }; + + //click function to launch tool window + var launchTool = function () { + window.open(ATutor.base_href + 'mods/_core/tool_manager/index.php?framed=1&popup=1&tool_file=' + ATutor.mods.editor.tool_file + '&cid=' + ATutor.mods.editor.content_id, 'newWin2', 'menubar=0,scrollbars=1,resizable=1,width=600,height=400'); + return false; + }; + + //set up click handlers and show/hide appropriate tools via setupPage + var initialize = function () { + jQuery("#previewtool").click(previewTool); + jQuery(".tool").click(launchTool); + jQuery("#formatting_radios > input").click(setupPage); + headTool.enabledTitle = ATutor.mods.editor.head_enabled_title; + headTool.disabledTitle = ATutor.mods.editor.head_disabled_title; + pasteTool.enabledTitle = ATutor.mods.editor.paste_enabled_title; + pasteTool.disabledTitle = ATutor.mods.editor.paste_disabled_title; + filemanTool.enabledTitle = ATutor.mods.editor.fileman_enabled_title; + filemanTool.disabledTitle = ATutor.mods.editor.fileman_disabled_title; + accessibilityTool.enabledTitle = ATutor.mods.editor.accessibility_enabled_title; + accessibilityTool.disabledTitle = ATutor.mods.editor.accessibility_disabled_title; + setupPage(); + }; + + jQuery(document).ready(initialize); +})(); \ No newline at end of file diff --git a/mods/_core/editor/preview.php b/mods/_core/editor/preview.php new file mode 100644 index 000000000..0fe2e6f78 --- /dev/null +++ b/mods/_core/editor/preview.php @@ -0,0 +1,71 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: preview.inc.php 7208 2008-01-09 16:07:24Z greg $ + +define('AT_INCLUDE_PATH', '../../../include/'); + +require(AT_INCLUDE_PATH.'vitals.inc.php'); +require(AT_INCLUDE_PATH.'../mods/_core/editor/editor_tab_functions.inc.php'); + +$cid = intval($_POST['cid']); + +if ($cid == 0) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $missing_fields[] = _AT('content_id'); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$result = $contentManager->getContentPage($cid); + +if (!($content_row = @mysql_fetch_assoc($result))) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('PAGE_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $course_base_href = 'get.php/'; +} else { + $course_base_href = 'content/' . $_SESSION['course_id'] . '/'; +} + +if ($content_row['content_path']) { + $content_base_href .= $content_row['content_path'].'/'; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + <div class="row"> + <?php + echo '<h2>'.AT_print($stripslashes($_POST['title']), 'content.title').'</h2>'; + if ($_POST['formatting'] == CONTENT_TYPE_WEBLINK) { + $url = $_POST['weblink_text']; + $validated_url = isValidURL($url); + if (!validated_url || $validated_url !== $url) { + $msg->addError(array('INVALID_INPUT', _AT('weblink'))); + $msg->printErrors(); + } else { + echo format_content($url, $_POST['formatting'], array()); + } + } else { + echo format_content($stripslashes($_POST['body_text']), $_POST['formatting'], $_POST['glossary_defs']); + } + ?> + </div> +<?php +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/editor/remove_alternative.php b/mods/_core/editor/remove_alternative.php new file mode 100644 index 000000000..35b8a3640 --- /dev/null +++ b/mods/_core/editor/remove_alternative.php @@ -0,0 +1,56 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ + +/** + * This script handles the ajax post submit from "content editor" =? "adpated content" + * to remove selected alternative from database + * @see mods/_core/editor/editor_tabs/alternatives.inc.php + * @var $_POST values: + * pid: primary resource id + * a_type: alternative type, must be one of the values in resource_types.type_id + */ + +define('AT_INCLUDE_PATH', '../../../include/'); +require (AT_INCLUDE_PATH.'vitals.inc.php'); + +$pid = intval($_POST['pid']); +$type_id = intval($_POST['a_type']); + +// check post vars +if ($pid == 0 || $type_id == 0) exit; + +global $db; +// delete the existing alternative for this (pid, a_type) +$sql = "SELECT sr.secondary_resource_id + FROM ".TABLE_PREFIX."secondary_resources sr, ".TABLE_PREFIX."secondary_resources_types srt + WHERE sr.secondary_resource_id = srt.secondary_resource_id + AND sr.primary_resource_id = ".$pid." + AND sr.language_code = '".$_SESSION['lang']."' + AND srt.type_id=".$type_id; +$existing_secondary_result = mysql_query($sql, $db); + +while ($existing_secondary = mysql_fetch_assoc($existing_secondary_result)) +{ + $sql = "DELETE FROM ".TABLE_PREFIX."secondary_resources + WHERE secondary_resource_id = ".$existing_secondary['secondary_resource_id']; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."secondary_resources_types + WHERE secondary_resource_id = ".$existing_secondary['secondary_resource_id']." + AND type_id=".$type_id; + $result = mysql_query($sql, $db); +} + +exit; + +?> \ No newline at end of file diff --git a/mods/_core/editor/save_alternative.php b/mods/_core/editor/save_alternative.php new file mode 100644 index 000000000..85edfb1e6 --- /dev/null +++ b/mods/_core/editor/save_alternative.php @@ -0,0 +1,68 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ + +/** + * This script handles the ajax post submit from "content editor" =? "adpated content" + * to save the selected alternative into database + * @see mods/_core/file_manager/filemanager_display.inc.php + * @var $_POST values: + * pid: primary resource id + * a_type: alternative type, must be one of the values in resource_types.type_id + * alternative: the location and name of the selected alternative + */ + +define('AT_INCLUDE_PATH', '../../../include/'); +require (AT_INCLUDE_PATH.'vitals.inc.php'); + +$pid = intval($_POST['pid']); +$type_id = intval($_POST['a_type']); +$secondary_resource = trim($_POST['alternative']); + +// check post vars +if ($pid == 0 || $type_id == 0 || $secondary_resource == '') exit; + +global $db; +// delete the existing alternative for this (pid, a_type) +$sql = "SELECT sr.secondary_resource_id + FROM ".TABLE_PREFIX."secondary_resources sr, ".TABLE_PREFIX."secondary_resources_types srt + WHERE sr.secondary_resource_id = srt.secondary_resource_id + AND sr.primary_resource_id = ".$pid." + AND sr.language_code = '".$_SESSION['lang']."' + AND srt.type_id=".$type_id; +$existing_secondary_result = mysql_query($sql, $db); + +while ($existing_secondary = mysql_fetch_assoc($existing_secondary_result)) +{ + $sql = "DELETE FROM ".TABLE_PREFIX."secondary_resources + WHERE secondary_resource_id = ".$existing_secondary['secondary_resource_id']; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."secondary_resources_types + WHERE secondary_resource_id = ".$existing_secondary['secondary_resource_id']." + AND type_id=".$type_id; + $result = mysql_query($sql, $db); +} + +// insert new alternative +$sql = "INSERT INTO ".TABLE_PREFIX."secondary_resources (primary_resource_id, secondary_resource, language_code) + VALUES (".$pid.", '".mysql_real_escape_string($secondary_resource)."', '".$_SESSION['lang']."')"; +$result = mysql_query($sql, $db); +$secondary_resource_id = mysql_insert_id(); + +$sql = "INSERT INTO ".TABLE_PREFIX."secondary_resources_types (secondary_resource_id, type_id) + VALUES (".$secondary_resource_id.", ".$type_id.")"; +$result = mysql_query($sql, $db); + +exit; + +?> \ No newline at end of file diff --git a/mods/_core/enrolment/admin/enroll_edit.php b/mods/_core/enrolment/admin/enroll_edit.php new file mode 100644 index 000000000..1cb3d686b --- /dev/null +++ b/mods/_core/enrolment/admin/enroll_edit.php @@ -0,0 +1,23 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: enroll_edit.php 7208 2008-01-09 16:07:24Z greg $ +define('AT_INCLUDE_PATH', '../../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); + +admin_authenticate(AT_ADMIN_PRIV_ENROLLMENT); + +$course_id = intval($_REQUEST['course_id']); + +require(AT_INCLUDE_PATH.'../mods/_core/enrolment/html/enroll_edit.inc.php'); +exit; +?> \ No newline at end of file diff --git a/mods/_core/enrolment/admin/index.php b/mods/_core/enrolment/admin/index.php new file mode 100644 index 000000000..2f59dc468 --- /dev/null +++ b/mods/_core/enrolment/admin/index.php @@ -0,0 +1,40 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: index.php 7208 2008-01-09 16:07:24Z greg $ +define('AT_INCLUDE_PATH', '../../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); + +admin_authenticate(AT_ADMIN_PRIV_ENROLLMENT); + +if (!isset($_REQUEST['course_id'])) { + $sql = "SELECT course_id FROM ".TABLE_PREFIX."courses ORDER BY title LIMIT 1"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $course_id = intval($row['course_id']); + } else { + require(AT_INCLUDE_PATH.'header.inc.php'); + echo _AT('none_found'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } +} else { + $course_id = intval($_REQUEST['course_id']); +} + + +if (isset($system_courses[$course_id]['member_id'])) { + + require(AT_INCLUDE_PATH.'../mods/_core/enrolment/html/enrollment.inc.php'); +} +exit; +?> \ No newline at end of file diff --git a/mods/_core/enrolment/admin/privileges.php b/mods/_core/enrolment/admin/privileges.php new file mode 100644 index 000000000..6e836a553 --- /dev/null +++ b/mods/_core/enrolment/admin/privileges.php @@ -0,0 +1,23 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: privileges.php 7208 2008-01-09 16:07:24Z greg $ +define('AT_INCLUDE_PATH', '../../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); + +admin_authenticate(AT_ADMIN_PRIV_ENROLLMENT); + +$course_id = intval($_REQUEST['course_id']); + +require(AT_INCLUDE_PATH.'../mods/_core/enrolment/html/privileges.inc.php'); +exit; +?> \ No newline at end of file diff --git a/mods/_core/enrolment/create_course_list.php b/mods/_core/enrolment/create_course_list.php new file mode 100644 index 000000000..3cade987f --- /dev/null +++ b/mods/_core/enrolment/create_course_list.php @@ -0,0 +1,70 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* 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. */ +/************************************************************************/ +// $Id: create_course_list.php 8901 2009-11-11 19:10:19Z cindy $ + +define('AT_INCLUDE_PATH', '../../../include/'); +require (AT_INCLUDE_PATH.'vitals.inc.php'); +authenticate(AT_PRIV_ENROLLMENT); + +require(AT_INCLUDE_PATH.'header.inc.php'); +$msg->printAll(); +?> +<div class="input-form"> + <fieldset class="group_form"><legend class="group_form"><?php echo _AT('list_create_course_list'); ?></legend> +<form action="mods/_core/enrolment/verify_list.php" method="post"> +<input type="hidden" name="from" value="create" /> +<div> + + <div class="row"> + <?php echo _AT('import_sep_txt'); ?><br /> + <input type="radio" name="sep_choice" id="und" value="_" checked="checked" /> + <label for="und"><?php echo _AT('underscore'); ?></label> + <input type="radio" name="sep_choice" id="per" value="." /> + <label for="per"><?php echo _AT('period'); ?></label> + </div> + + +<table class="data static" summary="" rules="cols"> +<thead> +<tr> + <th> </th> + <th><?php echo _AT('first_name'); ?></th> + <th><?php echo _AT('last_name'); ?></th> + <th><?php echo _AT('email'); ?></th> +</tr> +</thead> + +<tfoot> +<tr> + <td colspan="4"> + <input type="submit" name="submit" value="<?php echo _AT('list_add_course_list'); ?>" /> + <input type="submit" name="cancel" value="<?php echo _AT('cancel'); ?>" /> + </td> +</tr> +</tfoot> + +<tbody> +<?php for ($i=1; $i <= 5; $i++): ?> + <tr> + <td><?php echo $i; ?></td> + <td><input type="text" name="first_name<?php echo $i; ?>" /></td> + <td><input type="text" name="last_name<?php echo $i; ?>" /></td> + <td><input type="text" name="email<?php echo $i; ?>" /></td> + </tr> +<?php endfor; ?> +</tbody> + +</table> +</form> +</fieldset> +</div> +<?php require(AT_INCLUDE_PATH.'footer.inc.php'); ?> \ No newline at end of file diff --git a/mods/_core/enrolment/enroll_edit.php b/mods/_core/enrolment/enroll_edit.php new file mode 100644 index 000000000..6734f46f4 --- /dev/null +++ b/mods/_core/enrolment/enroll_edit.php @@ -0,0 +1,23 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/************************************************************************/ +// $Id: enroll_edit.php 7208 2008-01-09 16:07:24Z greg $ + +define('AT_INCLUDE_PATH', '../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); +authenticate(AT_PRIV_ENROLLMENT); + +$course_id = $_SESSION['course_id']; + +require(AT_INCLUDE_PATH.'../mods/_core/enrolment/html/enroll_edit.inc.php'); +exit; +?> \ No newline at end of file diff --git a/mods/_core/enrolment/export_course_list.php b/mods/_core/enrolment/export_course_list.php new file mode 100644 index 000000000..a3dc42ff3 --- /dev/null +++ b/mods/_core/enrolment/export_course_list.php @@ -0,0 +1,124 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* 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. */ +/************************************************************************/ +// $Id: export_course_list.php 7482 2008-05-06 17:44:49Z greg $ + +define('AT_INCLUDE_PATH', '../../../include/'); +require (AT_INCLUDE_PATH.'vitals.inc.php'); +authenticate(AT_PRIV_ENROLLMENT); + +$completed = 0; + +/*EXPORT LIST OF STUDENTS*/ +if(isset($_POST['export'])) { + //if not list was selected + if (!$_POST['enrolled'] && !$_POST['pending_enrollment'] && !$_POST['alumni']) { + $msg->addError('NO_STUDENT_SELECTED'); + } + //retrieve info from database based on selection (make sure that instructor is not exported!) + else { + if ($_POST['enrolled'] && $_POST['pending_enrollment'] && $_POST['alumni']) { + $condition = ""; + } else if ($_POST['enrolled'] && $_POST['pending_enrollment']) { + $condition = "AND approved <> 'a'"; + } else if ($_POST['enrolled'] && $_POST['alumni']) { + $condition = "AND approved <> 'n'"; + } else if ($_POST['pending_enrollment'] && $_POST['alumni']) { + $condition = "AND approved <> 'y'"; + } else if ($_POST['pending_enrollment']) { + $condition = "AND approved = 'n'"; + } else if ($_POST['enrolled']) { + $condition = "AND approved = 'y'"; + } else if ($_POST['alumni']) { + $condition = "AND approved = 'a'"; + } + + $sql = "SELECT m.first_name, m.last_name, m.email + FROM ".TABLE_PREFIX."course_enrollment cm JOIN ".TABLE_PREFIX."members m ON cm.member_id = m.member_id JOIN ".TABLE_PREFIX."courses c ON (cm.course_id = c.course_id AND cm.member_id <> c.member_id) WHERE cm.course_id = $_SESSION[course_id] " . $condition . "ORDER BY m.last_name"; + + $result = mysql_query($sql,$db); + while ($row = mysql_fetch_assoc($result)){ + $this_row .= quote_csv($row['first_name']).","; + $this_row .= quote_csv($row['last_name']).","; + $this_row .= quote_csv($row['email'])."\n"; + } + + if ($this_row) { + header('Content-Type: text/csv'); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="course_list_'.$_SESSION['course_id'].'.csv"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + + echo $this_row; + } else { + // nothing to send. empty file + $msg->addError('ENROLLMENT_NONE_FOUND'); + header('Location: export_course_list.php'); + } + exit; + } +} +if(isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} + +if(isset($_POST['done'])) { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} +require(AT_INCLUDE_PATH.'header.inc.php'); + + +?> + +<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" name="selectform"> +<div class="input-form"> + <fieldset class="group_form"><legend class="group_form"><?php echo _AT('export'); ?></legend> + <div class="row"> + <label><input type="checkbox" name="enrolled" value="1" id="enrolled" /><?php echo _AT('enrolled_list_includes_assistants'); ?></label><br /> + <label><input type="checkbox" name="pending_enrollment" value="1" id="pending_enrollment" /><?php echo _AT('pending_enrollment'); ?></label><br /> + <label><input type="checkbox" name="alumni" value="1" id="alumni" /><?php echo _AT('alumni'); ?></label> + </div> + + <div class="row buttons"> + <input type="submit" name="export" value="<?php echo _AT('export'); ?>" /> + <input type="submit" name="cancel" value="<?php echo _AT('cancel'); ?>" /> + </div> + </fieldset> +</div> +</form> + +<?php + +/** +* Creates csv file to be exported +* @access private +* @param string $line The line ot be converted to csv +* @return string The line after conversion to csv +* @author Shozub Qureshi +*/ +function quote_csv($line) { + $line = str_replace('"', '""', $line); + + $line = str_replace("\n", '\n', $line); + $line = str_replace("\r", '\r', $line); + $line = str_replace("\x00", '\0', $line); + + return '"'.$line.'"'; +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/enrolment/html/enroll_edit.inc.php b/mods/_core/enrolment/html/enroll_edit.inc.php new file mode 100644 index 000000000..c18c3f87e --- /dev/null +++ b/mods/_core/enrolment/html/enroll_edit.inc.php @@ -0,0 +1,371 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* 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. */ +/************************************************************************/ +// $Id: enroll_edit.php 6662 2006-11-20 15:52:49Z joel $ + +if (!defined('AT_INCLUDE_PATH')) { exit; } + +/** +* Generates the list of login ids of the selected user +* @access private +* @param string $member_ids the list of members to be checked +* @return string The list of login IDs +* @author Shozub Qureshi +*/ +function get_usernames ($member_ids) { + global $db; + + $sql = "SELECT login FROM ".TABLE_PREFIX."members WHERE `member_id` IN ($member_ids)"; + + $result = mysql_query($sql, $db); + + while ($row = mysql_fetch_assoc($result)) { + $str .= '<li>' . $row['login'] . '</li>'; + } + return $str; +} + +/** +* Checks if any of the selected users have non-zero roles or privileges +* @access private +* @param string $member_ids the list of members to be checked +* @return int whether the role/priv is empty or not (0 = if empty, 1 = if ok) +* @author Shozub Qureshi +*/ +function check_roles ($member_ids) { + global $db; + + $sql = "SELECT * FROM ".TABLE_PREFIX."course_enrollment WHERE `member_id` IN ($member_ids)"; + $result = mysql_query($sql, $db); + + while ($row = mysql_fetch_assoc($result)) { + if ($row['role'] != 'Student' || $row['privileges'] != 0) { + return 1; + } + } + return 0; +} + +/** +* Removes students from course enrollement +* @access private +* @param array $list the IDs of the members to be removed +* @author Shozub Qureshi +*/ +/* +// no longer used. Unenroll does this job AND removes groups too. +function remove ($list) { + global $db; + + $members = '(member_id='.$list[0].')'; + for ($i=1; $i < count($list); $i++) { + $members .= ' OR (member_id='.$list[$i].')'; + } + $sql = "DELETE FROM ".TABLE_PREFIX."course_enrollment WHERE course_id = $_SESSION[course_id] AND ($members)"; + $result = mysql_query($sql, $db); +}*/ + +/** +* Unenrolls students from course enrollement +* @access private +* @param array $list the IDs of the members to be removed +* @author Shozub Qureshi +* @author Greg Gay added Unsubscribe when unenrolling +*/ +function unenroll ($list) { + global $db, $system_courses, $course_id; + $members = implode(',', $list); + + if ($members) { + $members = addslashes($members); + + $sql = "DELETE FROM ".TABLE_PREFIX."course_enrollment WHERE course_id=$course_id AND member_id IN ($members)"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."groups_members WHERE member_id IN ($members)"; + $result = mysql_query($sql, $db); + // $groupModule->unenroll(course_id, user_id); + // $forumModule->unenroll(course_id, user_id); + + // remove forum subscriptions as admin else instructor + if($_SESSION['course_id'] == "-1"){ + $this_course_id = $_REQUEST['course_id']; + } else { + $this_course_id = $_SESSION['course_id']; + } + + // get a list for forums in this course + $sql = "SELECT forum_id from ".TABLE_PREFIX."forums_courses WHERE course_id = '$this_course_id'"; + $result = mysql_query($sql, $db); + + if($result && mysql_num_rows($result)>0){ + while($row = mysql_fetch_assoc($result)){ + $this_course_forums[] = $row['forum_id']; + } + $this_forum_list = implode(',', $this_course_forums); + + // delete from forum_subscription any member in $members (being unenrolled) + // with posts to forums in this course. + foreach ($this_course_forums as $this_course_forum){ + $sql1 = "DELETE FROM ".TABLE_PREFIX."forums_subscriptions WHERE forum_id = '$this_course_forum' AND member_id IN ($members)"; + $result_unsub = mysql_query($sql1, $db); + } + } + + // get a list of posts for forums in the current course + $sql = "SELECT post_id FROM ".TABLE_PREFIX."forums_threads WHERE forum_id IN ($this_forum_list)"; + $result = mysql_query($sql, $db); + if($result && mysql_num_rows($result)>0){ + while($row = mysql_fetch_assoc($result)){ + $this_course_posts[] = $row['post_id']; + } + $this_post_list = implode(',', $this_course_posts); + + // delete from forums_accessed any post with member_id in $members being unenrolled, + // and post_id in + foreach($this_course_posts as $this_course_post){ + + $sql2 = "DELETE FROM ".TABLE_PREFIX."forums_accessed WHERE post_id = '$this_course_post' AND member_id IN ($members)"; + $result_unsub2 = mysql_query($sql2, $db); + } + } + } +} + +/** +* Enrolls students into course enrollement +* @access private +* @param array $list the IDs of the members to be added +* @author Shozub Qureshi +*/ +function enroll ($list) { + global $db, $msg, $_config, $course_id, $owner; + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + $num_list = count($list); + $members = '(member_id='.$list[0].')'; + for ($i=0; $i < $num_list; $i++) { + $id = intval($list[$i]); + $members .= ' OR (member_id='.$id.')'; + $sql = "INSERT INTO ".TABLE_PREFIX."course_enrollment VALUES ($id, $course_id, 'y', 0, '', 0)"; + $result = mysql_query($sql, $db); + if (mysql_affected_rows($db) != 1) { + $sql = "UPDATE ".TABLE_PREFIX."course_enrollment SET approved='y' WHERE course_id=$course_id AND member_id=$id"; + $result = mysql_query($sql, $db); + } + } + + //get First_name, Last_name of course Instructor + $sql_from = "SELECT first_name, last_name, email FROM ".TABLE_PREFIX."members WHERE member_id = $owner"; + $result_from = mysql_query($sql_from, $db); + $row_from = mysql_fetch_assoc($result_from); + + $email_from_name = $row_from['first_name'] . ' ' . $row_from['last_name']; + $email_from = $row_from['email']; + + //get email addresses of users: + $sql_to = "SELECT email FROM ".TABLE_PREFIX."members WHERE ($members)"; + $result_to = mysql_query($sql_to, $db); + + while ($row_to = mysql_fetch_assoc($result_to)) { + // send email here. + $login_link = AT_BASE_HREF . 'login.php?course=' . $course_id; + $subject = SITE_NAME.': '._AT('enrol_message_subject'); + $body = SITE_NAME.': '._AT('enrol_message_approved', $_SESSION['course_title'], $login_link)."\n\n"; + + $mail = new ATutorMailer; + $mail->From = $_config['contact_email']; + $mail->FromName = $_config['site_name']; + $mail->AddAddress($row_to['email']); + $mail->Subject = $subject; + $mail->Body = $body; + + if (!$mail->Send()) { + $msg->addError('SENDING_ERROR'); + } + + unset($mail); + } +} + + +function group ($list, $gid) { + global $db,$msg; + $sql = "REPLACE INTO ".TABLE_PREFIX."groups_members VALUES "; + $gid=intval($gid); + for ($i=0; $i < count($list); $i++) { + $student_id = intval($list[$i]); + $sql .= "($gid, $student_id),"; + } + $sql = substr($sql, 0, -1); + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} + +function group_remove ($ids, $gid) { + global $db,$msg; + $gid=intval($gid); + + $ids=implode(',', $ids); + + if ($ids) { + $sql = "DELETE FROM ".TABLE_PREFIX."groups_members WHERE group_id=$gid AND member_id IN ($ids)"; + mysql_query($sql, $db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + + header('Location: index.php'); + exit; +} + +/** +* Marks a student as an alumni of the course (not enrolled, but can view course material and participate in forums) +* @access private +* @param array $list the IDs of the members to be alumni +* @author Heidi Hazelton +*/ +function alumni ($list) { + global $db, $course_id; + $members = '(member_id='.$list[0].')'; + for ($i=1; $i < count($list); $i++) { + $members .= ' OR (member_id='.$list[$i].')'; + } + + $sql = "UPDATE ".TABLE_PREFIX."course_enrollment SET approved = 'a' WHERE course_id=$course_id AND ($members)"; + $result = mysql_query($sql, $db); +} + + +//course_owner +$owner = $system_courses[$course_id]['member_id']; + +if (isset($_POST['submit_no'])) { + //if user decides to forgo option + $msg->addFeedback('CANCELLED'); + header('Location: index.php?current_tab='.$_POST['curr_tab'].SEP.'course_id='.$course_id); + exit; +} /* +// No longer used. Unenroll does the same job and removes from groups too. +else if (isset($_POST['submit_yes']) && $_POST['func'] =='remove' ) { + //Remove student from list (unenrolls automatically) + + //you cannot remove anyone unless you are the course owner + authenticate(AT_PRIV_ADMIN); + + //echo 'atleast this worked'; + remove($_POST['id']); + + $msg->addFeedback('MEMBERS_REMOVED'); + header('Location: index.php?current_tab=4'); + exit; +}*/ +else if (isset($_POST['submit_yes']) && $_POST['func'] =='unenroll' ) { + //Unenroll student from course + unenroll($_POST['id']); + +// $msg->addFeedback('MEMBERS_UNENROLLED'); + $msg->addFeedback('MEMBERS_REMOVED'); + header('Location: index.php?current_tab=4'.SEP.'course_id='.$course_id); + exit; +} else if (isset($_POST['submit_yes']) && $_POST['func'] =='enroll' ) { + //Enroll student in course + enroll($_POST['id']); + + $msg->addFeedback('MEMBERS_ENROLLED'); + header('Location: index.php?current_tab=0'.SEP.'course_id='.$course_id); + exit; +} else if (isset($_POST['submit_yes']) && $_POST['func'] =='alumni' ) { + //Mark student as course alumnus + alumni($_POST['id']); + + $msg->addFeedback('MEMBERS_ALUMNI'); + header('Location: index.php?current_tab=2'.SEP.'course_id='.$course_id); + exit; +} else if (isset($_POST['submit_yes']) && $_POST['func'] =='group' ) { + //Mark student as a member of the group + group($_POST['id'],$_POST['gid']); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php?current_tab='.$_POST['current_tab'].SEP.'course_id='.$course_id); + exit; +} else if (isset($_POST['submit_yes']) && $_POST['func'] =='group_remove' ) { + // Remove student as a member of the group + group_remove($_POST['id'],$_POST['gid']); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php?current_tab='.$_POST['current_tab'].SEP.'course_id='.$course_id); + exit; +} +require(AT_INCLUDE_PATH.'header.inc.php'); + +//Store id's into a hidden element for use by functions +$j = 0; +while ($_GET['id'.$j]) { + $_GET['id'.$j] = abs($_GET['id'.$j]); + if ($_GET['id'.$j] == $owner) { + //do nothing + } else { + $hidden_vars['id['.$j.']'] = $_GET['id'.$j]; + $member_ids .= $_GET['id'.$j].', '; + } + $j++; +} +$member_ids = substr($member_ids, 0, -2); + +$hidden_vars['func'] = $_GET['func']; +$hidden_vars['current_tab'] = $_GET['current_tab']; +$hidden_vars['gid'] = abs($_GET['gid']); +$hidden_vars['course_id'] = $course_id; +//get usernames of users about to be edited +$str = get_usernames($member_ids); + +//Print appropriate confirm msg for action +if ($_GET['func'] == 'remove') { + $confirm = array('REMOVE_STUDENT', $str); + $msg->addConfirm($confirm, $hidden_vars); +} else if ($_GET['func'] == 'enroll') { + $confirm = array('ENROLL_STUDENT', $str); + $msg->addconfirm($confirm, $hidden_vars); +} else if ($_GET['func'] == 'unenroll') { + if (check_roles($member_ids) == 1) { + $confirm = array('UNENROLL_PRIV', $str); + $msg->addConfirm($confirm, $hidden_vars); + } else { + $confirm = array('UNENROLL_STUDENT', $str); + $msg->addConfirm($confirm, $hidden_vars); + } +} else if ($_GET['func'] == 'alumni') { + $confirm = array('ALUMNI', $str); + $msg->addConfirm($confirm, $hidden_vars); +} else if ($_GET['func'] == 'group') { + $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=".$hidden_vars['gid']; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $confirm = array('STUDENT_GROUP', $row['title'], $str); + $msg->addConfirm($confirm, $hidden_vars); +} else if ($_GET['func'] == 'group_remove') { + $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=".$hidden_vars['gid']; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $confirm = array('STUDENT_REMOVE_GROUP', $row['title'], $str); + $msg->addConfirm($confirm, $hidden_vars); +} + +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_core/enrolment/html/enroll_tab_functions.inc.php b/mods/_core/enrolment/html/enroll_tab_functions.inc.php new file mode 100644 index 000000000..b3298196e --- /dev/null +++ b/mods/_core/enrolment/html/enroll_tab_functions.inc.php @@ -0,0 +1,132 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/************************************************************************/ +if (!defined('AT_INCLUDE_PATH')) { exit; } + +$db; + +/** +* Generates the tabs for the enroll admin page +* @access private +* @return string The tabs for the enroll_admin page +* @author Shozub Qureshi +*/ +function get_tabs() { + //these are the _AT(x) variable names and their include file + /* tabs[tab_id] = array(tab_name, file_name, accesskey) */ + $tabs[0] = array('enrolled', 'enroll_admin.php', 'e'); + $tabs[1] = array('unenrolled', 'enroll_admin.php', 'u'); + //$tabs[2] = array('assistants', 'enroll_admin.php', 'a'); + $tabs[2] = array('alumni', 'enroll_admin.php', 'a'); + + return $tabs; +} + +/** +* Generates the html for the enrollment tables +* @access private +* @param string $condition the condition to be imposed in the sql query (approved = y/n/a) +* @param string $col the column to be sorted +* @param string $order the sorting order (DESC or ASC) +* @param int $unenr is one if the unenrolled list is being generated +* @author Shozub Qureshi +* @author Joel Kronenberg +*/ +function generate_table($condition, $col, $order, $unenr, $filter) { + global $db; + + if ($filter['role'] == -1) { + $condition .= ' AND CE.privileges<>0'; + } + if ($filter['group'] > 0) { + $sql = "SELECT member_id FROM ".TABLE_PREFIX."groups_members WHERE group_id=".$filter['group']; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $members_list .= ',' . $row['member_id']; + } + $condition .= ' AND CE.member_id IN (0'.$members_list.')'; + } + if (isset($filter['status'])) { + $condition .= ' AND M.status='.$filter['status']; + } + + //output list of enrolled students + $sql = "SELECT CE.member_id, CE.role, M.login, M.first_name, M.last_name, M.email, M.status + FROM ".TABLE_PREFIX."course_enrollment CE, ".TABLE_PREFIX."members M + WHERE CE.course_id=$_SESSION[course_id] AND CE.member_id=M.member_id AND ($condition) + ORDER BY $col $order"; + $result = mysql_query($sql, $db); + echo '<tbody>'; + //if table is empty display message + if (mysql_num_rows($result) == 0) { + echo '<tr><td colspan="6">'._AT('none_found').'</td></tr>'; + } else { + while ($row = mysql_fetch_assoc($result)) { + echo '<tr onmousedown="document.selectform[\'m' . $row['member_id'] . '\'].checked = !document.selectform[\'m' . $row['member_id'] . '\'].checked;">'; + echo '<td>'; + + $act = ""; + if ($row['member_id'] == $_SESSION['member_id']) { + $act = 'disabled="disabled"'; + } + + echo '<input type="checkbox" name="id[]" value="'.$row['member_id'].'" id="m'.$row['member_id'].'" ' . $act . ' onmouseup="this.checked=!this.checked" title="'.AT_print($row['login'], 'members.login').'" />'; + echo AT_print($row['login'], 'members.login') . '</td>'; + echo '<td>' . AT_print($row['first_name'], 'members.name') . '</td>'; + echo '<td>' . AT_print($row['last_name'], 'members.name') . '</td>'; + echo '<td>' . AT_print($row['email'], 'members.email') . '</td>'; + + //if role not already assigned, assign role to be student + //and we are not vieiwing list of unenrolled students + echo '<td>'; + if ($row['status'] == AT_STATUS_DISABLED) { + echo _AT('disabled'); + } else if ($row['status'] == AT_STATUS_UNCONFIRMED) { + echo _AT('unconfirmed'); + } else if ($row['role'] == '' && $unenr != 1) { + echo _AT('Student'); + } else if ($unenr == 1) { + echo _AT('na'); + } else { + echo AT_print($row['role'], 'members.role'); + } + echo '</td>'; + + echo '</tr>'; + } + } + echo '</tbody>'; +} + +/** +* Generates the html for the SORTED enrollment tables +* @access private +* @param int $curr_tab the current tab (enrolled, unenrolled or alumni) +* @author Shozub Qureshi +*/ +function display_columns ($curr_tab) { + global $orders; + global $order; +?> + <th scope="col"><input type="checkbox" value="<?php echo _AT('select_all'); ?>" id="all" title="<?php echo _AT('select_all'); ?>" name="selectall" onclick="CheckAll();" /> <a href="mods/_core/enrolment/index.php?<?php echo $orders[$order]; ?>=login<?php echo SEP;?>current_tab=<?php echo $curr_tab; ?>"><?php echo _AT('login_name'); ?></a></th> + + <th scope="col"><a href="mods/_core/enrolment/index.php?<?php echo $orders[$order]; ?>=first_name<?php echo SEP;?>current_tab=<?php echo $curr_tab; ?>"><?php echo _AT('first_name'); ?></a></th> + + <th scope="col"><a href="mods/_core/enrolmentt/index.php?<?php echo $orders[$order]; ?>=last_name<?php echo SEP;?>current_tab=<?php echo $curr_tab; ?>"><?php echo _AT('last_name'); ?></a></th> + + <th scope="col"><a href="mods/_core/enrolment/index.php?<?php echo $orders[$order]; ?>=email<?php echo SEP;?>current_tab=<?php echo $curr_tab; ?>"><?php echo _AT('email'); ?></a></th> + + <th scope="col"><a href="mods/_core/enrolment/index.php?<?php echo $orders[$order]; ?>=role<?php echo SEP;?>current_tab=<?php echo $curr_tab; ?>"><?php echo _AT('role').'/'._AT('status'); ?></a></th> +<?php +} + +?> \ No newline at end of file diff --git a/mods/_core/enrolment/html/enrollment.inc.php b/mods/_core/enrolment/html/enrollment.inc.php new file mode 100644 index 000000000..31c826dab --- /dev/null +++ b/mods/_core/enrolment/html/enrollment.inc.php @@ -0,0 +1,430 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: enrollment.inc.php 8988 2009-12-01 18:03:08Z greg $ +if (!defined('AT_INCLUDE_PATH')) { exit; } + +if (isset($_POST['enroll'])) { + + if (!$_POST['id']) { + $msg->addError('NO_STUDENT_SELECTED'); + $_GET['tab'] = $_POST['tab']; + } else { + $i=0; + foreach ($_POST['id'] as $elem) { + $text .= 'id'.$i.'='.$elem.SEP; + $i++; + } + header('Location: enroll_edit.php?'.$text.'func=enroll'.SEP.'tab=0'.SEP.'course_id='.$course_id); + exit; + } +} else if (isset($_POST['unenroll'])) { + // different from a plain delete. This removes from groups as well. + if (!$_POST['id']) { + $msg->addError('NO_STUDENT_SELECTED'); + $_GET['tab'] = $_POST['tab']; + } else { + $i=0; + foreach ($_POST['id'] as $elem) { + $text .= 'id'.$i.'='.$elem.SEP; + $i++; + } + header('Location: enroll_edit.php?'.$text.'func=unenroll'.SEP.'tab=1'.SEP.'course_id='.$course_id); + exit; + } +} else if (isset($_POST['role'])) { + if (!$_POST['id']) { + $msg->addError('NO_STUDENT_SELECTED'); + $_GET['tab'] = $_POST['tab']; + } else { + $i=0; + foreach ($_POST['id'] as $elem) { + $text .= 'mid'.$i.'='.$elem.SEP; + $i++; + } + header('Location: privileges.php?'.$text.SEP.'course_id='.$course_id); + exit; + } +} else if (isset($_POST['alumni'])) { + if (!$_POST['id']) { + $msg->addError('NO_STUDENT_SELECTED'); + $_GET['tab'] = $_POST['tab']; + } else { + $i=0; + foreach ($_POST['id'] as $elem) { + $text .= 'id'.$i.'='.$elem.SEP; + $i++; + } + header('Location: enroll_edit.php?'.$text.'func=alumni'.SEP.'tab=2'.SEP.'course_id='.$course_id); + exit; + } +} + +//filter stuff: + +if ($_GET['reset_filter']) { + unset($_GET); +} + +$filter=array(); + +if (isset($_GET['role']) && ($_GET['role'] != '')) { + $filter['role'] = intval($_GET['role']); +} + +if (isset($_GET['status']) && ($_GET['status'] != '')) { + $filter['status'] = intval($_GET['status']); +} + +if (isset($_GET['group']) && ($_GET['group'] != '')) { + $filter['group'] = intval($_GET['group']); +} + +require(AT_INCLUDE_PATH.'../mods/_core/enrolment/html/enroll_tab_functions.inc.php'); +$tabs = get_tabs(); + + +//debug( $num_tabs); +$num_tabs = count($tabs); + +for ($i=0; $i < $num_tabs; $i++) { + if (isset($_POST['button_'.$i]) && ($_POST['button_'.$i] != -1)) { + $current_tab = $i; + $_POST['current_tab'] = $i; + break; + } +} + +//get present tab if specified +if ($_GET['current_tab']) { + $current_tab = $_GET['current_tab']; + $_POST['current_tab'] = $_GET['current_tab']; +} + +$orders = array('asc' => 'desc', 'desc' => 'asc'); +$cols = array('login' => 1, 'first_name' => 1, 'second_name' => 1, 'last_name' => 1, 'email' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'login'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'login'; +} else { + // no order set + $order = 'asc'; + $col = 'login'; +} +$view_select = intval($_POST['view_select']); + +// the possible tabs. order matters. +$tabs = array('enrolled', 'assistants', 'alumni', 'pending_enrollment', 'not_enrolled'); + + +// Remove Not Enrolled tab if system preference is turned off 1.6.2 +if($_config['allow_instructor_registration'] != 1){ + array_pop($tabs); +} + +$num_tabs = count($tabs); +if (isset($_REQUEST['tab'])) { + $current_tab = intval($_REQUEST['tab']); +} + +if (!isset($current_tab)) { + $current_tab = 0; +} + +if (isset($_GET['match']) && $_GET['match'] == 'one') { + $checked_match_one = ' checked="checked"'; + $page_string .= SEP.'match=one'; +} else { + $_GET['match'] = 'all'; + $checked_match_all = ' checked="checked"'; + $page_string .= SEP.'match=all'; +} + +if (admin_authenticate(AT_ADMIN_PRIV_ENROLLMENT, TRUE)) { + $page_string .= SEP.'course_id='.$course_id; +} + +if ($_GET['search']) { + $page_string .= SEP.'search='.urlencode($_GET['search']); + $search = $addslashes($_GET['search']); + $search = explode(' ', $search); + + if ($_GET['match'] == 'all') { + $predicate = 'AND '; + } else { + $predicate = 'OR '; + } + + $sql = ''; + foreach ($search as $term) { + $term = trim($term); + $term = str_replace(array('%','_'), array('\%', '\_'), $term); + if ($term) { + $term = '%'.$term.'%'; + $sql .= "((M.first_name LIKE '$term') OR (M.second_name LIKE '$term') OR (M.last_name LIKE '$term') OR (M.email LIKE '$term') OR (M.login LIKE '$term')) $predicate"; + } + } + $sql = '('.substr($sql, 0, -strlen($predicate)).')'; + $search = $sql; +} else { + $search = '1'; +} + +$instructor_id = $system_courses[$course_id]['member_id']; +// retrieve all the members of this course (used later to get all those who aren't in this course) +$course_enrollment = get_group_concat('course_enrollment', 'member_id', "course_id=$course_id AND member_id<>$instructor_id"); +$course_enrollment .= ','.$instructor_id; + +$tab_counts = array(); +$tab_sql_counts = array(); +$tab_sql_counts[0] = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."course_enrollment CE INNER JOIN ".TABLE_PREFIX."members M USING (member_id) WHERE CE.course_id=$course_id + AND CE.approved='y' AND M.member_id<>$instructor_id AND CE.privileges=0 AND $search"; +$tab_sql_counts[1] = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."course_enrollment CE INNER JOIN ".TABLE_PREFIX."members M USING (member_id) WHERE CE.course_id=$course_id + AND CE.approved='y' AND CE.privileges>0 AND $search"; +$tab_sql_counts[2] = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."course_enrollment CE INNER JOIN ".TABLE_PREFIX."members M USING (member_id) WHERE CE.course_id=$course_id + AND approved='a' AND $search"; +$tab_sql_counts[3] = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."course_enrollment CE INNER JOIN ".TABLE_PREFIX."members M USING (member_id) WHERE CE.course_id=$course_id + AND approved='n' AND $search"; +$tab_sql_counts[4] = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."members M WHERE M.status>1 AND M.member_id NOT IN ($course_enrollment) AND $search"; + +foreach ($tab_sql_counts as $tab => $sql) { + if ($tab == 3 && $system_courses[$course_id]['access'] != 'private') { + $tab_counts[$tab] = 0; + } else { + $result = mysql_query($sql); + $row = mysql_fetch_assoc($result); + $tab_counts[$tab] = $row['cnt']; + } +} + + +if ($current_tab == 0) { + // enrolled + $sql = "SELECT CE.member_id, CE.privileges, CE.approved, M.login, M.first_name, M.second_name, M.last_name, M.email + FROM ".TABLE_PREFIX."course_enrollment CE INNER JOIN ".TABLE_PREFIX."members M USING (member_id) + WHERE CE.course_id=$course_id AND approved='y' AND M.member_id<>$instructor_id AND CE.privileges=0 AND $search + ORDER BY $col $order"; +} else if ($current_tab == 1) { + // assistants + $sql = "SELECT CE.member_id, CE.approved, CE.privileges, M.login, M.first_name, M.second_name, M.last_name, M.email + FROM ".TABLE_PREFIX."course_enrollment CE INNER JOIN ".TABLE_PREFIX."members M USING (member_id) + WHERE CE.course_id=$course_id AND CE.approved='y' AND CE.privileges>0 AND $search + ORDER BY $col $order"; + +} else if ($current_tab == 3) { + // pending + if ($system_courses[$course_id]['access'] == 'private') { + $sql = "SELECT CE.member_id, CE.approved, CE.privileges, M.login, M.first_name, M.second_name, M.last_name, M.email + FROM ".TABLE_PREFIX."course_enrollment CE INNER JOIN ".TABLE_PREFIX."members M USING (member_id) + WHERE CE.course_id=$course_id AND approved='n' AND $search + ORDER BY $col $order"; + } else { + // not sure what this is about +// $sql_cnt = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."members WHERE 0"; + $sql = "SELECT login FROM ".TABLE_PREFIX."members WHERE 0"; + } +} else if ($current_tab == 2) { + // alumni + $sql = "SELECT CE.member_id, CE.approved, CE.privileges, M.login, M.first_name, M.second_name, M.last_name, M.email + FROM ".TABLE_PREFIX."course_enrollment CE INNER JOIN ".TABLE_PREFIX."members M USING (member_id) + WHERE CE.course_id=$course_id AND approved='a' AND $search + ORDER BY $col $order"; +} else { // current_tab == 4 + +// $sql_cnt= "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."members M WHERE M.status>1 AND M.member_id NOT IN ($course_enrollment) AND $search"; + + $sql = "SELECT M.member_id, M.login, M.first_name, M.second_name, M.last_name, M.email FROM ".TABLE_PREFIX."members M WHERE M.member_id NOT IN ($course_enrollment) AND M.status>1 AND $search ORDER BY $col $order"; +} + +$results_per_page = 50; + +$num_pages = max(ceil($tab_counts[$current_tab] / $results_per_page), 1); +$page = intval($_GET['p']); +if (!$page) { + $page = 1; +} +$count = (($page-1) * $results_per_page) + 1; +$offset = ($page-1)*$results_per_page; +$sql .= " LIMIT $offset, $results_per_page"; + +$enrollment_result = mysql_query($sql, $db); +$page_string_w_tab = $page_string . SEP . 'tab='.$current_tab; +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + +<form method="get" action="<?php echo $_SERVER['PHP_SELF']; ?>"> + <input type="hidden" name="tab" value="<?php echo $current_tab; ?>"/> + <input type="hidden" name="course_id" value="<?php echo $course_id; ?>"/> + <div class="input-form"> + <fieldset class="group_form"><legend class="group_form"><?php echo _AT('search'); ?></legend> + <?php if (admin_authenticate(AT_ADMIN_PRIV_ENROLLMENT, TRUE)): ?> + <div class="row"> + <label for="course"><?php echo _AT('course'); ?></label><br/> + <select name="course_id" id="course"> + <?php + $sql = "SELECT course_id, title FROM ".TABLE_PREFIX."courses ORDER BY title"; + $result = mysql_query($sql, $db); + while ($courses_row = mysql_fetch_assoc($result)) { + if ($courses_row['course_id'] == $course_id) { + echo '<option value="'.$courses_row['course_id'].'" selected="selected">'.validate_length($courses_row['title'], 45,VALIDATE_LENGTH_FOR_DISPLAY).'</option>'; + } else { + echo '<option value="'.$courses_row['course_id'].'">'.validate_length($courses_row['title'],45,VALIDATE_LENGTH_FOR_DISPLAY).'</option>'; + } + } + ?></select> + </div> + <?php endif; ?> + + <div class="row"> + <label for="search"><?php echo _AT('search'); ?> (<?php echo _AT('login_name').', '._AT('first_name').', '._AT('second_name').', '._AT('last_name') .', '._AT('email'); ?>)</label><br /> + <input type="text" name="search" id="search" size="40" value="<?php echo htmlspecialchars($_GET['search']); ?>" /> + <br/> + <?php echo _AT('search_match'); ?>: + <input type="radio" name="match" value="all" id="match_all" <?php echo $checked_match_all; ?> /><label for="match_all"><?php echo _AT('search_all_words'); ?></label> <input type="radio" name="match" value="one" id="match_one" <?php echo $checked_match_one; ?> /><label for="match_one"><?php echo _AT('search_any_word'); ?></label> + </div> + + <div class="row buttons"> + <input type="submit" name="filter" value="<?php echo _AT('filter'); ?>" /> + <input type="submit" name="reset_filter" value="<?php echo _AT('reset_filter'); ?>" /> + </div> + </fieldset> + </div> +</form> + +<?php print_paginator($page, $tab_counts[$current_tab], $page_string_w_tab . SEP . $order .'='. $col, $results_per_page); ?> + +<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" name="selectform"> +<input type="hidden" name="tab" value="<?php echo $current_tab; ?>" /> +<input type="hidden" name="course_id" value="<?php echo $course_id; ?>"/> + +<ul id="subnavlist"> + <?php for ($i = 0; $i< $num_tabs; $i++): ?> + <?php if ($current_tab == $i): ?> + <li><a href="<?php echo $_SERVER['PHP_SELF']; ?>?tab=<?php echo $i.$page_string; ?>" class="active"><strong><?php echo _AT($tabs[$i]); ?> - <?php echo $tab_counts[$i]; ?></strong></a></li> + <?php else: ?> + <li><a href="<?php echo $_SERVER['PHP_SELF']; ?>?tab=<?php echo $i.$page_string; ?>"><?php echo _AT($tabs[$i]); ?> - <?php echo $tab_counts[$i]; ?></a></li> + <?php endif; ?> + <?php endfor; ?> +</ul> + + +<table class="data" style="width:95%;" summary="" rules="cols" > +<colgroup> + <?php if ($col == 'login'): ?> + <col /> + <col class="sort" /> + <col span="4" /> + <?php elseif($col == 'first_name'): ?> + <col span="2" /> + <col class="sort" /> + <col span="3" /> + <?php elseif($col == 'second_name'): ?> + <col span="3" /> + <col class="sort" /> + <col span="2" /> + <?php elseif($col == 'last_name'): ?> + <col span="4" /> + <col class="sort" /> + <col /> + <?php elseif($col == 'email'): ?> + <col span="5" /> + <col class="sort" /> + <?php endif; ?> +</colgroup> +<thead> +<tr> + <th scope="col" align="left"><input type="checkbox" value="<?php echo _AT('select_all'); ?>" id="all" title="<?php echo _AT('select_all'); ?>" name="selectall" onclick="CheckAll();" /></th> + + <th scope="col"><a href="<?php echo $_SERVER['PHP_SELF']; ?>?<?php echo $orders[$order]; ?>=login<?php echo $page_string_w_tab;?>"><?php echo _AT('login_name'); ?></a></th> + + <th scope="col"><a href="<?php echo $_SERVER['PHP_SELF']; ?>?<?php echo $orders[$order]; ?>=first_name<?php echo $page_string_w_tab;?>"><?php echo _AT('first_name'); ?></a></th> + + <th scope="col"><a href="<?php echo $_SERVER['PHP_SELF']; ?>?<?php echo $orders[$order]; ?>=second_name<?php echo $page_string_w_tab;?>"><?php echo _AT('second_name'); ?></a></th> + + <th scope="col"><a href="<?php echo $_SERVER['PHP_SELF']; ?>?<?php echo $orders[$order]; ?>=last_name<?php echo $page_string_w_tab;?>"><?php echo _AT('last_name'); ?></a></th> + + <th scope="col"><a href="<?php echo $_SERVER['PHP_SELF']; ?>?<?php echo $orders[$order]; ?>=email<?php echo $page_string_w_tab;?>"><?php echo _AT('email'); ?></a></th> +</tr> +</thead> +<tfoot> +<tr> + <td colspan="6"> + <?php if ($current_tab == 0): ?> + <input type="submit" name="role" value="<?php echo _AT('privileges'); ?>" /> + <input type="submit" name="unenroll" value="<?php echo _AT('remove'); ?>" /> + <input type="submit" name="alumni" value="<?php echo _AT('mark_alumni'); ?>" /> + <?php elseif ($current_tab == 1): ?> + <input type="submit" name="role" value="<?php echo _AT('privileges'); ?>" /> + <input type="submit" name="unenroll" value="<?php echo _AT('remove'); ?>" /> + + <?php elseif ($current_tab == 2): ?> + <input type="submit" name="enroll" value="<?php echo _AT('enroll'); ?>" /> + <input type="submit" name="unenroll" value="<?php echo _AT('remove'); ?>" /> + + <?php elseif ($current_tab == 3): ?> + <input type="submit" name="enroll" value="<?php echo _AT('enroll'); ?>" /> + <input type="submit" name="unenroll" value="<?php echo _AT('remove'); ?>" /> + + <?php elseif ($current_tab == 4): ?> + <input type="submit" name="enroll" value="<?php echo _AT('enroll'); ?>" /> + + <?php endif; ?></td> +</tr> +</tfoot> +<tbody> +<?php if ($tab_counts[$current_tab]): ?> + <?php while ($row = mysql_fetch_assoc($enrollment_result)): ?> + <tr onmousedown="document.selectform['m<?php echo $row['member_id']; ?>'].checked = !document.selectform['m<?php echo $row['member_id']; ?>'].checked; togglerowhighlight(this, 'm<?php echo $row['member_id']; ?>');" id="rm<?php echo $row['member_id']; ?>"> + <td><input type="checkbox" name="id[]" value="<?php echo $row['member_id']; ?>" id="m<?php echo $row['member_id']; ?>" onmouseup="this.checked=!this.checked" title="<?php echo AT_print($row['login'], 'members.login'); ?>" /></td> + <td><?php echo AT_print($row['login'], 'members.login'); ?></td> + <td><?php echo AT_print($row['first_name'], 'members.name'); ?></td> + <td><?php echo AT_print($row['second_name'], 'members.name'); ?></td> + <td><?php echo AT_print($row['last_name'], 'members.name'); ?></td> + <td><?php echo AT_print($row['email'], 'members.email'); ?></td> + </tr> + <?php endwhile; ?> +<?php else: ?> + <tr> + <td colspan="6"><?php echo _AT('none_found'); ?></td> + </tr> +<?php endif; ?> +</tbody> +</table> +</form> + +<script language="JavaScript" type="text/javascript"> +//<!-- +function CheckAll() { + for (var i=0;i<document.selectform.elements.length;i++) { + var e = document.selectform.elements[i]; + if ((e.name == 'id[]') && (e.type=='checkbox')) { + e.checked = document.selectform.selectall.checked; + togglerowhighlight(document.getElementById("r" + e.id), e.id); + } + } +} + +function togglerowhighlight(obj, boxid) { + if (document.getElementById(boxid).checked) { + obj.className = 'selected'; + } else { + obj.className = ''; + } +} +//--> +</script> +<?php require(AT_INCLUDE_PATH.'footer.inc.php'); ?> \ No newline at end of file diff --git a/mods/_core/enrolment/html/privileges.inc.php b/mods/_core/enrolment/html/privileges.inc.php new file mode 100644 index 000000000..e589d0e59 --- /dev/null +++ b/mods/_core/enrolment/html/privileges.inc.php @@ -0,0 +1,159 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* 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. */ +/************************************************************************/ +// $Id: enroll_edit.php 6662 2006-11-20 15:52:49Z joel $ +if (!defined('AT_INCLUDE_PATH')) { exit; } + +$num_cols = 2; + +//if user wants to cancel action +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php?course_id='.$course_id); + exit; +} else if (isset($_POST['submit'])) { + + //update privileges + $mid = $_POST['dmid']; + $privs = $_POST['privs']; + $role = $_POST['role']; + + //loop through selected users to perform update + $i=0; + while ($mid[$i]) { + change_privs(intval($mid[$i]), $privs[$i]); + $i++; + } + + $msg->addFeedback('PRIVS_CHANGED'); + header('Location: index.php?tab=1'.SEP.'course_id='.$course_id); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + +<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post"> +<input type="hidden" name="course_id" value="<?php echo $course_id; ?>"/> +<div class="input-form"> +<?php + //Store id's into a hidden element for use by functions + $j = 0; + while ($_GET['mid'.$j]) { + echo '<input type="hidden" name="dmid[]" value="'.$_GET['mid'.$j].'" />'; + $j++; + } + + //loop through all the students +for ($k = 0; $k < $j; $k++) { + $mem_id = intval($_GET['mid'.$k]); + + //NO!!! extra check to ensure that user doesnt send in instructor for change privs + $sql = "SELECT CE.privileges, M.login FROM ".TABLE_PREFIX."course_enrollment CE INNER JOIN ".TABLE_PREFIX."members M USING (member_id) WHERE M.member_id=$mem_id AND CE.course_id=$course_id AND CE.approved='y'"; + + $result = mysql_query($sql, $db); + $student_row = mysql_fetch_assoc($result); +?> + <div class="row"> + <h3><?php echo $student_row['login']; ?></h3> + </div> + + <div class="row"> + <?php echo _AT('privileges'); ?><br /> + <table width="100%" border="0" cellspacing="5" cellpadding="0" summary=""> + <tr> + <?php + $count =0; + $student_row['privileges'] = intval($student_row['privileges']); + $module_list = $moduleFactory->getModules(AT_MODULE_STATUS_ENABLED, 0, TRUE); + $keys = array_keys($module_list); + foreach ($keys as $module_name) { + $module =& $module_list[$module_name]; + if (!($module->getPrivilege() > 1)) { + continue; + } + $count++; + echo '<td><label><input type="checkbox" name="privs['.$k.'][]" value="'.$module->getPrivilege().'" '; + + if (query_bit($student_row['privileges'], $module->getPrivilege())) { + echo 'checked="checked"'; + } + + echo ' />'.$module->getName().'</label></td>'; + + if (!($count % $num_cols)) { + echo '</tr><tr>'; + } + } + if ($count % $num_cols) { + echo '<td colspan="'.($num_cols-($count % $num_cols)).'"> </td>'; + } else { + echo '<td colspan="'.$num_cols.'"> </td>'; + } + ?> + </tr> + </table> + </div> +<?php + }//end for +?> + <div class="row buttons"> + <input type="submit" name="submit" value="<?php echo _AT('save'); ?>" accesskey="s" /> + <input type="submit" name="cancel" value="<?php echo _AT('cancel'); ?>" /> + </div> +</div> +</form> + +<?php + +/** +* Updates the Role & Priviliges of users +* @access private +* @param int $member The member_id of the user whose values are to be updated +* @param int $privs value of the privileges of the user +* @author Joel Kronenberg +*/ +function change_privs ($member, $privs) { + global $db, $course_id; + + //calculate privileges + $privilege = 0; + if (!(empty($privs))) { + foreach ($privs as $priv) { + $privilege += intval($priv); + } + } + + /* + * if we're making a student a GROUP TA then we have to remove them + * from all the groups they may belong to. + */ + if (query_bit($privilege, AT_PRIV_GROUPS)) { + $group_list = implode(',', $_SESSION['groups']); + if ($group_list) { + $sql = "DELETE FROM ".TABLE_PREFIX."groups_members WHERE group_id IN ($group_list) AND member_id=$member"; + $result = mysql_query($sql,$db); + } + } + + $sql = "UPDATE ".TABLE_PREFIX."course_enrollment SET `privileges`=$privilege WHERE member_id=$member AND course_id=$course_id AND `approved`='y'"; + $result = mysql_query($sql,$db); + + + //print error or confirm change + if (!$result) { + $msg->printErrors('DB_NOT_UPDATED'); + exit; + } +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); ?> \ No newline at end of file diff --git a/mods/_core/enrolment/import_course_list.php b/mods/_core/enrolment/import_course_list.php new file mode 100644 index 000000000..62ea74476 --- /dev/null +++ b/mods/_core/enrolment/import_course_list.php @@ -0,0 +1,50 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* 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. */ +/************************************************************************/ +// $Id: import_course_list.php 7482 2008-05-06 17:44:49Z greg $ + +define('AT_INCLUDE_PATH', '../../../include/'); +require (AT_INCLUDE_PATH.'vitals.inc.php'); +authenticate(AT_PRIV_ENROLLMENT); + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> +<form enctype="multipart/form-data" action="mods/_core/enrolment/verify_list.php" method="post"> +<input type="hidden" name="from" value="import" /> +<input type="hidden" name="MAX_FILE_SIZE" value="100000" /> + +<div class="input-form"> + <fieldset class="group_form"><legend class="group_form"><?php echo _AT('import'); ?></legend> + <div class="row"> + <p><?php echo _AT('list_import_howto'); ?></p> + </div> + + <div class="row"> + <label for="sep_choice"><?php echo _AT('import_sep_txt'); ?></label><br /> + <input type="radio" name="sep_choice" id="und" value="_" checked="checked" /> + <label for="und"><?php echo _AT('underscore'); ?></label> + <input type="radio" name="sep_choice" id="per" value="." /> + <label for="per"><?php echo _AT('period'); ?></label> + </div> + + <div class="row"> + <span class="required" title="<?php echo _AT('required_field'); ?>">*</span><label for="course_list"><?php echo _AT('list_import_course_list'); ?></label><br /> + <input type="file" name="file" id="course_list" /> + </div> + + <div class="row buttons"> + <input type="submit" name="submit" value="<?php echo _AT('list_import_course_list'); ?>" /> + </div> + </fieldset> +</div> +</form> + +<?php require(AT_INCLUDE_PATH.'footer.inc.php'); ?> \ No newline at end of file diff --git a/mods/_core/enrolment/index.php b/mods/_core/enrolment/index.php new file mode 100644 index 000000000..a3944a169 --- /dev/null +++ b/mods/_core/enrolment/index.php @@ -0,0 +1,28 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: index.php 7208 2008-01-09 16:07:24Z greg $ +define('AT_INCLUDE_PATH', '../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); + +if (!authenticate(AT_PRIV_ENROLLMENT, AT_PRIV_RETURN)) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('NOT_OWNER'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$course_id = $_SESSION['course_id']; + +require(AT_INCLUDE_PATH.'../mods/_core/enrolment/html/enrollment.inc.php'); +exit; +?> \ No newline at end of file diff --git a/mods/_core/enrolment/lib/enroll.inc.php b/mods/_core/enrolment/lib/enroll.inc.php new file mode 100644 index 000000000..d71b50353 --- /dev/null +++ b/mods/_core/enrolment/lib/enroll.inc.php @@ -0,0 +1,261 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/************************************************************************/ +// $Id: enroll.inc.php 8901 2009-11-11 19:10:19Z cindy $ + +function checkUserInfo($record) { + global $db, $addslashes; + static $email_list; + + if (empty($record['remove'])) { + $record['remove'] = FALSE; + } + + //error flags for this record + $record['err_email'] = FALSE; + $record['err_uname'] = FALSE; + $record['exists'] = FALSE; + + $record['email'] = trim($record['email']); + + /* email check */ + if ($record['email'] == '') { + $record['err_email'] = _AT('import_err_email_missing'); + } else if (!preg_match("/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,6}$/i", $record['email'])) { + $record['err_email'] = _AT('import_err_email_invalid'); + } else if (isset($email_list[$record['email']])) { + $record['err_email'] = _AT('import_err_email_exists'); + } else { + $record['email'] = $addslashes($record['email']); + + $sql="SELECT * FROM ".TABLE_PREFIX."members WHERE email LIKE '$record[email]'"; + $result = mysql_query($sql,$db); + if (mysql_num_rows($result) != 0) { + $row = mysql_fetch_assoc($result); + $record['exists'] = _AT('import_err_email_exists'); + $record['fname'] = $row['first_name']; + $record['lname'] = $row['last_name']; + $record['email'] = $row['email']; + $record['uname'] = $row['login']; + $record['status'] = $row['status']; + } else { + // it's good, add it to the list + $email_list[$record['email']] = true; + } + } + + /* username check */ + if (empty($record['uname'])) { + $record['uname'] = stripslashes (strtolower (substr ($record['fname'], 0, 1).$_POST['sep_choice'].$record['lname'])); + } + + $record['uname'] = preg_replace("{[^a-zA-Z0-9._-]}","", trim($record['uname'])); + + if (!(preg_match("/^[a-zA-Z0-9._-]([a-zA-Z0-9._-])*$/i", $record['uname']))) { + $record['err_uname'] = _AT('import_err_username_invalid'); + } + + if (isset($record['status']) && $record['status'] == AT_STATUS_DISABLED) { + $record['err_disabled'] = true; + } else { + $record['err_disabled'] = false; + } + + $record['uname'] = $addslashes($record['uname']); + $record['fname'] = $addslashes($record['fname']); + $record['lname'] = $addslashes($record['lname']); + + $sql = "SELECT member_id FROM ".TABLE_PREFIX."members WHERE login='$record[uname]'"; + $result = mysql_query($sql,$db); + if ((mysql_num_rows($result) != 0) && !$record['exists']) { + $record['err_uname'] = _AT('import_err_username_exists'); + } else { + $result = mysql_query("SELECT * FROM ".TABLE_PREFIX."admins WHERE login='$record[uname]'",$db); + if (mysql_num_rows($result) != 0) { + $record['err_uname'] = _AT('import_err_username_exists'); + } + } + + $sql = "SELECT member_id FROM ".TABLE_PREFIX."members WHERE first_name='$record[fname]' AND last_name='$record[lname]' LIMIT 1"; + $result = mysql_query($sql,$db); + if ((mysql_num_rows($result) != 0) && !$record['exists']) { + $record['err_uname'] = _AT('import_err_full_name_exists'); + } + + /* removed record? */ + if ($record['remove']) { + //unset errors + $record['err_email'] = ''; + $record['err_uname'] = ''; + $record['err_disabled'] = ''; + } + + $record['fname'] = htmlspecialchars(stripslashes(trim($record['fname']))); + $record['lname'] = htmlspecialchars(stripslashes(trim($record['lname']))); + $record['email'] = htmlspecialchars(stripslashes(trim($record['email']))); + $record['uname'] = htmlspecialchars(stripslashes(trim($record['uname']))); + + return $record; +} + +function add_users($user_list, $enroll, $course) { + global $db; + global $msg; + global $_config; + global $addslashes; + + require_once(AT_INCLUDE_PATH.'classes/phpmailer/atutormailer.class.php'); + + if (defined('AT_EMAIL_CONFIRMATION') && AT_EMAIL_CONFIRMATION) { + $status = AT_STATUS_UNCONFIRMED; + } else { + $status = AT_STATUS_STUDENT; + } + + + foreach ($user_list as $student) { + if (!$student['remove']) { + $student['uname'] = $addslashes($student['uname']); + $student['email'] = $addslashes($student['email']); + $student['fname'] = $addslashes($student['fname']); + $student['lname'] = $addslashes($student['lname']); + + if (!$student['exists']) { + $sql = "INSERT INTO ".TABLE_PREFIX."members + (login, + password, + email, + first_name, + last_name, + gender, + status, + preferences, + creation_date, + language, + inbox_notify, + private_email) + VALUES + ('$student[uname]', + '". sha1($student[uname]). "', + '$student[email]', + '$student[fname]', + '$student[lname]', + 'n', + $status, + '$_config[pref_defaults]', + NOW(), + '$_config[default_language]', + $_config[pref_inbox_notify], + 1)"; + + $result = mysql_query($sql, $db); + if (mysql_affected_rows($db) == 1) { + $m_id = mysql_insert_id($db); + + $student['exists'] = _AT('import_err_email_exists'); + + $sql = "INSERT INTO ".TABLE_PREFIX."course_enrollment (member_id, course_id, approved, last_cid) VALUES ($m_id, $course, '$enroll', 0)"; + + if ($result = mysql_query($sql,$db)) { + $enrolled_list .= '<li>' . $student['uname'] . '</li>'; + + if (defined('AT_EMAIL_CONFIRMATION') && AT_EMAIL_CONFIRMATION) { + + $sql = "SELECT email, creation_date FROM ".TABLE_PREFIX."members WHERE member_id=$m_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $code = substr(md5($row['email'] . $row['creation_date'] . $m_id), 0, 10); + + // send email here. + $confirmation_link = AT_BASE_HREF . 'confirm.php?id='.$m_id.SEP.'m='.$code; + + $subject = $_config['site_name'].': '._AT('email_confirmation_subject'); + $body = _AT(array('new_account_enroll_confirm', $_SESSION['course_title'], $confirmation_link))."\n\n"; + } else { + $subject = $_config['site_name'].': '._AT('account_information'); + $body = _AT(array('new_account_enroll',AT_BASE_HREF, $_SESSION['course_title']))."\n\n"; + } + + //$body .= SITE_NAME.': '._AT('account_information')."\n"; + $body .= _AT('web_site') .' : '.AT_BASE_HREF."\n"; + $body .= _AT('login_name') .' : '.$student['uname'] . "\n"; + $body .= _AT('password') .' : '.$student['uname'] . "\n"; + + $mail = new ATutorMailer; + $mail->From = $_config['contact_email']; + $mail->AddAddress($student['email']); + $mail->Subject = $subject; + $mail->Body = $body; + $mail->Send(); + + unset($mail); + } else { + $already_enrolled .= '<li>' . $student['uname'] . '</li>'; + } + } else { + //$msg->addError('LIST_IMPORT_FAILED'); + } + } else if (! $student['err_disabled']) { + $sql = "SELECT member_id FROM ".TABLE_PREFIX."members WHERE email='$student[email]'"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + + $m_id = $row['member_id']; + + $sql = "INSERT INTO ".TABLE_PREFIX."course_enrollment (member_id, course_id, approved, last_cid, role) VALUES ($m_id, $course, '$enroll', 0, '$role')"; + + if($result = mysql_query($sql,$db)) { + $enrolled_list .= '<li>' . $student['uname'] . '</li>'; + } else { + $sql = "REPLACE INTO ".TABLE_PREFIX."course_enrollment (member_id, course_id, approved, last_cid, role) VALUES ($m_id, $course, '$enroll', 0, '$role')"; + $result = mysql_query($sql,$db); + $enrolled_list .= '<li>' . $student['uname'] . '</li>'; + } + $subject = $_config['site_name'].': '._AT('email_confirmation_subject'); + $body = _AT(array('enrol_message_approved',$_SESSION['course_title'],AT_BASE_HREF))."\n\n"; + $body .= _AT('web_site') .' : '.AT_BASE_HREF."\n"; + $body .= _AT('login_name') .' : '.$student['uname'] . "\n"; + $mail = new ATutorMailer; + $mail->From = $_config['contact_email']; + $mail->AddAddress($student['email']); + $mail->Subject = $subject; + $mail->Body = $body; + $mail->Send(); + + unset($mail); + + + } + + + + + } else if ($student['err_disabled']) { + $not_enrolled_list .= '<li>' . $student['uname'] . '</li>'; + } + } + } + if ($already_enrolled) { + $feedback = array('ALREADY_ENROLLED', $already_enrolled); + $msg->addFeedback($feedback); + } + if ($enrolled_list) { + $feedback = array('ENROLLED', $enrolled_list); + $msg->addFeedback($feedback); + } + if ($not_enrolled_list) { + $feedback = array('NOT_ENROLLED', $not_enrolled_list); + $msg->addFeedback($feedback); + } +} + +?> \ No newline at end of file diff --git a/mods/_core/enrolment/module.php b/mods/_core/enrolment/module.php new file mode 100644 index 000000000..44e80a393 --- /dev/null +++ b/mods/_core/enrolment/module.php @@ -0,0 +1,54 @@ +<?php +if (!defined('AT_INCLUDE_PATH')) { exit; } +if (!isset($this) || (isset($this) && (strtolower(get_class($this)) != 'module'))) { exit(__FILE__ . ' is not a Module'); } + +define('AT_PRIV_ENROLLMENT', $this->getPrivilege()); +define('AT_ADMIN_PRIV_ENROLLMENT', $this->getAdminPrivilege()); + +$this->_stacks['users_online'] = array('title_var'=>'users_online', 'file'=>AT_INCLUDE_PATH.'html/dropdowns/users_online.inc.php'); + +if (admin_authenticate(AT_ADMIN_PRIV_ENROLLMENT, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + //$this->_pages[AT_NAV_ADMIN] = array('mods/_core/enrolment/admin/index.php'); + $this->_pages['mods/_core/enrolment/admin/index.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + + $this->_pages['mods/_core/enrolment/admin/index.php']['title_var'] = 'enrollment'; + $this->_pages['mods/_core/enrolment/admin/index.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + $this->_pages['mods/_core/enrolment/admin/index.php']['guide'] = 'admin/?p=enrollment.php'; + + $this->_pages['mods/_core/enrolment/admin/enroll_edit.php']['title_var'] = 'enrollment'; + $this->_pages['mods/_core/enrolment/admin/enroll_edit.php']['parent'] = 'mods/_core/enrolment/admin/index.php'; + + $this->_pages['mods/_core/enrolment/admin/privileges.php']['title_var'] = 'privileges'; + $this->_pages['mods/_core/enrolment/admin/privileges.php']['parent'] = 'mods/_core/enrolment/admin/index.php'; + $this->_pages['mods/_core/enrolment/admin/privileges.php']['guide'] = 'admin/?p=enrollment_privileges.php'; + + // linked from users.php + $this->_pages['admin/user_enrollment.php']['title_var'] = 'enrollment'; + $this->_pages['admin/user_enrollment.php']['parent'] = 'admin/users.php'; +} + +$this->_pages['mods/_core/enrolment/index.php']['title_var'] = 'enrollment'; +$this->_pages['mods/_core/enrolment/index.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_core/enrolment/index.php']['guide'] = 'instructor/?p=enrollment.php'; +$this->_pages['mods/_core/enrolment/index.php']['children'] = array('mods/_core/enrolment/export_course_list.php', 'mods/_core/enrolment/import_course_list.php', 'mods/_core/enrolment/create_course_list.php'); + + $this->_pages['mods/_core/enrolment/export_course_list.php']['title_var'] = 'list_export_course_list'; + $this->_pages['mods/_core/enrolment/export_course_list.php']['parent'] = 'mods/_core/enrolment/index.php'; + + $this->_pages['mods/_core/enrolment/import_course_list.php']['title_var'] = 'list_import_course_list'; + $this->_pages['mods/_core/enrolment/import_course_list.php']['parent'] = 'mods/_core/enrolment/index.php'; + + $this->_pages['mods/_core/enrolment/create_course_list.php']['title_var'] = 'list_create_course_list'; + $this->_pages['mods/_core/enrolment/create_course_list.php']['parent'] = 'mods/_core/enrolment/index.php'; + + $this->_pages['mods/_core/enrolment/verify_list.php']['title_var'] = 'course_list'; + $this->_pages['mods/_core/enrolment/verify_list.php']['parent'] = 'mods/_core/enrolment/index.php'; + + $this->_pages['mods/_core/enrolment/privileges.php']['title_var'] = 'privileges'; + $this->_pages['mods/_core/enrolment/privileges.php']['parent'] = 'mods/_core/enrolment/index.php'; + $this->_pages['mods/_core/enrolment/privileges.php']['guide'] = 'instructor/?p=enrollment_privileges.php'; + + $this->_pages['mods/_core/enrolment/enroll_edit.php']['title_var'] = 'enrollment'; + $this->_pages['mods/_core/enrolment/enroll_edit.php']['parent'] = 'mods/_core/enrolment/index.php'; + +?> \ No newline at end of file diff --git a/mods/_core/enrolment/module.xml b/mods/_core/enrolment/module.xml new file mode 100644 index 000000000..617b7e416 --- /dev/null +++ b/mods/_core/enrolment/module.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<module version="0.1"> + <name lang="en">Enrollment</name> + <description lang="en">Instructors can manage those enrolled in a course and set individual privileges.</description> + <maintainers> + <maintainer> + <name>ATutor Team</name> + <email>info@atutor.ca</email> + </maintainer> + </maintainers> + <url>http://atutor.ca</url> + <license>GPL</license> + <release> + <version>0.1</version> + <privileges> + <instructor_privilege>create</instructor_privilege> + <admin_privilege></admin_privilege> + </privileges> + <date>2005-08-25</date> + <state>stable</state> + <notes>This is a core module.</notes> + </release> +</module> \ No newline at end of file diff --git a/mods/_core/enrolment/module_delete.php b/mods/_core/enrolment/module_delete.php new file mode 100644 index 000000000..237f5ec1d --- /dev/null +++ b/mods/_core/enrolment/module_delete.php @@ -0,0 +1,13 @@ +<?php + +function enrolment_delete($course) { + global $db; + + $sql = "DELETE FROM ".TABLE_PREFIX."course_enrollment WHERE course_id=$course"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."auto_enroll_courses WHERE course_id=$course"; + $result = mysql_query($sql, $db); +} + +?> \ No newline at end of file diff --git a/mods/_core/enrolment/privileges.php b/mods/_core/enrolment/privileges.php new file mode 100644 index 000000000..2aeefd261 --- /dev/null +++ b/mods/_core/enrolment/privileges.php @@ -0,0 +1,27 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/************************************************************************/ +// $Id: privileges.php 7208 2008-01-09 16:07:24Z greg $ +define('AT_INCLUDE_PATH', '../../../include/'); +require (AT_INCLUDE_PATH.'vitals.inc.php'); + +if (!authenticate(AT_PRIV_ADMIN, true)) { + $msg->addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; +} + +$course_id = $_SESSION['course_id']; + +require(AT_INCLUDE_PATH.'../mods/_core/enrolment/html/privileges.inc.php'); +exit; +?> \ No newline at end of file diff --git a/mods/_core/enrolment/verify_list.php b/mods/_core/enrolment/verify_list.php new file mode 100644 index 000000000..2062589d4 --- /dev/null +++ b/mods/_core/enrolment/verify_list.php @@ -0,0 +1,223 @@ +<?php +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/************************************************************************/ +// $Id: verify_list.php 7208 2008-01-09 16:07:24Z greg $ + +define('AT_INCLUDE_PATH', '../../../include/'); +require (AT_INCLUDE_PATH.'vitals.inc.php'); +authenticate(AT_PRIV_ENROLLMENT); +require(AT_INCLUDE_PATH.'../mods/_core/enrolment/lib/enroll.inc.php'); + +/************ GETTING INFO FROM CREATE/IMPORT CALLS **********/ +if (isset($_POST['addmore'])) { + //$msg->addFeedback('ADDMORE'); + header('Location: create_course_list.php'); + exit; +} else if (isset($_POST['return'])) { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} else if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit']) && !$_POST['verify']) { + //CREATE COURSE LIST!!!!!! + if ($_POST['from'] == 'create') { + if (empty($_POST['first_name1']) && empty($_POST['last_name1']) && empty($_POST['email1'])) { + $msg->addError('INCOMPLETE'); + header('Location: ./create_course_list.php'); + exit; + } else { + $j=1; + while ($_POST['first_name'.$j] || $_POST['last_name'.$j] || $_POST['email'.$j]) { + $students[] = checkUserInfo(array('fname' => $_POST['first_name'.$j], 'lname' => $_POST['last_name'.$j], 'email' => $_POST['email'.$j])); + $j++; + } + } + } + //IMPORT COURSE LIST!!!!!! + else if ($_POST['from'] == 'import') { + if ($_FILES['file']['size'] < 1) { + $msg->addError('FILE_EMPTY'); + header('Location: ./import_course_list.php'); + exit; + } else { + $fp = fopen($_FILES['file']['tmp_name'],'r'); + $line_number=0; + while ($data = fgetcsv($fp, 100000, ',')) { + $line_number++; + $num_fields = count($data); + if ($num_fields == 3) { + $students[] = checkUserInfo(array('fname' => $data[0], 'lname' => $data[1], 'email' => $data[2])); + } else if ($num_fields != 1) { + $errors = array('INCORRECT_FILE_FORMAT', $line_number); + $msg->addError($errors); + header('Location: ./import_course_list.php'); + exit; + } else if (($num_fields == 1) && (trim($data[0]) != '')) { + $errors = array('INCORRECT_FILE_FORMAT', $line_number); + $msg->addError($errors); + header('Location: ./import_course_list.php'); + exit; + } + } + } + + } +} +/************* INFO GATHERED **************/ + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +if ($_POST['verify']) { + for ($i=0; $i<$_POST['count']; $i++) { + $info = array('fname' => $_POST['fname'.$i], 'lname' => $_POST['lname'.$i], 'email' => $_POST['email'.$i], 'uname' => $_POST['uname'.$i], 'remove' => $_POST['remove'.$i]); + $students[] = checkUserInfo($info); + + if (!empty($students[$i]['err_email']) || !empty($students[$i]['err_uname'])) { + $still_errors = TRUE; + } + } + + /**************************************************************************/ + // !!!!!!STEP 3 - INSERT INTO DB !!!!!!! + if (!$still_errors && (isset($_POST['submit_unenr']) || isset($_POST['submit_enr']))) { + + $enroll = 'y'; + if (isset($_POST['submit_unenr'])) { + $enroll = 'n'; + } + + add_users($students, $enroll, $_SESSION['course_id']); + + + ?> + <div id="container"> + <form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" name="finalform" /> + <div class="input-form"> + <?php + $msg->printFeedbacks(); + ?> + <div class="row buttons"> + <input type="submit" name="addmore" value="<?php echo _AT('add_more'); ?>" /> + <input type="submit" name="return" value="<?php echo _AT('done'); ?>" /> + </div> + </div> + </form></div><?php + } + +} + +// STEP 2 - INTERNAL VERIFICATION +if ($still_errors || !isset($_POST['verify']) || isset($_POST['resubmit'])) { ?> + + <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post"> + <input type="hidden" name="verify" value="1" /> + <input type="hidden" name="count" value="<?php echo count($students); ?>" /> + + <table class="data static" summary="" rules="cols"> + <thead> + <tr> + <th scope="col"><?php echo _AT('status'); ?></th> + <th scope="col"><?php echo _AT('first_name'); ?></th> + <th scope="col"><?php echo _AT('last_name'); ?></th> + <th scope="col"><?php echo _AT('email'); ?></th> + <th scope="col"><?php echo _AT('login_name'); ?></th> + <th scope="col"><?php echo _AT('remove'); ?></th> + </tr> + </thead><?php + + $err_count = 0; + $i=0; + if (is_array($students)) { + echo '<tbody>'; + foreach ($students as $student) { + echo '<tr><small>'; + echo '<td><span style="color: red;">'; + + //give status + if(!empty($student['err_email'])) { + echo $student['err_email']; + } + + if(!empty($student['err_uname'])) { + if(!empty($student['err_email'])) { + echo '<br />'; + } + echo $student['err_uname']; + } + if (empty($student['err_uname']) && empty($student['err_email'])) { + + if ($student['remove']) { + echo '</span><span style="color: purple;">'._AT('removed'); + } else if ($student['err_disabled']) { + echo '</span><span style="color: purple;">'._AT('disabled'); + } else if (!empty($student['exists'])) { + echo '</span><span style="color: green;">'._AT('ok').' - '.$student['exists']; + } else { + echo '</span><span style="color: green;">'._AT('ok'); + } + } else { + $err_count++; + } + echo '</span></td>'; + + if (empty($student['exists'])) { + echo '<td><input type="text" name="fname'.$i.'" value="'.$student['fname'].'" /></td>'; + echo '<td><input type="text" name="lname'.$i.'" value="'.$student['lname'].'" /></td>'; + echo '<td><input type="text" name="email'.$i.'" value="'.$student['email'].'" /></td>'; + echo '<td><input type="text" name="uname'.$i.'" value="'.$student['uname'].'" />'; + echo '<td><input type="checkbox" '; + echo ($student['remove'] ? 'checked="checked" value="on"' : ''); + echo 'name="remove'.$i.'" />'; + } else { + echo '<input type="hidden" name="fname'.$i.'" value="'.$student['fname'].'" />'; + echo '<input type="hidden" name="lname'.$i.'" value="'.$student['lname'].'" />'; + echo '<input type="hidden" name="email'.$i.'" value="'.$student['email'].'" />'; + echo '<input type="hidden" name="uname'.$i.'" value="'.$student['uname'].'" />'; + + echo '<td>'.AT_print($student['fname'], 'members.first_name').'</td>'; + echo '<td>'.AT_print($student['lname'], 'members.last_name').'</td>'; + echo '<td>'.AT_print($student['email'], 'members.email').'</td>'; + echo '<td>'.AT_print($student['uname'], 'members.login').'</td>'; + echo '<td><input type="checkbox" '; + echo ($student['remove'] ? 'checked="checked" value="on"' : ''); + echo 'name="remove'.$i.'" />'; + } + $i++; + echo '</tr>'; + } + echo '</tbody>'; + } + + $dsbld = ''; + if ($still_errors || $err_count>0) { + $dsbld = 'disabled="disabled"'; + } ?> + + <tfoot> + <tr> + <td colspan="6"> + <input type="submit" name="resubmit" value="<?php echo _AT('resubmit'); ?>" /> + <input type="submit" name="submit_enr" value="<?php echo _AT('list_add_enrolled_list'); ?>" <?php echo $dsbld; ?> /> + </td> + </tr> + </tfoot> + + </table> + </form><?php +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/file_manager/delete.php b/mods/_core/file_manager/delete.php new file mode 100644 index 000000000..e61d3296b --- /dev/null +++ b/mods/_core/file_manager/delete.php @@ -0,0 +1,185 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ + +define('AT_INCLUDE_PATH', '../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); +require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + +global $db; + +if (!authenticate(AT_PRIV_FILES,AT_PRIV_RETURN)) { + authenticate(AT_PRIV_CONTENT); +} + +$current_path = AT_CONTENT_DIR.$_SESSION['course_id'].'/'; + +$popup = $_REQUEST['popup']; +$framed = $_REQUEST['framed']; + +if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; +} + +if (isset($_POST['submit_yes'])) { + /* delete files and directories */ + /* delete the file */ + $pathext = $_POST['pathext']; + if (isset($_POST['listoffiles'])) { + $checkbox = explode(',',$_POST['listoffiles']); + $count = count($checkbox); + $result=true; + for ($i=0; $i<$count; $i++) { + $filename=$checkbox[$i]; + + if (course_realpath($current_path . $pathext . $filename) == FALSE) { + $msg->addError('FILE_NOT_DELETED'); + $result=false; + break; + } else if (!(@unlink($current_path.$pathext.$filename))) { + $msg->addError('FILE_NOT_DELETED'); + $result=false; + break; + } + } + if ($result) + { + // delete according definition of primary resources and alternatives for adapted content + $filename = '../'.$pathext.$filename; + + // 1. delete secondary resources types + $sql = "DELETE FROM ".TABLE_PREFIX."secondary_resources_types + WHERE secondary_resource_id in (SELECT secondary_resource_id + FROM ".TABLE_PREFIX."secondary_resources + WHERE secondary_resource = '".$filename."' + OR primary_resource_id in (SELECT primary_resource_id + FROM ".TABLE_PREFIX."primary_resources + WHERE resource='".$filename."'))"; + $result = mysql_query($sql, $db); + + // 2. delete secondary resources + $sql = "DELETE FROM ".TABLE_PREFIX."secondary_resources + WHERE secondary_resource = '".$filename."' + OR primary_resource_id in (SELECT primary_resource_id + FROM ".TABLE_PREFIX."primary_resources + WHERE resource='".$filename."')"; + $result = mysql_query($sql, $db); + + // 3. delete primary resources types + $sql = "DELETE FROM ".TABLE_PREFIX."primary_resources_types + WHERE primary_resource_id in (SELECT primary_resource_id + FROM ".TABLE_PREFIX."primary_resources + WHERE resource = '".$filename."')"; + $result = mysql_query($sql, $db); + + // 4. delete primary resources + $sql = "DELETE FROM ".TABLE_PREFIX."primary_resources + WHERE resource = '".$filename."'"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + } + /* delete directory */ + if (isset($_POST['listofdirs'])) { + + $checkbox = explode(',',$_POST['listofdirs']); + $count = count($checkbox); + $result=true; + for ($i=0; $i<$count; $i++) { + $filename=$checkbox[$i]; + + if (strpos($filename, '..') !== false) { + $msg->addError('UNKNOWN'); + $result=false; + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } else if (!is_dir($current_path.$pathext.$filename)) { + $msg->addError(array('DIR_NOT_DELETED',$filename)); + $result=false; + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } else if (!($result = clr_dir($current_path.$pathext.$filename))) { + $msg->addError('DIR_NO_PERMISSION'); + $result=false; + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } + } + if ($result) + $msg->addFeedback('DIR_DELETED'); + } + + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; +} + + require(AT_INCLUDE_PATH.'header.inc.php'); + // find the files and directories to be deleted + $total_list = explode(',', $_GET['list']); + $pathext = $_GET['pathext']; + $popup = $_GET['popup']; + $framed = $_GET['framed']; + $cp = $_GET['cp']; + $cid = $_GET['cid']; + $pid = $_GET['pid']; + $a_type = $_GET['a_type']; + + $count = count($total_list); + $countd = 0; + $countf = 0; + + foreach ($total_list as $list_item) { + if (is_dir($current_path.$pathext.$list_item)) { + $_dirs[$countd] = $list_item; + $countd++; + } else { + $_files[$countf] = $list_item; + $countf++; + } + } + + $hidden_vars['pathext'] = $pathext; + $hidden_vars['popup'] = $popup; + $hidden_vars['framed'] = $framed; + $hidden_vars['cp'] = $cp; + $hidden_vars['cid'] = $cid; + $hidden_vars['pid'] = $pid; + $hidden_vars['a_type'] = $a_type; + + if (isset($_files)) { + $list_of_files = implode(',', $_files); + $hidden_vars['listoffiles'] = $list_of_files; + + foreach ($_files as $file) { + $file_list_to_print .= '<li>'.$file.'</li>'; + } + $msg->addConfirm(array('FILE_DELETE', $file_list_to_print), $hidden_vars); + } + + if (isset($_dirs)) { + $list_of_dirs = implode(',', $_dirs); + $hidden_vars['listofdirs'] = $list_of_dirs; + + foreach ($_dirs as $dir) { + $dir_list_to_print .= '<li>'.$dir.'</li>'; + } + + $msg->addConfirm(array('DIR_DELETE',$dir_list_to_print), $hidden_vars); + } + + $msg->printConfirm(); + + require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/file_manager/edit.php b/mods/_core/file_manager/edit.php new file mode 100644 index 000000000..2b9d63ab2 --- /dev/null +++ b/mods/_core/file_manager/edit.php @@ -0,0 +1,146 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: edit.php 8237 2008-11-18 16:42:18Z hwong $ + +define('AT_INCLUDE_PATH', '../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); +require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + + +if (!authenticate(AT_PRIV_FILES,AT_PRIV_RETURN)) { + authenticate(AT_PRIV_CONTENT); +} + +$current_path = AT_CONTENT_DIR.$_SESSION['course_id'].'/'; + +$popup = $_REQUEST['popup']; +$framed = $_REQUEST['framed']; +$file = $_REQUEST['file']; +$pathext = $_REQUEST['pathext']; + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup']); + exit; +} + +if (isset($_POST['save'])) { + $content = str_replace("\r\n", "\n", $stripslashes($_POST['body_text'])); + $file = $_POST['file']; + + if (course_realpath($current_path . $pathext . $file) == FALSE) { + $msg->addError('FILE_NOT_SAVED'); + } else { + if (($f = @fopen($current_path.$pathext.$file, 'w')) && (@fwrite($f, $content) !== false) && @fclose($f)) { + $msg->addFeedback(array('FILE_SAVED', $file)); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup']); + exit; + } else { + $msg->addError('FILE_NOT_SAVED'); + } + } + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup']); + exit; +} + + +$path_parts = pathinfo($current_path.$pathext.$file); +$ext = strtolower($path_parts['extension']); + +// open file to edit +$real = realpath($current_path . $pathext . $file); + +if (course_realpath($current_path . $pathext . $file) == FALSE) { + // error: File does not exist + $msg->addError('FILE_NOT_EXIST'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup); + exit; +} else if (is_dir($current_path.$pathext.$file)) { + // error: cannot edit folder + $msg->addError('BAD_FILE_TYPE'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup); + exit; +} else if (!is_readable($current_path.$pathext.$file)) { + // error: File cannot open file + $msg->addError(array('CANNOT_OPEN_FILE', $file)); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup); + exit; +} else if (in_array($ext, $editable_file_types)) { + $_POST['body_text'] = file_get_contents($current_path.$pathext.$file); +} else { + //error: bad file type + $msg->addError('BAD_FILE_TYPE'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +require(AT_INCLUDE_PATH.'lib/tinymce.inc.php'); + +if (!isset($_REQUEST['setvisual']) && !isset($_REQUEST['settext'])) { + if ($_SESSION['prefs']['PREF_CONTENT_EDITOR'] == 1) { + $_POST['formatting'] = 1; + $_REQUEST['settext'] = 0; + $_REQUEST['setvisual'] = 0; + + } else if ($_SESSION['prefs']['PREF_CONTENT_EDITOR'] == 2) { + $_POST['formatting'] = 1; + $_POST['settext'] = 0; + $_POST['setvisual'] = 1; + + } else { // else if == 0 + $_POST['formatting'] = 0; + $_REQUEST['settext'] = 0; + $_REQUEST['setvisual'] = 0; + } +} +if (($_POST['setvisual'] && !$_POST['settext']) || $_GET['setvisual']) { + load_editor(false, 'body_text'); +} + + +?> + +<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" name="form"> +<input type="hidden" name="pathext" value="<?php echo $pathext; ?>" /> +<input type="hidden" name="framed" value="<?php echo $framed; ?>" /> +<input type="hidden" name="popup" value="<?php echo $popup; ?>" /> +<input type="hidden" name="file" value="<?php echo $file; ?>" /> +<input type="submit" name="submit" style="display:none;"/> +<div class="input-form"> + <div class="row"> + <h3><?php echo $file; ?></h3> + </div> + <div class="row"> + <?php + if (($_POST['setvisual'] && !$_POST['settext']) || $_GET['setvisual']){ + echo '<input type="hidden" name="setvisual" value="'.$_POST['setvisual'].'" />'; + echo '<input type="submit" name="settext" value="'._AT('switch_text').'" />'; + } else { + echo '<input type="submit" name="setvisual" value="'._AT('switch_visual').'" />'; + } + ?> + </div> + <div class="row"> + <label for="body_text"><?php echo _AT('body'); ?></label><br /> + <textarea name="body_text" id="body_text" rows="25"><?php echo htmlspecialchars($_POST['body_text']); ?></textarea> + </div> + + <div class="row buttons"> + <input type="submit" name="save" value="<?php echo _AT('save'); ?>" accesskey="s" /> + <input type="submit" name="cancel" value="<?php echo _AT('cancel'); ?>" /> + </div> +</div> +</form> + +<?php require(AT_INCLUDE_PATH.'footer.inc.php'); ?> \ No newline at end of file diff --git a/mods/_core/file_manager/filemanager.inc.php b/mods/_core/file_manager/filemanager.inc.php new file mode 100644 index 000000000..81d561acf --- /dev/null +++ b/mods/_core/file_manager/filemanager.inc.php @@ -0,0 +1,425 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +if (!defined('AT_INCLUDE_PATH')) { exit; } + +/** +* Allows the copying of entire directories. +* @access public +* @param string $source the source directory +* @param string $dest the destination directory +* @return boolean whether the copy was successful or not +* @link http://www.php.net/copy +* @author www at w8c dot com +*/ + +function copys($source,$dest) +{ + if (!is_dir($source)) { + return false; + } + if (!is_dir($dest)) { + mkdir($dest); + } + + $h=@dir($source); + while (@($entry=$h->read()) !== false) { + if (($entry == '.') || ($entry == '..')) { + continue; + } + + if (is_dir("$source/$entry") && $dest!=="$source/$entry") { + copys("$source/$entry", "$dest/$entry"); + } else { + @copy("$source/$entry", "$dest/$entry"); + } + } + $h->close(); + return true; +} + +/** +* Enables deletion of directory if not empty +* @access public +* @param string $dir the directory to delete +* @return boolean whether the deletion was successful +* @author Joel Kronenberg +*/ +function clr_dir($dir) { + if(!$opendir = @opendir($dir)) { + return false; + } + + while(($readdir=readdir($opendir)) !== false) { + if (($readdir !== '..') && ($readdir !== '.')) { + $readdir = trim($readdir); + + clearstatcache(); /* especially needed for Windows machines: */ + + if (is_file($dir.'/'.$readdir)) { + if(!@unlink($dir.'/'.$readdir)) { + return false; + } + } else if (is_dir($dir.'/'.$readdir)) { + /* calls itself to clear subdirectories */ + if(!clr_dir($dir.'/'.$readdir)) { + return false; + } + } + } + } /* end while */ + + @closedir($opendir); + + if(!@rmdir($dir)) { + return false; + } + return true; +} + +/** +* Calculate the size in Bytes of a directory recursively. +* @access public +* @param string $dir the directory to traverse +* @return int the total size in Bytes of the directory +* @author Joel Kronenberg +*/ +function dirsize($dir) { + if (is_dir($dir)) { + $dh = @opendir($dir); + } + if (!$dh) { + return -1; + } + $size = 0; + while (($file = readdir($dh)) !== false) { + + if ($file != '.' && $file != '..') { + $path = $dir.$file; + if (is_dir($path)) { + $size += dirsize($path.'/'); + } elseif (is_file($path)) { + $size += filesize($path); + } + } + + } + closedir($dh); + // echo 'something'; + // exit; + return $size; + // exit; +} + +/** +* This function gets used by PclZip when extracting a zip archive. +* @access private +* @return int whether or not to include the file +* @author Joel Kronenberg +*/ + function preExtractCallBack($p_event, &$p_header) { + global $translated_file_names; + + if ($p_header['folder'] == 1) { + return 1; + } + + if ($translated_file_names[$p_header['index']] == '') { + return 0; + } + + if ($translated_file_names[$p_header['index']]) { + $p_header['filename'] = substr($p_header['filename'], 0, -strlen($p_header['stored_filename'])); + $p_header['filename'] .= $translated_file_names[$p_header['index']]; + } + return 1; + } + +/** +* This function gets used by PclZip when creating a zip archive. +* @access private +* @return int whether or not to include the file +* @author Joel Kronenberg +*/ + function preImportCallBack($p_event, &$p_header) { + global $IllegalExtentions; + + if ($p_header['folder'] == 1) { + return 1; + } + + $path_parts = pathinfo($p_header['filename']); + $ext = $path_parts['extension']; + + if (in_array($ext, $IllegalExtentions)) { + return 0; + } + + return 1; + } + + +/* prints the <options> out of $cats which is an array of course categories where */ +/* $cats[parent_cat_id][] = $row */ +function print_course_cats($parent_cat_id, &$cats, $cat_row, $depth=0) { + $my_cats = $cats[$parent_cat_id]; + if (!is_array($my_cats)) { + return; + } + foreach ($my_cats as $cat) { + + echo '<option value="'.$cat['cat_id'].'"'; + if($cat['cat_id'] == $cat_row){ + echo ' selected="selected"'; + } + echo '>'; + echo str_pad('', $depth, '-'); + echo $cat['cat_name'].'</option>'."\n"; + + print_course_cats($cat['cat_id'], $cats, $cat_row, $depth+1); + } +} + +// returns the most appropriate representation of Bytes in MB, KB, or B +function get_human_size($num_bytes) { + $abs_num_bytes = abs($num_bytes); + + if ($abs_num_bytes >= AT_KBYTE_SIZE * AT_KBYTE_SIZE) { + return round(bytes_to_megabytes($num_bytes), 2) .' '. _AT('mb'); + } else if ($abs_num_bytes >= AT_KBYTE_SIZE) { + return round(bytes_to_kilobytes($num_bytes), 2) .' '._AT('kb') ; + } + // else: + + return $num_bytes . ' '._AT('bt'); +} + +/** +* Returns the MB representation of inputed bytes +* @access public +* @param int $num_bytes the input bytes to convert +* @return int MB representation of $num_bytes +* @author Heidi Hazelton +*/ +function bytes_to_megabytes($num_bytes) { + return $num_bytes/AT_KBYTE_SIZE/AT_KBYTE_SIZE; +} + +/** +* Returns the Byte representation of inputed MB +* @access public +* @param int $num_bytes the input MB to convert +* @return int the Bytes representation of $num_bytes +* @author Heidi Hazelton +*/ +function megabytes_to_bytes($num_bytes) { + return $num_bytes*AT_KBYTE_SIZE*AT_KBYTE_SIZE; +} + +/** +* Returns the KB representation of inputed Bytes +* @access public +* @param int $num_bytes the input Bytes to convert +* @return int the KB representation of $num_bytes +* @author Heidi Hazelton +*/ +function bytes_to_kilobytes($num_bytes) { + return $num_bytes/AT_KBYTE_SIZE; +} + +/** +* Returns the Bytes representation of inputed KBytes +* @access public +* @param int $num_bytes the input KBytes to convert +* @return int the KBytes representation of $num_bytes +* @author Heidi Hazelton +*/ +function kilobytes_to_bytes($num_bytes) { + return $num_bytes*AT_KBYTE_SIZE; +} + +/** +* Outputs the directories associated with a course in the form of <option> elements. +* @access public +* @param string $cur_dir the current directory to include in the options. +* @author Norma Thompson +*/ +function output_dirs($current_path,$cur_dir,$indent) { + // open the cur_dir + if ($dir = opendir($current_path.$cur_dir)) { + + // recursively call output_dirs() for all directories in this directory + while (false !== ($file = readdir($dir)) ) { + + //if the name is not a directory + if( ($file == '.') || ($file == '..') ) { + continue; + } + + // if it is a directory call function + if(is_dir($current_path.$cur_dir.$file)) { + $ldir = explode('/',$cur_dir.$file); + $count = count($ldir); + $label = $ldir[$count-1]; + + $dir_option .= '<option value="'.$cur_dir.$file.'/" >'.$indent.$label.'</option>'; + + $dir_option .= output_dirs($current_path,$cur_dir.$file.'/',$indent.'--'); + } + + } // end while + + closedir($dir); + } + return $dir_option; +} + +function display_tree($current_path, $cur_dir, $pathext, $ignore_children = false) { + // open the cur_dir + static $list_array; + if (!isset($list_array)) { + $list_array = explode(',', $_GET['list']); + } + if ($dir = opendir($current_path . $cur_dir)) { + + // recursively call output_dirs() for all directories in this directory + while (false !== ($file = readdir($dir)) ) { + + //if the name is not a directory + if( ($file == '.') || ($file == '..') ) { + continue; + } + + // if it is a directory call function + if (is_dir($current_path . $cur_dir . $file)) { + + //$ldir = explode('/',$cur_dir.$file); + //$count = count($ldir); + //$label = $ldir[$count-1]; + + $check = ''; + $here = ''; + if ($cur_dir . $file == substr($pathext, 0, -1)) { + $check = 'checked="checked"'; + $here = ' ' . _AT('current_location'); + } else if (($cur_dir == $pathext) && in_array($file, $list_array)) { + $ignore_children = true; + } + + if ($ignore_children) { + $check = 'disabled="disabled"'; + $class = ' disabled'; + } + + $dir_option .= '<ul><li class="folders'.$class.'">'; + $dir_option .= '<label><input type="radio" name="dir_name" value="'.$cur_dir.$file.'" '.$check. '/>'. $file . $here. '</label>'; + $dir_option .= ''.display_tree($current_path,$cur_dir.$file.'/', $pathext, $ignore_children).''; + $dir_option .= '</li></ul>'; + + if (($cur_dir == $pathext) && in_array($file, $list_array)) { + $ignore_children = false; + $class = ''; + } + } + + + } // end while + + closedir($dir); + } + return $dir_option; +} + +function course_realpath($file) { + if (!$_SESSION['course_id']) { + return FALSE; + } + + $course_path = AT_CONTENT_DIR . $_SESSION['course_id']; + + $path_parts = pathinfo($file); + + $dir_name = $path_parts['dirname']; + $file_name = $path_parts['basename']; + $ext_name = $path_parts['extension']; + + //1. determine the real path of the file/directory + if (is_dir($dir_name.DIRECTORY_SEPARATOR.$file_name) && $ext_name == '') { + //if directory ws passed through (moving file to diff directory) + $real = realpath($dir_name . DIRECTORY_SEPARATOR . $file_name); + } else { + //if file was passed through or no existant direcotry was passed through (rename/creating dir) + $real = realpath($dir_name); + } + + //2. and whether its in the course content directory + if (substr($real, 0, strlen($course_path)) != $course_path) { + return FALSE; + } + + //3. check if extensions are legal + + //4. Otherwise return the real path of the file + return $real; +} + +/** +* Returns canonicalized absolute pathname to a file/directory in the content directory +* @access public +* @param string $file the relative path to the file or directory +* @return string the full path to the file or directory, FALSE if it does not exist in our content directory. +*/ +function course_realpath_NEW_VERSION($file) { + if (!$_SESSION['course_id']) { + return FALSE; + } + + $course_path = AT_CONTENT_DIR . $_SESSION['course_id']; + + // determine the real path of the file/directory + $real = realpath($course_path . DIRECTORY_SEPARATOR . $file); + + if (!file_exists($real)) { + // the file or directory does not exist + return FALSE; + + } else if (substr($real, 0, strlen($course_path)) != $course_path) { + // the file or directory is not in the content path + return FALSE; + + } else { + // Otherwise return the real path of the file + return $real; + } +} + +/** +* Returns the name of the readme file in the given directory +* @access public +* @param string $dir_name the name of the directory +* @return string the name of the readme file +*/ +function get_readme($dir) +{ + if (!is_dir($dir)) return ''; + + $dh = opendir($dir); + + while (($file = readdir($dh)) !== false) { + if (stristr($file, 'readme') && substr($file, -4) <> '.php') + return $file; + } + + closedir($dh); + return ''; +} +?> \ No newline at end of file diff --git a/mods/_core/file_manager/filemanager_display.inc.php b/mods/_core/file_manager/filemanager_display.inc.php new file mode 100644 index 000000000..5824cabbe --- /dev/null +++ b/mods/_core/file_manager/filemanager_display.inc.php @@ -0,0 +1,620 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: filemanager.php 5078 2005-07-06 14:16:53Z joel $ + +if (!defined('AT_INCLUDE_PATH')) { exit; } + +function get_file_extension($file_name) { + $ext = pathinfo($file_name); + return $ext['extension']; +} + +function get_file_type_icon($file_name) { + static $mime; + + $ext = get_file_extension($file_name); + + if (!isset($mime)) { + require(AT_INCLUDE_PATH .'lib/mime.inc.php'); + } + + if (isset($mime[$ext]) && $mime[$ext][1]) { + return $mime[$ext][1]; + } + return 'generic'; +} + +function get_relative_path($src, $dest) { + if ($src == '') { + $path = $dest; + } else if (substr($dest, 0, strlen($src)) == $src) { + $path = substr($dest, strlen($src) + 1); + } else { + $depth = substr_count($src, '/'); + for ($i = 0; $i < $depth + 1; $i++) // $depth+1 because the last '/' is not recorded in content.content_path + $path .= '../'; + $path .= $dest; + } + + return $path; +} + +// get the course total in Bytes +$course_total = dirsize($current_path); + +$framed = intval($_GET['framed']); +$popup = intval($_GET['popup']); +$cp = $_GET['cp']; +$cid = intval($_GET['cid']); // content id, used at "adapted content" page, => add/edit alternatives +$pid = intval($_GET['pid']); // primary resource id, used at "adapted content" page, => add/edit alternatives +$a_type = intval($_GET['a_type']); // alternative_type, used at "adapted content" page, => add/edit alternatives + +if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) { + $get_file = 'get.php/'; +} else { + $get_file = 'content/' . $_SESSION['course_id'] . '/'; +} + +function fm_path(){ + global $pathext, $framed, $popup, $cp, $cid, $pid, $a_type; +echo '<p>'._AT('current_path').' '; + +if (isset($pathext) && $pathext != '') { + echo '<a href="'.$_SERVER['PHP_SELF'].'?popup=' . $popup . SEP . 'framed=' . $framed.SEP . 'cp=' . $cp.SEP . 'cid=' . $cid.SEP . 'pid=' . $pid.SEP . 'a_type=' . $a_type.'">'._AT('home').'</a> '; +} +else { + $pathext = ''; + echo _AT('home'); +} + + +if ($pathext == '' && isset($_POST['pathext'])) { + + $pathext = urlencode($_POST['pathext']); +} + +if ($pathext != '') { + $bits = explode('/', $pathext); + + foreach ($bits as $bit) { + if ($bit != '') { + $bit_path .= $bit . '/'; + echo ' / '; + + if ($bit_path == $pathext) { + echo $bit; + } else { + echo '<a href="'.$_SERVER['PHP_SELF'].'?pathext=' . urlencode($bit_path) . SEP . 'popup=' . $popup . SEP . 'framed=' . $framed . SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$cid.SEP.'a_type='.$a_type.'">' . $bit . '</a>'; + } + } + } + $bit_path = ""; + $bit = ""; +} +echo '</p>'; + +} + +if ($popup == TRUE) { + $totalcol = 6; +} else { + $totalcol = 5; +} +$labelcol = 3; + +if (TRUE || $framed != TRUE) { + + if ($_GET['overwrite'] != '') { + // get file name, out of the full path + $path_parts = pathinfo($current_path.$_GET['overwrite']); + + if (!file_exists($path_parts['dirname'].'/'.$pathext.$path_parts['basename']) + || !file_exists($path_parts['dirname'].'/'.$pathext.substr($path_parts['basename'], 5))) { + /* source and/or destination does not exist */ + $msg->addErrors('CANNOT_OVERWRITE_FILE'); + } else { + @unlink($path_parts['dirname'].'/'.$pathext.substr($path_parts['basename'], 5)); + $result = @rename($path_parts['dirname'].'/'.$pathext.$path_parts['basename'], $path_parts['dirname'].'/'.$pathext.substr($path_parts['basename'], 5)); + + if ($result) { + $msg->addFeedback('FILE_OVERWRITE'); + } else { + $msg->addErrors('CANNOT_OVERWRITE_FILE'); + } + } + } + + // filemanager listing table + // make new directory + echo '<div class="input-form"><fieldset class="group_form"><legend class="group_form">'._AT('add_file_folder').'</legend>'."\n"; + echo ' <div class="row">'."\n"; + echo ' <form name="form1" method="post" action="'.$_SERVER['PHP_SELF'].'?'.(($pathext != '') ? 'pathext='.urlencode($pathext).SEP : ''). 'popup='.$popup.SEP.'cp='.SEP.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$cid.SEP.'a_type='.$a_type.'">'."\n"; + if( $MakeDirOn ) { + if ($depth < $MaxDirDepth) { + echo ' <label for="dirname">'._AT('create_folder_here').'</label><br />'."\n"; + echo '  <small class="spacer">'._AT('keep_it_short').'</small><br />'."\n"; + echo ' <input type="text" name="dirname" id="dirname" size="20" /> '."\n"; + echo ' <input type="hidden" name="mkdir_value" value="true" /> '."\n"; + echo ' <input type="submit" name="mkdir" value="'._AT('create_folder').'" class="button" />'."\n"; + } else { + echo _AT('depth_reached')."\n"; + } + } + echo ' <input type="hidden" name="pathext" value="'.$pathext.'" />'."\n"; + echo ' </form>'."\n"; + echo ' </div>'."\n"; + + echo ' <div class="row"><hr /></div>'."\n"; + + + // If flash is available, provide the option of using Fluid's uploader or the basic uploader + if (isset($_SESSION['flash']) && $_SESSION['flash'] == "yes") { + echo '<div class="row">'."\n"; + if (isset($_COOKIE["fluid_on"]) && $_COOKIE["fluid_on"]=="yes") + $fluid_on = 'checked="checked"'; + echo '(<input type="checkbox" id="fluid_on" name="fluid_on" onclick="toggleform(\'simple-container\', \'fluid-container\'); setCheckboxCookie(this, \'fluid_on=yes\', \'fluid_on=no\',\'December 31, 2099\');" value="yes" '.$fluid_on.' /> '."\n"; + echo '<label for="fluid_on" >'._AT('enable_uploader').'</label>)'."\n"; + echo '</div>'."\n"; + } + + + // Create a new file + echo ' <div class="row" style="float: left;"><input type="button" class="button" name="new_file" value="' . _AT('file_manager_new') . '" onclick="window.location.href=\''.AT_BASE_HREF.'mods/_core/file_manager/new.php?pathext=' . urlencode($pathext) . SEP . 'framed=' . $framed . SEP . 'popup=' . $popup . '\'"/></div>'."\n"; + + $my_MaxCourseSize = $system_courses[$_SESSION['course_id']]['max_quota']; + + // upload file + if (($my_MaxCourseSize == AT_COURSESIZE_UNLIMITED) + || (($my_MaxCourseSize == AT_COURSESIZE_DEFAULT) && ($course_total < $MaxCourseSize)) + || ($my_MaxCourseSize-$course_total > 0)) { + echo ' <div class="row" style="float: left;">'._AT('OR').'</div>'."\n".' <div class="row" style="float: left;">'."\n"; + if (isset($_SESSION['flash']) && $_SESSION['flash'] == "yes") { + ?> + <div id="fluid-container" <?php echo (isset($_COOKIE["fluid_on"]) && $_COOKIE["fluid_on"]=="yes") ? '' : 'style="display:none;"'; ?>> + <input type="button" id="uploader_link" class="button" name="upload_file" value="<?php echo _AT('upload_files'); ?>" onclick="toggleform('uploader', 'uploader_link');" /> + <div id="uploader" style="display: none;"> + <form id="flc-uploader" class="flc-uploader fl-uploader fl-progEnhance-enhanced" method="get" enctype="multipart/form-data" action="" style="margin: 0px; padding: 0px;"> + <div class="start"> + <div class="fl-uploader-queue-wrapper"> + <div class="fl-uploader-queue-header"> + <table cellspacing="0" cellpadding="0"> + <tr> + <th scope="col" class="fileName"><?php echo _AT('file_name'); ?></th> + <th scope="col" class="fileSize"><?php echo _AT('size'); ?>  </th> + <th scope="col" class="fileRemove"> </th> + </tr> + </table> + </div> + + <div class="flc-scroller fl-scroller"> + <div class="fl-scroller-inner"> + <table cellspacing="0" class="flc-uploader-queue fl-uploader-queue" summary="Queue of files to upload."> + <tbody> + <!-- Rows will be rendered in here. --> + + <!-- Template markup for the file queue rows --> + <tr class="flc-uploader-file-tmplt flc-uploader-file fl-uploader-hidden-templates"> + <th class="flc-uploader-file-name fl-uploader-file-name" scope="row"><?php echo _AT('file_placeholder'); ?></th> + <td class="flc-uploader-file-size fl-uploader-file-size">0 <?php echo _AT('kb'); ?></td> + <td class="fl-uploader-file-actions"> + <button type="button" class="flc-uploader-file-action fl-uploader-file-action" title="<?php echo _AT('remove_queued_file'); ?>" tabindex="-1"> + <span class="fl-uploader-button-text fl-uploader-hidden"><?php echo _AT('remove_queued_file'); ?></span> + </button> + </td> + </tr> + + <!-- Template for the file error info rows --> + <tr class="flc-uploader-file-error-tmplt fl-uploader-file-error fl-uploader-hidden-templates"> + <td colspan="3" class="flc-uploader-file-error"></td> + </tr> + </tbody> + </table> + <div class="flc-uploader-file-progressor-tmplt fl-uploader-file-progress"><span class="fl-uploader-file-progress-text fl-uploader-hidden">76%</span></div> + </div> + </div> + + <div class="flc-uploader-browse-instructions fl-uploader-browse-instructions"> <?php echo _AT('click_browse_files'); ?> </div> + + <div class="flc-uploader-queue-footer fl-uploader-queue-footer"> + <table cellspacing="0" cellpadding="0"> + <tr> + <td class="flc-uploader-total-progress-text"><?php echo _AT('total'); ?>: <span class="fluid-uploader-totalFiles">0</span> <?php echo _AT('files'); ?> (<span class="fluid-uploader-totalBytes">0 <?php echo _AT('kb'); ?></span>)</td> + <td class="fl-uploader-footer-buttons" align="right" ><a class="flc-uploader-button-browse fl-uploader-browse" tabindex="0" ><?php echo _AT('browse_files'); ?></a></td> + </tr> + </table> + <div class="flc-uploader-total-progress fl-uploader-total-progress-okay"> </div> + </div> + </div> + + <div class="fl-uploader-btns"> + <button type="button" class="flc-uploader-button-pause fl-uploader-pause fl-uploader-hidden" onclick="toggleform('uploader', 'uploader_link');"><?php echo _AT('cancel'); ?></button> + <button type="button" class="flc-uploader-button-upload fl-uploader-upload fl-uploader-button-default fl-uploader-dim" ><?php echo _AT('upload'); ?></button> + </div> + + </div> + </form> + + </div> + </div> + <?php + if (isset($_COOKIE["fluid_on"]) && $_COOKIE["fluid_on"]=="yes") + echo '<div id="simple-container" style="display: none;">'; + else + echo '<div id="simple-container">'; + } else { + // Display as regular if there's no Flash detected + echo '<div id="simple-container">'."\n"; + } + + // Simple single file uploader + echo '<form onsubmit="openWindow(\''.AT_BASE_HREF.'tools/prog.php\');" class="fl-ProgEnhance-basic" name="form1" method="post" action="mods/_core/file_manager/upload.php?popup='.$popup.SEP. 'framed='.$framed.SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$cid.SEP.'a_type='.$a_type.'" enctype="multipart/form-data">'; + echo '<input type="hidden" name="MAX_FILE_SIZE" value="'.$my_MaxFileSize.'" />'; + echo '<label for="uploadedfile">'._AT('upload_files').'</label><br />'."\n"; + echo '<input type="file" name="uploadedfile" id="uploadedfile" class="formfield" size="20" /> '; + echo '<input type="submit" name="submit" value="'._AT('upload').'" class="button" />'; + echo '<input type="hidden" name="pathext" value="'.$pathext.'" /> '; + + if ($popup == TRUE) { + echo '<input type="hidden" name="popup" value="1" />'; + } + echo '</form>'; + echo '</div>'; + + echo ' </div>'."\n".' </fieldset></div>'; + + } else { + echo ' </fieldset></div>'."\n"; + $msg->printInfos('OVER_QUOTA'); + } +} + +// Directory and File listing +echo '<form name="checkform" action="'.$_SERVER['PHP_SELF'].'?'.(($pathext!='') ? 'pathext='.urlencode($pathext).SEP : '').'popup='.$popup .SEP. 'framed='.$framed.SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$cid.SEP.'a_type='.$a_type.'" method="post">'; +echo '<input type="hidden" name="pathext" value ="'.$pathext.'" />'; + +// display the section to use a remote URL as an alternative +if ($a_type > 0) { +?> +<div class="input-form" style="min-height:10px"> +<fieldset class="group_form" style="min-height: 0px;"><legend class="group_form"><?php echo _AT('use_url_as_alternative'); ?></legend> + <div class="row"> + <input name="remote_alternative" id="remote_alternative" value="http://" size="60" /> + <input class="button" type="button" name="alternative" value="<?php echo _AT('use_as_alternative'); ?>" onclick="javascript: setURLAlternative();" /> + </div> +</fieldset> +</div> +<?php }?> + +<table class="data static" summary="" border="0" rules="groups" style="width: 90%"> +<thead> +<tr> +<td colspan="5"> +<?php fm_path(); ?> +</td> +</tr> +<tr> + <th scope="col"><input type="checkbox" name="checkall" onclick="Checkall(checkform);" id="selectall" title="<?php echo _AT('select_all'); ?>" /></th> + <th> </th> + <th scope="col"><?php echo _AT('name'); ?></th> + <th scope="col"><?php echo _AT('date'); ?></th> + <th scope="col"><?php echo _AT('size'); ?></th> +</tr> +</thead> +<tfoot> +<tr> + <td colspan="5"><input type="submit" name="rename" value="<?php echo _AT('rename'); ?>" /> + <input type="submit" name="delete" value="<?php echo _AT('delete'); ?>" /> + <input type="submit" name="move" value="<?php echo _AT('move'); ?>" /></td> +</tr> + +<tr> + <td colspan="4" align="right"><strong><?php echo _AT('directory_total'); ?>:</strong></td> + <td align="right"> <strong><?php echo get_human_size(dirsize($current_path.$pathext.$file.'/')); ?></strong> </td> +</tr> + +<tr> + <td colspan="4" align="right"><strong><?php echo _AT('course_total'); ?>:</strong></td> + <td align="right"> <strong><?php echo get_human_size($course_total); ?></strong> </td> +</tr> +<tr> + <td colspan="4" align="right"><strong><?php echo _AT('course_available'); ?>:</strong></td> + <td align="right"><strong><?php + if ($my_MaxCourseSize == AT_COURSESIZE_UNLIMITED) { + echo _AT('unlimited'); + } else if ($my_MaxCourseSize == AT_COURSESIZE_DEFAULT) { + echo get_human_size($MaxCourseSize-$course_total); + } else { + echo get_human_size($my_MaxCourseSize-$course_total); + } ?></strong> </td> +</tr> +</tfoot> +<?php + + +if($pathext) : ?> + <tr> + <td colspan="5"><a href="<?php echo $_SERVER['PHP_SELF'].'?back=1'.SEP.'pathext='.$pathext.SEP. 'popup=' . $popup .SEP. 'framed=' . $framed .SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$cid.SEP.'a_type='.$a_type; ?>"><img src="images/arrowicon.gif" border="0" height="11" width="10" alt="" /> <?php echo _AT('back'); ?></a></td> + </tr> +<?php endif; ?> +<?php +$totalBytes = 0; + +if ($dir == '') + $dir=opendir($current_path); + +// loop through folder to get files and directory listing +while (false !== ($file = readdir($dir)) ) { + + // if the name is not a directory + if( ($file == '.') || ($file == '..') ) { + continue; + } + + // get some info about the file + $filedata = stat($current_path.$pathext.$file); + $path_parts = pathinfo($file); + $ext = strtolower($path_parts['extension']); + + $is_dir = false; + + // if it is a directory change the file name to a directory link + if(is_dir($current_path.$pathext.$file)) { + $size = dirsize($current_path.$pathext.$file.'/'); + $totalBytes += $size; + $filename = '<a href="'.$_SERVER['PHP_SELF'].'?pathext='.urlencode($pathext.$file.'/'). SEP . 'popup=' . $popup . SEP . 'framed='. $framed . SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$cid.SEP.'a_type='.$a_type.'">'.$file.'</a>'; + $fileicon = ' '; + $fileicon .= '<img src="images/folder.gif" alt="'._AT('folder').':'.$file.'" height="18" width="20" class="img-size-fm1" />'; + $fileicon .= ' '; + if(!$MakeDirOn) { + $deletelink = ''; + } + + $is_dir = true; + } else if ($ext == 'zip') { + + $totalBytes += $filedata[7]; + $filename = $file; + $fileicon = ' <img src="images/icon-zip.gif" alt="'._AT('zip_archive').':'.$file.'" height="16" width="16" border="0" class="img-size-fm2" /> '; + + } else { + $totalBytes += $filedata[7]; + $filename = $file; + $fileicon = ' <img src="images/file_types/'.get_file_type_icon($filename).'.gif" height="16" width="16" alt="" title="" class="img-size-fm2" /> '; + } + $file1 = strtolower($file); + // create listing for dirctor or file + if ($is_dir) { + + $dirs[$file1] .= '<tr><td align="center" width="0%">'; + $dirs[$file1] .= '<input type="checkbox" id="'.$file.'" value="'.$file.'" name="check[]"/></td>'; + $dirs[$file1] .= '<td align="center"><label for="'.$file.'" >'.$fileicon.'</label></td>'; + $dirs[$file1] .= '<td > '; + $dirs[$file1] .= $filename.'</td>'; + $dirs[$file1] .= '<td align="right"> '; + $dirs[$file1] .= AT_date(_AT('filemanager_date_format'), $filedata[10], AT_DATE_UNIX_TIMESTAMP); + $dirs[$file1] .= ' </td>'; + $dirs[$file1] .= '<td align="right">'; + $dirs[$file1] .= get_human_size($size).'</td></tr>'; + + + } else { + $files[$file1] .= '<tr> <td align="center">'; + $files[$file1] .= '<input type="checkbox" id="'.$file.'" value="'.$file.'" name="check[]"/> </td>'; + $files[$file1] .= '<td align="center"><label for="'.$file.'">'.$fileicon.'</label></td>'; + $files[$file1] .= '<td > '; + + if ($framed) { + $files[$file1] .= '<a href="'.$get_file.$pathext.urlencode($filename).'">'.$filename.'</a>'; + } else { + $files[$file1] .= '<a href="mods/_core/file_manager/preview.php?file='.$pathext.$filename.SEP.'pathext='.urlencode($pathext).SEP.'popup='.$popup.'">'.$filename.'</a>'; + } + + if ($ext == 'zip') { + $files[$file1] .= ' <a href="mods/_core/file_manager/zip.php?'.(($pathext!='') ? 'pathext='.urlencode($pathext).SEP : ''). 'file=' . urlencode($file) . SEP . 'popup=' . $popup . SEP . 'framed=' . $framed .'">'; + $files[$file1] .= '<img src="images/archive.gif" border="0" alt="'._AT('extract_archive').'" title="'._AT('extract_archive').'"height="16" width="11" class="img-size-fm3" />'; + $files[$file1] .= '</a>'; + } + + if (in_array($ext, $editable_file_types)) { + $files[$file1] .= ' <a href="mods/_core/file_manager/edit.php?'.(($pathext!='') ? 'pathext='.urlencode($pathext).SEP : ''). 'popup=' . $popup . SEP . 'framed=' . $framed . SEP . 'file=' . $file . '">'; + $files[$file1] .= '<img src="images/edit.gif" border="0" alt="'._AT('extract_archive').'" title="'._AT('edit').'" height="15" width="18" class="img-size-fm4" />'; + $files[$file1] .= '</a>'; + } + + $files[$file1] .= ' </td>'; + + $files[$file1] .= '<td align="right" style="white-space:nowrap">'; + + if ($popup == TRUE) { + if ($a_type > 0) // define content alternative + { + $files[$file1] .= '<input class="button" type="button" name="alternative" value="' ._AT('use_as_alternative') . '" onclick="javascript: setAlternative(\''.get_relative_path($_GET['cp'], $pathext).$file.'\', \''.AT_BASE_HREF.$get_file.$pathext.urlencode($file).'\', \''.$cid.'\', \''.$pid.'\', \''.$a_type.'\');" /> '; + } + else + $files[$file1] .= '<input class="button" type="button" name="insert" value="' ._AT('insert') . '" onclick="javascript:insertFile(\'' . $file . '\', \'' . get_relative_path($_GET['cp'], $pathext) . '\', \'' . $ext . '\', \'' .$_SESSION['prefs']['PREF_CONTENT_EDITOR']. '\');" /> '; + } + + $files[$file1] .= AT_date(_AT('filemanager_date_format'), $filedata[10], AT_DATE_UNIX_TIMESTAMP); + $files[$file1] .= ' </td>'; + + $files[$file1] .= '<td align="right" style="white-space:nowrap">'; + $files[$file1] .= get_human_size($filedata[7]).'</td></tr>'; + } +} // end while + +// sort listing and output directories +if (is_array($dirs)) { + ksort($dirs, SORT_STRING); + foreach($dirs as $x => $y) { + echo $y; + } +} + +//sort listing and output files +if (is_array($files)) { + ksort($files, SORT_STRING); + foreach($files as $x => $y) { + echo $y; + } +} + + +echo '</table></form>'; + + + +?> + +<script type="text/javascript"> +//<!-- +function insertFile(fileName, pathTo, ext, ed_pref) { + // pathTo + fileName should be relative to current path (specified by the Content Package Path) + + if (ext == "gif" || ext == "jpg" || ext == "jpeg" || ext == "png") { + var info = "<?php echo _AT('alternate_text'); ?>"; + var html = '<img src="' + pathTo+fileName + '" border="0" alt="' + info + '" />'; + + insertLink(html, ed_pref); + } else if (ext == "mpg" || ext == "avi" || ext == "wmv" || ext == "mov" || ext == "swf" || ext == "mp3" || ext == "wav" || ext == "ogg" || ext == "mid" ||ext == "flv"|| ext == "mp4") { + var html = '[media]'+ pathTo + fileName + '[/media]'; + + insertLink(html, ed_pref); + } else { + var info = "<?php echo _AT('put_link'); ?>"; + var html = '<a href="' + pathTo+fileName + '">' + info + '</a>'; + + insertLink(html, ed_pref); + } +} + +function insertLink(html, ed_pref) +{ + if (window.opener) { + var isNotVisual = (jQuery('#html:checked').val() !== null) && (ed_pref === '1'); + } + + if (!window.opener || !isNotVisual) { + if (!window.opener && window.parent.tinyMCE) + window.parent.tinyMCE.execCommand('mceInsertContent', false, html); + else + if (window.opener && window.opener.tinyMCE) + window.opener.tinyMCE.execCommand('mceInsertContent', false, html); + } else { + insertAtCursor(window.opener.document.form.body_text, html); + } +} + +function insertAtCursor(myField, myValue) { + //IE support + if (window.opener.document.selection) { + myField.focus(); + sel = window.opener.document.selection.createRange(); + sel.text = myValue; + } + //MOZILLA/NETSCAPE support + else if (myField.selectionStart || myField.selectionStart == '0') { + var startPos = myField.selectionStart; + var endPos = myField.selectionEnd; + myField.value = myField.value.substring(0, startPos) + + myValue + + myField.value.substring(endPos, myField.value.length); + myField.focus(); + } else { + myField.value += myValue; + myField.focus(); + } +} + +// This function does: +// 1. save into db via ajax +// 2. set the according field in opener window to the selected file +// 3. close file manager itself +function setAlternative(file, file_preview_link, cid, pid, a_type) { + jQuery.post("<?php echo AT_BASE_HREF; ?>mods/_core/editor/save_alternative.php", + {"pid":pid, "a_type":a_type, "alternative":file}, + function(data) {}); + + link_html = '\ + <a href="'+file_preview_link+'" title="<?php echo _AT('new_window'); ?>" target="_new">'+file+'</a><br /> \ + <a href="#" onclick="ATutor.poptastic(\\\'<?php echo AT_BASE_HREF; ?>mods/_core/file_manager/index.php?framed=1<?php echo SEP; ?>popup=1<?php echo SEP; ?>cp=<?php echo $cp.SEP; ?>cid='+cid+'<?php echo SEP; ?>pid='+pid+'<?php echo SEP; ?>a_type='+a_type+'\\\');return false;" title="<?php echo _AT('new_window'); ?>"> \ + <img src="<?php echo AT_BASE_HREF; ?>images/home-tests_sm.png" border="0" title="<?php echo _AT('alter'); ?>" alt="<?php echo _AT('alter'); ?>" /> \ + </a> \ + <a href="#" onclick="removeAlternative(\\\'<?php echo $cp; ?>\\\', '+cid+','+pid+','+a_type+');return false;"> \ + <img src="<?php echo AT_BASE_HREF; ?>images/icon_delete.gif" border="0" title="<?php echo _AT('remove'); ?>" alt="<?php echo _AT('remove'); ?>" /> \ + </a> \ + </div> \ +'; + eval("window.opener.document.getElementById(\""+pid+"_"+a_type+"\").innerHTML = '"+link_html+"'"); + + window.close(); +} + +// This function validates the url then call setAlternative() +function setURLAlternative() { + remote_url = jQuery('#remote_alternative').val(); + if (remote_url == '' || remote_url == 'http://') { + alert("<?php echo _AT('empty_url'); ?>"); + return false; + } + setAlternative(remote_url, remote_url, '<?php echo $cid; ?>.', '<?php echo $pid; ?>', '<?php echo $a_type; ?>'); +} + +<?php if (isset($_SESSION['flash']) && $_SESSION['flash'] == "yes") { ?> +// toggle the view between div object and button +function toggleform(id, link) { + var obj = document.getElementById(id); + var btn = document.getElementById(link); + + if (obj.style.display == "none") { + //show + obj.style.display=''; + obj.focus(); + + btn.style.display = 'none'; + + + } else { + //hide + obj.style.display='none'; + btn.style.display = ''; + } +} + +// set a cookie +function setCheckboxCookie(obj, value1, value2, date) +{ + var today = new Date(); + var the_date = new Date(date); + var the_cookie_date = the_date.toGMTString(); + if (obj.checked==true) + var the_cookie = value1 + ";expires=" + the_cookie_date; + else + var the_cookie = value2 + ";expires=" + the_cookie_date; + document.cookie = the_cookie; +} +<?php } ?> + +<?php +// When uploading a file as an alternative content, set the alternative field in the opener window +// and close "file manager" once the upload is successful +if ($a_type > 0 && isset($_GET['uploadfile']) && $_GET['uploadfile'] <> '') { ?> +function setAlternativeAndClose() { + setAlternative('<?php echo get_relative_path($_GET['cp'], $pathext).$_GET['uploadfile']; ?>', '<?php echo AT_BASE_HREF.$get_file.$pathext.urlencode($_GET['uploadfile']); ?>', '<?php echo $cid; ?>', '<?php echo $pid; ?>', '<?php echo $a_type; ?>'); + window.close(); +} + +window.onload=setAlternativeAndClose; +<?php } ?> + +//--> +</script> \ No newline at end of file diff --git a/mods/_core/file_manager/index.php b/mods/_core/file_manager/index.php new file mode 100644 index 000000000..ded887072 --- /dev/null +++ b/mods/_core/file_manager/index.php @@ -0,0 +1,126 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: index.php 8992 2009-12-01 18:38:19Z cindy $ + +define('AT_INCLUDE_PATH', '../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); +require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + +if ((isset($_REQUEST['popup']) && $_REQUEST['popup']) && + (!isset($_REQUEST['framed']) || !$_REQUEST['framed'])) { + $popup = TRUE; + $framed = FALSE; +} else if (isset($_REQUEST['framed']) && $_REQUEST['framed'] && isset($_REQUEST['popup']) && $_REQUEST['popup']) { + $popup = TRUE; + $framed = TRUE; +} else { + $popup = FALSE; + $framed = FALSE; +} + +// If Flash is detected, call the necessary css and js, and configure settings to use the Fluid Uploader +if (isset($_SESSION['flash']) && $_SESSION['flash'] == "yes") { + /* Provide the option of switching between Fluid Uploader and simple single file uploader + and save the user preference as a cookie */ + if (!isset($_COOKIE["fluid_on"])) + ATutor.setcookie("fluid_on", "yes", time()+1200); + + $fluid_dir = 'jscripts/infusion/'; + $framed = intval($_GET['framed']); + $popup = intval($_GET['popup']); + $current_path = AT_CONTENT_DIR.$_SESSION['course_id'].'/'; + + if ($_GET['pathext'] != '') { + $pathext = urldecode($_GET['pathext']); + } else if ($_POST['pathext'] != '') { + $pathext = $_POST['pathext']; + } + + if($_GET['back'] == 1) { + $pathext = substr($pathext, 0, -1); + $slashpos = strrpos($pathext, '/'); + if($slashpos == 0) { + $pathext = ''; + } else { + $pathext = substr($pathext, 0, ($slashpos+1)); + } + + } + + $_custom_head .= ' + <link href="'.$fluid_dir.'components/uploader/css/Uploader.css" rel="stylesheet" type="text/css" /> + <script src="'.$fluid_dir.'InfusionAll.js" type="text/javascript"></script> + <script language="JavaScript" type="text/javascript"> + + var myUpload; // mostly used for testing + + jQuery(document).ready(function () { + myUpload = fluid.progressiveEnhanceableUploader(".flc-uploader", ".fl-ProgEnhance-basic", { + uploadManager: { + type: "fluid.swfUploadManager", + + options: { + // Set the uploadURL to the URL for posting files to your server. + uploadURL: "'.$_base_href.'include/lib/upload.php?path='.urlencode($current_path.$pathext).'", + + // This option points to the location of the SWFUpload Flash object that ships with Fluid Infusion. + flashURL: "jscripts/infusion/lib/swfupload/flash/swfupload.swf" + } + }, + + listeners: { + onFileSuccess: function (file, serverData){ + // example assumes that the server code passes the new image URL in the serverData + window.location="'.$_SERVER['PHP_SELF'].'?pathext=' . urlencode($pathext) . SEP . 'popup=' . $popup . SEP . 'framed=' . $framed . SEP . 'msg=FILEUPLOAD_DONE"; + } + }, + + decorators: [{ + type: "fluid.swfUploadSetupDecorator", + options: { + // This option points to the location of the Browse Files button used with Flash 10 clients. + flashButtonImageURL: "'.AT_BASE_HREF.'jscripts/infusion/components/uploader/images/browse.png" + } + }] + }); + }); + </script> + '; +} + +global $msg; +if (isset($_GET['msg'])) + $msg -> addFeedback($_GET['msg']); + +require('top.php'); +$_SESSION['done'] = 1; + +require(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager_display.inc.php'); + +closedir($dir); + +?> +<script type="text/javascript"> +//<!-- +function Checkall(form){ + for (var i = 0; i < form.elements.length; i++){ + eval("form.elements[" + i + "].checked = form.checkall.checked"); + } +} +function openWindow(page) { + newWindow = window.open(page, "progWin", "width=400,height=200,toolbar=no,location=no"); + newWindow.focus(); +} +//--> +</script> +<?php require(AT_INCLUDE_PATH.'footer.inc.php'); ?> \ No newline at end of file diff --git a/mods/_core/file_manager/module.php b/mods/_core/file_manager/module.php new file mode 100644 index 000000000..51e1da196 --- /dev/null +++ b/mods/_core/file_manager/module.php @@ -0,0 +1,30 @@ +<?php +if (!defined('AT_INCLUDE_PATH')) { exit; } +if (!isset($this) || (isset($this) && (strtolower(get_class($this)) != 'module'))) { exit(__FILE__ . ' is not a Module'); } + +define('AT_PRIV_FILES', $this->getPrivilege()); + +$this->_pages['mods/_core/file_manager/index.php']['title_var'] = 'file_manager'; +$this->_pages['mods/_core/file_manager/index.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_core/file_manager/index.php']['guide'] = 'instructor/?p=file_manager.php'; +$this->_pages['mods/_core/file_manager/index.php']['children'] = array('mods/_core/file_manager/new.php'); + + $this->_pages['mods/_core/file_manager/new.php']['title_var'] = 'create_new_file'; + $this->_pages['mods/_core/file_manager/new.php']['parent'] = 'mods/_core/file_manager/index.php'; + + $this->_pages['mods/_core/file_manager/zip.php']['title_var'] = 'zip_file_manager'; + $this->_pages['mods/_core/file_manager/zip.php']['parent'] = 'mods/_core/file_manager/index.php'; + + $this->_pages['mods/_core/file_manager/rename.php']['title_var'] = 'rename'; + $this->_pages['mods/_core/file_manager/rename.php']['parent'] = 'mods/_core/file_manager/index.php'; + + $this->_pages['mods/_core/file_manager/move.php']['title_var'] = 'move'; + $this->_pages['mods/_core/file_manager/move.php']['parent'] = 'mods/_core/file_manager/index.php'; + + $this->_pages['mods/_core/file_manager/edit.php']['title_var'] = 'edit'; + $this->_pages['mods/_core/file_manager/edit.php']['parent'] = 'mods/_core/file_manager/index.php'; + + $this->_pages['mods/_core/file_manager/delete.php']['title_var'] = 'delete'; + $this->_pages['mods/_core/file_manager/delete.php']['parent'] = 'mods/_core/file_manager/index.php'; + +?> \ No newline at end of file diff --git a/mods/_core/file_manager/module.xml b/mods/_core/file_manager/module.xml new file mode 100644 index 000000000..70bed1c9a --- /dev/null +++ b/mods/_core/file_manager/module.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<module version="0.1"> + <name lang="en">File Manager</name> + <description lang="en">Allows an instructor to upload and manage files for a course. Files can then be made available to students by linking them into content pages.</description> + <maintainers> + <maintainer> + <name>ATutor Team</name> + <email>info@atutor.ca</email> + </maintainer> + </maintainers> + <url>http://atutor.ca</url> + <license>GPL</license> + <release> + <version>0.1</version> + <privileges> + <instructor_privilege>create</instructor_privilege> + <admin_privilege></admin_privilege> + </privileges> + <date>2005-08-25</date> + <state>stable</state> + <notes>This is a core module.</notes> + </release> +</module> \ No newline at end of file diff --git a/mods/_core/file_manager/module_backup.php b/mods/_core/file_manager/module_backup.php new file mode 100644 index 000000000..73f7e86b1 --- /dev/null +++ b/mods/_core/file_manager/module_backup.php @@ -0,0 +1,6 @@ +<?php + +$dirs = array(); +$dirs['content/'] = AT_CONTENT_DIR . '?' . DIRECTORY_SEPARATOR; // course_id gets added to the end + +?> \ No newline at end of file diff --git a/mods/_core/file_manager/module_delete.php b/mods/_core/file_manager/module_delete.php new file mode 100644 index 000000000..ca285b588 --- /dev/null +++ b/mods/_core/file_manager/module_delete.php @@ -0,0 +1,9 @@ +<?php + +function file_manager_delete($course) { + $path = AT_CONTENT_DIR . $course . '/'; + clr_dir($path); + +} + +?> \ No newline at end of file diff --git a/mods/_core/file_manager/move.php b/mods/_core/file_manager/move.php new file mode 100644 index 000000000..f8e379c4e --- /dev/null +++ b/mods/_core/file_manager/move.php @@ -0,0 +1,208 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: move.php 7208 2008-01-09 16:07:24Z greg $ + +define('AT_INCLUDE_PATH', '../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); +require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + +if (!authenticate(AT_PRIV_FILES,AT_PRIV_RETURN)) { + authenticate(AT_PRIV_CONTENT); +} + + +$current_path = AT_CONTENT_DIR.$_SESSION['course_id'].'/'; + +$popup = $_REQUEST['popup']; +$framed = $_REQUEST['framed']; + +if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_REQUEST['framed'].SEP.'popup='.$_REQUEST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; +} + +if (isset($_POST['submit_yes'])) { + $dest = $_POST['dest'] .'/'; + $pathext = $_POST['pathext']; + + if (isset($_POST['listofdirs'])) { + + $_dirs = explode(',',$_POST['listofdirs']); + $count = count($_dirs); + + for ($i = 0; $i < $count; $i++) { + $source = $_dirs[$i]; + + if (course_realpath($current_path . $pathext . $source) == FALSE) { + // error: File does not exist + $msg->addError('DIR_NOT_EXIST'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup.SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } + else if (course_realpath($current_path . $dest) == FALSE) { + // error: File does not exist + $msg->addError('UNKNOWN'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup.SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } + else if (strpos($source, '..') !== false) { + $msg->addError('UNKNOWN'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup.SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } + else { + @rename($current_path.$pathext.$source, $current_path.$dest.$source); + } + } + $msg->addFeedback('DIRS_MOVED'); + } + if (isset($_POST['listoffiles'])) { + + $_files = explode(',',$_POST['listoffiles']); + $count = count($_files); + + for ($i = 0; $i < $count; $i++) { + $source = $_files[$i]; + + if (course_realpath($current_path . $pathext . $source) == FALSE) { + // error: File does not exist + $msg->addError('FILE_NOT_EXIST'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup.SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } + else if (course_realpath($current_path . $dest) == FALSE) { + // error: File does not exist + $msg->addError('UNKNOWN'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup.SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } + else if (strpos($source, '..') !== false) { + $msg->addError('UNKNOWN'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup.SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } + else { + @rename($current_path.$pathext.$source, $current_path.$dest.$source); + } + } + $msg->addFeedback('MOVED_FILES'); + } + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; +} + +if (isset($_POST['dir_chosen'])) { + $hidden_vars['framed'] = $_REQUEST['framed']; + $hidden_vars['popup'] = $_REQUEST['popup']; + $hidden_vars['pathext'] = $_REQUEST['pathext']; + $hidden_vars['dest'] = $_REQUEST['dir_name']; + $hidden_vars['cp'] = $_REQUEST['cp']; + $hidden_vars['cid'] = $_REQUEST['cid']; + $hidden_vars['pid'] = $_REQUEST['pid']; + $hidden_vars['a_type'] = $_REQUEST['a_type']; + + if (isset($_POST['files'])) { + $list_of_files = implode(',', $_POST['files']); + $hidden_vars['listoffiles'] = $list_of_files; + $msg->addConfirm(array('FILE_MOVE', $list_of_files, $_POST['dir_name']), $hidden_vars); + } + if (isset($_POST['dirs'])) { + $list_of_dirs = implode(',', $_POST['dirs']); + $hidden_vars['listoffiles'] = $list_of_dirs; + $msg->addConfirm(array('DIR_MOVE', $list_of_dirs, $_POST['dir_name']), $hidden_vars); + } + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printConfirm(); + require(AT_INCLUDE_PATH.'footer.inc.php'); +} +else { + require(AT_INCLUDE_PATH.'header.inc.php'); + + $tree = AT_CONTENT_DIR.$_SESSION['course_id'].'/'; + $file = $_GET['file']; + $pathext = $_GET['pathext']; + $popup = $_GET['popup']; + $framed = $_GET['framed']; + $cp = $_GET['cp']; + $cid = $_GET['cid']; + $pid = $_GET['pid']; + $a_type = $_GET['a_type']; + + /* find the files and directories to be copied */ + $total_list = explode(',', $_GET['list']); + + $count = count($total_list); + $countd = 0; + $countf = 0; + for ($i=0; $i<$count; $i++) { + if (is_dir($current_path.$pathext.$total_list[$i])) { + $_dirs[$countd] = $total_list[$i]; + $hidden_dirs .= '<input type="hidden" name="dirs['.$countd.']" value="'.$_dirs[$countd].'" />'; + $countd++; + } else { + $_files[$countf] = $total_list[$i]; + $hidden_files .= '<input type="hidden" name="files['.$countf.']" value="'.$_files[$countf].'" />'; + $countf++; + } + } +?> + +<form name="move_form" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>"> +<div class="input-form"> + <div class="row"> + <p><?php echo _AT('select_directory'); ?></p> + </div> + + <div class="row"> + <ul> + <li class="folders"><label><input type="radio" name="dir_name" value=""<?php + if ($pathext == '') { + echo ' checked="checked"'; + $here = ' ' . _AT('current_location'); + } + echo '/>Home ' .$here.'</label>'; + + echo display_tree($current_path, '', $pathext); + ?></li> + </ul> + </div> + + <div class="row buttons"> + <input type="submit" name="dir_chosen" value="<?php echo _AT('move'); ?>" accesskey="s" /> + <input type="submit" name="cancel" value="<?php echo _AT('cancel'); ?>" /> + </div> +</div> + +<input type="hidden" name="pathext" value="<?php echo $pathext; ?>" /> +<input type="hidden" name="framed" value="<?php echo $framed; ?>" /> +<input type="hidden" name="popup" value="<?php echo $popup; ?>" /> +<input type="hidden" name="cp" value="<?php echo $cp; ?>" /> +<input type="hidden" name="cid" value="<?php echo $cid; ?>" /> +<input type="hidden" name="pid" value="<?php echo $pid; ?>" /> +<input type="hidden" name="a_type" value="<?php echo $a_type; ?>" /> + <?php + echo $hidden_dirs; + echo $hidden_files; +?> +</form> + +<?php require(AT_INCLUDE_PATH.'footer.inc.php'); +} +?> \ No newline at end of file diff --git a/mods/_core/file_manager/new.php b/mods/_core/file_manager/new.php new file mode 100644 index 000000000..ce9b6be0f --- /dev/null +++ b/mods/_core/file_manager/new.php @@ -0,0 +1,249 @@ +<?php +/****************************************************************/ +/* ATutor */ +/****************************************************************/ +/* Copyright (c) 2002-2010 */ +/* Inclusive Design Institute */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/****************************************************************/ +// $Id: new.php 8952 2009-11-20 21:18:51Z cindy $ + +define('AT_INCLUDE_PATH', '../../../include/'); +require(AT_INCLUDE_PATH.'vitals.inc.php'); +require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + +if (!authenticate(AT_PRIV_FILES,AT_PRIV_RETURN)) { + authenticate(AT_PRIV_CONTENT); +} + + +$current_path = AT_CONTENT_DIR.$_SESSION['course_id'].'/'; + +$popup = $_REQUEST['popup']; +$framed = $_REQUEST['framed']; + + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup']); + exit; +} + +if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup']); + exit; +} + +if (isset($_POST['submit_yes'])) { + $filename = preg_replace("{[^a-zA-Z0-9_]}","_", trim($_POST['filename'])); + $pathext = $_POST['pathext']; + + /* only html or txt extensions allowed */ + if ($_POST['extension'] == 'html') { + $extension = 'html'; + } else { + $extension = 'txt'; + } + + if (course_realpath($current_path . $pathext . $filename.'.'.$extension) == FALSE) { + $msg->addError('FILE_NOT_SAVED'); + /* take user to home page to avoid unspecified error warning */ + header('Location: index.php?pathext='.SEP.'framed='.$framed.SEP.'popup='.$popup); + exit; + } + + if (($f = @fopen($current_path.$pathext.$filename.'.'.$extension,'w')) && @fwrite($f, stripslashes($_POST['body_text'])) !== FALSE && @fclose($f)){ + $msg->addFeedback('FILE_OVERWRITE'); + } else { + $msg->addError('CANNOT_OVERWRITE_FILE'); + } + unset($_POST['newfile']); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup); + exit; +} + +if (isset($_POST['savenewfile'])) { + + if (isset($_POST['filename']) && ($_POST['filename'] != "")) { + $filename = preg_replace("{[^a-zA-Z0-9_]}","_", trim($_POST['filename'])); + $pathext = $_POST['pathext']; + $current_path = AT_CONTENT_DIR.$_SESSION['course_id'].'/'; + + /* only html or txt extensions allowed */ + if ($_POST['extension'] == 'html') { + $extension = 'html'; + $head_html = "<html>\n<head>\n<title>".$_POST['filename']."\n\n"; + $foot_html ="\n\n"; + } else { + $extension = 'txt'; + } + + if (!@file_exists($current_path.$pathext.$filename.'.'.$extension)) { + $content = str_replace("\r\n", "\n", $head_html.$_POST['body_text'].$foot_html); + + if (course_realpath($current_path . $pathext . $filename.'.'.$extension) == FALSE) { + $msg->addError('FILE_NOT_SAVED'); + /* take user to home page to avoid unspecified error warning */ + header('Location: index.php?pathext='.SEP.'framed='.$framed.SEP.'popup='.$popup); + exit; + } + + if (($f = fopen($current_path.$pathext.$filename.'.'.$extension, 'w')) && (@fwrite($f, stripslashes($content)) !== false) && (@fclose($f))) { + $msg->addFeedback(array('FILE_SAVED', $filename.'.'.$extension)); + header('Location: index.php?pathext='.urlencode($_POST['pathext']).SEP.'popup='.$_POST['popup']); + exit; + } else { + $msg->addError('FILE_NOT_SAVED'); + header('Location: index.php?pathext='.$pathext.SEP.'framed='.$framed.SEP.'popup='.$popup); + exit; + } + } + else { + require(AT_INCLUDE_PATH.'header.inc.php'); + $pathext = $_POST['pathext']; + $popup = $_POST['popup']; + + $_POST['newfile'] = "new"; + + $hidden_vars['pathext'] = $pathext; + $hidden_vars['filename'] = $filename; + $hidden_vars['extension'] = $extension; + $hidden_vars['body_text'] = $_POST['body_text']; + + $hidden_vars['popup'] = $popup; + $hidden_vars['framed'] = $framed; + + $msg->addConfirm(array('FILE_EXISTS', $filename.'.'.$extension), $hidden_vars); + $msg->printConfirm(); + + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + } else { + $msg->addError(array('EMPTY_FIELDS', _AT('file_name'))); + } +} + +$onload="on_load()"; + +require(AT_INCLUDE_PATH.'header.inc.php'); +require(AT_INCLUDE_PATH.'lib/tinymce.inc.php'); + +if (!isset($_REQUEST['setvisual']) && !isset($_REQUEST['settext'])) { + if ($_SESSION['prefs']['PREF_CONTENT_EDITOR'] == 1) { + $_POST['formatting'] = 1; + $_REQUEST['settext'] = 0; + $_REQUEST['setvisual'] = 0; + + } else if ($_SESSION['prefs']['PREF_CONTENT_EDITOR'] == 2) { + $_POST['formatting'] = 1; + $_POST['settext'] = 0; + $_POST['setvisual'] = 1; + + } else { // else if == 0 + $_POST['formatting'] = 0; + $_REQUEST['settext'] = 0; + $_REQUEST['setvisual'] = 0; + } +} + +// load tinymce library +load_editor(false, false, "none"); + +$pathext = $_GET['pathext']; +$popup = $_GET['popup']; + +$msg->printAll(); +if (!$_POST['extension']) { + $_POST['extension'] = 'txt'; + $_POST['formatting'] = 0; +}else if($_POST['extension'] == "html"){ + $_POST['formatting'] = 1; +} + +?> +
    + + + +
    +
    +
    + *
    + /> +
    + +
    + *
    + onclick="javascript: document.form.setvisualbutton.disabled=true;" /> + + + , onclick="javascript: document.form.setvisualbutton.disabled=false;"/> + + + + + +
    + +
    +
    + +
    + +
    + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/mods/_core/file_manager/preview.php b/mods/_core/file_manager/preview.php new file mode 100644 index 000000000..68ef317f9 --- /dev/null +++ b/mods/_core/file_manager/preview.php @@ -0,0 +1,46 @@ + + + + + <?php echo _AT('file_manager_frame'); ?> + + + + + + + + + + <p><?php echo _AT('frame_contains'); ?><br /> + * <a href="../mods/_core/file_manager/file_manager.php"><?php echo _AT('file_manager'); ?></a> + </p> + + + + \ No newline at end of file diff --git a/mods/_core/file_manager/preview_top.php b/mods/_core/file_manager/preview_top.php new file mode 100644 index 000000000..c89526134 --- /dev/null +++ b/mods/_core/file_manager/preview_top.php @@ -0,0 +1,44 @@ + + + + + <?php echo _AT('file_manager_frame'); ?> + + + + +

    + + + + | + + | + +

    + + + \ No newline at end of file diff --git a/mods/_core/file_manager/rename.php b/mods/_core/file_manager/rename.php new file mode 100644 index 000000000..5f0f3cda2 --- /dev/null +++ b/mods/_core/file_manager/rename.php @@ -0,0 +1,100 @@ +addFeedback('CANCELLED'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; +} + +if (isset($_POST['rename_action'])) { + + $_POST['new_name'] = trim($_POST['new_name']); + $_POST['new_name'] = str_replace(' ', '_', $_POST['new_name']); + $_POST['new_name'] = str_replace(array(' ', '/', '\\', ':', '*', '?', '"', '<', '>', '|', '\''), '', $_POST['new_name']); + + $_POST['oldname'] = trim($_POST['oldname']); + $_POST['oldname'] = str_replace(' ', '_', $_POST['oldname']); + $_POST['oldname'] = str_replace(array(' ', '/', '\\', ':', '*', '?', '"', '<', '>', '|', '\''), '', $_POST['oldname']); + + $path_parts_new = pathinfo($_POST['new_name']); + $ext_new = $path_parts_new['extension']; + $pathext = $_POST['pathext']; + + /* check if this file extension is allowed: */ + /* $IllegalExtentions is defined in ./include/config.inc.php */ + if (in_array($ext_new, $IllegalExtentions)) { + $errors = array('FILE_ILLEGAL', $ext_new); + $msg->addError($errors); + } + else if ($current_path.$pathext.$_POST['new_name'] == $current_path.$pathext.$_POST['oldname']) { + //do nothing + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php?pathext='.urlencode($_POST['pathext']).SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } + + //make sure new file is inside content directory + else if (course_realpath($current_path . $pathext . $_POST['new_name']) == FALSE) { + $msg->addError('CANNOT_RENAME'); + } + else if (course_realpath($current_path . $pathext . $_POST['oldname']) == FALSE) { + $msg->addError('CANNOT_RENAME'); + } + else if (file_exists($current_path . $pathext . $_POST['new_name'])) { + $msg->addError('CANNOT_RENAME'); + } + else { + @rename($current_path.$pathext.$_POST['oldname'], $current_path.$pathext.$_POST['new_name']); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php?pathext='.urlencode($_POST['pathext']).SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup'].SEP.'cp='.$_POST['cp'].SEP.'cid='.$_POST['cid'].SEP.'pid='.$_POST['pid'].SEP.'a_type='.$_POST['a_type']); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> +
    + + + + + +
    +
    + * +
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/file_manager/top.php b/mods/_core/file_manager/top.php new file mode 100644 index 000000000..27f2d4df8 --- /dev/null +++ b/mods/_core/file_manager/top.php @@ -0,0 +1,186 @@ +addError('NO_ITEM_SELECTED'); + } else if (count($_POST['check']) < 1) { + // error: you must select one file/dir to rename + $msg->addError('NO_ITEM_SELECTED'); + } else if (count($_POST['check']) > 1) { + // error: you must select ONLY one file/dir to rename + $msg->addError('SELECT_ONE_ITEM'); + } else { + header('Location: rename.php?pathext='.urlencode($_POST['pathext']).SEP.'framed='.$framed.SEP.'popup='.$popup.SEP.'oldname='.urlencode($_POST['check'][0]).SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type']); + exit; + } +} else if (isset($_POST['delete'])) { + + if (!is_array($_POST['check'])) { + $msg->addError('NO_ITEM_SELECTED'); + } else { + + $list = implode(',', $_POST['check']); + header('Location: delete.php?pathext=' . urlencode($_POST['pathext']) . SEP . 'framed=' . $framed . SEP . 'popup=' . $popup . SEP . 'list=' . urlencode($list).SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type']); + exit; + } +} else if (isset($_POST['move'])) { + + if (!is_array($_POST['check'])) { + $msg->addError('NO_ITEM_SELECTED'); + } else { + + $list = implode(',', $_POST['check']); + header('Location: move.php?pathext='.urlencode($_POST['pathext']).SEP.'framed='.$framed.SEP.'popup='.$popup.SEP.'list='.urlencode($list).SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type']); + exit; + } +} + +$MakeDirOn = true; + +/* get this courses MaxQuota and MaxFileSize: */ +$sql = "SELECT max_quota, max_file_size FROM ".TABLE_PREFIX."courses WHERE course_id=$_SESSION[course_id]"; +$result = mysql_query($sql, $db); +$row = mysql_fetch_array($result); +$my_MaxCourseSize = $row['max_quota']; +$my_MaxFileSize = $row['max_file_size']; + +if ($my_MaxCourseSize == AT_COURSESIZE_DEFAULT) { + $my_MaxCourseSize = $MaxCourseSize; +} +if ($my_MaxFileSize == AT_FILESIZE_DEFAULT) { + $my_MaxFileSize = $MaxFileSize; +} else if ($my_MaxFileSize == AT_FILESIZE_SYSTEM_MAX) { + $my_MaxFileSize = megabytes_to_bytes(substr(ini_get('upload_max_filesize'), 0, -1)); +} + +$MaxSubDirs = 5; +$MaxDirDepth = 10; + +if ($_GET['pathext'] != '') { + $pathext = urldecode($_GET['pathext']); +} else if ($_POST['pathext'] != '') { + $pathext = $_POST['pathext']; +} + +if (strpos($pathext, '..') !== false) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('UNKNOWN'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +if($_GET['back'] == 1) { + $pathext = substr($pathext, 0, -1); + $slashpos = strrpos($pathext, '/'); + if($slashpos == 0) { + $pathext = ''; + } else { + $pathext = substr($pathext, 0, ($slashpos+1)); + } + +} + +$start_at = 2; +/* remove the forward or backwards slash from the path */ +$newpath = $current_path; +$depth = substr_count($pathext, '/'); + +if ($pathext != '') { + $bits = explode('/', $pathext); + foreach ($bits as $bit) { + if ($bit != '') { + $bit_path .= $bit; + + $_section[$start_at][0] = $bit; + $_section[$start_at][1] = '../mods/_core/file_manager/index.php?pathext=' . urlencode($bit_path) . SEP . 'popup=' . $popup . SEP . 'framed=' . $framed; + + $start_at++; + } + } + $bit_path = ""; + $bit = ""; +} + +/* if upload successful, close the window */ +if ($f) { + $onload = 'closeWindow(\'progWin\');'; +} + +/* make new directory */ +if ($_POST['mkdir_value'] && ($depth < $MaxDirDepth) ) { + $_POST['dirname'] = trim($_POST['dirname']); + + /* anything else should be okay, since we're on *nix..hopefully */ + $_POST['dirname'] = preg_replace('/[^a-zA-Z0-9._]/', '', $_POST['dirname']); + + if ($_POST['dirname'] == '') { + $msg->addError(array('FOLDER_NOT_CREATED', $_POST['dirname'] )); + } + else if (strpos($_POST['dirname'], '..') !== false) { + $msg->addError('BAD_FOLDER_NAME'); + } + else { + $result = @mkdir($current_path.$pathext.$_POST['dirname'], 0700); + if($result == 0) { + $msg->addError(array('FOLDER_NOT_CREATED', $_POST['dirname'] )); + } + else { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + } +} + +$newpath = substr($current_path.$pathext, 0, -1); + +/* open the directory */ +if (!($dir = @opendir($newpath))) { + if (isset($_GET['create']) && ($newpath.'/' == $current_path)) { + @mkdir($newpath); + if (!($dir = @opendir($newpath))) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('CANNOT_CREATE_DIR'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } else { + $msg->addFeedback('CONTENT_DIR_CREATED'); + } + } else { + require(AT_INCLUDE_PATH.'header.inc.php'); + + $msg->printErrors('CANNOT_OPEN_DIR'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/file_manager/upload.php b/mods/_core/file_manager/upload.php new file mode 100644 index 000000000..5e8531930 --- /dev/null +++ b/mods/_core/file_manager/upload.php @@ -0,0 +1,173 @@ +addError($errors); + header('Location: index.php?pathext='.$_POST['pathext'].SEP. 'framed='.$framed.SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type']); + exit; + } + + /* also have to handle the 'application/x-zip-compressed' case */ + if ( ($_FILES['uploadedfile']['type'] == 'application/x-zip-compressed') + || ($_FILES['uploadedfile']['type'] == 'application/zip') + || ($_FILES['uploadedfile']['type'] == 'application/x-zip')){ + $is_zip = true; + } + + + /* anything else should be okay, since we're on *nix.. hopefully */ + $_FILES['uploadedfile']['name'] = str_replace(array(' ', '/', '\\', ':', '*', '?', '"', '<', '>', '|', '\''), '', $_FILES['uploadedfile']['name']); + + + /* if the file size is within allowed limits */ + if( ($_FILES['uploadedfile']['size'] > 0) && ($_FILES['uploadedfile']['size'] <= $my_MaxFileSize) ) { + + /* if adding the file will not exceed the maximum allowed total */ + $course_total = dirsize($path); + + if ((($course_total + $_FILES['uploadedfile']['size']) <= ($my_MaxCourseSize + $MaxCourseFloat)) || ($my_MaxCourseSize == AT_COURSESIZE_UNLIMITED)) { + + /* check if this file exists first */ + if (file_exists($path.$_FILES['uploadedfile']['name'])) { + /* this file already exists, so we want to prompt for override */ + + /* save it somewhere else, temporarily first */ + /* file_name.time ? */ + $_FILES['uploadedfile']['name'] = substr(time(), -4).'.'.$_FILES['uploadedfile']['name']; + + $f = array('FILE_EXISTS', + substr($_FILES['uploadedfile']['name'], 5), + $_FILES['uploadedfile']['name'], + $_POST['pathext'], + $_GET['popup'], + SEP); + $msg->addFeedback($f); + } + + /* copy the file in the directory */ + $result = move_uploaded_file( $_FILES['uploadedfile']['tmp_name'], $path.$_FILES['uploadedfile']['name'] ); + + if (!$result) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('FILE_NOT_SAVED'); + echo '' . _AT('back') . ''; + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } else { + if ($is_zip) { + $f = array('FILE_UPLOADED_ZIP', + urlencode($_POST['pathext']), + urlencode($_FILES['uploadedfile']['name']), + $_GET['popup'], + SEP); + $msg->addFeedback($f); + if ($alter) + header('Location: '.$_base_href.'editor/edit_content.php?cid='.$_REQUEST['cid'].SEP . 'pathext='.$_POST['pathext'].SEP. 'popup='.$_GET['popup'].SEP. 'tab='.$_REQUEST['tab']); + else + header('Location: index.php?pathext=' . $_POST['pathext'] . SEP . 'popup=' . $_GET['popup'].SEP. 'framed='.$framed.SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type']); + exit; + } /* else */ + + // uploading an alternative content object + if ($_GET['a_type'] > 0) { + header('Location: index.php?pathext=' . $_POST['pathext'] . SEP . 'popup=' . $_GET['popup'].SEP. 'framed='.$framed.SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type'].SEP.'uploadfile='.urlencode($_FILES['uploadedfile']['name'])); + } + else { + $msg->addFeedback('FILE_UPLOADED'); + + if ($alter) + header('Location: '.$_base_href.'editor/edit_content.php?cid='.$_REQUEST['cid'].SEP . 'pathext='.$_POST['pathext'].SEP. 'popup='.$_GET['popup'].SEP. 'tab='.$_REQUEST['tab']); + else + header('Location: index.php?pathext=' . $_POST['pathext'] . SEP . 'popup=' . $_GET['popup'].SEP. 'framed='.$framed.SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type']); + } + exit; + } + } else { + $msg->addError(array('MAX_STORAGE_EXCEEDED', get_human_size($my_MaxCourseSize))); + if ($alter) + header('Location: '.$_base_href.'editor/edit_content.php?cid='.$_REQUEST['cid'].SEP . 'pathext='.$_POST['pathext'].SEP. 'popup='.$_GET['popup'].SEP. 'tab='.$_REQUEST['tab']); + else + header('Location: index.php?pathext=' . $_POST['pathext'] . SEP . 'popup=' . $_GET['popup'].SEP. 'framed='.$framed.SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type']); + + exit; + } + } else { + $msg->addError(array('FILE_TOO_BIG', get_human_size($my_MaxFileSize))); + if ($alter) + header('Location: '.$_base_href.'editor/edit_content.php?cid='.$_REQUEST['cid'].SEP . 'pathext='.$_POST['pathext'].SEP. 'popup='.$_GET['popup'].SEP. 'tab='.$_REQUEST['tab']); + else + header('Location: index.php?pathext=' . $_POST['pathext'] . SEP . 'popup=' . $_GET['popup'].SEP. 'framed='.$framed.SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type']); + + exit; + } + } else { + $msg->addError('FILE_NOT_SELECTED'); + if ($alter) + header('Location: '.$_base_href.'editor/edit_content.php?cid='.$_REQUEST['cid'].SEP . 'pathext='.$_POST['pathext'].SEP. 'popup='.$_GET['popup'].SEP. 'tab='.$_REQUEST['tab']); + else + header('Location: index.php?pathext=' . $_POST['pathext'] . SEP . 'popup=' . $_GET['popup'].SEP. 'framed='.$framed.SEP.'cp='.$_GET['cp'].SEP.'pid='.$_GET['pid'].SEP.'cid='.$_GET['cid'].SEP.'a_type='.$_GET['a_type']); + exit; + } +} + +?> \ No newline at end of file diff --git a/mods/_core/file_manager/zip.php b/mods/_core/file_manager/zip.php new file mode 100644 index 000000000..ea41fd520 --- /dev/null +++ b/mods/_core/file_manager/zip.php @@ -0,0 +1,291 @@ +addFeedback('CANCELLED'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'popup='.$_POST['popup'].SEP.'framed='.$_POST['framed']); + exit; +} + + $path = AT_CONTENT_DIR . $_SESSION['course_id'].'/'; + + if ($_REQUEST['pathext'] != '') { + $pathext = $_REQUEST['pathext']; + } + if ($_REQUEST['file'] != '') { + $file = $_REQUEST['file']; + } + + if (strpos($file, '..') !== false) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('UNKNOWN'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + + $path_parts = pathinfo($pathext.$file); + + $temp_name = substr($file, 0, -strlen('.'.$path_parts['extension'])); + + $zip = new PclZip($path.$pathext.$file); + + if (($list = $zip->listContent()) == 0) { + die("Error : ".$zip->errorInfo(true)); + } + +/*****************************************************************/ + $totalBytes = 0; + $translated_file_names = array(); + + for ($i=0; $i'; + + $is_dir = true; + + } else if ($ext == 'zip') { + + $totalBytes += $list[$i]['size']; + $filename = $list[$i]['stored_filename']; + $fileicon = ''._AT('zip_archive').''; + + } else { + $totalBytes += $list[$i]['size']; + $filename = $list[$i]['stored_filename']; + $fileicon = ''._AT('file').''; + } + + if ($is_dir) { + $dirs[strtolower($filename)] .= ' + '.$filename.''; + + $dirs[strtolower($filename)] .= ''.get_human_size($list[$i]['size']).' '; + $dirs[strtolower($filename)] .= ' '; + + $dirs[strtolower($filename)] .= AT_date(_AT('filemanager_date_format'), $filedata[10], AT_DATE_UNIX_TIMESTAMP); + + $dirs[strtolower($filename)] .= ' '; + + $dirs[strtolower($filename)] .= ''; + } else { + + $files[strtolower($filename)] .= ' + '; + + if (in_array($ext, $IllegalExtentions)) { + $files[strtolower($filename)] .= ''.$filename.''; + } else { + $files[strtolower($filename)] .= $filename; + + $trans_name = str_replace(' ', '_', $path_parts['basename']); + $trans_name = preg_replace("/[^A-Za-z0-9._\-]/", '', $trans_name); + + if (in_array($path_parts['dirname'].$trans_name, $translated_file_names)) { + $trans_count = 2; + while (in_array($trans_name, $translated_file_names)) { + $part = substr($trans_name, 0, -strlen($ext)- 1 - (2*($trans_count-2))); + $trans_name = $part.'_'.$trans_count.'.'.$ext; + $trans_count++; + if ($trans_count>15){ + exit; // INF loop safety thing.. + } + } + } + + $translated_file_names[$list[$i]['index']] = $path_parts['dirname'].$trans_name; + + if ($path_parts['dirname'].$trans_name != $filename) { + $files[strtolower($filename)] .= ' => '.$trans_name; + } + + } + + $files[strtolower($filename)] .= ''; + + $files[strtolower($filename)] .= ''.get_human_size($list[$i]['size']).' '; + $files[strtolower($filename)] .= ' '; + + $files[strtolower($filename)] .= AT_date(_AT('filemanager_date_format'), $list[$i]['mtime'], AT_DATE_UNIX_TIMESTAMP); + + $files[strtolower($filename)] .= ''; + + $files[strtolower($filename)] .= ''; + } + } + + $sql = "SELECT max_quota, max_file_size FROM ".TABLE_PREFIX."courses WHERE course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $my_MaxCourseSize = $row['max_quota']; + $my_MaxFileSize = $row['max_file_size']; + + $course_total = dirsize($path); + if ($my_MaxCourseSize == AT_COURSESIZE_UNLIMITED) { + $total_after = 1; + } else if ($my_MaxCourseSize == AT_COURSESIZE_DEFAULT) { + $my_MaxCourseSize = $MaxCourseSize; + $total_after = get_human_size($my_MaxCourseSize-$course_total-$totalBytes); + }else{ + $total_after = get_human_size($my_MaxCourseSize - $course_total - $totalBytes); + } + + // if $total_after < 0: redirect with error msg + + if (isset($_POST['submit']) && ($total_after > 0)) { + $_POST['custom_path'] = trim($_POST['custom_path']); + $_POST['custom_path'] = str_replace(' ', '_', $_POST['custom_path']); + + /* anything else should be okay, since we're on *nix.. hopefully */ + $_POST['custom_path'] = preg_replace('/[^a-zA-Z0-9._\/]/', '', $_POST['custom_path']); + + if (strpos($_POST['pathext'].$_POST['custom_path'], '..') !== false) { + $msg->addError('UNKNOWN'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'framed='.$_POST['framed'].SEP.'popup='.$_POST['popup']); + exit; + } else if ($zip->extract( PCLZIP_OPT_PATH, $path. $_POST['pathext'] . $_POST['custom_path'], + PCLZIP_CB_PRE_EXTRACT, 'preExtractCallBack') == 0) { + + echo ("Error : ".$zip->errorInfo(true)); + } else { + $msg->addFeedback('ARCHIVE_EXTRACTED'); + header('Location: index.php?pathext='.$_POST['pathext'].SEP.'popup='.$_POST['popup'].SEP.'framed='.$_POST['framed']); + exit; + } + + header('Location: index.php'); + exit; + } + + require(AT_INCLUDE_PATH.'header.inc.php'); + + if (($my_MaxCourseSize != AT_COURSESIZE_UNLIMITED) && ($total_after + $MaxCourseFloat <= 0)) { + $msg->printErrors('NO_SPACE_LEFT'); + } else { +?> +
    + + + + +
    +
    +

    +

    +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    + + + + + + + + + + + + $y) { + echo $y; + } + } + + if (is_array($files)) { + foreach($files as $x => $y) { + echo $y; + } + } +?> + + + + + + + + + + + + + + + + + + + + + + + +
    : 
    : 
    : 
    :'; + echo $total_after; + echo ''; + } else { + echo $total_after; + } + } ?> 
    + + \ No newline at end of file diff --git a/mods/_core/glossary/dropdown/glossary.inc.php b/mods/_core/glossary/dropdown/glossary.inc.php new file mode 100644 index 000000000..fbaaca357 --- /dev/null +++ b/mods/_core/glossary/dropdown/glossary.inc.php @@ -0,0 +1,81 @@ +getContentPage($_GET['cid']); +} +if ($result && ($row = mysql_fetch_array($result))) { + $matches = find_terms($row['text']); + $matches = $matches[0]; + $words = str_replace(array('[?]', '[/?]'), '', $matches); + $words = str_replace("\n", ' ', $words); + + //case-insensitive, unique array of words + for($i=0;$i 0) { + $count = 0; + + $glossary_key_lower = array_change_key_case($glossary); + + foreach ($words as $k => $v) { + $original_v = $v; + $v = $strtolower(urlencode($v)); //array_change_key_case change everything to lowercase, including encoding. + + if (isset($glossary_key_lower[$v]) && $glossary_key_lower[$v] != '') { + + $v_formatted = urldecode(array_search($glossary_key_lower[$v], $glossary)); + + $def = htmlentities(AT_print($glossary_key_lower[$v], 'glossary.definition'), ENT_QUOTES, 'UTF-8'); + + $count++; + echo ''; + if ($strlen($original_v) > 26 ) { + $v_formatted = $substr($v_formatted, 0, 26-4).'...'; + } + echo AT_print($v_formatted, 'glossary.word').''; + echo '
    '; + } + } + + if ($count == 0) { + /* there are defn's, but they're not defined in the glossary */ + echo ''._AT('no_terms_found').''; + } + } else { + /* there are no glossary terms on this page */ + echo ''._AT('no_terms_found').''; + } +} else { + /* there are no glossary terms in the system for this course or error */ + echo ''._AT('na').''; +} + +$savant->assign('dropdown_contents', ob_get_contents()); +ob_end_clean(); + +$savant->assign('title', _AT('glossary')); +$savant->display('include/box.tmpl.php'); +?> diff --git a/mods/_core/glossary/index.php b/mods/_core/glossary/index.php new file mode 100644 index 000000000..697ef643e --- /dev/null +++ b/mods/_core/glossary/index.php @@ -0,0 +1,146 @@ +0 AND course_id=$_SESSION[course_id] ORDER BY related_word_id"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_array($result)) { + $glossary_related[$row['related_word_id']][] = $row['word_id']; +} + +$_GET['w'] = isset($_GET['w']) ? stripslashes($_GET['w']) : ''; + +if ($_GET['w']) { + $sql = "SELECT * FROM ".TABLE_PREFIX."glossary WHERE course_id=$_SESSION[course_id] AND word='".addslashes(urldecode($_GET['w']))."'"; +} else { + $sql = "SELECT * FROM ".TABLE_PREFIX."glossary WHERE course_id=$_SESSION[course_id] ORDER BY word"; +} + +$result= mysql_query($sql, $db); + +if(mysql_num_rows($result) > 0){ + + $gloss_results = array(); + while ($row = mysql_fetch_assoc($result)) { + $gloss_results[] = $row; + } + $num_results = count($gloss_results); + $results_per_page = 25; + $num_pages = ceil($num_results / $results_per_page); + $page = isset($_GET['p']) ? intval($_GET['p']) : 0; + if (!$page) { + $page = 1; + } + + $count = (($page-1) * $results_per_page) + 1; + $gloss_results = array_slice($gloss_results, ($page-1)*$results_per_page, $results_per_page); + + if($num_pages > 1): + ?> +
    +
      + +
    • + + + + + +
    • + +
    +
    + + + + +'; + } + $current_letter = $strtoupper($substr($item['word'], 0, 1)); ?> +

    +
    + + +
    + + + + + + + + '.urldecode($glossary_ids[$item['related_word_id']]).''; + $output = true; + } + + if (is_array($glossary_related[urlencode($item['word_id'])]) ) { + $my_related = $glossary_related[$item['word_id']]; + + $num_related = count($my_related); + for ($i=0; $i<$num_related; $i++) { + if ($glossary_ids[$my_related[$i]] == $glossary_ids[$item['related_word_id']]) { + continue; + } + if ($output) { + echo ', '; + } + + echo ''.urldecode($glossary_ids[$my_related[$i]]).''; + + $output = true; + } + } + echo ')'; + endif; ?> +
    + +


    + + + + +
    + +
    '._AT('view_all').''; + + if ($_GET['g_cid']) { + $path = $contentManager->getContentPath(intval($_GET['g_cid'])); + echo ' | '._AT('back_to').' '.$path[0]['title'].''; + } + } + +} else { + echo '

    '._AT('no_glossary_items').'

    '; +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/glossary/module.php b/mods/_core/glossary/module.php new file mode 100644 index 000000000..34de5ccb8 --- /dev/null +++ b/mods/_core/glossary/module.php @@ -0,0 +1,46 @@ +getPrivilege()); + +//side menu +$this->_stacks['glossary'] = array('title_var'=>'glossary', 'file'=>AT_INCLUDE_PATH.'../mods/_core/glossary/dropdown/glossary.inc.php'); + +// modules sub-content +$this->_list['glossary'] = array('title_var'=>'glossary','file'=>'mods/_core/glossary/sublinks.php'); + +// if this module is to be made available to students on the Home or Main Navigation +$_student_tool = 'mods/_core/glossary/index.php'; + +$this->_pages['mods/_core/glossary/tools/index.php']['title_var'] = 'glossary'; +$this->_pages['mods/_core/glossary/tools/index.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_core/glossary/tools/index.php']['children'] = array('mods/_core/glossary/tools/add.php'); + + $this->_pages['mods/_core/glossary/tools/add.php']['title_var'] = 'add_glossary'; + $this->_pages['mods/_core/glossary/tools/add.php']['parent'] = 'mods/_core/glossary/tools/index.php'; + + $this->_pages['mods/_core/glossary/tools/edit.php']['title_var'] = 'edit_glossary'; + $this->_pages['mods/_core/glossary/tools/edit.php']['parent'] = 'mods/_core/glossary/tools/index.php'; + + $this->_pages['mods/_core/glossary/tools/delete.php']['title_var'] = 'delete_glossary'; + $this->_pages['mods/_core/glossary/tools/delete.php']['parent'] = 'mods/_core/glossary/tools/index.php'; + +//student pages +$this->_pages['mods/_core/glossary/index.php']['title_var'] = 'glossary'; +$this->_pages['mods/_core/glossary/index.php']['img'] = 'images/home-glossary.png'; +$this->_pages['mods/_core/glossary/index.php']['icon'] = 'images/home-glossary_sm.png'; + +?> \ No newline at end of file diff --git a/mods/_core/glossary/module.xml b/mods/_core/glossary/module.xml new file mode 100644 index 000000000..16116d01a --- /dev/null +++ b/mods/_core/glossary/module.xml @@ -0,0 +1,23 @@ + + + Glossary + This module allows the ability to define course-related words in a glossary. Glossary terms can provide a mouse-over definition within course content using special tags. Glossary terms can also be added as a step in the content creation/editing process. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + + + 2005-08-25 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/glossary/module_backup.php b/mods/_core/glossary/module_backup.php new file mode 100644 index 000000000..1c0348715 --- /dev/null +++ b/mods/_core/glossary/module_backup.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/mods/_core/glossary/module_delete.php b/mods/_core/glossary/module_delete.php new file mode 100644 index 000000000..61c64ec4a --- /dev/null +++ b/mods/_core/glossary/module_delete.php @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/mods/_core/glossary/module_news.php b/mods/_core/glossary/module_news.php new file mode 100644 index 000000000..16c0b007b --- /dev/null +++ b/mods/_core/glossary/module_news.php @@ -0,0 +1,30 @@ + + */ +function glossary_news() { + global $db; + $news = array(); + + $sql = 'SELECT * FROM '.TABLE_PREFIX.'glossary WHERE course_id='.$_SESSION['course_id'].' ORDER BY word_id DESC'; + $result = mysql_query($sql, $db); + if($result){ + //no time entry, and not a _standard module + } + return $news; +} + +?> \ No newline at end of file diff --git a/mods/_core/glossary/sublinks.php b/mods/_core/glossary/sublinks.php new file mode 100644 index 000000000..ec074617b --- /dev/null +++ b/mods/_core/glossary/sublinks.php @@ -0,0 +1,35 @@ + detail view + +$sql = "SELECT * FROM ".TABLE_PREFIX."glossary WHERE course_id = $_SESSION[course_id] LIMIT $record_limit"; +$result = mysql_query($sql, $db); + +if (mysql_num_rows($result) > 0) { + while ($row = mysql_fetch_assoc($result)) { + $list[] = ' SUBLINK_TEXT_LEN ? ' title="'.$row['word'].'"' : '') .'>'. + validate_length($row['word'], SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''; + } + return $list; + +} else { + return 0; +} + + +?> \ No newline at end of file diff --git a/mods/_core/glossary/tools/add.php b/mods/_core/glossary/tools/add.php new file mode 100644 index 000000000..c4b40acaf --- /dev/null +++ b/mods/_core/glossary/tools/add.php @@ -0,0 +1,158 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} + +if (isset($_POST['submit'])) { + $num_terms = intval($_POST['num_terms']); + $missing_fields = array(); + + for ($i=0; $i<$num_terms; $i++) { + + if ($_POST['ignore'][$i] == '') { + $_POST['word'][$i] = trim($_POST['word'][$i]); + $_POST['definition'][$i] = trim($_POST['definition'][$i]); + + if ($_POST['word'][$i] == '') { + $missing_fields[] = _AT('glossary_term'); + } else{ + //60 is defined by the sql + $_POST['word'][$i] = validate_length($_POST['word'][$i], 60); + } + + + if ($_POST['definition'][$i] == '') { + $missing_fields[] = _AT('glossary_definition'); + } + + if ($terms_sql != '') { + $terms_sql .= ', '; + } + + $_POST['related_term'][$i] = intval($_POST['related_term'][$i]); + + /* for each item check if it exists: */ + + if ($glossary[urlencode($_POST['word'][$i])] != '' ) { + $errors = array('TERM_EXISTS', $_POST['word'][$i]); + $msg->addError($errors); + } else { + $_POST['word'][$i] = $addslashes($_POST['word'][$i]); + $_POST['definition'][$i] = $addslashes($_POST['definition'][$i]); + $_POST['related_term'][$i] = $addslashes($_POST['related_term'][$i]); + + $terms_sql .= "(NULL, $_SESSION[course_id], '{$_POST[word][$i]}', '{$_POST[definition][$i]}', {$_POST[related_term][$i]})"; + } + } + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $sql = "INSERT INTO ".TABLE_PREFIX."glossary VALUES $terms_sql"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; + } + $_GET['pcid'] = $_POST['pcid']; +} + +$onload = 'document.form.title0.focus();'; + +unset($word); + +$num_terms = 1; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + +
    + +'; + continue; + } + + for ($j=0;$j<$i;$j++) { + if ($word[$j] == $word[$i]) { + echo ''; + continue 2; + } + } + + if ($word[$i] == '') { + $word[$i] = ContentManager::cleanOutput($_POST['word'][$i]); + } +?> +
    +
    +
    + *
    + .'; + } + ?> +
    + +
    + *
    + +
    + +
    +
    + '; + echo ''; + do { + echo ''; + } while ($row_g = mysql_fetch_assoc($result)); + echo ''; + } else { + echo _AT('none_available'); + } + } // endfor + ?> +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/glossary/tools/delete.php b/mods/_core/glossary/tools/delete.php new file mode 100644 index 000000000..01456637c --- /dev/null +++ b/mods/_core/glossary/tools/delete.php @@ -0,0 +1,70 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} + +if ($_POST['submit_yes']) { + + $_POST['gid'] = intval($_POST['gid']); + + $sql = "DELETE FROM ".TABLE_PREFIX."glossary WHERE word_id=$_POST[gid] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + + $sql = "UPDATE ".TABLE_PREFIX."glossary SET related_word_id=0 WHERE related_word_id=$_POST[gid] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} else if ($_POST['submit_no']) { + + $msg->addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$_GET['gid'] = intval($_GET['gid']); + +if ($_GET['gid'] == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$hidden_vars['word'] = $_GET['t']; +$hidden_vars['gid'] = $_GET['gid']; + +$sql = "SELECT * from ".TABLE_PREFIX."glossary WHERE word_id = '$hidden_vars[gid]'"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)){ + $title = $row['word']; +} + +$msg->addConfirm(array('DELETE', htmlentities_utf8($title)), $hidden_vars); +$msg->addConfirm('GLOSSARY_REMAINS', $hidden_vars); + +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/glossary/tools/edit.php b/mods/_core/glossary/tools/edit.php new file mode 100644 index 000000000..14df3f2f1 --- /dev/null +++ b/mods/_core/glossary/tools/edit.php @@ -0,0 +1,150 @@ +addFeedback('CANCELLED'); + Header('Location: index.php'); + exit; +} + +if ($_POST['submit']) { + $missing_fields = array(); + + $_POST['word'] = trim($_POST['word']); + $_POST['definition'] = trim($_POST['definition']); + + if ($_POST['word'] == '') { + $missing_fields[] = _AT('glossary_term'); + } else{ + //60 is defined by the sql + $_POST['word'] = validate_length($_POST['word'], 60); + } + + if ($_POST['definition'] == '') { + $missing_fields[] = _AT('glossary_definition'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + $_POST['related_term'] = intval($_POST['related_term']); + + + if (!$msg->containsErrors()) { + $_POST['word'] = $addslashes($_POST['word']); + $_POST['definition'] = $addslashes($_POST['definition']); + + $sql = "UPDATE ".TABLE_PREFIX."glossary SET word='$_POST[word]', definition='$_POST[definition]', related_word_id=$_POST[related_term] WHERE word_id=$_POST[gid] AND course_id=$_SESSION[course_id]"; + + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + Header('Location: index.php'); + exit; + } +} + +$onload = 'document.form.title.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($_POST['submit']) { + $gid = intval($_POST['gid']); +} else { + $gid = intval($_GET['gid']); +} + +if ($gid == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$msg->printErrors(); + +$result = mysql_query("SELECT * FROM ".TABLE_PREFIX."glossary WHERE word_id=$gid", $db); + +if (!( $row = @mysql_fetch_array($result)) ) { + $msg->printErrors('ITEM_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +if ($_POST['submit']) { + $row['word'] = $_POST['word']; + $row['definition'] = $_POST['definition']; +} + +?> + +
    + + +
    +
    +
    + *
    + +
    + +
    + *
    + +
    + +
    +
    + $gid ORDER BY word"; + + $result = mysql_query($sql, $db); + if ($row_g = mysql_fetch_array($result)) { + echo ''; + + } else { + echo _AT('no_glossary_items'); + } + ?> +
    +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/glossary/tools/index.php b/mods/_core/glossary/tools/index.php new file mode 100644 index 000000000..526be1235 --- /dev/null +++ b/mods/_core/glossary/tools/index.php @@ -0,0 +1,122 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +//get terms +$sql = "SELECT * FROM ".TABLE_PREFIX."glossary WHERE course_id=$_SESSION[course_id] ORDER BY word"; +$result= mysql_query($sql, $db); + +$gloss_results = array(); +while ($row = mysql_fetch_assoc($result)) { + $gloss_results[] = $row; +} +$num_results = count($gloss_results); +$results_per_page = 25; +$num_pages = ceil($num_results / $results_per_page); +$page = intval($_GET['p']); +if (!$page) { + $page = 1; +} + +$count = (($page-1) * $results_per_page) + 1; +$gloss_results = array_slice($gloss_results, ($page-1)*$results_per_page, $results_per_page); + +if($num_pages > 1) { + echo _AT('page').': '; + for ($i=1; $i<=$num_pages; $i++) { + if ($i == $page) { + echo ''.$i.''; + } else { + echo ' | '.$i.''; + } + } +} +?> + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    +
    + + \ No newline at end of file diff --git a/mods/_core/groups/create.php b/mods/_core/groups/create.php new file mode 100644 index 000000000..59d2f5a7a --- /dev/null +++ b/mods/_core/groups/create.php @@ -0,0 +1,46 @@ + +
    +
    +
    +
    + +
    + +
    + +
    + +
    + +
    +
    +
    +
    + \ No newline at end of file diff --git a/mods/_core/groups/create_automatic.php b/mods/_core/groups/create_automatic.php new file mode 100644 index 000000000..92d2bce7b --- /dev/null +++ b/mods/_core/groups/create_automatic.php @@ -0,0 +1,230 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + $modules = ''; + if (isset($_POST['modules'])) { + $modules = implode('|', $_POST['modules']); + } + + $_POST['type_title'] = trim($_POST['type_title']); + $_POST['num_students'] = abs($_POST['num_students']); + $_POST['num_groups'] = abs($_POST['num_groups']); + $_POST['num_g'] = intval($_POST['num_g']); + + $missing_fields = array(); + + if (!$_POST['type_title']) { + $missing_fields[] = _AT('groups_type'); + } + + if (!$_POST['prefix']) { + $missing_fields[] = _AT('group_prefix'); + } + + $course_owner = $system_courses[$_SESSION['course_id']]['member_id']; + if (isset($_POST['fill'])) { + $sql = "SELECT member_id FROM ".TABLE_PREFIX."course_enrollment WHERE course_id=$_SESSION[course_id] AND approved='y' AND `privileges`&".AT_PRIV_GROUPS."=0 AND member_id<>$course_owner"; + $result = mysql_query($sql, $db); + $total_students = mysql_num_rows($result); + $students = array(); + while ($row = mysql_fetch_assoc($result)) { + $students[] = $row['member_id']; + } + shuffle($students); + } else { + $sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."course_enrollment WHERE course_id=$_SESSION[course_id] AND approved='y' AND `privileges`&".AT_PRIV_GROUPS."=0 AND member_id<>$course_owner"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $total_students = $row['cnt']; // 4 students in the course + } + + if ($_POST['num_g'] == 1) { // number of students per group + $num_students_per_group = $_POST['num_students']; + + if ($num_students_per_group == 0) { + $missing_fields[] = _AT('number_of_students_per_group'); + } else { + if ($total_students == 0) { + $msg->addError('GROUP_NO_STUDENTS'); + } else { + $num_groups = ceil($total_students / $num_students_per_group); + } + } + } else { // number of groups + $num_groups = $_POST['num_groups']; + + if ($num_groups == 0) { + $missing_fields[] = _AT('number_of_groups'); + } else { + if ($total_students > 0) { + // to uniformly distribute all the groups we place the remaining students + // into the first n groups, where n is the number of remaining students. + $remainder = $total_students % $num_groups; + if ($remainder) { + $num_students_per_group = floor($total_students / $num_groups); + } else { + $num_students_per_group = $total_students / $num_groups; + } + } else { + $num_students_per_group = 0; + } + } + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $_POST['type_title'] = $addslashes($_POST['type_title']); + $_POST['prefix'] = $addslashes($_POST['prefix']); + $_POST['description'] = $addslashes($_POST['description']); + + $sql = "INSERT INTO ".TABLE_PREFIX."groups_types VALUES (NULL, $_SESSION[course_id], '$_POST[type_title]')"; + $result = mysql_query($sql, $db); + $group_type_id = mysql_insert_id($db); + + $start_index = 0; + + for($i=0; $i<$num_groups; $i++) { + $group_title = $_POST['prefix'] . ' ' . ($i + 1); + $sql = "INSERT INTO ".TABLE_PREFIX."groups VALUES (NULL, $group_type_id, '$group_title', '$_POST[description]', '$modules')"; + $result = mysql_query($sql, $db); + + $group_id = mysql_insert_id($db); + $_SESSION['groups'][$group_id] = $group_id; + + // call module init scripts: + if (isset($_POST['modules'])) { + foreach ($_POST['modules'] as $mod) { + $module =& $moduleFactory->getModule($mod); + $module->createGroup($group_id); + } + } + + if (isset($_POST['fill'])) { + // put students in this group + for ($j = $start_index; $j < min(($start_index + $num_students_per_group), $total_students); $j++) { + $sql = "INSERT INTO ".TABLE_PREFIX."groups_members VALUES ($group_id, $students[$j])"; + mysql_query($sql, $db); + } + + $start_index = $j; + if ($remainder) { + $sql = "INSERT INTO ".TABLE_PREFIX."groups_members VALUES ($group_id, $students[$start_index])"; + mysql_query($sql, $db); + $start_index++; + $remainder--; + } + } + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: index.php'); + exit; + } else { + $_POST['type_title'] = $stripslashes($_POST['type_title']); + $_POST['prefix'] = $stripslashes($_POST['prefix']); + $_POST['description'] = $stripslashes($_POST['description']); + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> + +
    +
    +
    +
    + *
    + +
    + +
    + *
    + +
    + +
    +
    + +
    + +
    + *
    + +

    + + +
    + +
    + +
    +
    + +
    + +
    +
    + getModules(AT_MODULE_STATUS_ENABLED, 0, TRUE); + $keys = array_keys($modules); + $i=0; + ?> + + + getGroupTool() && (in_array($module->getGroupTool(),$_pages[AT_NAV_HOME]) || in_array($module->getGroupTool(),$_pages[AT_NAV_COURSE])) ): ?> +
    + + +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/groups/create_manual.php b/mods/_core/groups/create_manual.php new file mode 100644 index 000000000..b5c656c32 --- /dev/null +++ b/mods/_core/groups/create_manual.php @@ -0,0 +1,151 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + $modules = ''; + if (isset($_POST['modules'])) { + $modules = implode('|', $_POST['modules']); + } + + $_POST['type'] = abs($_POST['type']); + $_POST['prefix'] = trim($_POST['prefix']); + $_POST['new_type'] = trim($_POST['new_type']); + + $missing_fields = array(); + + if (!$_POST['type'] && !$_POST['new_type']) { + $missing_fields[] = _AT('groups_type'); + } + if (!$_POST['prefix']) { + $missing_fields[] = _AT('title'); + } + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $_POST['new_type'] = $addslashes($_POST['new_type']); + $_POST['prefix'] = $addslashes($_POST['prefix']); + $_POST['description'] = $addslashes($_POST['description']); + + if ($_POST['new_type']) { + $sql = "INSERT INTO ".TABLE_PREFIX."groups_types VALUES (NULL, $_SESSION[course_id], '$_POST[new_type]')"; + $result = mysql_query($sql, $db); + $type_id = mysql_insert_id($db); + } else { + $sql = "SELECT type_id FROM ".TABLE_PREFIX."groups_types WHERE course_id=$_SESSION[course_id] AND type_id=$_POST[type]"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $type_id = $row['type_id']; + } else { + $type_id = FALSE; + } + } + if ($type_id) { + $sql = "INSERT INTO ".TABLE_PREFIX."groups VALUES (NULL, $type_id, '$_POST[prefix]', '$_POST[description]', '$modules')"; + $result = mysql_query($sql, $db); + + $group_id = mysql_insert_id($db); + + $_SESSION['groups'][$group_id] = $group_id; + // call module init scripts: + if (isset($_POST['modules'])) { + foreach ($_POST['modules'] as $mod) { + $module =& $moduleFactory->getModule($mod); + $module->createGroup($group_id); + } + } + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: index.php'); + exit; + } else { + $_POST['new_type'] = $stripslashes($_POST['new_type']); + $_POST['prefix'] = $stripslashes($_POST['prefix']); + $_POST['description'] = $stripslashes($_POST['description']); + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$types = array(); +$sql = "SELECT type_id, title FROM ".TABLE_PREFIX."groups_types WHERE course_id=$_SESSION[course_id] ORDER BY title"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $types[$row['type_id']] = htmlentities_utf8($row['title']); +} + +?> + +
    +
    +
    +
    + *
    + + + + + + +
    + +
    + *
    + +
    + +
    +
    + +
    + +
    +
    + getModules(AT_MODULE_STATUS_ENABLED, 0, TRUE); + $keys = array_keys($modules); + $i=0; + ?> + + + getGroupTool() && (in_array($module->getGroupTool(),$_pages[AT_NAV_HOME]) || in_array($module->getGroupTool(),$_pages[AT_NAV_COURSE])) ): ?> +
    + + +
    + +
    + + +
    +
    +
    +
    + \ No newline at end of file diff --git a/mods/_core/groups/delete_group.php b/mods/_core/groups/delete_group.php new file mode 100644 index 000000000..7301e6eb5 --- /dev/null +++ b/mods/_core/groups/delete_group.php @@ -0,0 +1,90 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + $_POST['id'] = intval($_POST['id']); + $_POST['type_id'] = intval($_POST['type_id']); + + $id = intval($_POST['id']); + $type_id = intval($_POST['type_id']); + + $sql = "SELECT type_id FROM ".TABLE_PREFIX."groups_types WHERE type_id=$type_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $module_list = $moduleFactory->getModules(AT_MODULE_STATUS_ENABLED | AT_MODULE_STATUS_DISABLED); + $keys = array_keys($module_list); + foreach ($keys as $module_name) { + $module =& $module_list[$module_name]; + $module->deleteGroup($id); + } + + $sql = "DELETE FROM ".TABLE_PREFIX."groups WHERE group_id=$id AND type_id=$type_id"; + $result = mysql_query($sql, $db); + + if (mysql_affected_rows($db)) { + //remove all listings in groups_members table + $sql = "DELETE FROM ".TABLE_PREFIX."groups_members WHERE group_id=$id"; + $result = mysql_query($sql, $db); + + // should be handled by each module: + //remove all listings in tests_groups table + $sql = "DELETE FROM ".TABLE_PREFIX."tests_groups WHERE group_id=$id"; + $result = mysql_query($sql, $db); + } + } + + $msg->addFeedback('GROUP_DELETED'); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$_GET['id'] = intval($_GET['id']); + +$sql = "SELECT * FROM ".TABLE_PREFIX."groups WHERE group_id=$_GET[id]"; +$result = mysql_query($sql,$db); +if (!($row = mysql_fetch_assoc($result))) { + $msg->printErrors('GROUP_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$sql = "SELECT title FROM ".TABLE_PREFIX."groups_types WHERE type_id=$row[type_id] AND course_id=$_SESSION[course_id]"; +$result = mysql_query($sql,$db); +if (!($type_row = mysql_fetch_assoc($result))) { + $msg->printErrors('GROUP_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +unset($hidden_vars); +$hidden_vars['id'] = $_GET['id']; +$hidden_vars['type_id'] = $row['type_id']; + +$msg->addConfirm(array('DELETE_GROUP',htmlentities_utf8($row['title'])), $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/groups/delete_type.php b/mods/_core/groups/delete_type.php new file mode 100644 index 000000000..1f4717a95 --- /dev/null +++ b/mods/_core/groups/delete_type.php @@ -0,0 +1,71 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} +else if (isset($_POST['submit_yes'])) { + $type_id = abs($_POST['id']); + + $sql = "DELETE FROM ".TABLE_PREFIX."groups_types WHERE type_id=$type_id AND course_id=$_SESSION[course_id]"; + mysql_query($sql, $db); + if (mysql_affected_rows($db) == 1) { + $sql = "SELECT group_id FROM ".TABLE_PREFIX."groups WHERE type_id=$type_id"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $sql = "DELETE FROM ".TABLE_PREFIX."groups_members WHERE group_id=$row[group_id]"; + mysql_query($sql, $db); + + // should be handled by each module: + //remove all listings in tests_groups table + $sql = "DELETE FROM ".TABLE_PREFIX."tests_groups WHERE group_id=$row[group_id]"; + mysql_query($sql, $db); + } + $sql = "DELETE FROM ".TABLE_PREFIX."groups WHERE type_id=$type_id"; + $result = mysql_query($sql, $db); + } + + $msg->addFeedback('GROUP_TYPE_DELETED'); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$_GET['id'] = intval($_GET['id']); + +$sql = "SELECT * FROM ".TABLE_PREFIX."groups_types WHERE type_id=$_GET[id] AND course_id=$_SESSION[course_id]"; +$result = mysql_query($sql,$db); +if (!($row = mysql_fetch_assoc($result))) { + $msg->printErrors('GROUP_TYPE_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +unset($hidden_vars); +$hidden_vars['id'] = $_GET['id']; + +$msg->addConfirm(array('DELETE_GROUP_TYPE',$row['title']), $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/groups/edit_group.php b/mods/_core/groups/edit_group.php new file mode 100644 index 000000000..f1f5773d3 --- /dev/null +++ b/mods/_core/groups/edit_group.php @@ -0,0 +1,141 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + $modules = ''; + if (isset($_POST['modules'])) { + $modules = implode('|', $_POST['modules']); + } + + $_POST['title'] = trim($_POST['title']); + + if (!$_POST['title']) { + $msg->addError(array('EMPTY_FIELDS', _AT('title'))); + } + + if (!$msg->containsErrors()) { + $_POST['title'] = $addslashes($_POST['title']); + $_POST['description'] = $addslashes($_POST['description']); + + $id = intval($_POST['id']); + $type_id = intval($_POST['type_id']); + + $sql = "SELECT type_id FROM ".TABLE_PREFIX."groups_types WHERE type_id=$type_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $sql = "UPDATE ".TABLE_PREFIX."groups SET title='$_POST[title]', description='$_POST[description]', modules='$modules' WHERE group_id=$id AND type_id=$type_id"; + $result = mysql_query($sql, $db); + + // delete the modules that were un-checked + $old_modules = explode('|', $_POST['old_modules']); + $modules = explode('|', $modules); + + foreach ($old_modules as $mod) { + if (!in_array($mod, $modules)) { + $module =& $moduleFactory->getModule($mod); + $module->deleteGroup($id); + } + } + foreach ($modules as $mod) { + if (!in_array($mod, $old_modules)) { + $module =& $moduleFactory->getModule($mod); + $module->createGroup($id); + } + } + } + + $msg->addFeedback('GROUP_EDITED_SUCCESSFULLY'); + + header('Location: index.php'); + exit; + } + $_GET['id'] = abs($_POST['id']); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $_GET['id'] = intval($_GET['id']); + + $sql = "SELECT * FROM ".TABLE_PREFIX."groups WHERE group_id=$_GET[id]"; + $result = mysql_query($sql,$db); + if (!($row = mysql_fetch_assoc($result))) { + $msg->printErrors('GROUP_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + $sql = "SELECT title FROM ".TABLE_PREFIX."groups_types WHERE type_id=$row[type_id] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql,$db); + if (!($type_row = mysql_fetch_assoc($result))) { + $msg->printErrors('GROUP_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } +$old_modules = $row['modules']; +$row['modules'] = explode('|', $row['modules']); +?> + +
    + + + +
    +
    +
    +

    +
    + +
    + *
    + +
    + +
    +
    + +
    + +
    +
    + getModules(AT_MODULE_STATUS_ENABLED, 0, TRUE); + $keys = array_keys($modules); + $i=0; + ?> + + + getGroupTool() && (in_array($module->getGroupTool(),$_pages[AT_NAV_HOME]) || in_array($module->getGroupTool(),$_pages[AT_NAV_COURSE])) ): ?> + />
    + + +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/groups/edit_type.php b/mods/_core/groups/edit_type.php new file mode 100644 index 000000000..940e68c5d --- /dev/null +++ b/mods/_core/groups/edit_type.php @@ -0,0 +1,76 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + $_POST['title'] = trim($_POST['title']); + + if (!$_POST['title']) { + $msg->addError(array('EMPTY_FIELDS', _AT('title'))); + } + + if (!$msg->containsErrors()) { + $_POST['title'] = $addslashes($_POST['title']); + + $type_id = intval($_POST['type_id']); + + $sql = "UPDATE ".TABLE_PREFIX."groups_types SET title='$_POST[title]' WHERE course_id=$_SESSION[course_id] AND type_id=$type_id"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('GROUP_TYPE_EDITED_SUCCESSFULLY'); + + header('Location: index.php'); + exit; + } + $_GET['id'] = abs($_POST['type_id']); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $_GET['id'] = intval($_GET['id']); + + $sql = "SELECT * FROM ".TABLE_PREFIX."groups_types WHERE type_id=$_GET[id] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql,$db); + if (!($row = mysql_fetch_assoc($result))) { + $msg->printErrors('GROUP_TYPE_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } +?> + +
    + +
    +
    +
    + *
    + +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/groups/groups.php b/mods/_core/groups/groups.php new file mode 100644 index 000000000..f962ea553 --- /dev/null +++ b/mods/_core/groups/groups.php @@ -0,0 +1,53 @@ +printErrors('NOT_IN_ANY_GROUPS'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +$group_list = implode(',', $_SESSION['groups']); +$sql = "SELECT group_id, title, modules FROM ".TABLE_PREFIX."groups WHERE group_id IN ($group_list) ORDER BY title"; +$result = mysql_query($sql, $db); + +echo '
      '; + +while ($row = mysql_fetch_assoc($result)) { + echo '
    1. '.htmlentities_utf8($row['title']) . ' '; + + $modules = explode('|', $row['modules']); + asort($modules); + + if ($modules) { + echo '
        '; + foreach ($modules as $module_name) { + $fn = basename($module_name) . '_get_group_url'; + $module =& $moduleFactory->getModule($module_name); + if ($module->isEnabled() && function_exists($fn)) { + echo '
      • '._AT($_pages[$module->getGroupTool()]['title_var']).'
      • '; + } + } + echo '
      '; + } + echo '
    2. '; +} +echo '
    '; + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/groups/index.php b/mods/_core/groups/index.php new file mode 100644 index 000000000..ec418e872 --- /dev/null +++ b/mods/_core/groups/index.php @@ -0,0 +1,113 @@ +addError('NO_ITEM_SELECTED'); + } +} else if (isset($_GET['members']) || isset($_GET['delete']) || isset($_GET['edit'])) { + $msg->addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT type_id, title FROM ".TABLE_PREFIX."groups_types WHERE course_id=$_SESSION[course_id] ORDER BY title"; +$result = mysql_query($sql, $db); +?> +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + () +
    ()
    +
    +

    +
    + \ No newline at end of file diff --git a/mods/_core/groups/members.php b/mods/_core/groups/members.php new file mode 100644 index 000000000..bf2240e94 --- /dev/null +++ b/mods/_core/groups/members.php @@ -0,0 +1,221 @@ +printErrors('GROUP_TYPE_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$tmp_groups = array(); +$sql = "SELECT group_id, title FROM ".TABLE_PREFIX."groups WHERE type_id=$id ORDER BY title"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $tmp_groups[$row['group_id']] = htmlentities_utf8($row['title']); +} +$groups_keys = array_keys($tmp_groups); +$groups_keys = implode($groups_keys, ','); + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + $sql = "DELETE FROM ".TABLE_PREFIX."groups_members WHERE group_id IN ($groups_keys)"; + mysql_query($sql, $db); + + $sql = ''; + foreach ($_POST['groups'] as $mid => $gid) { + $mid = abs($mid); + $gid = abs($gid); + if ($gid) { + $sql .= "($gid, $mid),"; + } + } + if ($sql) { + $sql = substr($sql, 0, -1); + $sql = "INSERT INTO ".TABLE_PREFIX."groups_members VALUES $sql"; + mysql_query($sql, $db); + } + + $msg->addFeedback('GROUP_MEMBERS_SAVED'); + + header('Location: index.php'); + exit; +} else if (isset($_POST['assign'])) { + + $groups_counts = array(); + $sql = "SELECT group_id, COUNT(*) AS cnt FROM ".TABLE_PREFIX."groups_members WHERE group_id IN ($groups_keys) GROUP BY group_id ORDER BY cnt ASC"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $groups_counts[$row['group_id']] = $row['cnt']; + } + $total_assigned = array_sum($groups_counts); + + if (is_array($_POST['groups'])) { + foreach ($_POST['groups'] as $mid => $gid) { + if ($gid) { + unset($_POST['groups'][$mid]); + } + } + $students = array_keys($_POST['groups']); + + $total_unassigned = count($students); + + shuffle($students); + reset($students); + } + + $total_students = $total_unassigned + $total_assigned; + + $num_groups = count($tmp_groups); + + if ($total_students > 0) { + // to uniformly distribute all the groups we place the remaining students + // into the first n groups, where n is the number of remaining students. + $remainder = $total_students % $num_groups; + if ($remainder) { + $num_students_per_group = floor($total_students / $num_groups); + } else { + $num_students_per_group = $total_students / $num_groups; + } + + $sql = ''; + foreach($tmp_groups as $group_id => $garbage) { + + if (!isset($groups_counts[$group_id])) { + $groups_counts[$group_id] = 0; + } + while (($groups_counts[$group_id] < $num_students_per_group) && ($mid = current($students))) { + $sql .= "($group_id, $mid),"; + $groups_counts[$group_id]++; + next($students); + } + + if ($remainder) { + $mid = current($students); + if ($mid) { + $sql .= "($group_id, $mid),"; + $remainder--; + next($students); + $groups_counts[$group_id]++; + } + } + } + if ($sql) { + $sql = substr($sql, 0, -1); + $sql = "INSERT INTO ".TABLE_PREFIX."groups_members VALUES " . $sql; + mysql_query($sql, $db); + } + } + + $msg->addFeedback('GROUP_MEMBERS_SAVED'); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +echo '

    '.htmlentities_utf8($type_row['title']).'

    '; + + +if (isset($_GET['gid'])) { + $_GET['gid'] = abs($_GET['gid']); +} else { + $_GET['gid'] = 0; +} + +$groups_members = array(); +$sql = "SELECT member_id, group_id FROM ".TABLE_PREFIX."groups_members WHERE group_id IN ($groups_keys) ORDER BY member_id"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $groups_members[$row['member_id']] = $row['group_id']; +} +$groups_members_keys = array_keys($groups_members); +$groups_members_keys = implode($groups_members_keys, ','); + +$owner = $system_courses[$_SESSION['course_id']]['member_id']; + +$sql = "SELECT M.member_id, M.login, M.first_name, M.last_name FROM ".TABLE_PREFIX."members M INNER JOIN ".TABLE_PREFIX."course_enrollment E USING (member_id) WHERE E.course_id=$_SESSION[course_id] AND E.privileges&".AT_PRIV_GROUPS."=0 AND E.approved='y' AND E.member_id<>$owner ORDER BY M.login"; +$result = mysql_query($sql, $db); + +$count = 0; +?> +
    + + + + + + + + + + + + + + + + + + id="r"> + + + + + + + +
    + + + +
    + +
    +
    + + + + \ No newline at end of file diff --git a/mods/_core/groups/module.php b/mods/_core/groups/module.php new file mode 100644 index 000000000..42578c755 --- /dev/null +++ b/mods/_core/groups/module.php @@ -0,0 +1,43 @@ +getPrivilege()); + +$_student_tool = 'mods/_core/groups/groups.php'; + +$this->_pages['mods/_core/groups/index.php']['title_var'] = 'groups'; +$this->_pages['mods/_core/groups/index.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_core/groups/index.php']['children'] = array('mods/_core/groups/create.php'); +$this->_pages['mods/_core/groups/index.php']['guide'] = 'instructor/?p=groups.php'; + + $this->_pages['mods/_core/groups/edit_group.php']['title_var'] = 'edit'; + $this->_pages['mods/_core/groups/edit_group.php']['parent'] = 'mods/_core/groups/index.php'; + + $this->_pages['mods/_core/groups/delete_group.php']['title_var'] = 'delete'; + $this->_pages['mods/_core/groups/delete_group.php']['parent'] = 'mods/_core/groups/index.php'; + + $this->_pages['mods/_core/groups/edit_type.php']['title_var'] = 'edit'; + $this->_pages['mods/_core/groups/edit_type.php']['parent'] = 'mods/_core/groups/index.php'; + + $this->_pages['mods/_core/groups/delete_type.php']['title_var'] = 'delete'; + $this->_pages['mods/_core/groups/delete_type.php']['parent'] = 'mods/_core/groups/index.php'; + + $this->_pages['mods/_core/groups/create.php']['title_var'] = 'create_groups'; + $this->_pages['mods/_core/groups/create.php']['parent'] = 'mods/_core/groups/index.php'; + + $this->_pages['mods/_core/groups/create_manual.php']['title_var'] = 'groups_create_manual'; + $this->_pages['mods/_core/groups/create_manual.php']['parent'] = 'mods/_core/groups/create.php'; + + $this->_pages['mods/_core/groups/create_automatic.php']['title_var'] = 'groups_create_automatic'; + $this->_pages['mods/_core/groups/create_automatic.php']['parent'] = 'mods/_core/groups/create.php'; + + $this->_pages['mods/_core/groups/members.php']['title_var'] = 'group_members'; + $this->_pages['mods/_core/groups/members.php']['parent'] = 'mods/_core/groups/index.php'; + +// student stuff +$this->_pages['mods/_core/groups/groups.php']['title_var'] = 'groups'; +$this->_pages['mods/_core/groups/groups.php']['img'] = 'images/home-acollab.png'; +$this->_pages['mods/_core/groups/groups.php']['text'] = _AT('groups_text'); + +?> \ No newline at end of file diff --git a/mods/_core/groups/module.xml b/mods/_core/groups/module.xml new file mode 100644 index 000000000..36a30af16 --- /dev/null +++ b/mods/_core/groups/module.xml @@ -0,0 +1,23 @@ + + + Groups + Allows an instructor to assign students to groups. Tests can then be given to students within certain groups. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + + + + 2005-08-25 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/groups/module_backup.php b/mods/_core/groups/module_backup.php new file mode 100644 index 000000000..305c5e558 --- /dev/null +++ b/mods/_core/groups/module_backup.php @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/mods/_core/groups/module_delete.php b/mods/_core/groups/module_delete.php new file mode 100644 index 000000000..39982a570 --- /dev/null +++ b/mods/_core/groups/module_delete.php @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/mods/_core/imsafa/classes/A4a.class.php b/mods/_core/imsafa/classes/A4a.class.php new file mode 100644 index 000000000..2e0b7f9cf --- /dev/null +++ b/mods/_core/imsafa/classes/A4a.class.php @@ -0,0 +1,243 @@ +cid = intval($cid); + } + + + // Return resources type hash mapping. + function getResourcesType($type_id=0){ + global $db; + + $type_id = intval($type_id); + + //if this is the first time calling this function, grab the list from db + if (empty($resource_types)){ + $sql = 'SELECT * FROM '.TABLE_PREFIX.'resource_types'; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)){ + $this->resource_types[$row['type_id']] = $row['type']; + } + } + + if (!empty($this->resource_types[$type_id])){ + return $this->resource_types[$type_id]; + } + return $this->resource_types; + } + + + // Get primary resources + function getPrimaryResources(){ + global $db; + + $pri_resources = array(); // cid=>[resource, language code] + $sql = 'SELECT * FROM '.TABLE_PREFIX.'primary_resources WHERE content_id='.$this->cid; + $result = mysql_query($sql, $db); + if (mysql_numrows($result) > 0){ + while ($row = mysql_fetch_assoc($result)){ + $pri_resources[$row['primary_resource_id']]['resource'] = $row['resource']; + if ($row['language_code'] != ''){ + $pri_resources[$row['primary_resource_id']]['language_code'] = $row['language_code']; + } + } + } + return $pri_resources; + } + + + // Get primary resources types + function getPrimaryResourcesTypes($pri_resource_id=0){ + global $db; + + $pri_resource_id = intval($pri_resource_id); + + //quit if id not specified + if ($pri_resource_id == 0) { + return array(); + } + + $pri_resources_types = array(); // cid=>[type id]+ + $sql = 'SELECT * FROM '.TABLE_PREFIX.'primary_resources_types WHERE primary_resource_id='.$pri_resource_id; + $result = mysql_query($sql, $db); + if ($result){ + while ($row = mysql_fetch_assoc($result)){ + $pri_resources_types[$pri_resource_id][] = $row['type_id']; + } + } + return $pri_resources_types; + } + + + // Get secondary resources + function getSecondaryResources($pri_resource_id=0){ + global $db; + + $pri_resource_id = intval($pri_resource_id); + + //quit if id not specified + if ($pri_resource_id == 0) { + return array(); + } + + $sec_resources = array(); // cid=>[resource, language code] + $sql = 'SELECT * FROM '.TABLE_PREFIX.'secondary_resources WHERE primary_resource_id='.$pri_resource_id; + $result = mysql_query($sql, $db); + if ($result){ + while ($row = mysql_fetch_assoc($result)){ + $sec_resources[$row['secondary_resource_id']]['resource'] = $row['secondary_resource']; + if ($row['language_code'] != ''){ + $sec_resources[$row['secondary_resource_id']]['language_code'] = $row['language_code']; + } + } + } + return $sec_resources; + } + + + // Get secondary resources types + function getSecondaryResourcesTypes($sec_resource_id=0){ + global $db; + + $sec_resource_id = intval($sec_resource_id); + + //quit if id not specified + if ($sec_resource_id == 0) { + return array(); + } + + $sec_resources_types = array(); // cid=>[type id]+ + $sql = 'SELECT * FROM '.TABLE_PREFIX.'secondary_resources_types WHERE secondary_resource_id='.$sec_resource_id; + $result = mysql_query($sql, $db); + if ($result){ + while ($row = mysql_fetch_assoc($result)){ + $sec_resources_types[] = $row['type_id']; + } + } + return $sec_resources_types; + } + + + // Insert primary resources into the db + // @return primary resource id. + function setPrimaryResource($content_id, $file_name, $lang){ + global $addslashes, $db; + + $content_id = intval($content_id); + $file_name = $addslashes($file_name); + $lang = $addslashes($lang); + + $sql = "INSERT INTO ".TABLE_PREFIX."primary_resources SET content_id=$content_id, resource='$file_name', language_code='$lang'"; + $result = mysql_query($sql, $db); + if ($result){ + return mysql_insert_id(); + } + return false; + } + + // Insert primary resource type + function setPrimaryResourceType($primary_resource_id, $type_id){ + global $db; + + $primary_resource_id= intval($primary_resource_id); + $type_id = intval($type_id); + + $sql = "INSERT INTO ".TABLE_PREFIX."primary_resources_types SET primary_resource_id=$primary_resource_id, type_id=$type_id"; + $result = mysql_query($sql, $db); + } + + // Insert secondary resource + // @return secondary resource id + function setSecondaryResource($primary_resource_id, $file_name, $lang){ + global $addslashes, $db; + + $primary_resource_id = intval($primary_resource_id); + $file_name = $addslashes($file_name); + $lang = $addslashes($lang); + + $sql = "INSERT INTO ".TABLE_PREFIX."secondary_resources SET primary_resource_id=$primary_resource_id, secondary_resource='$file_name', language_code='$lang'"; + $result = mysql_query($sql, $db); + if ($result){ + return mysql_insert_id(); + } + return false; + } + + // Insert secondary resource + function setSecondaryResourceType($secondary_resource, $type_id){ + global $db; + + $secondary_resource = intval($secondary_resource); + $type_id = intval($type_id); + + $sql = "INSERT INTO ".TABLE_PREFIX."secondary_resources_types SET secondary_resource_id=$secondary_resource, type_id=$type_id"; + $result = mysql_query($sql, $db); + } + + + // Set the relative path to all files + function setRelativePath($path){ + $this->relative_path = $path . '/'; + } + + + // Delete all materials associated with this content + function deleteA4a(){ + global $db; + + $pri_resource_ids = array(); + + // Get all primary resources ID out that're associated with this content + $sql = 'SELECT a.primary_resource_id FROM '.TABLE_PREFIX.'primary_resources a LEFT JOIN '.TABLE_PREFIX.'primary_resources_types b ON a.primary_resource_id=b.primary_resource_id WHERE content_id='.$this->cid; + $result = mysql_query($sql); + + while($row=mysql_fetch_assoc($result)){ + $pri_resource_ids[] = $row['primary_resource_id']; + } + + //If the are primary resources found + if (!empty($pri_resource_ids)){ + $glued_pri_ids = implode(",", $pri_resource_ids); + + // Delete all secondary a4a + $sql = 'DELETE c, d FROM '.TABLE_PREFIX.'secondary_resources c LEFT JOIN '.TABLE_PREFIX.'secondary_resources_types d ON c.secondary_resource_id=d.secondary_resource_id WHERE primary_resource_id IN ('.$glued_pri_ids.')'; + $result = mysql_query($sql); + + // If successful, remove all primary resources + if ($result){ + $sql = 'DELETE a, b FROM '.TABLE_PREFIX.'primary_resources a LEFT JOIN '.TABLE_PREFIX.'primary_resources_types b ON a.primary_resource_id=b.primary_resource_id WHERE content_id='.$this->cid; + mysql_query($sql); + } + } + } +} + +?> \ No newline at end of file diff --git a/mods/_core/imsafa/classes/A4a.tmpl.php b/mods/_core/imsafa/classes/A4a.tmpl.php new file mode 100644 index 000000000..a5e8bdb07 --- /dev/null +++ b/mods/_core/imsafa/classes/A4a.tmpl.php @@ -0,0 +1,56 @@ + + orig_access_mode)): + foreach($this->orig_access_mode as $orig_access_mode): + ?> + + + informative + language_code; ?> + + + + secondary_resources)): ?> + secondary_resources as $uri): ?> + relative_path.$uri; ?> + + + + primary_resources) && $this->primary_resources!=''): ?> + + relative_path.$this->primary_resource_uri; ?> + full + + primary_resources as $type_id): ?> + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imsafa/classes/A4aExport.class.php b/mods/_core/imsafa/classes/A4aExport.class.php new file mode 100644 index 000000000..e25eff584 --- /dev/null +++ b/mods/_core/imsafa/classes/A4aExport.class.php @@ -0,0 +1,181 @@ +[incrementer]=>[resource, language, type] +// var $alternative_files = array(); //secondary files aka. alternative, equivalent + + /** + * Get information for this content + * @return the xml content + */ + function getAlternative(){ + $resources = parent::getPrimaryResources(); + foreach($resources as $rid => $prop){ + $resources_types = parent::getPrimaryResourcesTypes($rid); + $temp = array(); + $secondary_array = array(); + foreach($resources_types as $rtid){ + $sec_resources['secondary_resources'] = parent::getSecondaryResources($rid); + //determine secondary resource type + foreach ($sec_resources['secondary_resources'] as $sec_id => $sec_resource){ + $current_sec_file = $sec_resource['resource']; + $secondary_array['secondary_resources'][] = $current_sec_file ; + //add to secondary file array if it's not there + if (!isset($this->original_files[$current_sec_file]) || empty($this->original_files[$current_sec_file]) ){ + //TODO merge these values i think + $this->original_files[$current_sec_file ] = $sec_resource; + $this->original_files[$current_sec_file ]['resource_type'][$prop['resource']][] = parent::getSecondaryResourcesTypes($sec_id); + } else { + $this->original_files[$current_sec_file ]['resource_type'][$prop['resource']][] = parent::getSecondaryResourcesTypes($sec_id); + } + //add this primary file ref, and the resources type to the secondary file + $this->original_files[$current_sec_file]['primary_resources'][$prop['resource']] = $rtid; +// $this->original_files[$current_sec_file]['primary_resources'][$prop['resource']]['language_code'] = $sec_resource['language_code']; + } + } + $res_type['resource_type'] = $rtid; //could be 1+ + $temp = array_merge($prop, $res_type, $secondary_array); + + if (isset($this->original_files[$temp['resource']])){ + //use the existing temp array values, but merge in the secondary_array + $temp = array_merge($this->original_files[$temp['resource']], $secondary_array); + } + if(!empty($temp)){ + $this->original_files[$temp['resource']] = $temp; + // debug($this->original_files['7dolomiti_1a_como1e_como_road1b.jpg'], $rid); + } + } + return $this->original_files; + } + + /** + * Get all secondary files + * @return array of secondary files that is being used in this->content. + */ + function getAllSecondaryFiles(){ + global $db; + $secondary_files = array(); + + $sql = "SELECT DISTINCT secondary_resource FROM ".TABLE_PREFIX."primary_resources a LEFT JOIN ".TABLE_PREFIX."secondary_resources s + ON a.primary_resource_id = s.primary_resource_id WHERE content_id=".$this->cid; + $result = mysql_query ($sql); + if ($result){ + while ($row = mysql_fetch_assoc($result)){ + if (!empty($row['secondary_resource'])){ + $secondary_files[] = $row['secondary_resource']; + } + } + } + return $secondary_files; + } + + // Save all the xml into an array. + // key=filename, value=xml content + function exportA4a(){ + global $savant; + + $xml_array = array(); //array of xml + + // Get the alt content first. + $this->getAlternative(); + + // Get original files' xml + foreach($this->original_files as $id=>$resource){ + $orig_access_mode = array(); + foreach($resource['resource_type'] as $type_id){ + if (!is_array($type_id)){ + //primary resource will always have just on type + $orig_access_mode[] = $this->getResourceNameById($type_id); + } else { + foreach($type_id as $k=>$type_id2){ + $orig_access_mode[] = $this->getResourceNameById($type_id2[0]); + } + } + } + $savant->assign('relative_path', $this->relative_path); //the template will need the relative path + $savant->assign('orig_access_mode', $orig_access_mode); + $savant->assign('language_code', $resource['language_code']); + $savant->assign('secondary_resources', $resource['secondary_resources']); + + // If this is an alternative, and it is mapping to + // 1+ original files. Each of these mapping requires + // its own xml + if(isset($resource['primary_resources'])){ + foreach($resource['primary_resources'] as $uri=>$pri_resource_types){ + $savant->assign('primary_resource_uri', $uri); + $savant->assign('primary_resources', $pri_resource_types); + //A file can be both original and alternative, and each could represent diff language + //Tried to resolve but the A4a v.2 only accept 1 language +// $savant->assign('language_code', $pri_resource_types['language_code']); + //overwrite orig_access_mode + $orig_access_mode = array(); //reinitialize + foreach($resource['resource_type'][$uri] as $type_id){ + $orig_access_mode[] = $this->getResourceNameById($type_id); + $savant->assign('orig_access_mode', $orig_access_mode); + $xml_array[$id.' to '.$uri][] = $savant->fetch(AT_INCLUDE_PATH.'../mods/_core/imsafa/classes/A4a.tmpl.php'); + } + + } + } else { + $savant->assign('primary_resource_uri', ''); + $savant->assign('primary_resources', ''); + $xml_array[$id] = $savant->fetch(AT_INCLUDE_PATH.'../mods/_core/imsafa/classes/A4a.tmpl.php'); + } + } + return $xml_array; + } + + /** + * Get resource name by id + * @return array + */ + function getResourceNameById($type_id){ + $orig_access_mode = ''; + if (is_array($type_id)) { + $type_id = $type_id[0]; + } + switch($type_id){ + case 1: + $orig_access_mode = 'auditory'; + break; + case 3: + $orig_access_mode = 'textual'; + break; + case 2: + $orig_access_mode = 'sign_language'; + break; + case 4: + $orig_access_mode = 'visual'; + break; + default: + $orig_access_mode = ''; + } + return $orig_access_mode; + } + +} + +?> \ No newline at end of file diff --git a/mods/_core/imsafa/classes/A4aImport.class.php b/mods/_core/imsafa/classes/A4aImport.class.php new file mode 100644 index 000000000..6f0ebed72 --- /dev/null +++ b/mods/_core/imsafa/classes/A4aImport.class.php @@ -0,0 +1,158 @@ + $a4a_resources){ + foreach ($a4a_resources as $resource){ + //If it has adaptation/alternative, this is a primary file. + if (isset($resource['hasAdaptation']) && !empty($resource['hasAdaptation'])){ + //we only have one language in the table, [1], [2], etc will be the same + $pri_lang = $resource['language'][0]; + + //insert primary resource + $primary_id = $this->setPrimaryResource($this->cid, str_replace($this->relative_path, '', $file_path), $pri_lang); + + //get primary resource type + $resources_attrs = $resource['access_stmt_originalAccessMode']; + + $attrs = $this->toResourceTypeId($resources_attrs); + + //insert primary resource type associations + foreach ($attrs as $resource_type_id){ + $this->setPrimaryResourceType($primary_id, $resource_type_id); + } + + //insert secondary resource + $secondary_resources = $resource['hasAdaptation']; //uri array + foreach ($secondary_resources as $secondary_resource){ + //some paths will reference files above this directory, as a result + //we will see ../, but since everything is under 'resources/', the relative_path + //we can safely take it out. + //@edited Dec 6th, imscc import uses relative paths, ims doesn't. + if (substr($secondary_resource, 0, 7) == 'http://' || substr($secondary_resource, 0, 8) == 'https://') { + $secondary_resource_with_relative_path = $secondary_resource; + } else { + $secondary_resource_with_relative_path = $this->relative_path.$secondary_resource; + } + + $secondary_files = $items[$secondary_resource_with_relative_path]; + if (in_array($secondary_resource_with_relative_path, $imported_files)){ + continue; + } + $imported_files[] = $secondary_resource_with_relative_path; + if (empty($secondary_files)){ + //tweak: if this is empty, then most likely it is an ims import. + $secondary_resource = preg_replace('/^\.\.\//', '', $secondary_resource); + $secondary_files = $items[$secondary_resource_with_relative_path]; + } + //check if this secondary file is the adaptation of + // this primary file + foreach($secondary_files as $secondary_file){ + //isAdaptation is 1-to-1 mapping, save to use [0] + if (substr($secondary_file['isAdaptationOf'][0], 0, 7) == 'http://' + || substr($secondary_file['isAdaptationOf'][0], 0, 8) == 'https://') { + $adaption_with_relative_path = $secondary_file['isAdaptationOf'][0]; + } else { + $adaption_with_relative_path = $this->relative_path.$secondary_file['isAdaptationOf'][0]; + } + + if($adaption_with_relative_path == $file_path){ + $secondary_lang = $secondary_file['language'][0]; + + //access_stmt_originalAccessMode cause we want the language for the secondary file. + $secondary_attr = $this->toResourceTypeId($secondary_file['access_stmt_originalAccessMode']); + $secondary_id = $this->setSecondaryResource($primary_id, $secondary_resource, $secondary_lang); + //insert secondary resource type associations + foreach ($secondary_attr as $secondary_resource_type_id){ + $this->setSecondaryResourceType($secondary_id, $secondary_resource_type_id); + } + //break; //1-to-1 mapping, no need to go further + } + } + } //foreach of secondary_resources + $imported_files = array(); //reset the imported file for the next resource + } + } //foreach of resources + } //foreach of item array + } + + /** + * By the given attrs array, decide which resource type it is + * auditory = type 1 + * sign_language = type 2 + * textual = type 3 + * visual = type 4 + * @param array + * return type id array + */ + function toResourceTypeId($resources_attrs){ + $result = array(); + + //if empty + if (empty($resources_attrs)){ + return $result; + } + if (is_array($resources_attrs)) { + if (in_array('auditory', $resources_attrs)){ + $result[] = 1; + } + if (in_array('sign_language', $resources_attrs)){ + $result[] = 2; + } + if (in_array('textual', $resources_attrs)){ + $result[] = 3; + } + if (in_array('visual', $resources_attrs)){ + $result[] = 4; + } + } else { + if ($resources_attrs=='auditory'){ + $result[] = 1; + } elseif ($resources_attrs=='sign_language'){ + $result[] = 2; + } elseif ($resources_attrs=='textual'){ + $result[] = 3; + } elseif ($resources_attrs=='visual'){ + $result[] = 4; + } + } + return $result; + } +} + +?> diff --git a/mods/_core/imsafa/html/resources_parser.inc.php b/mods/_core/imsafa/html/resources_parser.inc.php new file mode 100644 index 000000000..67563d9e0 --- /dev/null +++ b/mods/_core/imsafa/html/resources_parser.inc.php @@ -0,0 +1,148 @@ + + +img => src +a => href // ignore if href doesn't exist (ie. ) +object => data | classid // probably only want data +applet => classid | archive // whatever these two are should double check to see if it's a valid file (not a dir) +script => src +input => src +iframe => src +*/ + +class MyHandler { + function MyHandler(){} + function openHandler(& $parser,$name,$attrs) { + global $my_files; + + $name = strtolower($name); + $attrs = array_change_key_case($attrs, CASE_LOWER); + + $elements = array( 'img' => 'src', + 'a' => 'href', + 'object' => array('data', 'classid'), + 'applet' => array('classid', 'archive'), + 'script' => 'src', + 'input' => 'src', + 'iframe' => 'src', + 'embed' => 'src', + ); + + /* check if this attribute specifies the files in different ways: (ie. java) */ + if (is_array($elements[$name])) { + $items = $elements[$name]; + + foreach ($items as $item) { + if ($attrs[$item] != '') { + + /* some attributes allow a listing of files to include seperated by commas (ie. applet->archive). */ + if (strpos($attrs[$item], ',') !== false) { + $files = explode(',', $attrs[$item]); + foreach ($files as $file) { + $my_files[] = trim($file); + } + } else { + $my_files[] = $attrs[$item]; + } + } + } + } else if (isset($elements[$name]) && ($attrs[$elements[$name]] != '')) { + /* we know exactly which attribute contains the reference to the file. */ + $my_files[] = $attrs[$elements[$name]]; + } + } + function closeHandler(& $parser,$name) { } + } + +/* get all the content */ +$handler=new MyHandler(); +$parser = new XML_HTMLSax(); +$parser->set_object($handler); +$parser->set_element_handler('openHandler','closeHandler'); + +/* generate the resources and save the HTML files */ + +ob_start(); + +global $parser, $my_files; +global $course_id; + +/* add the resource dependancies */ +$my_files = array(); +$content_files = "\n"; + +//in order to control if some [media] is in the body_text +$body = embed_media($body_t); + +$parser->parse($body); + +// add by Cindy Li. +// This resolves the problem introduced by [media] tag: when [media] is +// parsed into , same resource appears a few times in with different +// format to cater for different browsers or players. This way creates prolem that different +// formats in are all parsed and considered as different resource. array_unique() +// call solves this problem. But, it introduces the new problem that when a same resource +// appears at different places in the content and users do want to have them with different +// alternatives. With this solution, this same resource only shows up once at "adapt content" +// and only can have one alternative associate with. Table and scripts need to re-design +// to solve this problem, for example, include line number in table. +$my_files = array_unique($my_files); + +/* handle @import */ +$import_files = get_import_files($body); + +if (count($import_files) > 0) $my_files = array_merge($my_files, $import_files); + +$i=0; + +foreach ($my_files as $file) { + /* filter out full urls */ + $url_parts = @parse_url($file); +// if (isset($url_parts['scheme']) && substr($file, 0, strlen(AT_BASE_HREF)) != AT_BASE_HREF) { +// continue; +// } + + /* file should be relative to content. let's double check */ + if ((substr($file, 0, 1) == '/')) { + continue; + } + + $resources[$i] = $file; + $i++; +} + +$organizations_str = ob_get_contents(); +ob_end_clean(); \ No newline at end of file diff --git a/mods/_core/imscc/classes/DiscussionTools.class.php b/mods/_core/imscc/classes/DiscussionTools.class.php new file mode 100644 index 000000000..d4a4a4f08 --- /dev/null +++ b/mods/_core/imscc/classes/DiscussionTools.class.php @@ -0,0 +1,43 @@ +title = $title; + $this->text = $text; + } + + function getTitle(){ + return htmlspecialchars(trim($this->title)); + } + + function getText(){ + //change the $IMS-CC-FILEBASE$ to the base of this directory + //TODO: The returned value may contains HTML, ATutor doesn't check + // if it contains malicious javascript at this point. + $this->text = preg_replace('/\$IMS\-CC\-FILEBASE\$/', '', $this->text); + return trim(html_entity_decode($this->text)); + } +} +?> \ No newline at end of file diff --git a/mods/_core/imscc/classes/DiscussionToolsImport.class.php b/mods/_core/imscc/classes/DiscussionToolsImport.class.php new file mode 100644 index 000000000..95964d24c --- /dev/null +++ b/mods/_core/imscc/classes/DiscussionToolsImport.class.php @@ -0,0 +1,79 @@ +getTitle(); + $text = $forum_obj->getText(); + + $this->fid = $this->createForum($title, $text); + $this->associateForum($cid, $this->fid); + } + + + /** + * create a forum + * @param string title + * @param string text/description + * @return added forum's id + */ + function createForum($title, $text){ + global $db; + //create POST array + $temp['title'] = $title; + $temp['body'] = $text; + $temp['edit'] = 0; //default 0 minutes + + add_forum($temp); //check forums.inc.php + + $sql = 'SELECT MAX(forum_id) FROM '.TABLE_PREFIX.'forums'; + $result = mysql_query($sql, $db); + $row = mysql_fetch_row($result); + return $row[0]; + } + + + /** + * create an association between forum and content + * @param int content id + * @return + */ + function associateForum($cid, $fid){ + global $db; + $sql = 'INSERT INTO '.TABLE_PREFIX."content_forums_assoc (content_id, forum_id) VALUES ($cid, $fid)"; + mysql_query($sql, $db); + } + + /** + * Return the fid that was created by this import + * @return int forum id. + */ + function getFid(){ + return $this->fid; + } +} +?> \ No newline at end of file diff --git a/mods/_core/imscc/classes/DiscussionToolsParser.class.php b/mods/_core/imscc/classes/DiscussionToolsParser.class.php new file mode 100644 index 000000000..059dffd95 --- /dev/null +++ b/mods/_core/imscc/classes/DiscussionToolsParser.class.php @@ -0,0 +1,97 @@ +parser = xml_parser_create(); + + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($this->parser, 'startElement', 'endElement'); + xml_set_character_data_handler($this->parser, 'characterData'); + } + + // public + // @return true if parsed successfully, false otherwise + function parse($xml_data) { + $this->element_path = array(); + $this->character_data = ''; + xml_parse($this->parser, $xml_data, TRUE); + } + + // private + function startElement($parser, $name, $attributes) { + //save attributes. + switch($name) { + case 'text': + $this->text_type = $attributes['texttype']; + break; + } + array_push($this->element_path, $name); + } + + // private + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) { + switch($name) { + case 'title': + $this->title = $this->character_data; + break; + case 'text': + $this->text = $this->character_data; + break; + } + + //pop stack and reset character data, o/w it will stack up + array_pop($this->element_path); + $this->character_data = ''; + } + + // private + function characterData($parser, $data){ + global $addslashes; + if (trim($data)!=''){ + $this->character_data .= preg_replace('/[\t\0\x0B(\r\n)]*/', '', $data); +// $this->character_data .= trim($data); + } + } + + //public + function close(){ + //Free the XML parser + xml_parser_free($this->parser); + } + + //get title + function getDT(){ + return new DiscussionTools($this->title, $this->text); + } +} +?> \ No newline at end of file diff --git a/mods/_core/imscc/classes/Weblinks.class.php b/mods/_core/imscc/classes/Weblinks.class.php new file mode 100644 index 000000000..f4e600839 --- /dev/null +++ b/mods/_core/imscc/classes/Weblinks.class.php @@ -0,0 +1,65 @@ +title = $title; + $this->url['href'] = $url; + $this->setUrlPrefs(); //set defaults values + } + + /** + * Set Url prefs + * @param string resembles HTML target attribute, [_self, _blank, _parent, _top, ], default '_self' + * @param string browser window settings + */ + function setUrlPrefs($target='_self', $window_features=''){ + $this->url['target'] = $target; + $this->url['window_features'] = $window_features; + } + + + /** + * Return the title of this weblink + * @return string + */ + function getTitle(){ + return $this->title; + } + + + /** + * Return the URL array of this weblink + * @return mixed + */ + function getUrl(){ + return $this->url; + } +} +?> \ No newline at end of file diff --git a/mods/_core/imscc/classes/Weblinks.tmpl.php b/mods/_core/imscc/classes/Weblinks.tmpl.php new file mode 100644 index 000000000..b11dae3eb --- /dev/null +++ b/mods/_core/imscc/classes/Weblinks.tmpl.php @@ -0,0 +1,7 @@ +' ?> + + <?php echo $this->title; ?> + + diff --git a/mods/_core/imscc/classes/WeblinksExport.class.php b/mods/_core/imscc/classes/WeblinksExport.class.php new file mode 100644 index 000000000..650596a85 --- /dev/null +++ b/mods/_core/imscc/classes/WeblinksExport.class.php @@ -0,0 +1,56 @@ +wl = $wl; + } + + + /** + * Export + */ + function export(){ + global $savant; + + //localize + $wl = $this->wl; + + //assign all the neccessarily values to the template. + $savant->assign('title', htmlentities($wl->getTitle(), ENT_QUOTES, 'UTF-8')); + $url = $wl->getUrl(); + $savant->assign('url_href', htmlentities($url['href'], ENT_QUOTES, 'UTF-8')); + $savant->assign('url_target', $url['target']); + //TODO: not supported yet + //$savant->assign('url_window_features', $url['window_features']); + + //generates xml + $xml = $savant->fetch(AT_INCLUDE_PATH.'../mods/_core/imscc/classes/Weblinks.tmpl.php'); + + return $xml; + } +} +?> \ No newline at end of file diff --git a/mods/_core/imscc/classes/WeblinksParser.class.php b/mods/_core/imscc/classes/WeblinksParser.class.php new file mode 100644 index 000000000..0a1bb3f81 --- /dev/null +++ b/mods/_core/imscc/classes/WeblinksParser.class.php @@ -0,0 +1,102 @@ +parser = xml_parser_create(); + + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($this->parser, 'startElement', 'endElement'); + xml_set_character_data_handler($this->parser, 'characterData'); + } + + + // public + // @return true if parsed successfully, false otherwise + function parse($xml_data) { + $this->element_path = array(); + $this->character_data = ''; + xml_parse($this->parser, $xml_data, TRUE); + } + + // private + function startElement($parser, $name, $attributes) { + //save attributes. + switch($name) { + case 'url': + $this->url = $attributes['href']; + break; + } + array_push($this->element_path, $name); + } + + // private + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) { + //check element path + $current_pos = count($this->element_path) - 1; + $last_element = $this->element_path[$current_pos - 1]; + + switch($name) { + case 'title': + $this->title = $this->character_data; + break; + } + + //pop stack and reset character data, o/w it will stack up + array_pop($this->element_path); + $this->character_data = ''; + } + + // private + function characterData($parser, $data){ + global $addslashes; + if (trim($data)!=''){ + $this->character_data .= preg_replace('/[\t\0\x0B(\r\n)]*/', '', $data); +// $this->character_data .= trim($data); + } + } + + //public + function close(){ + //Free the XML parser + xml_parser_free($this->parser); + } + + //gets + function getTitle(){ + return $this->title; + } + function getUrl(){ + return $this->url; + } +} + +?> diff --git a/mods/_core/imscc/ims_export.php b/mods/_core/imscc/ims_export.php new file mode 100644 index 000000000..556054e70 --- /dev/null +++ b/mods/_core/imscc/ims_export.php @@ -0,0 +1,347 @@ + 0) + $msg->addFeedback(array('TILE_IMPORT_SUCCESS', AT_TILE_VIEW_COURSE_URL.$tile_course_id)); + else + { + // No response from transformable, the package file might be too big + if (trim($error) == '') $error = _AT('tile_no_response'); + else { + // delete this access token since it cannot import into Transformable + $sql = "DELETE FROM ".TABLE_PREFIX."oauth_client_tokens + WHERE token = '".$access_token_key."' + AND token_type='access'"; + $result = mysql_query($sql, $db); + } + $msg->addError(array('TILE_IMPORT_FAIL', $error)); + } + + header('Location: '.AT_BASE_HREF.'mods/_core/imscp/index.php'); + exit; +} else if (isset($_GET['m'])) { + /* for TILE */ + + /* request (hopefully) coming from a TILE server, send the content package */ + + $_user_location = 'public'; + require(AT_INCLUDE_PATH.'vitals.inc.php'); + $m = md5(DB_PASSWORD . 'x' . ADMIN_PASSWORD . 'x' . $_SERVER['SERVER_ADDR'] . 'x' . $cid . 'x' . $c . 'x' . date('Ymd')); + if (($m != $_GET['m']) || !$c) { + header('HTTP/1.1 404 Not Found'); + echo 'Document not found.'; + exit; + } + + $course_id = $c; + if (isset($_GET['a4a'])){ + $use_a4a = true; + } +} else { + $use_a4a = false; + if (isset($_REQUEST['to_a4a'])){ + $use_a4a = true; + } + require(AT_INCLUDE_PATH.'vitals.inc.php'); + $course_id = $_SESSION['course_id']; +} +//load the following after vitals is included +require(AT_INCLUDE_PATH.'../mods/_standard/tests/classes/testQuestions.class.php'); +require(AT_INCLUDE_PATH.'../mods/_core/imsafa/classes/A4aExport.class.php'); +require(AT_INCLUDE_PATH.'../mods/_core/imscc/classes/Weblinks.class.php'); +require(AT_INCLUDE_PATH.'../mods/_core/imscc/classes/WeblinksExport.class.php'); + +$use_cc = true; +$instructor_id = $system_courses[$course_id]['member_id']; +$course_desc = htmlspecialchars($system_courses[$course_id]['description'], ENT_QUOTES, 'UTF-8'); +$course_title = htmlspecialchars($system_courses[$course_id]['title'], ENT_QUOTES, 'UTF-8'); +$course_language = $system_courses[$course_id]['primary_language']; + +$courseLanguage =& $languageManager->getLanguage($course_language); +//If course language cannot be found, use UTF-8 English +//@author harris, Oct 30,2008 +if ($courseLanguage == null){ + $courseLanguage =& $languageManager->getLanguage('en'); +} + +$course_language_charset = $courseLanguage->getCharacterSet(); +$course_language_code = $courseLanguage->getCode(); + +require(AT_INCLUDE_PATH.'classes/zipfile.class.php'); /* for zipfile */ +require(AT_INCLUDE_PATH.'classes/vcard.php'); /* for vcard */ +require(AT_INCLUDE_PATH.'classes/XML/XML_HTMLSax/XML_HTMLSax.php'); /* for XML_HTMLSax */ +require(AT_INCLUDE_PATH.'../mods/_core/imscc/include/ims_template.inc.php'); /* for ims templates + print_organizations() */ + +if (isset($_POST['cancel'])) { + $msg->addFeedback('EXPORT_CANCELLED'); + header('Location: ../index.php'); + exit; +} + + +$zipfile = new zipfile(); +$zipfile->create_dir('resources/'); + +/* + the following resources are to be identified: + even if some of these can't be images, they can still be files in the content dir. + theoretically the only urls we wouldn't deal with would be for a + + img => src + a => href // ignore if href doesn't exist (ie. ) + object => data | classid // probably only want data + applet => classid | archive // whatever these two are should double check to see if it's a valid file (not a dir) + link => href + script => src + form => action + input => src + iframe => src + +*/ +class MyHandler { + function MyHandler(){} + function openHandler(& $parser,$name,$attrs) { + global $my_files; + + $name = strtolower($name); + $attrs = array_change_key_case($attrs, CASE_LOWER); + + $elements = array( 'img' => 'src', + 'a' => 'href', + 'object' => array('data', 'classid'), + 'applet' => array('classid', 'archive'), + 'link' => 'href', + 'script' => 'src', + 'form' => 'action', + 'input' => 'src', + 'iframe' => 'src', + 'embed' => 'src', + 'param' => 'value'); + + /* check if this attribute specifies the files in different ways: (ie. java) */ + if (is_array($elements[$name])) { + $items = $elements[$name]; + foreach ($items as $item) { + if ($attrs[$item] != '') { + + /* some attributes allow a listing of files to include seperated by commas (ie. applet->archive). */ + if (strpos($attrs[$item], ',') !== false) { + $files = explode(',', $attrs[$item]); + foreach ($files as $file) { + $my_files[] = trim($file); + } + } else { + $my_files[] = $attrs[$item]; + } + } + } + } else if (isset($elements[$name]) && ($attrs[$elements[$name]] != '')) { + /* we know exactly which attribute contains the reference to the file. */ + //hack, if param[name]=autoplay or autostart, ignore + if (!($name=='param' && ($attrs['name']=='autoplay' || $attrs['name']=='autoStart'))){ + //skip glossary.html, tweak to accomodate atutor imscp; also skip repeated entries. + if (strpos($attrs[$elements[$name]], 'glossary.html')===false + && !in_array($attrs[$elements[$name]], $my_files)){ + $my_files[] = $attrs[$elements[$name]]; + } + } + } + } + function closeHandler(& $parser,$name) { } +} + +/* get all the content */ +$content = array(); +$paths = array(); +$top_content_parent_id = 0; + +$handler=new MyHandler(); +$parser = new XML_HTMLSax(); +$parser->set_object($handler); +$parser->set_element_handler('openHandler','closeHandler'); + +if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) { + $sql = "SELECT *, UNIX_TIMESTAMP(last_modified) AS u_ts FROM ".TABLE_PREFIX."content WHERE course_id=$course_id ORDER BY content_parent_id, ordering"; +} else { + $sql = "SELECT *, UNIX_TIMESTAMP(last_modified) AS u_ts FROM ".TABLE_PREFIX."content WHERE course_id=$course_id ORDER BY content_parent_id, ordering"; +} +$cid = $_REQUEST['cid']; //takes care of some system which lost the REQUEST[cid] +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) || $contentManager->isReleased($row['content_id']) === TRUE) { + $content[$row['content_parent_id']][] = $row; + if ($cid == $row['content_id']) { + $top_content = $row; + $top_content_parent_id = $row['content_parent_id']; + } + } +} + +if ($cid) { + /* filter out the top level sections that we don't want */ + $top_level = $content[$top_content_parent_id]; + foreach($top_level as $page) { + if ($page['content_id'] == $cid) { + $content[$top_content_parent_id] = array($page); + } else { + /* this is a page we don't want, so might as well remove it's children too */ + unset($content[$page['content_id']]); + } + } + $ims_course_title = $course_title . ' - ' . $content[$top_content_parent_id][0]['title']; +} else { + $ims_course_title = $course_title; +} + +$imsmanifest_xml = str_replace(array('{COURSE_TITLE}', '{COURSE_DESCRIPTION}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'), + array($ims_course_title, $course_desc, $course_language_charset, $course_language_code), + $ims_template_xml['header']); +//debug($imsmanifest_xml); +//exit; + +/* get the first content page to default the body frame to */ +$first = $content[$top_content_parent_id][0]; + +$test_ids = array(); //global array to store all the test ids + +//TODO**************BOLOGNA***************REMOVE ME***************************/ +//Exoprt Forum: +global $forum_list, $test_xml_items;; +$forum_list = array(); + +/* generate the resources and save the HTML files */ +$used_glossary_terms = array(); +ob_start(); +print_organizations($top_content_parent_id, $content, 0, '', array(), $toc_html); + +//Exoprt Forum: +print_resources_forum(); + +$organizations_str = ob_get_contents(); +ob_end_clean(); +if (count($used_glossary_terms)) { + $used_glossary_terms = array_unique($used_glossary_terms); + sort($used_glossary_terms); + reset($used_glossary_terms); + $terms_xml = ''; + foreach ($used_glossary_terms as $term) { + $term_key = htmlspecialchars($term, ENT_QUOTES, 'UTF-8'); + $glossary[$term_key] = htmlentities($glossary[$term_key], ENT_QUOTES, 'UTF-8'); + $glossary[$term_key] = str_replace('&', '&', $glossary[$term_key]); + $terms_xml .= str_replace( array('{TERM}', '{DEFINITION}'), + array($term_key, $glossary[$term_key]), + $glossary_term_xml); + } + $glossary_xml = str_replace(array('{GLOSSARY_TERMS}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}'), + array($terms_xml, $course_language_charset), + $glossary_xml); + //add to resource + $resources .= $ims_template_xml['resource_glossary']; +} else { + unset($glossary_xml); +} + +if ($glossary_xml){ + $glossary_manifest_xml = $ims_template_xml['glossary']; +} else { + $glossary_manifest_xml = ''; +} + +/* append the Organizations and Resources to the imsmanifest */ +$imsmanifest_xml .= str_replace( array('{ORGANIZATIONS}', '{GLOSSARY}', '{RESOURCES}', '{TEST_ITEMS}', '{COURSE_TITLE}'), + array($organizations_str, $glossary_manifest_xml, $resources, $test_xml_items, $ims_course_title), + $ims_template_xml['final']); + +/* generate the vcard for the instructor/author */ +$sql = "SELECT first_name, last_name, email, website, login, phone FROM ".TABLE_PREFIX."members WHERE member_id=$instructor_id"; +$result = mysql_query($sql, $db); +$vcard = new vCard(); +if ($row = mysql_fetch_assoc($result)) { + $vcard->setName($row['last_name'], $row['first_name'], $row['login']); + $vcard->setEmail($row['email']); + $vcard->setNote('Originated from an ATutor at '.AT_BASE_HREF.'. See ATutor.ca for additional information.'); + $vcard->setURL($row['website']); + + $imsmanifest_xml = str_replace('{VCARD}', $vcard->getVCard(), $imsmanifest_xml); +} else { + $imsmanifest_xml = str_replace('{VCARD}', '', $imsmanifest_xml); +} + +/* save the imsmanifest.xml file */ +$zipfile->add_file($imsmanifest_xml, 'imsmanifest.xml'); + +if ($glossary_xml) { + $zipfile->create_dir('resources/GlossaryItem/'); + $zipfile->add_file($glossary_xml, 'resources/GlossaryItem/glossary.xml'); +} +$zipfile->close(); // this is optional, since send_file() closes it anyway + +$ims_course_title = str_replace(array(' ', ':'), '_', $ims_course_title); +/** + * A problem here with the preg_replace below. + * Originally was designed to remove all werid symbols to avoid file corruptions. + * In UTF-8, all non-english chars are considered to be 'werid symbols' + * We can still replace it as is, or add fileid to the filename to avoid these problems + * Well then again people won't be able to tell what this file is about + * If we are going to take out the preg_replace, some OS might not be able to understand + * these characters and will have problems importing. + */ +$ims_course_title = preg_replace("{[^a-zA-Z0-9._-]}","", trim($ims_course_title)); +$zipfile->send_file($ims_course_title . '_imscc'); + +exit; +?> diff --git a/mods/_core/imscc/ims_import.php b/mods/_core/imscc/ims_import.php new file mode 100644 index 000000000..2871a8c16 --- /dev/null +++ b/mods/_core/imscc/ims_import.php @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/mods/_core/imscc/include/ims_template.inc.php b/mods/_core/imscc/include/ims_template.inc.php new file mode 100644 index 000000000..940fe5210 --- /dev/null +++ b/mods/_core/imscc/include/ims_template.inc.php @@ -0,0 +1,692 @@ + 0) + { + $start_pos = strpos($text, $tag); + if ($start_pos !== false) + { + $text = substr($text, $start_pos); + $start_pos = strlen($tag); + $len = strpos($text, ';') - strlen($tag); + + $file = substr(trim($text), $start_pos, $len); + + // remove these characters from file name: url, (, ), ", ' + $file = trim(preg_replace('/(\'|\"|url|\(|\))/', '', $file)); + + // strip processed tag + $text = substr($text, $start_pos); + array_push($files, $file); + } + + } + return $files; +} + +function print_organizations($parent_id, + &$_menu, + $depth, + $path='', + $children, + &$string) { + + global $html_content_template, $default_html_style, $zipfile, $resources, $ims_template_xml, $parser, $my_files; + global $used_glossary_terms, $course_id, $course_language_charset, $course_language_code; + static $paths, $zipped_files; + global $glossary; + global $test_list, $test_zipped_files, $test_files, $test_xml_items, $use_a4a; + /* added by bologna*///TODO***********BOLOGNA**************REMOVE ME*****************/ + global $db,$forum_list;//forum_list contiene tutti i forum distinti associati ai contenuti. poich� la funzione in questione � ricorsiva deve essere globale in modo che in fase di creazione dell'archivio zip i file descrittori dei forum non vengano ripetuti + + $space = ' '; + $prefix = ' '; + + if ($depth == 0) { + $string .= '
      '; + } + $top_level = $_menu[$parent_id]; + if (!is_array($paths)) { + $paths = array(); + } + if (!is_array($zipped_files)) { + $zipped_files = array(); + } + if ( is_array($top_level) ) { + + $counter = 1; + $num_items = count($top_level); + + foreach ($top_level as $garbage => $content) { + $link = ''; + //XSL characters handling + $content['title'] = str_replace('&', '&', $content['title']); + + if ($content['content_path'] && (substr($content['content_path'],-1)!='/')) { + $content['content_path'] .= '/'; + } + + /* + * generate weblinks + * Reason to put it here is cause we don't want the content to be overwrittened. + */ + if ($content['content_type']==CONTENT_TYPE_WEBLINK){ + $wl = new Weblinks($content['title'], $content['text']); + $wlexport = new WeblinksExport($wl); + $wl_xml = $wlexport->export(); + $wl_filename = 'weblinks_'.$content['content_id'].'.xml'; + $zipfile->add_file($wl_xml , 'Weblinks/'.$wl_filename, $content['u_ts']); + $resources .= str_replace( array('{PATH}', '{CONTENT_ID}'), + array($wl_filename, $content['content_id']), + $ims_template_xml['resource_weblink']); + //Done. +// continue; + } + + if ($content['content_type']==CONTENT_TYPE_FOLDER){ + $link .= $prefix.''."\n"; + $link .= $prefix.$space.''.$content['title'].''."\n"; + } else { + $link .= ''."\n"; + $link .= $prefix.$space.''.$content['title'].''."\n$prefix$space"; + } + $html_link = ''.$content['title'].''; + + /* save the content as HTML files */ + /* @See: include/lib/format_content.inc.php */ + $content['text'] = str_replace('CONTENT_DIR/', '', $content['text']); + /* get all the glossary terms used */ + $terms = find_terms($content['text']); + if (is_array($terms)) { + foreach ($terms[2] as $term) { + $used_glossary_terms[] = $term; + } + } + + + //TODO**************BOLOGNA****************REMOVE ME************/ + $f_count = count($forum_list); //count all distinct forum_id associated to a content page + //la funzione è ricorsiva quindi lo devo ricavare attraverso la variabile globale forum_list + + /* TODO *************BOLOGNA*************REMOVE ME*********/ + //recupero i forum associati al contenuto corrente + $sql = "SELECT cf.forum_id, f.title, f.description FROM (SELECT * FROM ".TABLE_PREFIX."content_forums_assoc WHERE content_id=$content[content_id]) AS cf LEFT JOIN ".TABLE_PREFIX."forums f ON cf.forum_id=f.forum_id"; + $result_cf = mysql_query($sql,$db); + $cf_count = mysql_num_rows($result_cf); + + //per ogni forum ottenuto controllo se è già stato caricato nell'array + //necessario mantenerlo distinto poichè NON si prevedono funzioni sul + //controllo dei nomi nell'inserimento di file nell'archivio. + $find=false; + $forums_dependency=''; //template for associate Discussion Topic to the current content into the manifest + while($current_forum = mysql_fetch_assoc($result_cf)) { + for($j=0;$j<$f_count;$j++) { + if($forum_list[$j]['id'] == $current_forum['forum_id']) + $find= true; + } + if(!$find) { + + $forum_list[$f_count]['id']=$current_forum['forum_id']; + $forum_list[$f_count]['title']=$current_forum['title']; + $forum_list[$f_count]['description']=$current_forum['description']; + $find=false; + $f_count++; + } + $forums_dependency .= $prefix.$space.''; + } + + /** Test dependency **/ + $sql = 'SELECT * FROM '.TABLE_PREFIX.'content_tests_assoc WHERE content_id='.$content['content_id']; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)){ + //add test dependency on top of forum dependency + $forums_dependency .= $prefix.$space.''; + } + + + /* calculate how deep this page is: */ + $path = '../'; + if ($content['content_path']) { + $depth = substr_count($content['content_path'], '/'); + + $path .= str_repeat('../', $depth); + } + + $content['text'] = format_content($content['text'], $content['formatting'], $glossary, $path); + + /* add HTML header and footers to the files */ + + /* use default style if '; + +//output this as header.html +$html_mainheader = ' + + + + + {COURSE_TITLE} + +

      {COURSE_TITLE}

      '; + + +$html_toc = ' + + + + + + +{TOC}'; + +// index.html +$html_frame = ' + + + {COURSE_TITLE} + + + + + + + + + + <h1>{COURSE_TITLE}</h1> + <p><a href="toc.html">Table of Contents</a> | <a href="footer.html">About</a><br /> + </p> + + +'; + + + +$glossary_xml = ' + + + + {GLOSSARY_TERMS} + +'; + +$glossary_term_xml = ' + {TERM} + {DEFINITION} + '; + +$glossary_body_html = '

      Glossary

      +
        +{BODY} +
      '; + +$glossary_term_html = '
    • {TERM}
      + {DEFINITION}

    • '; + +?> diff --git a/mods/_core/imscp/domainProfile_0/imsccauth_v1p0.xsd b/mods/_core/imscp/domainProfile_0/imsccauth_v1p0.xsd new file mode 100644 index 000000000..738ccda70 --- /dev/null +++ b/mods/_core/imscp/domainProfile_0/imsccauth_v1p0.xsd @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This group is defined exactly as in IMS Content Packaging v 1.2. + Any namespaced element from any namespace may be included within an "any" element. + The namespace for the imported element must be defined in the instance, and the schema must be imported. + The extension has a definition of "strict" i.e. they must have their own namespace. + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_0/imsccauth_v1p0_localised.xsd b/mods/_core/imscp/domainProfile_0/imsccauth_v1p0_localised.xsd new file mode 100644 index 000000000..d73795cc2 --- /dev/null +++ b/mods/_core/imscp/domainProfile_0/imsccauth_v1p0_localised.xsd @@ -0,0 +1,55 @@ + + + + + + + + + + + + general: This specification defines the authorizations for Common Cartridges and the roles to be used for selective display of resources to Learner or Instructor. + + + + + + + + + + + + + + + + + + + + + + + + + + + + This group is defined exactly as in IMS Content Packaging v 1.2. + Any namespaced element from any namespace may be included within an "any" element. + The namespace for the imported element must be defined in the instance, and the schema must be imported. + The extension has a definition of "strict" i.e. they must have their own namespace. + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/anyElement.xsd b/mods/_core/imscp/domainProfile_1/anyElement.xsd new file mode 100644 index 000000000..5b2ba1d24 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/anyElement.xsd @@ -0,0 +1,36 @@ + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides the element group declaration and the + attribute group declaration used for extension XML elements and attributes. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/anyElement_localised.xsd b/mods/_core/imscp/domainProfile_1/anyElement_localised.xsd new file mode 100644 index 000000000..0dfe131ac --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/anyElement_localised.xsd @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides the element group declaration and the + attribute group declaration used for extension XML elements and attributes. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/common/anyElement.xsd b/mods/_core/imscp/domainProfile_1/common/anyElement.xsd new file mode 100644 index 000000000..34d6c6a63 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/common/anyElement.xsd @@ -0,0 +1,39 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides the element group declaration and the + attribute group declaration used for extension XML elements and attributes. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/common/dataTypes.xsd b/mods/_core/imscp/domainProfile_1/common/dataTypes.xsd new file mode 100644 index 000000000..eebfb932d --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/common/dataTypes.xsd @@ -0,0 +1,118 @@ + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion defines global schema data type declarations + for data types defined in the LOMv1.0 base schema. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/common/elementNames.xsd b/mods/_core/imscp/domainProfile_1/common/elementNames.xsd new file mode 100644 index 000000000..019dd2e39 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/common/elementNames.xsd @@ -0,0 +1,783 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defintion defines global element declarations for + each of the data elements defined in the LOMv1.0 base schema. This component + schema definition is used to check for the uniqueness of elements declared + to be unique within their parent elements by the presence of the + "uniqueElementName" attribute. The XML Schema constraint "unique" is used + to enforce uniqueness constraints. + + Duplicate declarations are included as comments for completeness. These + declarations should remain commented out or they can be removed completely. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/common/elementTypes.xsd b/mods/_core/imscp/domainProfile_1/common/elementTypes.xsd new file mode 100644 index 000000000..9d45a7114 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/common/elementTypes.xsd @@ -0,0 +1,779 @@ + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion defines global schema data type declarations + for data elements defined in the LOMv1.0 base schema. This component XSD + defines the aggregation relationship among the LOM data elements. These aggregation + relationships enforce the LOMv1.0 base schema requirement that elements can only + be present in a LOM XML instance as elements of the aggregate element to which they + belong. + + Duplicate declarations are included as comments for completeness. These declarations + should remain commented out or they can be removed completely. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/common/rootElement.xsd b/mods/_core/imscp/domainProfile_1/common/rootElement.xsd new file mode 100644 index 000000000..936f045d8 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/common/rootElement.xsd @@ -0,0 +1,43 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defintion provides the element name declaration for the + root element for all LOM XML instances. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/common/vocabTypes.xsd b/mods/_core/imscp/domainProfile_1/common/vocabTypes.xsd new file mode 100644 index 000000000..128de9e2e --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/common/vocabTypes.xsd @@ -0,0 +1,355 @@ + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion provides global type declarations for those + LOM data elements whose values are taken from a Vocabulary data type. + + + + This component XSD requires schema components from other + schemas that are defined in other namespaces. These statements import the + appropriate components. The xsi:schemaLocation attribute is used to specify + the location of the file that contains the schema that defines the namespace. + The xsi:schemaLocation attribute is optional and is ommitted. By definition of + the composite schemas the appropriate namespaces and related files where those + namespaces are defined are brought into scope. Some XML parsers may require + these import statements to contain the optional xsi:schemaLocation attribute. + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/common/vocabValues.xsd b/mods/_core/imscp/domainProfile_1/common/vocabValues.xsd new file mode 100644 index 000000000..8d26b6e23 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/common/vocabValues.xsd @@ -0,0 +1,266 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides global type declarations for the standard + LOMv1.0 vocabulary tokens for those LOM data elements whose values are taken from + a Vocabulary data type. + + This component schema defintion defines the stanard vocabulary value + declarations as defined in the LOMv1.0 base schema. These vocabulary + value declarations are used in conjunction with both vocab/custom.xsd and + vocab/loose.xsd. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/dataTypes_localised.xsd b/mods/_core/imscp/domainProfile_1/dataTypes_localised.xsd new file mode 100644 index 000000000..a3747096a --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/dataTypes_localised.xsd @@ -0,0 +1,130 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion defines global schema data type declarations + for data types defined in the LOMv1.0 base schema. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/elementNames_localised.xsd b/mods/_core/imscp/domainProfile_1/elementNames_localised.xsd new file mode 100644 index 000000000..9ab993e5b --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/elementNames_localised.xsd @@ -0,0 +1,787 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defintion defines global element declarations for + each of the data elements defined in the LOMv1.0 base schema. This component + schema definition is used to check for the uniqueness of elements declared + to be unique within their parent elements by the presence of the + "uniqueElementName" attribute. The XML Schema constraint "unique" is used + to enforce uniqueness constraints. + + Duplicate declarations are included as comments for completeness. These + declarations should remain commented out or they can be removed completely. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/elementTypes_localised.xsd b/mods/_core/imscp/domainProfile_1/elementTypes_localised.xsd new file mode 100644 index 000000000..406435b4f --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/elementTypes_localised.xsd @@ -0,0 +1,905 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion defines global schema data type declarations + for data elements defined in the LOMv1.0 base schema. This component XSD + defines the aggregation relationship among the LOM data elements. These aggregation + relationships enforce the LOMv1.0 base schema requirement that elements can only + be present in a LOM XML instance as elements of the aggregate element to which they + belong. + + Duplicate declarations are included as comments for completeness. These declarations + should remain commented out or they can be removed completely. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + explanation: metaMetadata is unused. + + + + + + + + explanation: lom.annotation is unused. + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + explanation: General.structure is unused. + + + + explanation: General.aggregationLevel is unused. + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: liveCycle.version is unused. + + + + explanation: lifeCycle.status is unused. + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: technical.size is unused. + + + + explanation: technical.location is unused. + + + + explanation: technical.requirement is unused. + + + + explanation: technical.installationRemarks is unused. + + + + explanation: technical.otherPlatformRequirements is unused. + + + + explanation: technical.duration is unused. + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: interactivityType is unused. + + + + + explanation: interactivityLevel is unused. + + + + explanation: semanticDensity is unused. + + + + explanation: intendedEndUserRole is unused. + + + + explanation: Context is unused. + + + + explanation: typicalAgeRange is unused. + + + + explanation: difficulty is unused. + + + + explanation: typicalLearningTime is unused. + + + + explanation: description is unused in educational context. + + + + explanation: language unused in technical context, only in general context. + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/extend/custom.xsd b/mods/_core/imscp/domainProfile_1/extend/custom.xsd new file mode 100644 index 000000000..bba5a3f56 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/extend/custom.xsd @@ -0,0 +1,52 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defines the XML Schema content model groups customElements + and customAttributes to support validation of extension XML elements and attributes. + + This component XSD should be used if extensions are to be supported in LOM + XML instances. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/imscc_m_definition.xsd b/mods/_core/imscp/domainProfile_1/imscc_m_definition.xsd new file mode 100644 index 000000000..7c67154d5 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/imscc_m_definition.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/lomLoose.xsd b/mods/_core/imscp/domainProfile_1/lomLoose.xsd new file mode 100644 index 000000000..ecb82c767 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/lomLoose.xsd @@ -0,0 +1,71 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This file represents a composite schema for validating + LOM XML Instances. This file is built by default to represent a + composite schema for validation of the following: + + 1) The use of LOMv1.0 base schema (i.e., 1484.12.1-2002) vocabulary + source/value pairs only + 2) Uniqueness constraints defined by LOMv1.0 base schema + 3) No existenace of any defined extensions: + LOMv1.0 base schema XML element extension, + LOMv1.0 base schema XML attribute extension and + LOMv1.0 base schema vocabulary data type extension + + Alternative composite schemas can be assembled by selecting + from the various alternative component schema listed below. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/lomLoose_localised.xsd b/mods/_core/imscp/domainProfile_1/lomLoose_localised.xsd new file mode 100644 index 000000000..db2aece2f --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/lomLoose_localised.xsd @@ -0,0 +1,96 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This file represents a composite schema for validating + LOM XML Instances. This file is built by default to represent a + composite schema for validation of the following: + + 1) The use of LOMv1.0 base schema (i.e., 1484.12.1-2002) vocabulary + source/value pairs only + 2) Uniqueness constraints defined by LOMv1.0 base schema + 3) No existenace of any defined extensions: + LOMv1.0 base schema XML element extension, + LOMv1.0 base schema XML attribute extension and + LOMv1.0 base schema vocabulary data type extension + + Alternative composite schemas can be assembled by selecting + from the various alternative component schema listed below. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + conformance: This profile restricts 'IEEE LOM 1.0 loose' to the elements needed to cover unqualified Dublin Core. +dc:contributor, dc:creator, dc:publisher map to lifeCycle.contribute.entity with appropriate value of lifeCycle.contribute.role, +dc:coverage maps to general.coverage, +dc:date maps to lifeCycle.contribute.date, +dc:description maps to general.description, +dc:format maps to technical.format, +dc:identifier maps to general.identifier, +dc:language maps to general.language, +dc:relation maps to Relation, +dc:rights maps to Rights, +dc:source is not mapped, +dc:subject maps to general.keyword (see also classification.keyword), +dc:title maps to general.title +dc:type maps to Educational.learningResourceType + + scope: This profile is used within the Common Cartridge specification. + name: IMS Common Cartridge profile of IEEE LOM V1.0 loose for unqualified Dublin Core + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/loose.xsd b/mods/_core/imscp/domainProfile_1/loose.xsd new file mode 100644 index 000000000..bc6a0474e --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/loose.xsd @@ -0,0 +1,292 @@ + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides attribute group declarations for + LOM data elements to support schema-based validation of uniqueness constraints + within a LOM XML instance where the exact set of attributes associated with each + element has to be as specified by the LOM XML Schema binding (i.e., where extra + attributes to enforce uniqueness have to be avoided). + + Duplicate declarations are included as comments for completeness. These + declarations should remain commented out or they can be removed completely. + + NOTE: The absence of the enforcement of the uniqueness constraints does not + relieve a particular LOM XML instance from satisfying the uniqueness constraints + described in the LOMv1.0 base schema. Applications that require the use of + the unique/loose.xsd component XSD have to enforce those uniqueness constraints + by other means. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/rootElement_localised.xsd b/mods/_core/imscp/domainProfile_1/rootElement_localised.xsd new file mode 100644 index 000000000..dfddcb234 --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/rootElement_localised.xsd @@ -0,0 +1,47 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defintion provides the element name declaration for the + root element for all LOM XML instances. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/unique/loose.xsd b/mods/_core/imscp/domainProfile_1/unique/loose.xsd new file mode 100644 index 000000000..8047676bd --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/unique/loose.xsd @@ -0,0 +1,295 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides attribute group declarations for + LOM data elements to support schema-based validation of uniqueness constraints + within a LOM XML instance where the exact set of attributes associated with each + element has to be as specified by the LOM XML Schema binding (i.e., where extra + attributes to enforce uniqueness have to be avoided). + + Duplicate declarations are included as comments for completeness. These + declarations should remain commented out or they can be removed completely. + + NOTE: The absence of the enforcement of the uniqueness constraints does not + relieve a particular LOM XML instance from satisfying the uniqueness constraints + described in the LOMv1.0 base schema. Applications that require the use of + the unique/loose.xsd component XSD have to enforce those uniqueness constraints + by other means. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/vocab/loose.xsd b/mods/_core/imscp/domainProfile_1/vocab/loose.xsd new file mode 100644 index 000000000..c25d38fab --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/vocab/loose.xsd @@ -0,0 +1,147 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides simple type declarations for LOM + data elements that are defined as Vocabulary data types. + + This component schema definition enforces that vocabulary sources and values + are character strings, which simplifies the schema validation process for those + applications that perform vocabulary source/value validation using + post-schema-validation. + + This component schema definition relaxes the validation constraints by + allowing both sources and values to be arbitrary character strings. + + NOTE: The absence of the enforcement of vocabulary values does not relieve a + particular LOM XML instance from satisfying vocabulary requirements defined + in the LOMv1.0 base schema. Applications that require the use of vocab/loose.xsd + component XSD should enforce those vocabulary requirements by other means. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_1/vocabTypes_localised.xsd b/mods/_core/imscp/domainProfile_1/vocabTypes_localised.xsd new file mode 100644 index 000000000..57ad0a1cf --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/vocabTypes_localised.xsd @@ -0,0 +1,379 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion provides global type declarations for those + LOM data elements whose values are taken from a Vocabulary data type. + + + + This component XSD requires schema components from other + schemas that are defined in other namespaces. These statements import the + appropriate components. The xsi:schemaLocation attribute is used to specify + the location of the file that contains the schema that defines the namespace. + The xsi:schemaLocation attribute is optional and is ommitted. By definition of + the composite schemas the appropriate namespaces and related files where those + namespaces are defined are brought into scope. Some XML parsers may require + these import statements to contain the optional xsi:schemaLocation attribute. + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: learningResourceType must be 'IMS Common Cartridge' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + + + + + + + explanation: No custom elements are allowed. + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_1/vocabValues_localised.xsd b/mods/_core/imscp/domainProfile_1/vocabValues_localised.xsd new file mode 100644 index 000000000..81429041b --- /dev/null +++ b/mods/_core/imscp/domainProfile_1/vocabValues_localised.xsd @@ -0,0 +1,270 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides global type declarations for the standard + LOMv1.0 vocabulary tokens for those LOM data elements whose values are taken from + a Vocabulary data type. + + This component schema defintion defines the stanard vocabulary value + declarations as defined in the LOMv1.0 base schema. These vocabulary + value declarations are used in conjunction with both vocab/custom.xsd and + vocab/loose.xsd. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/anyElement.xsd b/mods/_core/imscp/domainProfile_2/anyElement.xsd new file mode 100644 index 000000000..de01fb98b --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/anyElement.xsd @@ -0,0 +1,36 @@ + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides the element group declaration and the + attribute group declaration used for extension XML elements and attributes. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/anyElement_localised.xsd b/mods/_core/imscp/domainProfile_2/anyElement_localised.xsd new file mode 100644 index 000000000..df66c7f36 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/anyElement_localised.xsd @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides the element group declaration and the + attribute group declaration used for extension XML elements and attributes. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/common/anyElement.xsd b/mods/_core/imscp/domainProfile_2/common/anyElement.xsd new file mode 100644 index 000000000..936f372cd --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/common/anyElement.xsd @@ -0,0 +1,39 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides the element group declaration and the + attribute group declaration used for extension XML elements and attributes. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/common/dataTypes.xsd b/mods/_core/imscp/domainProfile_2/common/dataTypes.xsd new file mode 100644 index 000000000..fbd3e102e --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/common/dataTypes.xsd @@ -0,0 +1,118 @@ + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion defines global schema data type declarations + for data types defined in the LOMv1.0 base schema. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/common/elementNames.xsd b/mods/_core/imscp/domainProfile_2/common/elementNames.xsd new file mode 100644 index 000000000..bc20ce060 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/common/elementNames.xsd @@ -0,0 +1,783 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defintion defines global element declarations for + each of the data elements defined in the LOMv1.0 base schema. This component + schema definition is used to check for the uniqueness of elements declared + to be unique within their parent elements by the presence of the + "uniqueElementName" attribute. The XML Schema constraint "unique" is used + to enforce uniqueness constraints. + + Duplicate declarations are included as comments for completeness. These + declarations should remain commented out or they can be removed completely. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/common/elementTypes.xsd b/mods/_core/imscp/domainProfile_2/common/elementTypes.xsd new file mode 100644 index 000000000..b7731c157 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/common/elementTypes.xsd @@ -0,0 +1,779 @@ + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion defines global schema data type declarations + for data elements defined in the LOMv1.0 base schema. This component XSD + defines the aggregation relationship among the LOM data elements. These aggregation + relationships enforce the LOMv1.0 base schema requirement that elements can only + be present in a LOM XML instance as elements of the aggregate element to which they + belong. + + Duplicate declarations are included as comments for completeness. These declarations + should remain commented out or they can be removed completely. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/common/rootElement.xsd b/mods/_core/imscp/domainProfile_2/common/rootElement.xsd new file mode 100644 index 000000000..70bd42a3e --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/common/rootElement.xsd @@ -0,0 +1,43 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defintion provides the element name declaration for the + root element for all LOM XML instances. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/common/vocabTypes.xsd b/mods/_core/imscp/domainProfile_2/common/vocabTypes.xsd new file mode 100644 index 000000000..55d428760 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/common/vocabTypes.xsd @@ -0,0 +1,355 @@ + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion provides global type declarations for those + LOM data elements whose values are taken from a Vocabulary data type. + + + + This component XSD requires schema components from other + schemas that are defined in other namespaces. These statements import the + appropriate components. The xsi:schemaLocation attribute is used to specify + the location of the file that contains the schema that defines the namespace. + The xsi:schemaLocation attribute is optional and is ommitted. By definition of + the composite schemas the appropriate namespaces and related files where those + namespaces are defined are brought into scope. Some XML parsers may require + these import statements to contain the optional xsi:schemaLocation attribute. + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/common/vocabValues.xsd b/mods/_core/imscp/domainProfile_2/common/vocabValues.xsd new file mode 100644 index 000000000..f31f18ec5 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/common/vocabValues.xsd @@ -0,0 +1,266 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides global type declarations for the standard + LOMv1.0 vocabulary tokens for those LOM data elements whose values are taken from + a Vocabulary data type. + + This component schema defintion defines the stanard vocabulary value + declarations as defined in the LOMv1.0 base schema. These vocabulary + value declarations are used in conjunction with both vocab/custom.xsd and + vocab/loose.xsd. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/dataTypes_localised.xsd b/mods/_core/imscp/domainProfile_2/dataTypes_localised.xsd new file mode 100644 index 000000000..35ea03388 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/dataTypes_localised.xsd @@ -0,0 +1,127 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion defines global schema data type declarations + for data types defined in the LOMv1.0 base schema. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/elementNames_localised.xsd b/mods/_core/imscp/domainProfile_2/elementNames_localised.xsd new file mode 100644 index 000000000..dd1b9ee7d --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/elementNames_localised.xsd @@ -0,0 +1,787 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defintion defines global element declarations for + each of the data elements defined in the LOMv1.0 base schema. This component + schema definition is used to check for the uniqueness of elements declared + to be unique within their parent elements by the presence of the + "uniqueElementName" attribute. The XML Schema constraint "unique" is used + to enforce uniqueness constraints. + + Duplicate declarations are included as comments for completeness. These + declarations should remain commented out or they can be removed completely. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/elementTypes_localised.xsd b/mods/_core/imscp/domainProfile_2/elementTypes_localised.xsd new file mode 100644 index 000000000..9b9f77b62 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/elementTypes_localised.xsd @@ -0,0 +1,806 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion defines global schema data type declarations + for data elements defined in the LOMv1.0 base schema. This component XSD + defines the aggregation relationship among the LOM data elements. These aggregation + relationships enforce the LOMv1.0 base schema requirement that elements can only + be present in a LOM XML instance as elements of the aggregate element to which they + belong. + + Duplicate declarations are included as comments for completeness. These declarations + should remain commented out or they can be removed completely. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/extend/custom.xsd b/mods/_core/imscp/domainProfile_2/extend/custom.xsd new file mode 100644 index 000000000..611012e8e --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/extend/custom.xsd @@ -0,0 +1,52 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defines the XML Schema content model groups customElements + and customAttributes to support validation of extension XML elements and attributes. + + This component XSD should be used if extensions are to be supported in LOM + XML instances. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/imscc_mR_definition.xsd b/mods/_core/imscp/domainProfile_2/imscc_mR_definition.xsd new file mode 100644 index 000000000..9b82a7b5d --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/imscc_mR_definition.xsd @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/lomLoose.xsd b/mods/_core/imscp/domainProfile_2/lomLoose.xsd new file mode 100644 index 000000000..791eb7cf6 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/lomLoose.xsd @@ -0,0 +1,71 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This file represents a composite schema for validating + LOM XML Instances. This file is built by default to represent a + composite schema for validation of the following: + + 1) The use of LOMv1.0 base schema (i.e., 1484.12.1-2002) vocabulary + source/value pairs only + 2) Uniqueness constraints defined by LOMv1.0 base schema + 3) No existenace of any defined extensions: + LOMv1.0 base schema XML element extension, + LOMv1.0 base schema XML attribute extension and + LOMv1.0 base schema vocabulary data type extension + + Alternative composite schemas can be assembled by selecting + from the various alternative component schema listed below. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/lomLoose_localised.xsd b/mods/_core/imscp/domainProfile_2/lomLoose_localised.xsd new file mode 100644 index 000000000..81ea05a9e --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/lomLoose_localised.xsd @@ -0,0 +1,79 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This file represents a composite schema for validating + LOM XML Instances. This file is built by default to represent a + composite schema for validation of the following: + + 1) The use of LOMv1.0 base schema (i.e., 1484.12.1-2002) vocabulary + source/value pairs only + 2) Uniqueness constraints defined by LOMv1.0 base schema + 3) No existenace of any defined extensions: + LOMv1.0 base schema XML element extension, + LOMv1.0 base schema XML attribute extension and + LOMv1.0 base schema vocabulary data type extension + + Alternative composite schemas can be assembled by selecting + from the various alternative component schema listed below. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/loose.xsd b/mods/_core/imscp/domainProfile_2/loose.xsd new file mode 100644 index 000000000..a41244a97 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/loose.xsd @@ -0,0 +1,292 @@ + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides attribute group declarations for + LOM data elements to support schema-based validation of uniqueness constraints + within a LOM XML instance where the exact set of attributes associated with each + element has to be as specified by the LOM XML Schema binding (i.e., where extra + attributes to enforce uniqueness have to be avoided). + + Duplicate declarations are included as comments for completeness. These + declarations should remain commented out or they can be removed completely. + + NOTE: The absence of the enforcement of the uniqueness constraints does not + relieve a particular LOM XML instance from satisfying the uniqueness constraints + described in the LOMv1.0 base schema. Applications that require the use of + the unique/loose.xsd component XSD have to enforce those uniqueness constraints + by other means. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/rootElement_localised.xsd b/mods/_core/imscp/domainProfile_2/rootElement_localised.xsd new file mode 100644 index 000000000..8101ec3e2 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/rootElement_localised.xsd @@ -0,0 +1,47 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema defintion provides the element name declaration for the + root element for all LOM XML instances. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/unique/loose.xsd b/mods/_core/imscp/domainProfile_2/unique/loose.xsd new file mode 100644 index 000000000..0defa0398 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/unique/loose.xsd @@ -0,0 +1,295 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides attribute group declarations for + LOM data elements to support schema-based validation of uniqueness constraints + within a LOM XML instance where the exact set of attributes associated with each + element has to be as specified by the LOM XML Schema binding (i.e., where extra + attributes to enforce uniqueness have to be avoided). + + Duplicate declarations are included as comments for completeness. These + declarations should remain commented out or they can be removed completely. + + NOTE: The absence of the enforcement of the uniqueness constraints does not + relieve a particular LOM XML instance from satisfying the uniqueness constraints + described in the LOMv1.0 base schema. Applications that require the use of + the unique/loose.xsd component XSD have to enforce those uniqueness constraints + by other means. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/vocab/loose.xsd b/mods/_core/imscp/domainProfile_2/vocab/loose.xsd new file mode 100644 index 000000000..b216d835c --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/vocab/loose.xsd @@ -0,0 +1,147 @@ + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides simple type declarations for LOM + data elements that are defined as Vocabulary data types. + + This component schema definition enforces that vocabulary sources and values + are character strings, which simplifies the schema validation process for those + applications that perform vocabulary source/value validation using + post-schema-validation. + + This component schema definition relaxes the validation constraints by + allowing both sources and values to be arbitrary character strings. + + NOTE: The absence of the enforcement of vocabulary values does not relieve a + particular LOM XML instance from satisfying vocabulary requirements defined + in the LOMv1.0 base schema. Applications that require the use of vocab/loose.xsd + component XSD should enforce those vocabulary requirements by other means. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/domainProfile_2/vocabTypes_localised.xsd b/mods/_core/imscp/domainProfile_2/vocabTypes_localised.xsd new file mode 100644 index 000000000..c44208cc5 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/vocabTypes_localised.xsd @@ -0,0 +1,408 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + This component schema defintion provides global type declarations for those + LOM data elements whose values are taken from a Vocabulary data type. + + + + This component XSD requires schema components from other + schemas that are defined in other namespaces. These statements import the + appropriate components. The xsi:schemaLocation attribute is used to specify + the location of the file that contains the schema that defines the namespace. + The xsi:schemaLocation attribute is optional and is ommitted. By definition of + the composite schemas the appropriate namespaces and related files where those + namespaces are defined are brought into scope. Some XML parsers may require + these import statements to contain the optional xsi:schemaLocation attribute. + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + explanation: The vocabulary for intendedEndUserRole is 'IMSGLC_CC_Rolesv1p0'. + + + + + + + + + + + explanation: The source for a context object is fixed to 'LOMv1.0' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: Possible intendedEndUserRoles are only 'Learner' and 'Instructor'. + + + + + + + + + + + + + + + + + + + + + + + + + + + + explanation: The value of context is fixed to 'higher education'. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_2/vocabValues_localised.xsd b/mods/_core/imscp/domainProfile_2/vocabValues_localised.xsd new file mode 100644 index 000000000..42128ac08 --- /dev/null +++ b/mods/_core/imscp/domainProfile_2/vocabValues_localised.xsd @@ -0,0 +1,270 @@ + + + + + + + + + + + + + This work is licensed under the Creative Commons Attribution-ShareAlike + License. To view a copy of this license, see the file license.txt, + visit http://creativecommons.org/licenses/by-sa/2.0 or send a letter to + Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + + + + This component schema definition provides global type declarations for the standard + LOMv1.0 vocabulary tokens for those LOM data elements whose values are taken from + a Vocabulary data type. + + This component schema defintion defines the stanard vocabulary value + declarations as defined in the LOMv1.0 base schema. These vocabulary + value declarations are used in conjunction with both vocab/custom.xsd and + vocab/loose.xsd. + + + This file has been modified by the Knowledge Media Institute of the + University Koblenz-Landau (http://iwm.uni-koblenz.de). It contains the + following changes: + 1) Instead of "unique/strict.xsd" the schema "unique/loose.xsd" is imported + because this reflects what is said in 1484.12.3-2005, page 35 (section C.1.3) + 2) In all component XSDs the schemaLocation attribute was amended to the + xs:import and xs:include statements. This enables the usage of the schemas + with tools which don't deal well with missing schemaLocation informations. + + This file is available at "http://iwm.uni-koblenz.de/xsd/IEEE-LOM/loose" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_3/imscp_extensionv1p2.xsd b/mods/_core/imscp/domainProfile_3/imscp_extensionv1p2.xsd new file mode 100644 index 000000000..c0ca9a70b --- /dev/null +++ b/mods/_core/imscp/domainProfile_3/imscp_extensionv1p2.xsd @@ -0,0 +1,179 @@ + + + + + + XSD Data File Information + ------------------------- + Author: Colin Smythe (IMS, UK) + Date: 31st October, 2006 + Version: 2.0 + Status: Public Draft + Description: This is a normative representation of the IMS Packaging Extension 1.0 Information Model for binding + purposes. Read the corresponding IMS Content Packaging Information Model for the Platform + Independent Model representation. + + History: Version 2 includes the modified definition of LingualTitle. + Version 1 of the IMS Packaging Utility v1.2 XSD for public draft release. + It has a target namespace of http://www.imsglobal.org/xsd/imscp_extensionv1p2. + This Utility uses this general approach to modeling: + (1) All of the elements and attributes are defined as local to their host object; + (2) There are multiple host objects; + (3) Comments have been added to the complexType definitions. + + Copyright: 2006 (c) IMS Global Learning Consortium Inc. All Rights Reserved. + IMS Global Learning Consortium, Inc. (IMS/GLC) is publishing the information + contained in this binding ("Specification") for purposes of scientific + experimental and scholarly collaboration only. IMS/GLC makes no warranty or + representation regarding the accuracy or completeness of the Specification. + This material is provided on an "As Is" and "As Available basis". + The Specification is at all times subject to change and revision without + notice. It is your sole responsibility to evaluate the usefulness, accuracy + and completeness of the Specification as it relates to you. IMS/GLC would + appreciate receiving your comments and suggestions. Please contact IMS/GLC + through our website at: http://www.imsglobal.org. + + Source XSLT File Information + ---------------------------- + XSL Generator: UMLtoXSDTransformv0p7.xsl + XSLT Processor: Xalan + Release: 1.0 Beta 1 + Date: 30th November, 2005 + + Auto-generation Tool + -------------------- + This WSDL/XSD was auto-generated using the IMS WSDL/XSD auto-generation tool. While every attempt + has been made to ensure that this tool auto-generates the XSDs correctly, users should be aware + that this is an experimental tool. Permission is given to make use of this tool. IMS makes no + claim on the materials created by third party users of this tool. Details on how to use this tool + are contained in the IMS document: "IMS General Web Services: WSDL/XSD Binding Auto-generation" + available at the IMS web-site. + Tool Copyright: 2006 (c) IMS Global Learning Consortium Inc. All Rights Reserved. + + + + + + + + + + + + + Any namespaced element from any namespace may be included within an "any" element. + The namespace for the imported element must be defined in the instance, and the schema must be imported. + The extension has a definition of "strict" i.e. they must have their own namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + A bound instance of an IPointer object allows a packager to associate + a specific XML node set in the same IMS Manifest Document that contains + it or an XML node set in a different IMS Manifest Document instance with + the parent object containing an IMS Pointer instance. + + A referenced node set must be a valid child of the referencing parent element, + both as to kind and multiplicity in a referencing parent's context. + +

      Represents a binding of the kinds of objects defined as children of ims-cp-imManifest : Manifest.[ ManifestMetadata, Organizations, Resources, Manifest, Extension ].

      +
      +
      + + + + + + + +
      + + + + + An instance of the metadata element contains data structures that declare descriptive + information about a metadata element's parent only. + + One or more different metadata models may be declared as child extensions of a + metadata element. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imMetadata: Metadata.[ Extension ].

      +
      +
      + + + +
      + + + + + A variant element is closely analogous to a resource element in the + IMS Content Packaging Information Model. Variant is a container for a + an alternative resource. A resource may contain references + to assets that are all of the same type or different types (i.e., file formats). + + The Variant class points to the alternatibe resource. Metadata is used to + describe the nature of a collection of alternative assets and their intended + use. Examples include, but are not limited to, use as lingual variants, + visual or auditory variants, remediation variants, or platform delivery variants. + + The scope of referenced assets is specific to a Variant object. Their use is in the + context of the parent object containing a variant instance, typically a bound instance + of a Resource object from the IMS CP namespace. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imResource: Resource.[ Metadata, File, Dependency, Extension ].

      +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + +
      diff --git a/mods/_core/imscp/domainProfile_3/imscp_extensionv1p2_localised.xsd b/mods/_core/imscp/domainProfile_3/imscp_extensionv1p2_localised.xsd new file mode 100644 index 000000000..ea9129a4d --- /dev/null +++ b/mods/_core/imscp/domainProfile_3/imscp_extensionv1p2_localised.xsd @@ -0,0 +1,187 @@ + + + + + + + + + + + + + XSD Data File Information + ------------------------- + Author: Colin Smythe (IMS, UK) + Date: 31st October, 2006 + Version: 2.0 + Status: Public Draft + Description: This is a normative representation of the IMS Packaging Extension 1.0 Information Model for binding + purposes. Read the corresponding IMS Content Packaging Information Model for the Platform + Independent Model representation. + + History: Version 2 includes the modified definition of LingualTitle. + Version 1 of the IMS Packaging Utility v1.2 XSD for public draft release. + It has a target namespace of http://www.imsglobal.org/xsd/imscp_extensionv1p2. + This Utility uses this general approach to modeling: + (1) All of the elements and attributes are defined as local to their host object; + (2) There are multiple host objects; + (3) Comments have been added to the complexType definitions. + + Copyright: 2006 (c) IMS Global Learning Consortium Inc. All Rights Reserved. + IMS Global Learning Consortium, Inc. (IMS/GLC) is publishing the information + contained in this binding ("Specification") for purposes of scientific + experimental and scholarly collaboration only. IMS/GLC makes no warranty or + representation regarding the accuracy or completeness of the Specification. + This material is provided on an "As Is" and "As Available basis". + The Specification is at all times subject to change and revision without + notice. It is your sole responsibility to evaluate the usefulness, accuracy + and completeness of the Specification as it relates to you. IMS/GLC would + appreciate receiving your comments and suggestions. Please contact IMS/GLC + through our website at: http://www.imsglobal.org. + + Source XSLT File Information + ---------------------------- + XSL Generator: UMLtoXSDTransformv0p7.xsl + XSLT Processor: Xalan + Release: 1.0 Beta 1 + Date: 30th November, 2005 + + Auto-generation Tool + -------------------- + This WSDL/XSD was auto-generated using the IMS WSDL/XSD auto-generation tool. While every attempt + has been made to ensure that this tool auto-generates the XSDs correctly, users should be aware + that this is an experimental tool. Permission is given to make use of this tool. IMS makes no + claim on the materials created by third party users of this tool. Details on how to use this tool + are contained in the IMS document: "IMS General Web Services: WSDL/XSD Binding Auto-generation" + available at the IMS web-site. + Tool Copyright: 2006 (c) IMS Global Learning Consortium Inc. All Rights Reserved. + + general: This profile of the extension schema of IMS CP 1.2 restricts extensions to use the variant element only. + + + + + + + + + + + + Any namespaced element from any namespace may be included within an "any" element. + The namespace for the imported element must be defined in the instance, and the schema must be imported. + The extension has a definition of "strict" i.e. they must have their own namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + A bound instance of an IPointer object allows a packager to associate + a specific XML node set in the same IMS Manifest Document that contains + it or an XML node set in a different IMS Manifest Document instance with + the parent object containing an IMS Pointer instance. + + A referenced node set must be a valid child of the referencing parent element, + both as to kind and multiplicity in a referencing parent's context. + +

      Represents a binding of the kinds of objects defined as children of ims-cp-imManifest : Manifest.[ ManifestMetadata, Organizations, Resources, Manifest, Extension ].

      +
      +
      + + + + + + + +
      + + + + + An instance of the metadata element contains data structures that declare descriptive + information about a metadata element's parent only. + + One or more different metadata models may be declared as child extensions of a + metadata element. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imMetadata: Metadata.[ Extension ].

      +
      +
      + + + +
      + + + + + A variant element is closely analogous to a resource element in the + IMS Content Packaging Information Model. Variant is a container for a + an alternative resource. A resource may contain references + to assets that are all of the same type or different types (i.e., file formats). + + The Variant class points to the alternatibe resource. Metadata is used to + describe the nature of a collection of alternative assets and their intended + use. Examples include, but are not limited to, use as lingual variants, + visual or auditory variants, remediation variants, or platform delivery variants. + + The scope of referenced assets is specific to a Variant object. Their use is in the + context of the parent object containing a variant instance, typically a bound instance + of a Resource object from the IMS CP namespace. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imResource: Resource.[ Metadata, File, Dependency, Extension ].

      +
      +
      + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + +
      diff --git a/mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2.xsd b/mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2.xsd new file mode 100644 index 000000000..46585ee78 --- /dev/null +++ b/mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2.xsd @@ -0,0 +1,2203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2_def_copy.xsd b/mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2_def_copy.xsd new file mode 100644 index 000000000..182d5b54f --- /dev/null +++ b/mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2_def_copy.xsd @@ -0,0 +1,2218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2_localised.xsd b/mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2_localised.xsd new file mode 100644 index 000000000..0777fa824 --- /dev/null +++ b/mods/_core/imscp/domainProfile_4/ims_qtiasiv1p2_localised.xsd @@ -0,0 +1,2148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_4/imscc_q_definition.xsd b/mods/_core/imscp/domainProfile_4/imscc_q_definition.xsd new file mode 100644 index 000000000..0541296b5 --- /dev/null +++ b/mods/_core/imscp/domainProfile_4/imscc_q_definition.xsd @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_4/xml.xsd b/mods/_core/imscp/domainProfile_4/xml.xsd new file mode 100644 index 000000000..aec62fc9b --- /dev/null +++ b/mods/_core/imscp/domainProfile_4/xml.xsd @@ -0,0 +1,145 @@ + + + + + + See http://www.w3.org/XML/1998/namespace.html and + http://www.w3.org/TR/REC-xml for information about this namespace. + + This schema document describes the XML namespace, in a form + suitable for import by other schema documents. + + Note that local names in this namespace are intended to be defined + only by the World Wide Web Consortium or its subgroups. The + following names are currently defined in this namespace and should + not be used with conflicting semantics by any Working Group, + specification, or document instance: + + base (as an attribute name): denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification. + + id (as an attribute name): denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification. + + lang (as an attribute name): denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification. + + space (as an attribute name): denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification. + + Father (in any context at all): denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: + + In appreciation for his vision, leadership and dedication + the W3C XML Plenary on this 10th day of February, 2000 + reserves for Jon Bosak in perpetuity the XML name + xml:Father + + + + + This schema defines attributes and an attribute group + suitable for use by + schemas wishing to allow xml:base, xml:lang, xml:space or xml:id + attributes on elements they define. + + To enable this, such a schema must import this schema + for the XML namespace, e.g. as follows: + <schema . . .> + . . . + <import namespace="http://www.w3.org/XML/1998/namespace" + schemaLocation="http://www.w3.org/2001/xml.xsd"/> + + Subsequently, qualified reference to any of the attributes + or the group defined below will have the desired effect, e.g. + + <type . . .> + . . . + <attributeGroup ref="xml:specialAttrs"/> + + will define a type which will schema-validate an instance + element with any of those attributes + + + + In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + http://www.w3.org/2007/08/xml.xsd. + At the date of issue it can also be found at + http://www.w3.org/2001/xml.xsd. + The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML Schema + itself, or with the XML namespace itself. In other words, if the XML + Schema or XML namespaces change, the version of this document at + http://www.w3.org/2001/xml.xsd will change + accordingly; the version at + http://www.w3.org/2007/08/xml.xsd will not change. + + + + + + Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. See + RFC 3066 at http://www.ietf.org/rfc/rfc3066.txt and the IANA registry + at http://www.iana.org/assignments/lang-tag-apps.htm for + further information. + + The union allows for the 'un-declaration' of xml:lang with + the empty string. + + + + + + + + + + + + + + + + + + + + + + + + See http://www.w3.org/TR/xmlbase/ for + information about this attribute. + + + + + + See http://www.w3.org/TR/xml-id/ for + information about this attribute. + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_5/imswl_v1p0.xsd b/mods/_core/imscp/domainProfile_5/imswl_v1p0.xsd new file mode 100644 index 000000000..076005a64 --- /dev/null +++ b/mods/_core/imscp/domainProfile_5/imswl_v1p0.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_5/imswl_v1p0_localised.xsd b/mods/_core/imscp/domainProfile_5/imswl_v1p0_localised.xsd new file mode 100644 index 000000000..5f97a9517 --- /dev/null +++ b/mods/_core/imscp/domainProfile_5/imswl_v1p0_localised.xsd @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_6/imsdt_v1p0.xsd b/mods/_core/imscp/domainProfile_6/imsdt_v1p0.xsd new file mode 100644 index 000000000..8ad75a8d5 --- /dev/null +++ b/mods/_core/imscp/domainProfile_6/imsdt_v1p0.xsd @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/domainProfile_6/imsdt_v1p0_localised.xsd b/mods/_core/imscp/domainProfile_6/imsdt_v1p0_localised.xsd new file mode 100644 index 000000000..5364cef28 --- /dev/null +++ b/mods/_core/imscp/domainProfile_6/imsdt_v1p0_localised.xsd @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/export.php b/mods/_core/imscp/export.php new file mode 100644 index 000000000..3a0388534 --- /dev/null +++ b/mods/_core/imscp/export.php @@ -0,0 +1,95 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + header('Location: '.$_base_href.'mods/_core/imscp/ims_export.php?cid=' . intval($_POST['cid'])); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (!isset($_main_menu)) { + $_main_menu = $contentManager->getContent(); +} + +function print_menu_sections(&$menu, $parent_content_id = 0, $depth = 0, $ordering = '') { + $my_children = $menu[$parent_content_id]; + $cid = $_GET['cid']; + + if (!is_array($my_children)) { + return; + } + foreach ($my_children as $children) { + echo ''; + + print_menu_sections($menu, $children['content_id'], $depth+1, $new_ordering); + } +} + + if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && ($_SESSION['packaging'] == 'none')) { + echo '

      '._AT('content_packaging_disabled').'

      '; + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } else if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && ($_SESSION['packaging'] == 'top')) { + $_main_menu = array($_main_menu[0]); + } +?> + + +
      +
      +
      +

      +

      +
      + +
      +
      + +
      + +
      + + +
      +
      +
      + + \ No newline at end of file diff --git a/mods/_core/imscp/glossary.xsd b/mods/_core/imscp/glossary.xsd new file mode 100644 index 000000000..bc78ddb01 --- /dev/null +++ b/mods/_core/imscp/glossary.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/ims_export.php b/mods/_core/imscp/ims_export.php new file mode 100644 index 000000000..e737f4a52 --- /dev/null +++ b/mods/_core/imscp/ims_export.php @@ -0,0 +1,388 @@ + 0) + $msg->addFeedback(array('TILE_IMPORT_SUCCESS', AT_TILE_VIEW_COURSE_URL.$tile_course_id)); + else + { + // No response from transformable, the package file might be too big + if (trim($error) == '') $error = _AT('tile_no_response'); + else { + // delete this access token since it cannot import into Transformable + $sql = "DELETE FROM ".TABLE_PREFIX."oauth_client_tokens + WHERE token = '".$access_token_key."' + AND token_type='access'"; + $result = mysql_query($sql, $db); + } + $msg->addError(array('TILE_IMPORT_FAIL', $error)); + } + + header('Location: '.AT_BASE_HREF.'mods/_core/imscp/index.php'); + exit; +} else if (isset($_GET['m'])) { + /* for TILE */ + + /* request (hopefully) coming from a TILE server, send the content package */ + + $_user_location = 'public'; + require(AT_INCLUDE_PATH.'vitals.inc.php'); + $m = md5(DB_PASSWORD . 'x' . ADMIN_PASSWORD . 'x' . $_SERVER['SERVER_ADDR'] . 'x' . $cid . 'x' . $c . 'x' . date('Ymd')); + if (($m != $_GET['m']) || !$c) { + header('HTTP/1.1 404 Not Found'); + echo 'Document not found.'; + exit; + } + + $course_id = $c; + if (isset($_GET['a4a'])){ + $use_a4a = true; + } +} else { + $use_a4a = false; + if (isset($_REQUEST['to_a4a'])){ + $use_a4a = true; + } + require(AT_INCLUDE_PATH.'vitals.inc.php'); + $course_id = $_SESSION['course_id']; +} +//load the following after vitals is included +require(AT_INCLUDE_PATH.'../mods/_standard/tests/classes/testQuestions.class.php'); +require(AT_INCLUDE_PATH.'../mods/_core/imsafa/classes/A4aExport.class.php'); + +$instructor_id = $system_courses[$course_id]['member_id']; +$course_desc = htmlspecialchars($system_courses[$course_id]['description'], ENT_QUOTES, 'UTF-8'); +$course_title = htmlspecialchars($system_courses[$course_id]['title'], ENT_QUOTES, 'UTF-8'); +$course_language = $system_courses[$course_id]['primary_language']; + +$courseLanguage =& $languageManager->getLanguage($course_language); +//If course language cannot be found, use UTF-8 English +//@author harris, Oct 30,2008 +if ($courseLanguage == null){ + $courseLanguage =& $languageManager->getLanguage('en'); +} + +$course_language_charset = $courseLanguage->getCharacterSet(); +$course_language_code = $courseLanguage->getCode(); + +require(AT_INCLUDE_PATH.'classes/zipfile.class.php'); /* for zipfile */ +require(AT_INCLUDE_PATH.'classes/vcard.php'); /* for vcard */ +require(AT_INCLUDE_PATH.'classes/XML/XML_HTMLSax/XML_HTMLSax.php'); /* for XML_HTMLSax */ +require(AT_INCLUDE_PATH.'../mods/_core/imscp/include/ims_template.inc.php'); /* for ims templates + print_organizations() */ + +if (isset($_POST['cancel'])) { + $msg->addFeedback('EXPORT_CANCELLED'); + header('Location: ../content/index.php'); + exit; +} + + +$zipfile = new zipfile(); +$zipfile->create_dir('resources/'); + +/* + the following resources are to be identified: + even if some of these can't be images, they can still be files in the content dir. + theoretically the only urls we wouldn't deal with would be for a + + img => src + a => href // ignore if href doesn't exist (ie. ) + object => data | classid // probably only want data + applet => classid | archive // whatever these two are should double check to see if it's a valid file (not a dir) + link => href + script => src + form => action + input => src + iframe => src + +*/ +class MyHandler { + function MyHandler(){} + function openHandler(& $parser,$name,$attrs) { + global $my_files; + + $name = strtolower($name); + $attrs = array_change_key_case($attrs, CASE_LOWER); + + $elements = array( 'img' => 'src', + 'a' => 'href', + 'object' => array('data', 'classid'), + 'applet' => array('classid', 'archive'), + 'link' => 'href', + 'script' => 'src', + 'form' => 'action', + 'input' => 'src', + 'iframe' => 'src', + 'embed' => 'src', + 'param' => 'value'); + + /* check if this attribute specifies the files in different ways: (ie. java) */ + if (is_array($elements[$name])) { + $items = $elements[$name]; + + foreach ($items as $item) { + if ($attrs[$item] != '') { + + /* some attributes allow a listing of files to include seperated by commas (ie. applet->archive). */ + if (strpos($attrs[$item], ',') !== false) { + $files = explode(',', $attrs[$item]); + foreach ($files as $file) { + $my_files[] = trim($file); + } + } else { + $my_files[] = $attrs[$item]; + } + } + } + } else if (isset($elements[$name]) && ($attrs[$elements[$name]] != '')) { + /* we know exactly which attribute contains the reference to the file. */ + $my_files[] = $attrs[$elements[$name]]; + } + } + function closeHandler(& $parser,$name) { } +} + +/* get all the content */ +$content = array(); +$paths = array(); +$top_content_parent_id = 0; + +$handler=new MyHandler(); +$parser = new XML_HTMLSax(); +$parser->set_object($handler); +$parser->set_element_handler('openHandler','closeHandler'); + +if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN)) { + $sql = "SELECT *, UNIX_TIMESTAMP(last_modified) AS u_ts FROM ".TABLE_PREFIX."content WHERE course_id=$course_id ORDER BY content_parent_id, ordering"; +} else { + $sql = "SELECT *, UNIX_TIMESTAMP(last_modified) AS u_ts FROM ".TABLE_PREFIX."content WHERE course_id=$course_id ORDER BY content_parent_id, ordering"; +} +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + if (authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) || $contentManager->isReleased($row['content_id']) === TRUE) { + $content[$row['content_parent_id']][] = $row; + if ($cid == $row['content_id']) { + $top_content = $row; + $top_content_parent_id = $row['content_parent_id']; + } + } +} + +if ($cid) { + /* filter out the top level sections that we don't want */ + $top_level = $content[$top_content_parent_id]; + foreach($top_level as $page) { + if ($page['content_id'] == $cid) { + $content[$top_content_parent_id] = array($page); + } else { + /* this is a page we don't want, so might as well remove it's children too */ + unset($content[$page['content_id']]); + } + } + $ims_course_title = $course_title . ' - ' . $content[$top_content_parent_id][0]['title']; +} else { + $ims_course_title = $course_title; +} + + +/* generate the imsmanifest.xml header attributes */ +$imsmanifest_xml = str_replace(array('{COURSE_TITLE}', '{COURSE_DESCRIPTION}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'), + array($ims_course_title, $course_desc, $course_language_charset, $course_language_code), + $ims_template_xml['header']); +//debug($imsmanifest_xml); +//exit; + +/* get the first content page to default the body frame to */ +$first = $content[$top_content_parent_id][0]; + +$test_ids = array(); //global array to store all the test ids +//if ($my_files == null) +//$my_files = array(); + +/* generate the IMS QTI resource and files */ +/* +/* in print_organizations +foreach ($content[0] as $content_box){ + $content_test_rs = $contentManager->getContentTestsAssoc($content_box['content_id']); + while ($content_test_row = mysql_fetch_assoc($content_test_rs)){ + //export + $test_ids[] = $content_test_row['test_id']; + //the 'added_files' is for adding into the manifest file in this zip + $added_files = test_qti_export($content_test_row['test_id'], '', $zipfile); + + //Save all the xml files in this array, and then print_organizations will add it to the manifest file. + foreach($added_files as $filename=>$file_array){ + $my_files[] = $filename; + foreach ($file_array as $garbage=>$filename2){ + $my_files[] = $filename2; + } + } + } +} +*/ + +/* generate the resources and save the HTML files */ +$used_glossary_terms = array(); +ob_start(); +print_organizations($top_content_parent_id, $content, 0, '', array(), $toc_html); +$organizations_str = ob_get_contents(); +ob_end_clean(); + +if (count($used_glossary_terms)) { + $used_glossary_terms = array_unique($used_glossary_terms); + sort($used_glossary_terms); + reset($used_glossary_terms); + + $terms_xml = ''; + foreach ($used_glossary_terms as $term) { + $term_key = urlencode($term); + $glossary[$term_key] = htmlentities($glossary[$term_key], ENT_QUOTES, 'UTF-8'); + $glossary[$term_key] = str_replace('&', '&', $glossary[$term_key]); + $escaped_term = str_replace('&', '&', $term); + $terms_xml .= str_replace( array('{TERM}', '{DEFINITION}'), + array($escaped_term, $glossary[$term_key]), + $glossary_term_xml); + + $terms_html .= str_replace( array('{ENCODED_TERM}', '{TERM}', '{DEFINITION}'), + array($term_key, $term, $glossary[$term_key]), + $glossary_term_html); + } + + $glossary_body_html = str_replace('{BODY}', $terms_html, $glossary_body_html); + + $glossary_xml = str_replace(array('{GLOSSARY_TERMS}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}'), + array($terms_xml, $course_language_charset), + $glossary_xml); + $glossary_html = str_replace( array('{CONTENT}', '{KEYWORDS}', '{TITLE}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'), + array($glossary_body_html, '', 'Glossary', $course_language_charset, $course_language_code), + $html_template); + $toc_html .= ''; +} else { + unset($glossary_xml); +} + +$toc_html = str_replace(array('{TOC}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'), + array($toc_html, $course_language_charset, $course_language_code), + $html_toc); + +if ($first['content_path']) { + $first['content_path'] .= '/'; +} +$frame = str_replace( array('{COURSE_TITLE}', '{FIRST_ID}', '{PATH}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'), + array($ims_course_title, $first['content_id'], $first['content_path'], $course_language_charset, $course_language_code), + $html_frame); + +$html_mainheader = str_replace(array('{COURSE_TITLE}', '{COURSE_PRIMARY_LANGUAGE_CHARSET}', '{COURSE_PRIMARY_LANGUAGE_CODE}'), + array($ims_course_title, $course_language_charset, $course_language_code), + $html_mainheader); + + + +/* append the Organizations and Resources to the imsmanifest */ +$imsmanifest_xml .= str_replace( array('{ORGANIZATIONS}', '{RESOURCES}', '{COURSE_TITLE}'), + array($organizations_str, $resources, $ims_course_title), + $ims_template_xml['final']); + +/* generate the vcard for the instructor/author */ +$sql = "SELECT first_name, last_name, email, website, login, phone FROM ".TABLE_PREFIX."members WHERE member_id=$instructor_id"; +$result = mysql_query($sql, $db); +$vcard = new vCard(); +if ($row = mysql_fetch_assoc($result)) { + $vcard->setName($row['last_name'], $row['first_name'], $row['login']); + $vcard->setEmail($row['email']); + $vcard->setNote('Originated from an ATutor at '.AT_BASE_HREF.'. See ATutor.ca for additional information.'); + $vcard->setURL($row['website']); + + $imsmanifest_xml = str_replace('{VCARD}', $vcard->getVCard(), $imsmanifest_xml); +} else { + $imsmanifest_xml = str_replace('{VCARD}', '', $imsmanifest_xml); +} + +/* save the imsmanifest.xml file */ + +$zipfile->add_file($frame, 'index.html'); +$zipfile->add_file($toc_html, 'toc.html'); +$zipfile->add_file($imsmanifest_xml, 'imsmanifest.xml'); +$zipfile->add_file($html_mainheader, 'header.html'); +if ($glossary_xml) { + $zipfile->add_file($glossary_xml, 'glossary.xml'); + $zipfile->add_file($glossary_html, 'glossary.html'); +} +$zipfile->add_file(file_get_contents(AT_INCLUDE_PATH.'../mods/_core/imscp/include/adlcp_rootv1p2.xsd'), 'adlcp_rootv1p2.xsd'); +$zipfile->add_file(file_get_contents(AT_INCLUDE_PATH.'../mods/_core/imscp/include/ims_xml.xsd'), 'ims_xml.xsd'); +$zipfile->add_file(file_get_contents(AT_INCLUDE_PATH.'../mods/_core/imscp/include/imscp_rootv1p1p2.xsd'), 'imscp_rootv1p1p2.xsd'); +$zipfile->add_file(file_get_contents(AT_INCLUDE_PATH.'../mods/_core/imscp/include/imsmd_rootv1p2p1.xsd'), 'imsmd_rootv1p2p1.xsd'); +$zipfile->add_file(file_get_contents(AT_INCLUDE_PATH.'../mods/_core/imscp/include/ims.css'), 'ims.css'); +$zipfile->add_file(file_get_contents(AT_INCLUDE_PATH.'../mods/_core/imscp/include/footer.html'), 'footer.html'); +$zipfile->add_file(file_get_contents('../../../images/logo.gif'), 'logo.gif'); + +$zipfile->close(); // this is optional, since send_file() closes it anyway + +$ims_course_title = str_replace(array(' ', ':'), '_', $ims_course_title); +/** + * A problem here with the preg_replace below. + * Originally was designed to remove all werid symbols to avoid file corruptions. + * In UTF-8, all non-english chars are considered to be 'werid symbols' + * We can still replace it as is, or add fileid to the filename to avoid these problems + * Well then again people won't be able to tell what this file is about + * If we are going to take out the preg_replace, some OS might not be able to understand + * these characters and will have problems importing. + */ +$ims_course_title = preg_replace("{[^a-zA-Z0-9._-]}","", trim($ims_course_title)); +$zipfile->send_file($ims_course_title . '_ims'); + +exit; +?> \ No newline at end of file diff --git a/mods/_core/imscp/ims_import.php b/mods/_core/imscp/ims_import.php new file mode 100644 index 000000000..245d52589 --- /dev/null +++ b/mods/_core/imscp/ims_import.php @@ -0,0 +1,1394 @@ +\n"; + switch ($error->level) { + case LIBXML_ERR_WARNING: + $return .= "Warning $error->code: "; + break; + case LIBXML_ERR_ERROR: + $return .= "Error $error->code: "; + break; + case LIBXML_ERR_FATAL: + $return .= "Fatal Error $error->code: "; + break; + } + $return .= trim($error->message); + if ($error->file) { + $return .= " in $error->file"; + } + $return .= " on line $error->line\n"; + + return $return; +} + +/** + * Validate all the XML in the package, including checking XSDs, missing data. + * @param string the path of the directory that contains all the package files + * @return boolean true if every file exists in the manifest, false if any is missing. + */ +function checkResources($import_path){ + global $items, $msg, $skip_ims_validation, $avail_dt; + + if (!is_dir($import_path)){ + return; + } + + //if the package has access for all content, skip validation for now. + //todo: import the XSD into our validator + if ($skip_ims_validation){ + return true; + } + + //generate a file tree + $data = rscandir($import_path); + + //check if every file is presented in the manifest + foreach($data as $filepath){ + $filepath = substr($filepath, strlen($import_path)); + + //validate xml via its xsd/dtds + if (preg_match('/(.*)\.xml/', $filepath)){ + libxml_use_internal_errors(true); + $dom = new DOMDocument(); + $dom->load(realpath($import_path.$filepath)); + if (!@$dom->schemaValidate('main.xsd')){ + $errors = libxml_get_errors(); + foreach ($errors as $error) { + //suppress warnings + if ($error->level==LIBXML_ERR_WARNING){ + continue; + } + $msg->addError(array('IMPORT_CARTRIDGE_FAILED', libxml_display_error($error))); + } + libxml_clear_errors(); + } + //if this is the manifest file, we do not have to check for its existance. +// if (preg_match('/(.*)imsmanifest\.xml/', $filepath)){ +// continue; +// } + } + } + + //Create an array that mimics the structure of the data array, based on the xml items + $filearray = array(); + foreach($items as $name=>$fileinfo){ + if(isset($fileinfo['file']) && is_array($fileinfo['file']) && !empty($fileinfo['file'])){ + foreach($fileinfo['file'] as $fn){ + if (!in_array(realpath($import_path.$fn), $filearray)){ + //if url, skip + if (preg_match('/^http[s]?\:/', $fn) == 0){ + $filearray[] = realpath($import_path. $fn); + } + } + } + } + + //validate the xml by its schema + if (preg_match('/imsqti\_(.*)/', $fileinfo['type'])){ + $qti = new QTIParser($fileinfo['type']); + $xml_content = @file_get_contents($import_path . $fileinfo['href']); + $qti->parse($xml_content); //will add error to $msg if failed + } + + //add all dependent discussion tools to a list + if(isset($fileinfo['dependency']) && !empty($fileinfo['dependency'])){ + $avail_dt = array_merge($avail_dt, $fileinfo['dependency']); + } + } + + //check if all files in the xml is presented in the archieve + $result = array_diff($filearray, $data); + //using sizeof because array_diff only + //returns an array containing all the entries from array1 that are not present in any of the + //other arrays. + //Using sizeof make sure it's not a subset of array2. + //-1 on data because it always contain the imsmanifest.xml file + if (!$skip_ims_validation){ + if (!empty($result) || sizeof($data)-1>sizeof($filearray)){ + $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('ims_missing_references'))); + } + } + return true; +} + +/* + * @example rscandir(dirname(__FILE__).'/')); + * @param string $base + * @param array $omit + * @param array $data + * @return array + */ +function rscandir($base='', &$data=array()) { + $array = array_diff(scandir($base), array('.', '..')); # remove ' and .. from the array */ + foreach($array as $value) : /* loop through the array at the level of the supplied $base */ + + if (is_dir($base.$value)) : /* if this is a directory */ +// don't save the directory name +// $data[] = $base.$value.'/'; /* add it to the $data array */ + $data = rscandir($base.$value.'/', $data); /* then make a recursive call with the + current $value as the $base supplying the $data array to carry into the recursion */ + + elseif (is_file($base.$value)) : /* else if the current $value is a file */ + $data[] = realpath($base.$value); /* just add the current $value to the $data array */ + + endif; + + endforeach; + return $data; // return the $data array + +} + +/** + * Function to restructure the $items. So that old import will merge the top page into its children, and + * create a new folder on top of it + */ +function rehash($items){ + global $order; + $parent_page_maps = array(); //old=>new + $temp_popped_items = array(); + $rehashed_items = array(); //the reconstructed array + foreach($items as $id => $content){ + $parent_obj = $items[$content['parent_content_id']]; + $rehashed_items[$id] = $content; //copy + //first check if this is the top folder of the archieve, we don't want the top folder, remove it. +/* if (isset($content['parent_content_id']) && !isset($parent_obj) && !isset($content['type'])){ + //if we can get into here, it means the parent_content_id of this is empty + //implying this is the first folder. + //note: it checks content[type] cause it could be a webcontent. In that case, + // we do want to keep it. + debug($content, 'hit'); + unset($rehashed_items[$id]); + continue; + } + //then check if there exists a mapping for this item, if so, simply replace is and next. + else +*/ if (isset($parent_page_maps[$content['parent_content_id']])){ + $rehashed_items [$id]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']]; + $rehashed_items [$id]['ordering']++; + } + //If its parent page is a top page and have an identiferref + elseif (isset($parent_obj) && isset($parent_obj['href'])){ + if (!isset($parent_obj['href'])){ + //check if this top page is already a folder, if so, next. + continue; + } + //else, make its parent page to a folder + $new_item['title'] = $parent_obj['title']; + //check if this parent has been modified, if so, chnage it + if (isset($parent_page_maps[$parent_obj['parent_content_id']])){ + $new_item['parent_content_id'] = $parent_page_maps[$parent_obj['parent_content_id']]; + } else { + $new_item['parent_content_id'] = $parent_obj['parent_content_id']; + } + //all ordering needs to be +1 because we are creating a new folder on top of + //everything, except the first page. + $new_item['ordering'] = $parent_obj['ordering']; + if ($new_item['parent_content_id']!='0'){ + $new_item['ordering']++; + } + + //assign this new parent folder to the pending items array + $new_item_name = $content['parent_content_id'].'_FOLDER'; + //a not so brilliant way to append the folder in its appropriate position + $reordered_hashed_items = array(); //use to store the new rehashed item with the correct item order + foreach($rehashed_items as $rh_id=>$rh_content){ + if ($rh_id == $content['parent_content_id']){ + //add the folder in before the parent subpage. + $reordered_hashed_items[$new_item_name] = $new_item; + } + $reordered_hashed_items[$rh_id] = $rh_content; //clone + } + $rehashed_items = $reordered_hashed_items; //replace it back + unset($reordered_hashed_items); + $parent_page_maps[$content['parent_content_id']] = $new_item_name; //save this page on the hash map + + //reconstruct the parent + $rehashed_items[$content['parent_content_id']]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']]; + $rehashed_items[$content['parent_content_id']]['ordering'] = 0; //always the first one. + + //reconstruct itself + $rehashed_items[$id]['parent_content_id'] = $parent_page_maps[$content['parent_content_id']]; + $rehashed_items[$id]['ordering']++; + + } + } + return $rehashed_items; +} + + +/** + * This function will take the test accessment XML and add these to the database. + * @param string The path of the XML, without the import_path. + * @param mixed An item singleton. Contains the info of this item, namely, the accessment details. + * The item must be an object created by the ims class. + * @param string the import path + * @return mixed An Array that contains all the question IDs that have been imported. + */ + function addQuestions($xml, $item, $import_path){ + global $test_title; + $qti_import = new QTIImport($import_path); + $tests_xml = $import_path.$xml; + + //Mimic the array for now. + $test_attributes['resource']['href'] = $item['href']; + $test_attributes['resource']['type'] = preg_match('/imsqti_xmlv1p2/', $item['type'])==1?'imsqti_xmlv1p2':'imsqti_xmlv1p1'; + $test_attributes['resource']['file'] = $item['file']; + + //Get the XML file out and start importing them into our database. + //TODO: See question_import.php 287-289. + $qids = $qti_import->importQuestions($test_attributes); + $test_title = $qti_import->title; + + return $qids; + } + + + /* called at the start of en element */ + /* builds the $path array which is the path from the root to the current element */ + function startElement($parser, $name, $attrs) { + global $items, $path, $package_base_path, $all_package_base_path, $package_real_base_path; + global $element_path, $import_path, $skip_ims_validation; + global $xml_base_path, $test_message, $content_type; + global $current_identifier, $msg, $ns, $ns_cp; + + //check if the xml is valid +/* + if(isset($attrs['xsi:schemaLocation']) && $name == 'manifest'){ + //run the loop and check it thru the ns.inc.php + } elseif ($name == 'manifest' && !isset($attrs['xsi:schemaLocation'])) { + //$msg->addError('MANIFEST_NOT_WELLFORM: NO NAMESPACE'); + $msg->addError('IMPORT_CARTRIDGE_FAILED'); + } else { + //error + } + //error if the tag names are wrong + if (preg_match('/^xsi\:/', $name) >= 1){ + //$msg->addError('MANIFEST_NOT_WELLFORM'); + $msg->addError('IMPORT_CARTRIDGE_FAILED'); + } +*/ + + //validate namespaces + if(!$skip_ims_validation && isset($attrs['xsi:schemaLocation']) && $name=='manifest'){ + $schema_location = array(); + $split_location = preg_split('/[\r\n\s]+/', trim($attrs['xsi:schemaLocation'])); + + //check if the namespace is actually right, have an array or some sort in IMS class + if(sizeof($split_location)%2==1){ + //schema is not in the form of "The first URI reference in each pair is a namespace name, + //and the second is the location of a schema that describes that namespace." + //$msg->addError('MANIFEST_NOT_WELLFORM'); + $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('schema_error'))); + } + + //turn the xsi:schemaLocation URI into a schema that describe namespace. + //name = url + //http://msdn.microsoft.com/en-us/library/ms256100(VS.85).aspx + //http://www.w3.org/TR/xmlschema-1/ + for($i=0; $i < sizeof($split_location);$i=$i+2){ + /* + if (isset($ns[$split_location[$i]]) && $ns[$split_location[$i]] != $split_location[$i+1]){ + //$msg->addError('MANIFEST_NOT_WELLFORM: SCHEMA'); + $msg->addError('IMPORT_CARTRIDGE_FAILED'); + } + */ + //if the key of the namespace is not defined. Throw error. + if(!isset($ns[$split_location[$i]]) && !isset($ns_cp[$split_location[$i]])){ + $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('schema_error'))); + } + } + } else { + //throw error + } + + if ($name == 'manifest' && isset($attrs['xml:base']) && $attrs['xml:base']) { + $xml_base_path = $attrs['xml:base']; + } else if ($name == 'file') { + // check if it misses file references + if(!$skip_ims_validation && (!isset($attrs['href']) || $attrs['href']=='')){ + //$msg->addError('MANIFEST_NOT_WELLFORM'); + $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT('ims_missing_references'))); + } + + // special case for webCT content packages that don't specify the `href` attribute + // with the `` element. + // we take the `href` from the first `` element. + if (isset($items[$current_identifier]) && ($items[$current_identifier]['href'] == '')) { + $attrs['href'] = urldecode($attrs['href']); + $items[$current_identifier]['href'] = $attrs['href']; + } + + $temp_path = pathinfo($attrs['href']); + $temp_path = explode('/', $temp_path['dirname']); + if (empty($package_base_path)){ + $package_base_path = $temp_path; + } + if ($all_package_base_path!='' && empty($all_package_base_path)){ + $all_package_base_path = $temp_path; + } + $package_base_path = array_intersect_assoc($package_base_path, $temp_path); + + //calculate the depths of relative paths + if ($all_package_base_path!=''){ + $no_relative_temp_path = $temp_path; + foreach($no_relative_temp_path as $path_node){ + if ($path_node=='..'){ + array_pop($no_relative_temp_path); + array_pop($no_relative_temp_path); //not a typo, have to pop twice, both itself('..'), and the one before. + } + } + $all_package_base_path = array_intersect_assoc($all_package_base_path, $no_relative_temp_path); + if (empty($all_package_base_path)){ + $all_package_base_path = ''; //unset it, there is no intersection. + } + } + + //save the actual content base path + if (in_array('..', $temp_path)){ + $sizeofrp = array_count_values($temp_path); + } + + //for IMSCC, assume that all resources lies in the same folder, except styles.css + if ($items[$current_identifier]['type']=='webcontent' || $items[$current_identifier]['type']=='imsdt_xmlv1p0'){ + //find the intersection of each item's related files, then that intersection is the content_path + if (isset($items[$current_identifier]['file'])){ + foreach ($items[$current_identifier]['file'] as $resource_path){ + $temp_path = pathinfo($resource_path); + $temp_path = explode('/', $temp_path['dirname']); + $package_base_path = array_intersect_assoc($package_base_path, $temp_path); + } + } + } + + //real content path + if($sizeofrp['..'] > 0 && !empty($all_package_base_path)){ + for ($i=0; $i<$sizeofrp['..']; $i++){ + array_pop($all_package_base_path); + } + } + + if (count($package_base_path) > 0) { + $items[$current_identifier]['new_path'] = implode('/', $package_base_path); + } + +/* + * @harris, reworked the package_base_path + if ($package_base_path=="") { + $package_base_path = $temp_path; + } + elseif (is_array($package_base_path) && $content_type != 'IMS Common Cartridge') { + //if this is a content package, we want only intersection + $package_base_path = array_intersect($package_base_path, $temp_path); + $temp_path = $package_base_path; + } + //added these 2 lines in so that pictures would load. making the elseif above redundant. + //if there is a bug for pictures not load, then it's the next 2 lines. + $package_base_path = array_intersect($package_base_path, $temp_path); + $temp_path = $package_base_path; + } + $items[$current_identifier]['new_path'] = implode('/', $temp_path); +*/ + if ( isset($_POST['allow_test_import']) && isset($items[$current_identifier]) + && preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $attrs['href'])) { + $items[$current_identifier]['tests'][] = $attrs['href']; + } + if ( isset($_POST['allow_a4a_import']) && isset($items[$current_identifier])) { + $items[$current_identifier]['a4a_import_enabled'] = true; + } + } else if (($name == 'item') && ($attrs['identifierref'] != '')) { + $path[] = $attrs['identifierref']; + } else if (($name == 'item') && ($attrs['identifier'])) { + $path[] = $attrs['identifier']; +// } else if (($name == 'resource') && is_array($items[$attrs['identifier']])) { + } else if (($name == 'resource')) { + $current_identifier = $attrs['identifier']; + $items[$current_identifier]['type'] = $attrs['type']; + if ($attrs['href']) { + $attrs['href'] = urldecode($attrs['href']); + + $items[$attrs['identifier']]['href'] = $attrs['href']; + + // href points to a remote url + if (preg_match('/^http.*:\/\//', trim($attrs['href']))) { + $items[$attrs['identifier']]['new_path'] = ''; + } else // href points to local file + { + $temp_path = pathinfo($attrs['href']); + $temp_path = explode('/', $temp_path['dirname']); +// if (empty($package_base_path)) { + $package_base_path = $temp_path; +// } +// else { +// $package_base_path = array_intersect($package_base_path, $temp_path); +// } + $items[$attrs['identifier']]['new_path'] = implode('/', $temp_path); + } + } + + //if test custom message has not been saved +// if (!isset($items[$current_identifier]['test_message'])){ +// $items[$current_identifier]['test_message'] = $test_message; +// } + } else if ($name=='dependency' && $attrs['identifierref']!='') { + //if there is a dependency, attach it to the item array['file'] + $items[$current_identifier]['dependency'][] = $attrs['identifierref']; + } + if (($name == 'item') && ($attrs['parameters'] != '')) { + $items[$attrs['identifierref']]['test_message'] = $attrs['parameters']; + } + if ($name=='file'){ + if(!isset($items[$current_identifier]) && $attrs['href']!=''){ + $items[$current_identifier]['href'] = $attrs['href']; + } + if (substr($attrs['href'], 0, 7) == 'http://' || substr($attrs['href'], 0, 8) == 'https://' || file_exists($import_path.$attrs['href']) || $skip_ims_validation){ + $items[$current_identifier]['file'][] = $attrs['href']; + } else { + $msg->addError(array('IMPORT_CARTRIDGE_FAILED', _AT(array('ims_files_missing', $attrs['href'])))); + } + } + if ($name=='cc:authorizations'){ + //don't have authorization setup. + //$msg->addError(''); + $msg->addError('IMS_AUTHORIZATION_NOT_SUPPORT'); + } + array_push($element_path, $name); + } + + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) { + global $path, $element_path, $my_data, $items; + global $current_identifier, $skip_ims_validation; + global $msg, $content_type; + static $resource_num = 0; + + if ($name == 'item') { + array_pop($path); + } + + //check if this is a test import + if ($name == 'schema'){ + if (trim($my_data)=='IMS Question and Test Interoperability'){ + $msg->addError('IMPORT_FAILED'); + } + $content_type = trim($my_data); + } + + //Handles A4a + if ($current_identifier != ''){ + $my_data = trim($my_data); + $last_file_name = $items[$current_identifier]['file'][(sizeof($items[$current_identifier]['file']))-1]; + + if ($name=='originalAccessMode'){ + if (in_array('accessModeStatement', $element_path)){ + $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['access_stmt_originalAccessMode'] = $my_data; + } elseif (in_array('adaptationStatement', $element_path)){ + $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['adapt_stmt_originalAccessMode'] = $my_data; + } + } elseif (($name=='language') && in_array('accessModeStatement', $element_path)){ + $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['language'][] = $my_data; + } elseif ($name=='hasAdaptation') { + $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['hasAdaptation'][] = $my_data; + } elseif ($name=='isAdaptationOf'){ + $items[$current_identifier]['a4a'][$last_file_name][$resource_num]['isAdaptationOf'][] = $my_data; + } elseif ($name=='accessForAllResource'){ + /* the head node of accessForAll Metadata, if this exists in the manifest. Skip XSD validation, + * because A4a doesn't have a xsd yet. Our access for all is based on ISO which will not pass + * the current IMS validation. + * Also, since ATutor is the only one (as of Oct 21, 2009) that exports IMS with access for all + * content, we can almost assume that any ims access for all content is by us, and is valid. + */ + $skip_ims_validation = true; + $resource_num++; + } elseif($name=='file'){ + $resource_num = 0; //reset resournce number to 0 when the file tags ends + } + } + + if ($element_path === array('manifest', 'metadata', 'imsmd:lom', 'imsmd:general', 'imsmd:title', 'imsmd:langstring')) { + global $package_base_name; + $package_base_name = trim($my_data); + } + + array_pop($element_path); + $my_data = ''; + } + + /* called when there is character data within elements */ + /* constructs the $items array using the last entry in $path as the parent element */ + function characterData($parser, $data){ + global $path, $items, $order, $my_data, $element_path; + global $current_identifier; + + $str_trimmed_data = trim($data); + + if (!empty($str_trimmed_data)) { + $size = count($path); + if ($size > 0) { + $current_item_id = $path[$size-1]; + if ($size > 1) { + $parent_item_id = $path[$size-2]; + } else { + $parent_item_id = 0; + } + + if (isset($items[$current_item_id]['parent_content_id']) && is_array($items[$current_item_id])) { + + /* this item already exists, append the title */ + /* this fixes {\n, \t, `, &} characters in elements */ + + /* horible kludge to fix the */ + /* from TILE */ + if (in_array('accessForAllResource', $element_path)){ + //skip this tag + } elseif ($element_path[count($element_path)-1] != 'ns1:objectiveDesc') { + $items[$current_item_id]['title'] .= $data; + } + + } else { + $order[$parent_item_id] ++; + $item_tmpl = array( 'title' => $data, + 'parent_content_id' => $parent_item_id, + 'ordering' => $order[$parent_item_id]-1); + //append other array values if it exists + if (is_array($items[$current_item_id])){ + $items[$current_item_id] = array_merge($items[$current_item_id], $item_tmpl); + } else { + $items[$current_item_id] = $item_tmpl; + } + } + } + } + + $my_data .= $data; + } + + /* glossary parser: */ + function glossaryStartElement($parser, $name, $attrs) { + global $element_path; + + array_push($element_path, $name); + } + + /* called when an element ends */ + /* removed the current element from the $path */ + function glossaryEndElement($parser, $name) { + global $element_path, $my_data, $imported_glossary; + static $current_term; + + if ($element_path === array('glossary', 'item', 'term') || + $element_path === array('glossary:glossary', 'item', 'term')) { + $current_term = $my_data; + + } else if ($element_path === array('glossary', 'item', 'definition') || + $element_path === array('glossary:glossary', 'item', 'definition')) { + $imported_glossary[trim($current_term)] = trim($my_data); + } + + array_pop($element_path); + $my_data = ''; + } + + function glossaryCharacterData($parser, $data){ + global $my_data; + + $my_data .= $data; + } + +if (!isset($_POST['submit']) && !isset($_POST['cancel'])) { + /* just a catch all */ + + $errors = array('FILE_MAX_SIZE', ini_get('post_max_size')); + $msg->addError($errors); + + header('Location: ./index.php'); + exit; +} else if (isset($_POST['cancel'])) { + $msg->addFeedback('IMPORT_CANCELLED'); + + header('Location: ../content/index.php'); + exit; +} + +$cid = intval($_POST['cid']); + +//If user chooses to ignore validation. +if(isset($_POST['ignore_validation']) && $_POST['ignore_validation']==1) { + $skip_ims_validation = true; +} + +if (isset($_POST['url']) && ($_POST['url'] != 'http://') ) { + if ($content = @file_get_contents($_POST['url'])) { + // save file to /content/ + $filename = substr(time(), -6). '.zip'; + $full_filename = AT_CONTENT_DIR . $filename; + + if (!$fp = fopen($full_filename, 'w+b')) { + echo "Cannot open file ($filename)"; + exit; + } + + if (fwrite($fp, $content, strlen($content) ) === FALSE) { + echo "Cannot write to file ($filename)"; + exit; + } + fclose($fp); + } + $_FILES['file']['name'] = $filename; + $_FILES['file']['tmp_name'] = $full_filename; + $_FILES['file']['size'] = strlen($content); + unset($content); + $url_parts = pathinfo($_POST['url']); + $package_base_name_url = $url_parts['basename']; +} +$ext = pathinfo($_FILES['file']['name']); +$ext = $ext['extension']; + +if ($ext != 'zip') { + $msg->addError('IMPORTDIR_IMS_NOTVALID'); +} else if ($_FILES['file']['error'] == 1) { + $errors = array('FILE_MAX_SIZE', ini_get('upload_max_filesize')); + $msg->addError($errors); +} else if ( !$_FILES['file']['name'] || (!is_uploaded_file($_FILES['file']['tmp_name']) && !$_POST['url'])) { + $msg->addError('FILE_NOT_SELECTED'); +} else if ($_FILES['file']['size'] == 0) { + $msg->addError('IMPORTFILE_EMPTY'); +} + +if ($msg->containsErrors()) { + if (isset($_GET['tile'])) { + header('Location: '.$_base_path.'mods/_standard/tile_search/index.php'); + } else { + header('Location: index.php'); + } + exit; +} + +/* check if ../content/import/ exists */ +$import_path = AT_CONTENT_DIR . 'import/'; +$content_path = AT_CONTENT_DIR; + +if (!is_dir($import_path)) { + if (!@mkdir($import_path, 0700)) { + $msg->addError('IMPORTDIR_FAILED'); + } +} + +$import_path .= $_SESSION['course_id'].'/'; +if (is_dir($import_path)) { + clr_dir($import_path); +} + +if (!@mkdir($import_path, 0700)) { + $msg->addError('IMPORTDIR_FAILED'); +} + +if ($msg->containsErrors()) { + if (isset($_GET['tile'])) { + header('Location: '.$_base_path.'mods/_standard/tile_search/index.php'); + } else { + header('Location: index.php'); + } + exit; +} + +/* extract the entire archive into AT_COURSE_CONTENT . import/$course using the call back function to filter out php files */ +error_reporting(0); +$archive = new PclZip($_FILES['file']['tmp_name']); +if ($archive->extract( PCLZIP_OPT_PATH, $import_path, + PCLZIP_CB_PRE_EXTRACT, 'preImportCallBack') == 0) { + $msg->addError('IMPORT_FAILED'); + echo 'Error : '.$archive->errorInfo(true); + clr_dir($import_path); + header('Location: index.php'); + exit; +} +error_reporting(AT_ERROR_REPORTING); + +/* get the course's max_quota */ +$sql = "SELECT max_quota FROM ".TABLE_PREFIX."courses WHERE course_id=$_SESSION[course_id]"; +$result = mysql_query($sql, $db); +$q_row = mysql_fetch_assoc($result); + +if ($q_row['max_quota'] != AT_COURSESIZE_UNLIMITED) { + + if ($q_row['max_quota'] == AT_COURSESIZE_DEFAULT) { + $q_row['max_quota'] = $MaxCourseSize; + } + $totalBytes = dirsize($import_path); + $course_total = dirsize(AT_CONTENT_DIR . $_SESSION['course_id'].'/'); + $total_after = $q_row['max_quota'] - $course_total - $totalBytes + $MaxCourseFloat; + + if ($total_after < 0) { + /* remove the content dir, since there's no space for it */ + $errors = array('NO_CONTENT_SPACE', number_format(-1*($total_after/AT_KBYTE_SIZE), 2 ) ); + $msg->addError($errors); + + clr_dir($import_path); + + if (isset($_GET['tile'])) { + header('Location: '.$_base_path.'mods/_standard/tile_search/index.php'); + } else { + header('Location: index.php'); + } + exit; + } +} + + +$items = array(); /* all the content pages */ +$order = array(); /* keeps track of the ordering for each content page */ +$path = array(); /* the hierarchy path taken in the menu to get to the current item in the manifest */ +$dependency_files = array(); /* the file path for the dependency files */ + +/* +$items[content_id/resource_id] = array( + 'title' + 'real_content_id' // calculated after being inserted + 'parent_content_id' + 'href' + 'ordering' + ); +*/ +$ims_manifest_xml = @file_get_contents($import_path.'imsmanifest.xml'); +//scan for manifest xml if it's not on the top level. +if ($ims_manifest_xml === false){ + $data = rscandir($import_path); + $manifest_array = array(); + foreach($data as $scanned_file){ + $scanned_file = realpath($scanned_file); + //change the file string to an array + $this_file_array = explode(DIRECTORY_SEPARATOR, $scanned_file); + if(empty($manifest_array)){ + $manifest_array = $this_file_array; + } + $manifest_array = array_intersect_assoc($this_file_array, $manifest_array); + + if (strpos($scanned_file, 'imsmanifest')!==false){ + $ims_manifest_xml = @file_get_contents($scanned_file); + } + } + if ($ims_manifest_xml !== false){ + $import_path = implode(DIRECTORY_SEPARATOR, $manifest_array) . DIRECTORY_SEPARATOR; + } +} + +//if no imsmanifest.xml found in the entire package, throw error. +if ($ims_manifest_xml === false) { + $msg->addError('NO_IMSMANIFEST'); + + if (file_exists($import_path . 'atutor_backup_version')) { + $msg->addError('NO_IMS_BACKUP'); + } + clr_dir($import_path); + + if (isset($_GET['tile'])) { + header('Location: '.$_base_path.'mods/_standard/tile_search/index.php'); + } else { + header('Location: index.php'); + } + exit; +} + +$xml_parser = xml_parser_create(); + +xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ +xml_set_element_handler($xml_parser, 'startElement', 'endElement'); +xml_set_character_data_handler($xml_parser, 'characterData'); + +if (!xml_parse($xml_parser, $ims_manifest_xml, true)) { + die(sprintf("XML error: %s at line %d", + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser))); +} +xml_parser_free($xml_parser); +/* check if the glossary terms exist */ +$glossary_path = ''; +if ($content_type == 'IMS Common Cartridge'){ + $glossary_path = 'resources/GlossaryItem/'; +// $package_base_path = ''; +} +if (file_exists($import_path . $glossary_path . 'glossary.xml')){ + $glossary_xml = @file_get_contents($import_path.$glossary_path.'glossary.xml'); + $element_path = array(); + $xml_parser = xml_parser_create(); + + /* insert the glossary terms into the database (if they're not in there already) */ + /* parse the glossary.xml file and insert the terms */ + xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($xml_parser, 'glossaryStartElement', 'glossaryEndElement'); + xml_set_character_data_handler($xml_parser, 'glossaryCharacterData'); + + if (!xml_parse($xml_parser, $glossary_xml, true)) { + die(sprintf("XML error: %s at line %d", + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser))); + } + xml_parser_free($xml_parser); + $contains_glossary_terms = true; + foreach ($imported_glossary as $term => $defn) { + if (!$glossary[$term]) { + $sql = "INSERT INTO ".TABLE_PREFIX."glossary VALUES (NULL, $_SESSION[course_id], '$term', '$defn', 0)"; + mysql_query($sql, $db); + } + } +} + +// Check if all the files exists in the manifest, iff it's a IMS CC package. +if ($content_type == 'IMS Common Cartridge') { + checkResources($import_path); +} + +// Check if there are any errors during parsing. +if ($msg->containsErrors()) { + if (isset($_GET['tile'])) { + header('Location: '.$_base_path.'mods/_standard/tile_search/index.php'); + } else { + header('Location: index.php'); + } + exit; +} + +/* generate a unique new package base path based on the package file name and date as needed. */ +/* the package name will be the dir where the content for this package will be put, as a result */ +/* the 'content_path' field in the content table will be set to this path. */ +/* $package_base_name_url comes from the URL file name (NOT the file name of the actual file we open)*/ +if (!$package_base_name && $package_base_name_url) { + $package_base_name = substr($package_base_name_url, 0, -4); +} else if (!$package_base_name) { + $package_base_name = substr($_FILES['file']['name'], 0, -4); +} + +$package_base_name = strtolower($package_base_name); +$package_base_name = str_replace(array('\'', '"', ' ', '|', '\\', '/', '<', '>', ':'), '_' , $package_base_name); +$package_base_name = preg_replace("/[^A-Za-z0-9._\-]/", '', $package_base_name); + +if (is_dir(AT_CONTENT_DIR . $_SESSION['course_id'].'/'.$package_base_name)) { + $package_base_name .= '_'.date('ymdHis'); +} + +if ($package_base_path) { + $package_base_path = implode('/', $package_base_path); +} elseif (empty($package_base_path)){ + $package_base_path = ''; +} + +if ($xml_base_path) { + $package_base_path = $xml_base_path . $package_base_path; + + mkdir(AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$xml_base_path); + $package_base_name = $xml_base_path . $package_base_name; +} + +/* get the top level content ordering offset */ +$sql = "SELECT MAX(ordering) AS ordering FROM ".TABLE_PREFIX."content WHERE course_id=$_SESSION[course_id] AND content_parent_id=$cid"; +$result = mysql_query($sql, $db); +$row = mysql_fetch_assoc($result); +$order_offset = intval($row['ordering']); /* it's nice to have a real number to deal with */ +$lti_offset = array(); //since we don't need lti tools, the ordering needs to be subtracted +//reorder the items stack +$items = rehash($items); +//debug($items);exit; +foreach ($items as $item_id => $content_info) +{ + //formatting field, default 1 + $content_formatting = 1; //CONTENT_TYPE_CONTENT + + //don't want to display glossary as a page + if ($content_info['href']== $glossary_path . 'glossary.xml'){ + continue; + } + + //if discussion tools, add it to the list of unhandled dts + if ($content_info['type']=='imsdt_xmlv1p0'){ + //if it will be taken care after (has dependency), then move along. + if (in_array($item_id, $avail_dt)){ + $lti_offset[$content_info['parent_content_id']]++; + continue; + } + } + + //handle the special case of cc import, where there is no content association. The resource should + //still be imported. + if(!isset($content_info['parent_content_id'])){ + //if this is a question bank + if ($content_info['type']=="imsqti_xmlv1p2/imscc_xmlv1p0/question-bank"){ + addQuestions($content_info['href'], $content_info, $import_path); + } + } + + //if it has no title, most likely it is not a page but just a normal item, skip it + if (!isset($content_info['title'])){ + continue; + } + + //check dependency immediately, then handles it + $head = ''; + if (is_array($content_info['dependency']) && !empty($content_info['dependency'])){ + foreach($content_info['dependency'] as $dependency_ref){ + //handle styles + /** handled by get_html_head in vitals.inc.php + if (preg_match('/(.*)\.css$/', $items[$dependency_ref]['href'])){ + //calculate where this is based on our current base_href. + //assuming the dependency folders are siblings of the item + $head = ''; + } + */ + //check if this is a discussion tool dependency + if ($items[$dependency_ref]['type']=='imsdt_xmlv1p0'){ + $items[$item_id]['forum'][$dependency_ref] = $items[$dependency_ref]['href']; + } + //check if this is a QTI dependency + if (strpos($items[$dependency_ref]['type'], 'imsqti_xmlv1p2/imscc_xmlv1p0') !== false){ + $items[$item_id]['tests'][$dependency_ref] = $items[$dependency_ref]['href']; + } + } + } + + //check file array, see if there are css. + //edited nov 26, harris + //removed cuz i added link to the html_tags + /* + if (is_array($content_info['file']) && !empty($content_info['file'])){ + foreach($content_info['file'] as $dependency_ref){ + //handle styles + if (preg_match('/(.*)\.css$/', $dependency_ref)){ + //calculate where this is based on our current base_href. + //assuming the dependency folders are siblings of the item + $head = ''; + } + } + } + */ + + // remote href + if (preg_match('/^http.*:\/\//', trim($content_info['href'])) ) + { + $content = ''.$content_info['title'].''; + } + else + { + if ($content_type == 'IMS Common Cartridge'){ + //to handle import with purely images but nothing else + //don't need a content base path for it. + $content_new_path = $content_info['new_path']; + $content_info['new_path'] = ''; + } + if (isset($content_info['href'], $xml_base_path)) { + $content_info['href'] = $xml_base_path . $content_info['href']; + } + if (!isset($content_info['href'])) { + // this item doesn't have an identifierref. so create an empty page. + // what we called a folder according to v1.2 Content Packaging spec + // Hop over + $content = ''; + $ext = ''; + $last_modified = date('Y-m-d H:i:s'); + } else { + //$file_info = @stat(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$content_info['href']); + $file_info = @stat($import_path.$content_info['href']); + if ($file_info === false) { + continue; + } + + //$path_parts = pathinfo(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$content_info['href']); + $path_parts = pathinfo($import_path.$content_info['href']); + $ext = strtolower($path_parts['extension']); + + $last_modified = date('Y-m-d H:i:s', $file_info['mtime']); + } + if (in_array($ext, array('gif', 'jpg', 'bmp', 'png', 'jpeg'))) { + /* this is an image */ + $content = ''.$content_info['title'].''; + } else if ($ext == 'swf') { + /* this is flash */ + /* Using default size of 550 x 400 */ + + $content = ''; + + } else if ($ext == 'mov') { + /* this is a quicktime movie */ + /* Using default size of 550 x 400 */ + + $content = ''; + + /* Oct 19, 2009 + * commenting this whole chunk out. It's part of my test import codes, not sure why it's here, + * and I don't think it should be here. Remove this whole comment after further testing and confirmation. + * @harris + * + //Mimic the array for now. + $test_attributes['resource']['href'] = $test_xml_file; + $test_attributes['resource']['type'] = isset($items[$item_id]['type'])?'imsqti_xmlv1p2':'imsqti_xmlv1p1'; + $test_attributes['resource']['file'] = $items[$item_id]['file']; +// $test_attributes['resource']['file'] = array($test_xml_file); + + //Get the XML file out and start importing them into our database. + //TODO: See question_import.php 287-289. + $qids = $qti_import->importQuestions($test_attributes); + + */ + } else if ($ext == 'mp3') { + $content = ''; + } else if (in_array($ext, array('wav', 'au'))) { + $content = '<bgsound src="'.$content_info['href'].'">'; + + } else if (in_array($ext, array('txt', 'css', 'html', 'htm', 'csv', 'asc', 'tsv', 'xml', 'xsl'))) { + if ($content_type == 'IMS Common Cartridge'){ + $content_info['new_path'] = $content_new_path; + } + + /* this is a plain text file */ + //$content = file_get_contents(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$content_info['href']); + $content = file_get_contents($import_path.$content_info['href']); + if ($content === false) { + /* if we can't stat() it then we're unlikely to be able to read it */ + /* so we'll never get here. */ + continue; + } + + // get the contents of the 'head' element + $head .= get_html_head_by_tag($content, $html_head_tags); + + // Specifically handle eXe package + // NOTE: THIS NEEDS WORK! TO FIND A WAY APPLY EXE .CSS FILES ONLY ON COURSE CONTENT PART. + // NOW USE OUR OWN .CSS CREATED SOLELY FOR EXE + $isExeContent = false; + + // check xml file in eXe package + if (preg_match("/*/", $ims_manifest_xml)) + { + $isExeContent = true; + } + + // use ATutor's eXe style sheet as the ones from eXe conflicts with ATutor's style sheets + if ($isExeContent) + { + $head = preg_replace ('/()(.*)(<\/style>)/ms', '\\1@import url(/docs/exestyles.css);\\3', $head); + } + + // end of specifically handle eXe package + + $content = get_html_body($content); + if ($contains_glossary_terms) + { + // replace glossary content package links to real glossary mark-up using [?] [/?] + // refer to bug 3641, edited by Harris + $content = preg_replace('/([.\w\d\s&;"]+|.*)<\/a>/i', '[?]\\2[/?]', $content); + } + + /* potential security risk? */ + if ( strpos($content_info['href'], '..') === false && !preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $content_info['href'])) { +// @unlink(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$content_info['href']); + } + + /* overwrite content if this is discussion tool. */ + if ($content_info['type']=='imsdt_xmlv1p0'){ + $dt_parser = new DiscussionToolsParser(); + $xml_content = @file_get_contents($import_path . $content_info['href']); + $dt_parser->parse($xml_content); + $forum_obj = $dt_parser->getDt(); + $content = $forum_obj->getText(); + unset($forum_obj); + $dt_parser->close(); + } + } else if ($ext) { + /* non text file, and can't embed (example: PDF files) */ + $content = ''.$content_info['title'].''; + } + } + $content_parent_id = $cid; + if ($content_info['parent_content_id'] !== 0) { + $content_parent_id = $items[$content_info['parent_content_id']]['real_content_id']; + //if it's not there, use $cid + if (!$content_parent_id){ + $content_parent_id = $cid; + } + } + + $my_offset = 0; + if ($content_parent_id == $cid) { + $my_offset = $order_offset; + } + + /* replace the old path greatest common denomiator with the new package path. */ + /* we don't use str_replace, b/c there's no knowing what the paths may be */ + /* we only want to replace the first part of the path. + */ + if(is_array($all_package_base_path)){ + $all_package_base_path = implode('/', $all_package_base_path); + } + + if ($all_package_base_path != '') { + $content_info['new_path'] = $package_base_name . substr($content_info['new_path'], strlen($all_package_base_path)); + } else { + $content_info['new_path'] = $package_base_name . '/' . $content_info['new_path']; + } + + //handles weblinks + if ($content_info['type']=='imswl_xmlv1p0'){ + $weblinks_parser = new WeblinksParser(); + $xml_content = @file_get_contents($import_path . $content_info['href']); + $weblinks_parser->parse($xml_content); + $content_info['title'] = $weblinks_parser->getTitle(); + $content = $weblinks_parser->getUrl(); + $content_folder_type = CONTENT_TYPE_WEBLINK; + $content_formatting = 2; + } + $head = addslashes($head); + $content_info['title'] = addslashes($content_info['title']); + $content_info['test_message'] = addslashes($content_info['test_message']); + + //if this file is a test_xml, create a blank page instead, for imscc. + if (preg_match('/((.*)\/)*tests\_[0-9]+\.xml$/', $content_info['href']) + || preg_match('/imsqti\_(.*)/', $content_info['type'])) { + $content = ' '; + } else { + $content = addslashes($content); + } + + //check for content_type + if ($content_formatting!=CONTENT_TYPE_WEBLINK){ + $content_folder_type = (!isset($content_info['type'])?CONTENT_TYPE_FOLDER:CONTENT_TYPE_CONTENT); + } + + $sql= 'INSERT INTO '.TABLE_PREFIX.'content' + . '(course_id, + content_parent_id, + ordering, + last_modified, + revision, + formatting, + release_date, + head, + use_customized_head, + keywords, + content_path, + title, + text, + test_message, + content_type) + VALUES + ('.$_SESSION['course_id'].',' + .intval($content_parent_id).',' + .($content_info['ordering'] + $my_offset - $lti_offset[$content_info['parent_content_id']] + 1).',' + .'"'.$last_modified.'", + 0,' + .$content_formatting.' , + NOW(),"' + . $head .'", + 1, + "",' + .'"'.$content_info['new_path'].'",' + .'"'.$content_info['title'].'",' + .'"'.$content.'",' + .'"'.$content_info['test_message'].'",' + .$content_folder_type.')'; + + $result = mysql_query($sql, $db) or die(mysql_error()); + + /* get the content id and update $items */ + $items[$item_id]['real_content_id'] = mysql_insert_id($db); + + /* get the tests associated with this content */ + if (!empty($items[$item_id]['tests']) || strpos($items[$item_id]['type'], 'imsqti_xmlv1p2/imscc_xmlv1p0') !== false){ + $qti_import = new QTIImport($import_path); + if (isset($items[$item_id]['tests'])){ + $loop_var = $items[$item_id]['tests']; + } else { + $loop_var = $items[$item_id]['file']; + } + + foreach ($loop_var as $array_id => $test_xml_file){ + //check if this item is the qti item object, or it is the content item obj + //switch it to qti obj if it's content item obj + if ($items[$item_id]['type'] == 'webcontent'){ + $item_qti = $items[$array_id]; + } else { + $item_qti = $items[$item_id]; + } + //call subrountine to add the questions. + $qids = addQuestions($test_xml_file, $item_qti, $import_path); + + //import test + if ($test_title==''){ + $test_title = $content_info['title']; + } + + $tid = $qti_import->importTest($test_title); + + //associate question and tests + foreach ($qids as $order=>$qid){ + if (isset($qti_import->weights[$order])){ + $weight = round($qti_import->weights[$order]); + } else { + $weight = 0; + } + $new_order = $order + 1; + $sql = "INSERT INTO " . TABLE_PREFIX . "tests_questions_assoc" . + "(test_id, question_id, weight, ordering, required) " . + "VALUES ($tid, $qid, $weight, $new_order, 0)"; + $result = mysql_query($sql, $db); + } + + //associate content and test + $sql = 'INSERT INTO ' . TABLE_PREFIX . 'content_tests_assoc' . + '(content_id, test_id) ' . + 'VALUES (' . $items[$item_id]['real_content_id'] . ", $tid)"; + $result = mysql_query($sql, $db); + +// if (!$msg->containsErrors()) { +// $msg->addFeedback('IMPORT_SUCCEEDED'); +// } + } + } + + /* get the a4a related xml */ + if (isset($items[$item_id]['a4a_import_enabled']) && isset($items[$item_id]['a4a']) && !empty($items[$item_id]['a4a'])) { + $a4a_import = new A4aImport($items[$item_id]['real_content_id']); + $a4a_import->setRelativePath($items[$item_id]['new_path']); + $a4a_import->importA4a($items[$item_id]['a4a']); + } + + /* get the discussion tools (dependent to content)*/ + if (isset($items[$item_id]['forum']) && !empty($items[$item_id]['forum'])){ + foreach($items[$item_id]['forum'] as $forum_ref => $forum_link){ + $dt_parser = new DiscussionToolsParser(); + $dt_import = new DiscussionToolsImport(); + + //if this forum has not been added, parse it and add it. + if (!isset($added_dt[$forum_ref])){ + $xml_content = @file_get_contents($import_path . $forum_link); + $dt_parser->parse($xml_content); + $forum_obj = $dt_parser->getDt(); + $dt_import->import($forum_obj, $items[$item_id]['real_content_id']); + $added_dt[$forum_ref] = $dt_import->getFid(); + } + //associate the fid and content id + $dt_import->associateForum($items[$item_id]['real_content_id'], $added_dt[$forum_ref]); + } + } elseif ($items[$item_id]['type']=='imsdt_xmlv1p0'){ + //optimize this, repeated codes as above + $dt_parser = new DiscussionToolsParser(); + $dt_import = new DiscussionToolsImport(); + $xml_content = @file_get_contents($import_path . $content_info['href']); + $dt_parser->parse($xml_content); + $forum_obj = $dt_parser->getDt(); + $dt_import->import($forum_obj, $items[$item_id]['real_content_id']); + $added_dt[$item_id] = $dt_import->getFid(); + + //associate the fid and content id + $dt_import->associateForum($items[$item_id]['real_content_id'], $added_dt[$item_id]); + } +} +//exit;//harris +if ($package_base_path == '.') { + $package_base_path = ''; +} + +// loop through the files outside the package folder, and copy them to its relative path +if (is_dir(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/resources')) { + $handler = opendir(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/resources'); + while ($file = readdir($handler)){ + $filename = AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/resources/'.$file; + if(is_file($filename)){ + @rename($filename, AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$package_base_name.'/'.$file); + } + } + closedir($handler); +} +//takes care of the condition where the whole package doesn't have any contents but question banks +if(is_array($all_package_base_path)){ + $all_package_base_path = implode('/', $all_package_base_path); +} + +if (@rename($import_path.$all_package_base_path, AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$package_base_name) === false) { + if (!$msg->containsErrors()) { + $msg->addError('IMPORT_FAILED'); + } +} +//check if there are still resources missing +foreach($items as $idetails){ + $temp_path = pathinfo($idetails['href']); + @rename(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$temp_path['dirname'], AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$package_base_name . '/' . $temp_path['dirname']); +} +clr_dir(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id']); + +if (file_exists($full_filename)) { + @unlink($full_filename); +} + + +if ($_POST['s_cid']){ + if (!$msg->containsErrors()) { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + header('Location: ../editor/edit_content.php?cid='.intval($_POST['cid'])); + exit; +} else { + if (!$msg->containsErrors()) { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + if ($_GET['tile']) { + header('Location: '.AT_BASE_HREF.'mods/_standard/tile_search/index.php'); + } else { + header('Location: ./index.php?cid='.intval($_POST['cid'])); + } + exit; +} + +?> diff --git a/mods/_core/imscp/imscc_c1p2maeV0p15_definition.xsd b/mods/_core/imscp/imscc_c1p2maeV0p15_definition.xsd new file mode 100644 index 000000000..4f38ca9ad --- /dev/null +++ b/mods/_core/imscp/imscc_c1p2maeV0p15_definition.xsd @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/imscp_v1p2.xsd b/mods/_core/imscp/imscp_v1p2.xsd new file mode 100644 index 000000000..326003090 --- /dev/null +++ b/mods/_core/imscp/imscp_v1p2.xsd @@ -0,0 +1,329 @@ + + + + + + XSD Data File Information + ------------------------- + Author: CP1.2 Project Team + Date: 31st October, 2006 + Version: 2.0 + Status: CM/DN Release + Description: This is a normative representation of the IMS CP 1.2 Information Model for binding + purposes. Read the corresponding IMS CP Information Model for the Platform + Independent Model representation. + + History: This is version 1 of the IMS CP v1.2 XSD. It directly supercedes IMS CP v1.1.4 XSD. + Note that the target namespace has NOT been changed. + Apart from the functional additions, the main structural changes are: + (1) All of the elements and attributes are defined as local to their host object; + (2) Comments have been added to the complexType definitions. + + Copyright: 2006 (c) IMS Global Learning Consortium Inc. All Rights Reserved. + IMS Global Learning Consortium, Inc. (IMS/GLC) is publishing the information + contained in this binding ("Specification") for purposes of scientific + experimental and scholarly collaboration only. IMS/GLC makes no warranty or + representation regarding the accuracy or completeness of the Specification. + This material is provided on an "As Is" and "As Available basis". + The Specification is at all times subject to change and revision without + notice. It is your sole responsibility to evaluate the usefulness, accuracy + and completeness of the Specification as it relates to you. IMS/GLC would + appreciate receiving your comments and suggestions. Please contact IMS/GLC + through our website at: http://www.imsglobal.org. + + Source XSLT File Information + ---------------------------- + XSL Generator: UMLtoXSDTransformv0p7.xsl + XSLT Processor: Xalan + Release: 1.0 Beta 1 + Date: 30th November, 2005 + + Auto-generation Tool + -------------------- + This WSDL/XSD was auto-generated using the IMS WSDL/XSD auto-generation tool. While every attempt + has been made to ensure that this tool auto-generates the XSDs correctly, users should be aware + that this is an experimental tool. Permission is given to make use of this tool. IMS makes no + claim on the materials created by third party users of this tool. Details on how to use this tool + are contained in the IMS document: "IMS General Web Services: WSDL/XSD Binding Auto-generation" + available at the IMS web-site. + Tool Copyright: 2005 (c) IMS Global Learning Consortium Inc. All Rights Reserved. + + + + + + + + + + + + + Any namespaced element from any namespace may be included within an "any" element. + The namespace for the imported element must be defined in the instance, and the schema must be imported. + The extension has a definition of "strict" i.e. they must have their own namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + A manifest element is a container for data structures whose contents describe a + semantically complete instance of the IMS Content Packaging Information Model. + + A manifest element may contain and reference child manifest elements + in the same IMS Manifest document. The root manifest element defines an + entire IMS Package. A child manifest element defines a semantically complete + subset of that Package. + +

      Represents a binding of the kinds of objects defined as children of ims-cp-imManifest : Manifest.[ ManifestMetadata, Organizations, Resources, Manifest, Extension ].

      +
      +
      + + + + + + + + + + + +
      + + + + + An instance of the metadata element contains data structures + that declare descriptive information about a metadata element's + parent only. + + One or more different metadata models may be declared as + child extensions of a metadata element. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imMetadata: Metadata.[ Extension ].

      +
      +
      + + + + + +
      + + + + + The organizations element is a container for all data structures + that describe the way or ways that information encapsulated by + its parent manifest element is structured. + +

      Represents of binding of the child objects of ims-cp-imOrganizations: Organizations.[ Organization, Extension ].

      +
      +
      + + + + + + +
      + + + + + The Resources element is a container for data structures containing + references to one or more assets. Asset references may be grouped + within a containing resources element in whatever manner seems best. + + The scope of referenced assets is specific to a resources element's parent + manifest element only. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imResources: Resources.[ Resource, Extension ].

      +
      +
      + + + + + + +
      + + + + + An organization element is a container for all data structures relating + to a particular way or view that information encapsulated by a + grandparent manifest object is structured. + + Multiple organization elements within the same parent organizations + element are equivalent in purpose: Each shows a different way for + structuring the same information declared within a grandparent + manifest object. + +

      Represents a binding of the kinds of child objects defined for ims-cp-Organization: Organization[ Title, Item, Metadata, Extension ].

      +
      +
      + + + + + + + + + +
      + + + + + A resource element is a container for a particular asset + or collection of assets. A resource may contain references + to assets that are all of the same type or different types (i.e., file formats). + + The scope or boundary of an IMS Package is defined by the asset + references collected into all resources containers associated with the + root manifest element, whether as a child, direcdt descendant, or externally + linked descendant. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imResource: Resource.[ Metadata, File, Dependency, Extension ].

      +
      +
      + + + + + + + + + + + +
      + + + + + An item element represents a structural node in a particular organization. + An item element may be a parent or sibling of other Item elements, + each one representing a unique structural node. + + An organization has no meaning unless it has at least one Item element. + +

      Represents a binding of the kinds of child objects of ims-cp-imItem: Item.[ Title, Item, Metadata, Extension ].

      +
      +
      + + + + + + + + + + + +
      + + + + + A file element declares a reference to a single asset. The reference may + be relative to the Package containing the file element or absolute + (external to the Package). + + A file element may contain child extensions declaring alternative references + to the same asset as that referenced by the file element's href attribute. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imFile: File.[ Metadata, Extension ].

      +
      +
      + + + + + + +
      + + + + + A dependency element provides a way to associate another collection of + asset references within the scope of the dependency element's parent + resource element. + + This element allows the parsimonious declaration of asset references. + Shared asset references can be declared once and associated many + times through a Dependency element. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imDependency: Dependency.[ Extension ].

      +
      +
      + + + + + +
      + + + + + This metadata element contains data structures that declare + descriptive information about an entire Package. + + One or more different metadata models may be declared as + child extensions of a metadata element. + + The schema and schemaversion children define the kind or collection + of metadata models being used. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imManifestMetadata: ManifestMetadata.[ Schema, SchemaVersion, MetadataModel ]..

      +
      +
      + + + + + +
      + + + + + + + + + + + + + +
      diff --git a/mods/_core/imscp/imscp_v1p2_constraintsDocument.scmt b/mods/_core/imscp/imscp_v1p2_constraintsDocument.scmt new file mode 100644 index 000000000..e23040dd0 --- /dev/null +++ b/mods/_core/imscp/imscp_v1p2_constraintsDocument.scmt @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + Assertion failed for pattern_3. A Resource object which is a Learning Object associated resource may contain Dependency objects which reference Resource objects with Type 'webcontent'.(#S03) + + + + + + Assertion failed for pattern_4. An Item object which represents a folder is indicated by the absence of an IdentifierRef characteristic object. Folder Items support unlimited nesting of other folder Items and learning object link Items. Learning Application Resource Item objects may be nested by folder Item object but may not nest other folder or Learning Application resource Item objects.(#S04) + + + + + + Error: Assertion failed for pattern_5: If a cartridge web content or associated content resource is linked from a Learning Application Object link Item object it must have an Href characteristic object which represents the launchable resource.(#S05) + + + + + + Error: Assertion failed for pattern_5: For Discussion Topic Resources the Resource object must contain a single File object which references the Discussion Topic descriptor XML file which conforms to the http://www.imsglobal.org/xsd/imsdt_v1p0 schema. Discussion Topic resources must not contain href (#S06) + + + + + + Error: Assertion validation failed for pattern_7: For Web Link Resources the Resource object must contain a single File object which references the Web Link descriptor XML file which conforms to the http://www.imsglobal.org/xsd/imswl_v1p0 schema.It must contain neither Dependency objects nor an href attribute.(#S07) + + + + + + + Error: Assertion validation failed for pattern_11a: For Assessment resources the Resource object must contain a single File object which references the QTI XML file. This file must conform to the IMS CC profile of QTI 1.2.1. The profile is contained in the package of this profile as imscc_q*.zip. The derived schema of this QTI profile is in the package of this profile with the name ims_qtiasiv1p2_localised.xsd. The resource must not have an href attribute(#S11a) + + + + + + Error: Assertion validation failed for pattern_11b1: For Question Bank resources the Resource object must contain a single File object which references the QTI XML file. (#S11b1) + + + + + + Error: Assertion validation failed for pattern_11b2: A Question Bank Resource must not have an href attribute. (#S11b2) + + + + + + Error: Assertion validation failed for pattern_11b3: A Question Bank Resource must not be referenced from an item. (#S11b3) + + + + + + Error: Assertion validation failed for pattern_11b4: There can be only one Questionbank Resource in a cartridge.(#S11b4) + + + + + + Assertion failed for pattern_12. A Resource object which is a Discussion Topic associated resource may contain Dependency objects which reference Resource objects with Type 'webcontent' or 'associatedcontent/imscc_xmlv1p0/learning-application-resource'.(#S12) + + + + + + Assertion failed for pattern_14. A Resource object which is an assessment may contain Dependency objects which reference Resource objects with Type 'webcontent' or 'associatedcontent/imscc_xmlv1p0/learning-application-resource'.(#S14) + + + + + + Assertion failed for pattern_15. A Resource object which is a Question Bank may contain Dependency objects which reference Resource objects with Type 'webcontent' or 'associatedcontent/imscc_xmlv1p0/learning-application-resource'.(#S15) + + + + \ No newline at end of file diff --git a/mods/_core/imscp/imscp_v1p2_localised.xsd b/mods/_core/imscp/imscp_v1p2_localised.xsd new file mode 100644 index 000000000..c8a87f1d2 --- /dev/null +++ b/mods/_core/imscp/imscp_v1p2_localised.xsd @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + XSD Data File Information + ------------------------- + Author: CP1.2 Project Team + Date: 31st October, 2006 + Version: 2.0 + Status: CM/DN Release + Description: This is a normative representation of the IMS CP 1.2 Information Model for binding + purposes. Read the corresponding IMS CP Information Model for the Platform + Independent Model representation. + + History: This is version 1 of the IMS CP v1.2 XSD. It directly supercedes IMS CP v1.1.4 XSD. + Note that the target namespace has NOT been changed. + Apart from the functional additions, the main structural changes are: + (1) All of the elements and attributes are defined as local to their host object; + (2) Comments have been added to the complexType definitions. + + Copyright: 2006 (c) IMS Global Learning Consortium Inc. All Rights Reserved. + IMS Global Learning Consortium, Inc. (IMS/GLC) is publishing the information + contained in this binding ("Specification") for purposes of scientific + experimental and scholarly collaboration only. IMS/GLC makes no warranty or + representation regarding the accuracy or completeness of the Specification. + This material is provided on an "As Is" and "As Available basis". + The Specification is at all times subject to change and revision without + notice. It is your sole responsibility to evaluate the usefulness, accuracy + and completeness of the Specification as it relates to you. IMS/GLC would + appreciate receiving your comments and suggestions. Please contact IMS/GLC + through our website at: http://www.imsglobal.org. + + Source XSLT File Information + ---------------------------- + XSL Generator: UMLtoXSDTransformv0p7.xsl + XSLT Processor: Xalan + Release: 1.0 Beta 1 + Date: 30th November, 2005 + + Auto-generation Tool + -------------------- + This WSDL/XSD was auto-generated using the IMS WSDL/XSD auto-generation tool. While every attempt + has been made to ensure that this tool auto-generates the XSDs correctly, users should be aware + that this is an experimental tool. Permission is given to make use of this tool. IMS makes no + claim on the materials created by third party users of this tool. Details on how to use this tool + are contained in the IMS document: "IMS General Web Services: WSDL/XSD Binding Auto-generation" + available at the IMS web-site. + Tool Copyright: 2005 (c) IMS Global Learning Consortium Inc. All Rights Reserved. + + + + + + + + + + + + + Any namespaced element from any namespace may be included within an "any" element. + The namespace for the imported element must be defined in the instance, and the schema must be imported. + The extension has a definition of "strict" i.e. they must have their own namespace. + + + + + + + + + + Any namespaced element from any namespace may be included within an "any" element. + The namespace for the imported element must be defined in the instance, and the schema must be imported. + The extension has a definition of "strict" i.e. they must have their own namespace. + + + + + + + + + + Any namespaced element from any namespace may be included within an "any" element. + The namespace for the imported element must be defined in the instance, and the schema must be imported. + The extension has a definition of "strict" i.e. they must have their own namespace. + + + + + + + + + + Any namespaced element from any namespace may be included within an "any" element. + The namespace for the imported element must be defined in the instance, and the schema must be imported. + The extension has a definition of "strict" i.e. they must have their own namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + A manifest element is a container for data structures whose contents describe a + semantically complete instance of the IMS Content Packaging Information Model. + + A manifest element may contain and reference child manifest elements + in the same IMS Manifest document. The root manifest element defines an + entire IMS Package. A child manifest element defines a semantically complete + subset of that Package. + +

      Represents a binding of the kinds of objects defined as children of ims-cp-imManifest : Manifest.[ ManifestMetadata, Organizations, Resources, Manifest, Extension ].

      +
      +
      + + + + + + + + + + + +
      + + + + + An instance of the metadata element contains data structures + that declare descriptive information about a metadata element's + parent only. + + One or more different metadata models may be declared as + child extensions of a metadata element. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imMetadata: Metadata.[ Extension ].

      +
      +
      + + + + + +
      + + + + + The organizations element is a container for all data structures + that describe the way or ways that information encapsulated by + its parent manifest element is structured. + +

      Represents of binding of the child objects of ims-cp-imOrganizations: Organizations.[ Organization, Extension ].

      +
      +
      + + + + + + +
      + + + + + The Resources element is a container for data structures containing + references to one or more assets. Asset references may be grouped + within a containing resources element in whatever manner seems best. + + The scope of referenced assets is specific to a resources element's parent + manifest element only. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imResources: Resources.[ Resource, Extension ].

      +
      +
      + + + + + + +
      + + + + + An organization element is a container for all data structures relating + to a particular way or view that information encapsulated by a + grandparent manifest object is structured. + + Multiple organization elements within the same parent organizations + element are equivalent in purpose: Each shows a different way for + structuring the same information declared within a grandparent + manifest object. + +

      Represents a binding of the kinds of child objects defined for ims-cp-Organization: Organization[ Title, Item, Metadata, Extension ].

      +
      +
      + + + + + + + + + +
      + + + + + + A resource element is a container for a particular asset + or collection of assets. A resource may contain references + to assets that are all of the same type or different types (i.e., file formats). + + The scope or boundary of an IMS Package is defined by the asset + references collected into all resources containers associated with the + root manifest element, whether as a child, direcdt descendant, or externally + linked descendant. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imResource: Resource.[ Metadata, File, Dependency, Extension ].

      +
      +
      + + + + + + + + + + + + +
      + + + + + + An item element represents a structural node in a particular organization. + An item element may be a parent or sibling of other Item elements, + each one representing a unique structural node. + + An organization has no meaning unless it has at least one Item element. + +

      Represents a binding of the kinds of child objects of ims-cp-imItem: Item.[ Title, Item, Metadata, Extension ].

      +
      +
      + + + + + + + + + + + +
      + + + + An item element represents a structural node in a particular organization. + An item element may be a parent or sibling of other Item elements, + each one representing a unique structural node. + + An organization has no meaning unless it has at least one Item element. + +

      Represents a binding of the kinds of child objects of ims-cp-imItem: Item.[ Title, Item, Metadata, Extension ].

      +
      +
      + + + + + + + + + + + +
      + + + + + A file element declares a reference to a single asset. The reference may + be relative to the Package containing the file element or absolute + (external to the Package). + + A file element may contain child extensions declaring alternative references + to the same asset as that referenced by the file element's href attribute. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imFile: File.[ Metadata, Extension ].

      +
      +
      + + + + + + +
      + + + + + A dependency element provides a way to associate another collection of + asset references within the scope of the dependency element's parent + resource element. + + This element allows the parsimonious declaration of asset references. + Shared asset references can be declared once and associated many + times through a Dependency element. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imDependency: Dependency.[ Extension ].

      +
      +
      + + + + + + +
      + + + + + This metadata element contains data structures that declare + descriptive information about an entire Package. + + One or more different metadata models may be declared as + child extensions of a metadata element. + + The schema and schemaversion children define the kind or collection + of metadata models being used. + +

      Represents a binding of the kinds of child objects defined for ims-cp-imManifestMetadata: ManifestMetadata.[ Schema, SchemaVersion, MetadataModel ]..

      +
      +
      + + + + + +
      + + + + + + + + + + + + + +
      diff --git a/mods/_core/imscp/imsdt_v1p0_localised.xsd b/mods/_core/imscp/imsdt_v1p0_localised.xsd new file mode 100644 index 000000000..4fd9bfdc7 --- /dev/null +++ b/mods/_core/imscp/imsdt_v1p0_localised.xsd @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/imsmanifest.xml b/mods/_core/imscp/imsmanifest.xml new file mode 100644 index 000000000..175e7fb95 --- /dev/null +++ b/mods/_core/imscp/imsmanifest.xml @@ -0,0 +1,56 @@ + + + + IMS Common Cartridge + 1.0.0 + + + + + + Common Cartridge Sample Course + + Generate Unique ID Values + + + Test Bank + + + Quiz + + + WebLink - Google Site + + + Arctic Fox + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/imswl_v1p0_localised.xsd b/mods/_core/imscp/imswl_v1p0_localised.xsd new file mode 100644 index 000000000..88f14baf0 --- /dev/null +++ b/mods/_core/imscp/imswl_v1p0_localised.xsd @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/imscp/include/adlcp_rootv1p2.xsd b/mods/_core/imscp/include/adlcp_rootv1p2.xsd new file mode 100644 index 000000000..f93069069 --- /dev/null +++ b/mods/_core/imscp/include/adlcp_rootv1p2.xsd @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/include/footer.html b/mods/_core/imscp/include/footer.html new file mode 100644 index 000000000..44ea97054 --- /dev/null +++ b/mods/_core/imscp/include/footer.html @@ -0,0 +1,17 @@ + + + + + About this Content Package + + + + + + + + +
      ATutor.ca - opens in a new windowFor general help with using the ATutor system see the official ATutor Handbook .
      + + \ No newline at end of file diff --git a/mods/_core/imscp/include/ims.css b/mods/_core/imscp/include/ims.css new file mode 100644 index 000000000..a855c1fe2 --- /dev/null +++ b/mods/_core/imscp/include/ims.css @@ -0,0 +1,40 @@ +/************************************************************************/ +/* ATutor */ +/************************************************************************/ +/* Copyright (c) 2002-2008 by Greg Gay, Joel Kronenberg & Heidi Hazelton*/ +/* Adaptive Technology Resource Centre / University of Toronto */ +/* http://atutor.ca */ +/* */ +/* 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. */ +/************************************************************************/ +/* $Id: ims.css 7208 2008-01-09 16:07:24Z greg $ */ + +/* header */ +body.headerbody { font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #EFEFEF;} +h3 { font-family: Verdana, Arial, Helvetica, sans-serif; color: #384F89; } + +/* table of contents */ +ul { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: gray; + font-size: small; +} +ul a { color:#384F89; } +ul a:hover { text-decoration:none; } +ul a:visited { color:black; } + +li { + margin-left: -10px; +} + +/* body */ +body.contentbody { font-family: Verdana, Arial, Helvetica, sans-serif;} + +/* footer */ +body.footerbody { font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #384F89; margin-top: 0px;} +body.footerbody a { font-family: Verdana, Arial, Helvetica, sans-serif; color: white; } +body.footerbody a:hover { font-family: Verdana, Arial, Helvetica, sans-serif; text-decoration:none; } +.footer { font-family: Verdana, Arial, Helvetica, sans-serif; color: white; } + diff --git a/mods/_core/imscp/include/ims_template.inc.php b/mods/_core/imscp/include/ims_template.inc.php new file mode 100644 index 000000000..df7accd86 --- /dev/null +++ b/mods/_core/imscp/include/ims_template.inc.php @@ -0,0 +1,565 @@ + 0) + { + $start_pos = strpos($text, $tag); + if ($start_pos !== false) + { + $text = substr($text, $start_pos); + $start_pos = strlen($tag); + $len = strpos($text, ';') - strlen($tag); + + $file = substr(trim($text), $start_pos, $len); + + // remove these characters from file name: url, (, ), ", ' + $file = trim(preg_replace('/(\'|\"|url|\(|\))/', '', $file)); + + // strip processed tag + $text = substr($text, $start_pos); + array_push($files, $file); + } + + } + + return $files; +} + +function print_organizations($parent_id, + &$_menu, + $depth, + $path='', + $children, + &$string) { + + global $html_content_template, $default_html_style, $zipfile, $resources, $ims_template_xml, $parser, $my_files; + global $used_glossary_terms, $course_id, $course_language_charset, $course_language_code; + static $paths, $zipped_files; + global $glossary; + global $test_zipped_files, $use_a4a, $db; + + $space = ' '; + $prefix = ' '; + + if ($depth == 0) { + $string .= '
        '; + } + $top_level = $_menu[$parent_id]; + if (!is_array($paths)) { + $paths = array(); + } + if (!is_array($zipped_files)) { + $zipped_files = array(); + } + if ( is_array($top_level) ) { + + $counter = 1; + $num_items = count($top_level); + + foreach ($top_level as $garbage => $content) { + $link = ''; + + if ($content['content_path']) { + $content['content_path'] .= '/'; + } + //if this is a folder, export it without identifierref + if ($content['content_type']==CONTENT_TYPE_FOLDER){ + $link = $prevfix.''."\n"; + } else { + $link = $prevfix.''."\n"; + } + $html_link = ''.$content['title'].''; + + /* save the content as HTML files */ + /* @See: include/lib/format_content.inc.php */ + $content['text'] = str_replace('CONTENT_DIR/', '', $content['text']); + /* get all the glossary terms used */ + $terms = find_terms($content['text']); + if (is_array($terms)) { + foreach ($terms[2] as $term) { + $used_glossary_terms[] = $term; + } + } + + /** Test dependency **/ + $test_dependency = ''; //Template for test + $sql = 'SELECT * FROM '.TABLE_PREFIX.'content_tests_assoc WHERE content_id='.$content['content_id']; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)){ + //add test dependency ontop to forums dependency + $test_dependency .= $prefix.$space.''; + } + + /* calculate how deep this page is: */ + $path = '../'; + if ($content['content_path']) { + $depth = substr_count($content['content_path'], '/'); + + $path .= str_repeat('../', $depth); + } + + $content['text'] = format_content($content['text'], $content['formatting'], $glossary, $path); + + /* add HTML header and footers to the files */ + + /* use default style if + {TITLE} + + + +{CONTENT} +'; + +$html_content_template = ' + + + + {HEAD} + {TITLE} + + + +{CONTENT} +'; + +$default_html_style = ' '; + +//output this as header.html +$html_mainheader = ' + + + + + {COURSE_TITLE} + +

        {COURSE_TITLE}

        '; + + +$html_toc = ' + + + + + + +{TOC}'; + +// index.html +$html_frame = ' + + + {COURSE_TITLE} + + + + + + + + + + <h1>{COURSE_TITLE}</h1> + <p><a href="toc.html">Table of Contents</a> | <a href="footer.html">About</a><br /> + </p> + + +'; + + + +$glossary_xml = ' + + + + + + +]> + + + {GLOSSARY_TERMS} + +'; + +$glossary_term_xml = ' + {TERM} + {DEFINITION} + '; + +$glossary_body_html = '

        Glossary

        +
          +{BODY} +
        '; + +$glossary_term_html = '
      • {TERM}
        + {DEFINITION}

      • '; + +?> \ No newline at end of file diff --git a/mods/_core/imscp/include/ims_xml.xsd b/mods/_core/imscp/include/ims_xml.xsd new file mode 100644 index 000000000..dbb4ddc44 --- /dev/null +++ b/mods/_core/imscp/include/ims_xml.xsd @@ -0,0 +1,20 @@ + + + + + + In namespace-aware XML processors, the "xml" prefix is bound to the namespace name http://www.w3.org/XML/1998/namespace. + Do not reference this file in XML instances + + + + Refers to universal XML 1.0 lang attribute + + + + + Refers to XML Base: http://www.w3.org/TR/xmlbase + + + + diff --git a/mods/_core/imscp/include/imscp_rootv1p1p2.xsd b/mods/_core/imscp/include/imscp_rootv1p1p2.xsd new file mode 100644 index 000000000..911a7f86b --- /dev/null +++ b/mods/_core/imscp/include/imscp_rootv1p1p2.xsd @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + DRAFT XSD for IMS Content Packaging version 1.1 DRAFT + Copyright (c) 2001 IMS GLC, Inc. + 2000-04-21, Adjustments by T.D. Wason from CP 1.0. + 2001-02-22, T.D.Wason: Modify for 2000-10-24 XML-Schema version. Modified to support extension. + 2001-03-12, T.D.Wason: Change filename, target and meta-data namespaces and meta-data fielname. Add meta-data to itemType, fileType and organizationType. + Do not define namespaces for xml in XML instances generated from this xsd. + Imports IMS meta-data xsd, lower case element names. + This XSD provides a reference to the IMS meta-data root element as imsmd:record + If the IMS meta-data is to be used in the XML instance then the instance must define an IMS meta-data prefix with a namespace. The meta-data targetNamespace should be used. + 2001-03-20, Thor Anderson: Remove manifestref, change resourceref back to identifierref, change manifest back to contained by manifest. --Tom Wason: manifest may contain _none_ or more manifests. + 2001-04-13 Tom Wason: corrected attirbute name structure. Was misnamed type. + 2001-05-14 Schawn Thropp: Made all complexType extensible with the group.any + Added the anyAttribute to all complexTypes. Changed the href attribute on the fileType and resourceType to xsd:string + Changed the maxLength of the href, identifierref, parameters, structure attributes to match the Information model. + 2001-07-25 Schawn Thropp: Changed the namespace for the Schema of Schemas to the 5/2/2001 W3C XML Schema + Recommendation. attributeGroup attr.imsmd deleted, was not used anywhere. Any attribute declarations that have + use = "default" changed to use="optional" - attr.structure.req. + Any attribute declarations that have value="somevalue" changed to default="somevalue", + attr.structure.req (hierarchical). Removed references to IMS MD Version 1.1. + Modified attribute group "attr.resourcetype.req" to change use from optional + to required to match the information model. As a result the default value also needed to be removed + Name change for XSD. Changed to match version of CP Spec + + + + Inclusions and Imports + + + + + + Attribute Declarations + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + element groups + + + + + Any namespaced element from any namespace may be included within an "any" element. The namespace for the imported element must be defined in the instance, and the schema must be imported. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/include/imsmd_rootv1p2p1.xsd b/mods/_core/imscp/include/imsmd_rootv1p2p1.xsd new file mode 100644 index 000000000..99bb356c6 --- /dev/null +++ b/mods/_core/imscp/include/imsmd_rootv1p2p1.xsd @@ -0,0 +1,573 @@ + + + + + + + + + + + 2001-04-26 T.D.Wason. IMS meta-data 1.2 XML-Schema. + 2001-06-07 S.E.Thropp. Changed the multiplicity on all elements to match the + Final 1.2 Binding Specification. + Changed all elements that use the langstringType to a multiplicy of 1 or more + Changed centity in the contribute element to have a multiplicity of 0 or more. + Changed the requirement element to have a multiplicity of 0 or more. + 2001-07-25 Schawn Thropp. Updates to bring the XSD up to speed with the W3C + XML Schema Recommendation. The following changes were made: Change the + namespace to reference the 5/2/2001 W3C XML Schema Recommendation,the base + type for the durtimeType, simpleType, was changed from timeDuration to duration. + Any attribute declarations that have use="default" had to change to use="optional" + - attr.type. Any attribute declarations that have value ="somevalue" had to change + to default = "somevalue" - attr.type (URI) + 2001-09-04 Schawn Thropp + Changed the targetNamespace and namespace of schema to reflect version change + + + + + + + + + + + + + + + + + + + + Any namespaced element from any namespace may be used for an "any" element. The namespace for the imported element must be defined in the instance, and the schema must be imported. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mods/_core/imscp/index.php b/mods/_core/imscp/index.php new file mode 100644 index 000000000..a7ca4d470 --- /dev/null +++ b/mods/_core/imscp/index.php @@ -0,0 +1,196 @@ +getContent(); +} + +function print_menu_sections(&$menu, $only_print_content_folder = false, $parent_content_id = 0, $depth = 0, $ordering = '') { + $my_children = $menu[$parent_content_id]; + $cid = $_GET['cid']; + + if (!is_array($my_children)) { + return; + } + foreach ($my_children as $children) { + /* test content association, we don't want to display the test pages + * as part of the menu section. If test, skip it. + */ + if (isset($children['test_id'])){ + continue; + } + if ($only_print_content_folder && $children['content_type'] != CONTENT_TYPE_FOLDER) { + continue; + } + + echo ''; + + print_menu_sections($menu, $only_print_content_folder, $children['content_id'], $depth+1, $new_ordering); + } +} + +if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && ($_SESSION['packaging'] == 'none')) { + echo '

        '._AT('content_packaging_disabled').'

        '; + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} else if (!authenticate(AT_PRIV_CONTENT, AT_PRIV_RETURN) && ($_SESSION['packaging'] == 'top')) { + $_main_menu = array($_main_menu[0]); +} +?> +
        +
        +
        +
        +

        +
        + + +
        +
        + +
        + + +
        + +
        + + +
        +
        + +
        + + +
        + + +
        + + +
        +
        + +
        + +
        + + +
        +
        + + +

        + + +
        +
        +
        +
        + +

        +
        + +
        +
        + +
        + +
        + +
        + +
        + +
        +
        + +
        +
        + +
        + +
        +
        + +
        + +
        + + +
        +
        +
        + + + + \ No newline at end of file diff --git a/mods/_core/imscp/main.xsd b/mods/_core/imscp/main.xsd new file mode 100644 index 000000000..bf8511cc2 --- /dev/null +++ b/mods/_core/imscp/main.xsd @@ -0,0 +1,27 @@ + + + + + + + + + + + + diff --git a/mods/_core/imscp/module.php b/mods/_core/imscp/module.php new file mode 100644 index 000000000..1e8c99f7c --- /dev/null +++ b/mods/_core/imscp/module.php @@ -0,0 +1,21 @@ +_pages['mods/_core/imscp/index.php']['title_var'] = 'content_packaging'; +//$this->_pages['mods/_core/imscp/index.php']['parent'] = 'tools/content/index.php'; +//$this->_pages['mods/_core/imscp/index.php']['guide'] = 'instructor/?p=content_packages.php'; + +//students +//$this->_pages['export.php']['title_var'] = 'export_content'; +//$this->_pages['export.php']['img'] = 'images/home-export_content.png'; +//$this->_pages['export.php']['text'] = _AT('export_content_text'); +//$this->_pages['export.php']['guide'] = 'general/?p=export_content.php'; + +?> \ No newline at end of file diff --git a/mods/_core/imscp/module.xml b/mods/_core/imscp/module.xml new file mode 100644 index 000000000..636e6b3d7 --- /dev/null +++ b/mods/_core/imscp/module.xml @@ -0,0 +1,20 @@ + + + Import/Export Content + Instructors can import or export content packages. Students can export content into content packages for offline viewing or reference. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + true + 2005-08-25 + stable + + + \ No newline at end of file diff --git a/mods/_core/imscp/ns.inc.php b/mods/_core/imscp/ns.inc.php new file mode 100644 index 000000000..2cc42c6b9 --- /dev/null +++ b/mods/_core/imscp/ns.inc.php @@ -0,0 +1,67 @@ + diff --git a/mods/_core/imscp/oauth/OAuth.php b/mods/_core/imscp/oauth/OAuth.php new file mode 100644 index 000000000..97b400c0b --- /dev/null +++ b/mods/_core/imscp/oauth/OAuth.php @@ -0,0 +1,798 @@ +getByConsumerKeyAndSecret($key, $secret); +// +// if (!is_array($consumer)) throw new OAuthException('Consumer is not registered.'); +// else { + $this->key = $key; + $this->secret = $secret; + $this->callback_url = $callback_url; +// } + } + + function __toString() { + return "OAuthConsumer[key=$this->key,secret=$this->secret]"; + } +} + +class OAuthToken { + // access tokens and request tokens + public $key; + public $secret; + + /** + * key = the token + * secret = the token secret + */ + function __construct($key, $secret) { + $this->key = $key; + $this->secret = $secret; + } + + /** + * generates the basic string serialization of a token that a server + * would respond to request_token and access_token calls with + */ + function to_string() { + return "oauth_token=" . + OAuthUtil::urlencode_rfc3986($this->key) . + "&oauth_token_secret=" . + OAuthUtil::urlencode_rfc3986($this->secret); + } + + function __toString() { + return $this->to_string(); + } +} + +class OAuthSignatureMethod { + public function check_signature(&$request, $consumer, $token, $signature) { + $built = $this->build_signature($request, $consumer, $token); + return $built == $signature; + } +} + +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { + function get_name() { + return "HMAC-SHA1"; + } + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + + return base64_encode(hash_hmac('sha1', $base_string, $key, true)); + } +} + +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { + public function get_name() { + return "PLAINTEXT"; + } + + public function build_signature($request, $consumer, $token) { + $sig = array( + OAuthUtil::urlencode_rfc3986($consumer->secret) + ); + + if ($token) { + array_push($sig, OAuthUtil::urlencode_rfc3986($token->secret)); + } else { + array_push($sig, ''); + } + + $raw = implode("&", $sig); + // for debug purposes + $request->base_string = $raw; + + return OAuthUtil::urlencode_rfc3986($raw); + } +} + +class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { + public function get_name() { + return "RSA-SHA1"; + } + + protected function fetch_public_cert(&$request) { + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // either way should return a string representation of the certificate + throw Exception("fetch_public_cert not implemented"); + } + + protected function fetch_private_cert(&$request) { + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // either way should return a string representation of the certificate + throw Exception("fetch_private_cert not implemented"); + } + + public function build_signature(&$request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + // Fetch the private key cert based on the request + $cert = $this->fetch_private_cert($request); + + // Pull the private key ID from the certificate + $privatekeyid = openssl_get_privatekey($cert); + + // Sign using the key + $ok = openssl_sign($base_string, $signature, $privatekeyid); + + // Release the key resource + openssl_free_key($privatekeyid); + + return base64_encode($signature); + } + + public function check_signature(&$request, $consumer, $token, $signature) { + $decoded_sig = base64_decode($signature); + + $base_string = $request->get_signature_base_string(); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + // Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + // Check the computed signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + // Release the key resource + openssl_free_key($publickeyid); + + return $ok == 1; + } +} + +class OAuthRequest { + private $parameters; + private $http_method; + private $http_url; + // for debug purposes + public $base_string; + public static $version = '1.0'; + public static $POST_INPUT = 'php://input'; + + function __construct($http_method, $http_url, $parameters=NULL) { + @$parameters or $parameters = array(); + $this->parameters = $parameters; + $this->http_method = $http_method; + $this->http_url = $http_url; + } + + + /** + * attempt to build up a request from what was passed to the server + */ + public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) { + $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") + ? 'http' + : 'https'; + @$http_url or $http_url = $scheme . + '://' . $_SERVER['HTTP_HOST'] . + ':' . + $_SERVER['SERVER_PORT'] . + $_SERVER['REQUEST_URI']; + @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; + + // We weren't handed any parameters, so let's find the ones relevant to + // this request. + // If you run XML-RPC or similar you should use this to provide your own + // parsed parameter-list + if (!$parameters) { + // Find request headers + $request_headers = OAuthUtil::get_headers(); + + // Parse the query-string to find GET parameters + $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); + + // It's a POST request of the proper content-type, so parse POST + // parameters and add those overriding any duplicates from GET + if ($http_method == "POST" + && @strstr($request_headers["Content-Type"], + "application/x-www-form-urlencoded") + ) { + $post_data = OAuthUtil::parse_parameters( + file_get_contents(self::$POST_INPUT) + ); + $parameters = array_merge($parameters, $post_data); + } + + // We have a Authorization-header with OAuth data. Parse the header + // and add those overriding any duplicates from GET or POST + if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { + $header_parameters = OAuthUtil::split_header( + $request_headers['Authorization'] + ); + $parameters = array_merge($parameters, $header_parameters); + } + + } + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + /** + * pretty much a helper function to set up the request + */ + public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) { + @$parameters or $parameters = array(); + $defaults = array("oauth_version" => OAuthRequest::$version, + "oauth_nonce" => OAuthRequest::generate_nonce(), + "oauth_timestamp" => OAuthRequest::generate_timestamp(), + "oauth_consumer_key" => $consumer->key); + if ($token) + $defaults['oauth_token'] = $token->key; + + $parameters = array_merge($defaults, $parameters); + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + public function set_parameter($name, $value, $allow_duplicates = true) { + if ($allow_duplicates && isset($this->parameters[$name])) { + // We have already added parameter(s) with this name, so add to the list + if (is_scalar($this->parameters[$name])) { + // This is the first duplicate, so transform scalar (string) + // into an array so we can add the duplicates + $this->parameters[$name] = array($this->parameters[$name]); + } + + $this->parameters[$name][] = $value; + } else { + $this->parameters[$name] = $value; + } + } + + public function get_parameter($name) { + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + } + + public function get_parameters() { + return $this->parameters; + } + + public function unset_parameter($name) { + unset($this->parameters[$name]); + } + + /** + * The request parameters, sorted and concatenated into a normalized string. + * @return string + */ + public function get_signable_parameters() { + // Grab all parameters + $params = $this->parameters; + + // Remove oauth_signature if present + // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") + if (isset($params['oauth_signature'])) { + unset($params['oauth_signature']); + } + + return OAuthUtil::build_http_query($params); + } + + /** + * Returns the base string of this request + * + * The base string defined as the method, the url + * and the parameters (normalized), each urlencoded + * and the concated with &. + */ + public function get_signature_base_string() { + $parts = array( + $this->get_normalized_http_method(), + $this->get_normalized_http_url(), + $this->get_signable_parameters() + ); + + $parts = OAuthUtil::urlencode_rfc3986($parts); + + return implode('&', $parts); + } + + /** + * just uppercases the http method + */ + public function get_normalized_http_method() { + return strtoupper($this->http_method); + } + + /** + * parses the url and rebuilds it to be + * scheme://host/path + */ + public function get_normalized_http_url() { + $parts = parse_url($this->http_url); + + $port = @$parts['port']; + $scheme = $parts['scheme']; + $host = $parts['host']; + $path = @$parts['path']; + + $port or $port = ($scheme == 'https') ? '443' : '80'; + + if (($scheme == 'https' && $port != '443') + || ($scheme == 'http' && $port != '80')) { + $host = "$host:$port"; + } + return "$scheme://$host$path"; + } + + /** + * builds a url usable for a GET request + */ + public function to_url() { + $post_data = $this->to_postdata(); + $out = $this->get_normalized_http_url(); + if ($post_data) { + $out .= '?'.$post_data; + } + return $out; + } + + /** + * builds the data one would send in a POST request + */ + public function to_postdata() { + return OAuthUtil::build_http_query($this->parameters); + } + + /** + * builds the Authorization: header + */ + public function to_header() { + $out ='Authorization: OAuth realm=""'; + $total = array(); + foreach ($this->parameters as $k => $v) { + if (substr($k, 0, 5) != "oauth") continue; + if (is_array($v)) { + throw new OAuthException('Arrays not supported in headers'); + } + $out .= ',' . + OAuthUtil::urlencode_rfc3986($k) . + '="' . + OAuthUtil::urlencode_rfc3986($v) . + '"'; + } + return $out; + } + + public function __toString() { + return $this->to_url(); + } + + + public function sign_request($signature_method, $consumer, $token) { + $this->set_parameter( + "oauth_signature_method", + $signature_method->get_name(), + false + ); + $signature = $this->build_signature($signature_method, $consumer, $token); + $this->set_parameter("oauth_signature", $signature, false); + } + + public function build_signature($signature_method, $consumer, $token) { + $signature = $signature_method->build_signature($this, $consumer, $token); + return $signature; + } + + /** + * util function: current timestamp + */ + private static function generate_timestamp() { + return time(); + } + + /** + * util function: current nonce + */ + private static function generate_nonce() { + $mt = microtime(); + $rand = mt_rand(); + + return md5($mt . $rand); // md5s look nicer than numbers + } +} + +class OAuthServer { + protected $timestamp_threshold; // in seconds, five minutes + protected $version = 1.0; // hi blaine + protected $signature_methods = array(); + + protected $data_store; + + function __construct($data_store) { + $this->data_store = $data_store; + } + + public function add_signature_method($signature_method) { + $this->signature_methods[$signature_method->get_name()] = + $signature_method; + } + + // high level functions + + /** + * process a request_token request + * returns the request token on success + */ + public function fetch_request_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // no token required for the initial token request + $token = NULL; + + $this->check_signature($request, $consumer, $token); + + $new_token = $this->data_store->new_request_token($consumer); + + return $new_token; + } + + /** + * process an access_token request + * returns the access token on success + */ + public function fetch_access_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // requires authorized request token + $token = $this->get_token($request, $consumer, "request"); + + $this->check_signature($request, $consumer, $token); + + if ($this->data_store->lookup_authenticate_request_token($token)) + { + $new_token = $this->data_store->new_access_token($token, $consumer); + return $new_token; + } + else + throw new OAuthException("User authenticate is not performed."); + } + + /** + * verify an api call, checks all the parameters + */ + public function verify_request(&$request) { + $this->get_version($request); + $consumer = $this->get_consumer($request); + $token = $this->get_token($request, $consumer, "access"); + $this->check_signature($request, $consumer, $token); + return array($consumer, $token); + } + + // Internals from here + /** + * version 1 + */ + private function get_version(&$request) { + $version = $request->get_parameter("oauth_version"); + if (!$version) { + $version = 1.0; + } + if ($version && $version != $this->version) { + throw new OAuthException("OAuth version '$version' not supported"); + } + return $version; + } + + /** + * figure out the signature with some defaults + */ + private function get_signature_method(&$request) { + $signature_method = + @$request->get_parameter("oauth_signature_method"); + if (!$signature_method) { + $signature_method = "PLAINTEXT"; + } + if (!in_array($signature_method, + array_keys($this->signature_methods))) { + throw new OAuthException( + "Signature method '$signature_method' not supported " . + "try one of the following: " . + implode(", ", array_keys($this->signature_methods)) + ); + } + return $this->signature_methods[$signature_method]; + } + + /** + * try to find the consumer for the provided request's consumer key + */ + private function get_consumer(&$request) { + $consumer_key = @$request->get_parameter("oauth_consumer_key"); + if (!$consumer_key) { + throw new OAuthException("Invalid consumer key"); + } + + $consumer = $this->data_store->lookup_consumer($consumer_key); + if (!$consumer) { + throw new OAuthException("Invalid consumer"); + } + + return $consumer; + } + + /** + * try to find the token for the provided request's token key + */ + private function get_token(&$request, $consumer, $token_type="access") { + $token_field = @$request->get_parameter('oauth_token'); + $token = $this->data_store->lookup_token( + $consumer, $token_type, $token_field + ); + if (!$token) { + throw new OAuthException("Invalid $token_type token: $token_field"); + } + return $token; + } + + /** + * all-in-one function to check the signature on a request + * should guess the signature method appropriately + */ + private function check_signature(&$request, $consumer, $token) { + // this should probably be in a different method + $timestamp = @$request->get_parameter('oauth_timestamp'); + $nonce = @$request->get_parameter('oauth_nonce'); + + $this->timestamp_threshold = $this->data_store->lookup_expire_threshold($consumer); + + $this->check_timestamp($timestamp); + $this->check_nonce($consumer, $token, $nonce, $timestamp); + + $signature_method = $this->get_signature_method($request); + + $signature = $request->get_parameter('oauth_signature'); + $valid_sig = $signature_method->check_signature( + $request, + $consumer, + $token, + $signature + ); + + if (!$valid_sig) { + throw new OAuthException("Invalid signature"); + } + } + + /** + * check that the timestamp is new enough + */ + private function check_timestamp($timestamp) { + // verify that timestamp is recentish + // when threshold is 0, never expire + $now = time(); + if ($this->timestamp_threshold <> 0 && $now - $timestamp > $this->timestamp_threshold) { + throw new OAuthException( + "Expired timestamp, yours $timestamp, ours $now" + ); + } + } + + /** + * check that the nonce is not repeated + */ + private function check_nonce($consumer, $token, $nonce, $timestamp) { + // verify that the nonce is uniqueish + $found = $this->data_store->lookup_nonce( + $consumer, + $token, + $nonce, + $timestamp + ); + if ($found) { + throw new OAuthException("Nonce already used: $nonce"); + } + } + +} + +class OAuthDataStore { + function lookup_consumer($consumer_key) { + // implement me + } + + function lookup_token($consumer, $token_type, $token) { + // implement me + } + + function lookup_nonce($consumer, $token, $nonce, $timestamp) { + // implement me + } + + function new_request_token($consumer) { + // return a new token attached to this consumer + } + + function new_access_token($token, $consumer) { + // return a new access token attached to this consumer + // for the user associated with this token if the request token + // is authorized + // should also invalidate the request token + } + +} + +class OAuthUtil { + public static function urlencode_rfc3986($input) { + if (is_array($input)) { + return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input); + } else if (is_scalar($input)) { + return str_replace( + '+', + ' ', + str_replace('%7E', '~', rawurlencode($input)) + ); + } else { + return ''; + } +} + + + // This decode function isn't taking into consideration the above + // modifications to the encoding process. However, this method doesn't + // seem to be used anywhere so leaving it as is. + public static function urldecode_rfc3986($string) { + return urldecode($string); + } + + // Utility function for turning the Authorization: header into + // parameters, has to do some unescaping + // Can filter out any non-oauth parameters if needed (default behaviour) + public static function split_header($header, $only_allow_oauth_parameters = true) { + $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; + $offset = 0; + $params = array(); + while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { + $match = $matches[0]; + $header_name = $matches[2][0]; + $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0]; + if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) { + $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content); + } + $offset = $match[1] + strlen($match[0]); + } + + if (isset($params['realm'])) { + unset($params['realm']); + } + + return $params; + } + + // helper to try to sort out headers for people who aren't running apache + public static function get_headers() { + if (function_exists('apache_request_headers')) { + // we need this to get the actual Authorization: header + // because apache tends to tell us it doesn't exist + return apache_request_headers(); + } + // otherwise we don't have apache and are just going to have to hope + // that $_SERVER actually contains what we need + $out = array(); + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) == "HTTP_") { + // this is chaos, basically it is just there to capitalize the first + // letter of every word that is not an initial HTTP and strip HTTP + // code from przemek + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("_", " ", substr($key, 5)))) + ); + $out[$key] = $value; + } + } + return $out; + } + + // This function takes a input like a=b&a=c&d=e and returns the parsed + // parameters like this + // array('a' => array('b','c'), 'd' => 'e') + public static function parse_parameters( $input ) { + if (!isset($input) || !$input) return array(); + + $pairs = explode('&', $input); + + $parsed_parameters = array(); + foreach ($pairs as $pair) { + $split = explode('=', $pair, 2); + $parameter = OAuthUtil::urldecode_rfc3986($split[0]); + $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; + + if (isset($parsed_parameters[$parameter])) { + // We have already recieved parameter(s) with this name, so add to the list + // of parameters with this name + + if (is_scalar($parsed_parameters[$parameter])) { + // This is the first duplicate, so transform scalar (string) into an array + // so we can add the duplicates + $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]); + } + + $parsed_parameters[$parameter][] = $value; + } else { + $parsed_parameters[$parameter] = $value; + } + } + return $parsed_parameters; + } + + public static function build_http_query($params) { + if (!$params) return ''; + + // Urlencode both keys and values + $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); + $values = OAuthUtil::urlencode_rfc3986(array_values($params)); + $params = array_combine($keys, $values); + + // Parameters are sorted by name, using lexicographical byte value ordering. + // Ref: Spec: 9.1.1 (1) + uksort($params, 'strcmp'); + + $pairs = array(); + foreach ($params as $parameter => $value) { + if (is_array($value)) { + // If two or more parameters share the same name, they are sorted by their value + // Ref: Spec: 9.1.1 (1) + natsort($value); + foreach ($value as $duplicate_value) { + $pairs[] = $parameter . '=' . $duplicate_value; + } + } else { + $pairs[] = $parameter . '=' . $value; + } + } + // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) + // Each name-value pair is separated by an '&' character (ASCII code 38) + return implode('&', $pairs); + } +} + +?> diff --git a/mods/_core/imscp/oauth/OAuthUtility.class.php b/mods/_core/imscp/oauth/OAuthUtility.class.php new file mode 100644 index 000000000..e78b91e11 --- /dev/null +++ b/mods/_core/imscp/oauth/OAuthUtility.class.php @@ -0,0 +1,78 @@ + diff --git a/mods/_core/imscp/oauth/oauth_authenticate.php b/mods/_core/imscp/oauth/oauth_authenticate.php new file mode 100644 index 000000000..c77747eec --- /dev/null +++ b/mods/_core/imscp/oauth/oauth_authenticate.php @@ -0,0 +1,223 @@ +addError(array('TILE_AUTHENTICATION_FAIL', _AT('tile_not_accessible'))); + header('Location: '.AT_BASE_HREF.'mods/_core/imscp/index.php'); + exit; +} + +// check whether the last access token has expired. If not, return it, otherwise, get a new access token. +// skip this step when this script is called by oauth server callback +if (isset($_SESSION['member_id'])) + $access_token_key = OAuthUtility::getUnexpiredAccessToken(); + +if ($access_token_key == '') +{ + // initialize basic variables + $sig_method = new OAuthSignatureMethod_HMAC_SHA1(); // use HMAC signature method as default + + if (!isset($_GET['oauth_token'])) // before oauth server authentication, get request token from oauth server + { + // 1. register consumer + $sql = "SELECT * FROM ".TABLE_PREFIX."oauth_client_servers + WHERE oauth_server='".mysql_real_escape_string($_config['transformable_uri'])."'"; + $result = mysql_query($sql, $db); + + if (mysql_num_rows($result) == 0) + { + $register_consumer_url = AT_TILE_OAUTH_REGISTER_CONSUMER_URL.'?consumer='.urlencode(AT_BASE_HREF).'&expire='.$_config['transformable_oauth_expire']; + $oauth_server_response = file_get_contents($register_consumer_url); + +// debug('register consumer - request: '.$register_consumer_url); +// debug('register consumer - OAUTH response: '.$oauth_server_response); + + // handle OAUTH response on register consumer + foreach (explode('&', $oauth_server_response) as $rtn) + { + $rtn_pair = explode('=', $rtn); + + if ($rtn_pair[0] == 'consumer_key') $consumer_key = $rtn_pair[1]; + if ($rtn_pair[0] == 'consumer_secret') $consumer_secret = $rtn_pair[1]; + if ($rtn_pair[0] == 'expire') $expire_threshold = $rtn_pair[1]; + if ($rtn_pair[0] == 'error') $error = urldecode($rtn_pair[1]); + } + + if ($error <> '') + { + $msg->addError(array('TILE_AUTHENTICATION_FAIL', $error)); + header('Location: '.AT_BASE_HREF.'mods/_core/imscp/index.php'); + exit; + } + else + { + $sql = "INSERT INTO ".TABLE_PREFIX."oauth_client_servers + (oauth_server, consumer_key, consumer_secret, expire_threshold, create_date) + VALUES ('".mysql_real_escape_string($_config['transformable_uri'])."', '".$consumer_key."', + '".$consumer_secret."', ".$expire_threshold.", now())"; + $result = mysql_query($sql, $db); + $oauth_server_id = mysql_insert_id(); + } + } + else + { + $row = mysql_fetch_assoc($result); + $oauth_server_id = $row['oauth_server_id']; + $consumer_key = $row['consumer_key']; + $consumer_secret = $row['consumer_secret']; + $expire_threshold = $row['expire_threshold']; + } + $consumer = new OAuthConsumer($consumer_key, $consumer_secret, $client_callback_url); + + // debug('consumer: '.$consumer); + // debug('--- END OF REGISTERING CONSUMER ---'); + + // 2. get request token + $req_req = OAuthRequest::from_consumer_and_token($consumer, NULL, "GET", AT_TILE_OAUTH_REQUEST_TOKEN_URL); + $req_req->sign_request($sig_method, $consumer, NULL); + + $oauth_server_response = file_get_contents($req_req); + + // debug('request token - request: '."\n".$req_req); + // debug('request token - response: '."\n".$oauth_server_response); + + // handle OAUTH request token response + foreach (explode('&', $oauth_server_response) as $rtn) + { + $rtn_pair = explode('=', $rtn); + + if ($rtn_pair[0] == 'oauth_token') $request_token_key = $rtn_pair[1]; + if ($rtn_pair[0] == 'oauth_token_secret') $request_token_secret = $rtn_pair[1]; + if ($rtn_pair[0] == 'error') $error = urldecode($rtn_pair[1]); + } + + if ($error == '' && strlen($request_token_key) > 0 && strlen($request_token_secret) > 0) + { + $sql = "INSERT INTO ".TABLE_PREFIX."oauth_client_tokens + (oauth_server_id, token, token_type, token_secret, member_id, assign_date) + VALUES (".$oauth_server_id.", '".$request_token_key."', 'request', + '".$request_token_secret."', ".$_SESSION['member_id'].", now())"; + $result = mysql_query($sql, $db); + } + else + { + $msg->addError(array('TILE_AUTHENTICATION_FAIL', $error)); + header('Location: '.AT_BASE_HREF.'mods/_core/imscp/index.php'); + exit; + } + + $request_token = new OAuthToken($request_token_key, $request_token_secret); + + // debug('--- END OF REQESTING REQUEST TOKEN ---'); + + // 3. authorization + $auth_req = AT_TILE_OAUTH_AUTHORIZATION_URL.'?oauth_token='.$request_token_key.'&oauth_callback='.urlencode($client_callback_url); + + header('Location: '.$auth_req); + exit; + } + else // authenticated + { + // get consumer id by request token + $sql = "SELECT ocs.oauth_server_id, ocs.consumer_key, ocs.consumer_secret, + ocs.expire_threshold, oct.member_id, oct.token_secret + FROM ".TABLE_PREFIX."oauth_client_servers ocs, ".TABLE_PREFIX."oauth_client_tokens oct + WHERE ocs.oauth_server_id = oct.oauth_server_id + AND oct.token = '".$_GET['oauth_token']."' + AND token_type='request'"; + + $result = mysql_query($sql, $db); + if (mysql_num_rows($result)==0) + { + $msg->addError(array('TILE_AUTHENTICATION_FAIL', _AT('wrong_request_token'))); + header('Location: '.AT_BASE_HREF.'mods/_core/imscp/index.php'); + exit; + } + + $row = mysql_fetch_assoc($result); + + $consumer = new OAuthConsumer($row['consumer_key'], $row['consumer_secret'], $client_callback_url); + $request_token = new OAuthToken($_GET['oauth_token'], $row['token_secret']); + + // 4. get access token + $access_req = OAuthRequest::from_consumer_and_token($consumer, $request_token, "GET", AT_TILE_OAUTH_ACCESS_TOKEN_URL); + $access_req->sign_request($sig_method, $consumer, NULL); + + $oauth_server_response = file_get_contents($access_req); + + // debug('access token - request: '."\n".$access_req); + // debug('access token - response: '."\n".$oauth_server_response); + + // handle OAUTH response on access token + foreach (explode('&', $oauth_server_response) as $rtn) + { + $rtn_pair = explode('=', $rtn); + + if ($rtn_pair[0] == 'oauth_token') $access_token_key = $rtn_pair[1]; + if ($rtn_pair[0] == 'oauth_token_secret') $access_token_secret = $rtn_pair[1]; + if ($rtn_pair[0] == 'error') $error = urldecode($rtn_pair[1]); + } + + if ($error == '' && strlen($access_token_key) > 0 && strlen($access_token_secret) > 0) + { + // insert access token + $sql = "INSERT INTO ".TABLE_PREFIX."oauth_client_tokens + (oauth_server_id, token, token_type, token_secret, member_id, assign_date) + VALUES (".$row['oauth_server_id'].", '".$access_token_key."', 'access', + '".$access_token_secret."', ".$row['member_id'].", now())"; + $result = mysql_query($sql, $db); + + // delete request_token + $sql = "DELETE FROM ".TABLE_PREFIX."oauth_client_tokens + WHERE token = '".$_GET['oauth_token']."' + AND token_type='request'"; + $result = mysql_query($sql, $db); + } + else + { + $msg->addError(array('TILE_AUTHENTICATION_FAIL', $error)); + header('Location: '.AT_BASE_HREF.'mods/_core/imscp/index.php'); + exit; + } + } +} +//debug('access token key: '.$access_token_key); +// debug('--- END OF REQESTING ACCESS TOKEN ---'); +// exit; +?> \ No newline at end of file diff --git a/mods/_core/imsqti/classes/QTIImport.class.php b/mods/_core/imsqti/classes/QTIImport.class.php new file mode 100644 index 000000000..4ec378710 --- /dev/null +++ b/mods/_core/imsqti/classes/QTIImport.class.php @@ -0,0 +1,417 @@ +import_path = $import_path; + } + + //Creates the parameters array for TestQuestion::importQTI + function constructParams($qti_params){ + global $addslashes; + //save guarding + $qti_params['required'] = intval($qti_params['required']); + $qti_params['question'] = trim($qti_params['question']); + $qti_params['category_id'] = intval($qti_params['category_id']); + $qti_params['feedback'] = trim($qti_params['feedback']); + + //assign answers + if (sizeof($qti_params['answers']) > 1){ + $qti_params['answer'] = $qti_params['answers']; + } elseif (sizeof($qti_params['answers'])==1) { + $qti_params['answer'] = intval($qti_params['answers'][0]); + } + $this->qti_params = $qti_params; + } + + //Decide which question type to import based in the integer + function getQuestionType($question_type){ + $qti_obj = TestQuestions::getQuestion($question_type); + if ($qti_obj != null){ + $qid = $qti_obj->importQTI($this->qti_params); + if ($qid > 0) { + $this->qid = $qid; + } + } + } + + + /** + * This function will add the attributes that are extracted from the qti xml + * into the database. + * + * @param array attributes that are extracted from the QTI XML. + * @return int the question ids. + */ + function importQuestions($attributes){ + global $supported_media_type, $msg; + $qids = array(); + + foreach($attributes as $resource=>$attrs){ + if (preg_match('/imsqti\_(.*)/', $attrs['type'])){ + //Instantiate class obj + $xml = new QTIParser($attrs['type']); + $xml_content = @file_get_contents($this->import_path . $attrs['href']); + $xml->setRelativePath($package_base_name); + + if (!$xml->parse($xml_content)){ + $msg->addError('QTI_WRONG_PACKAGE'); + break; + } + + //set test title + $this->title = $xml->title; + +//if ($attrs[href] =='56B1BEDC-A820-7AA8-A21D-F32017189445/56B1BEDC-A820-7AA8-A21D-F32017189445.xml'){ +// debug($xml, 'attributes'); +//} + //import file, should we use file href? or jsut this href? + //Aug 25, use both, so then it can check for respondus media as well. + foreach($attrs['file'] as $file_id => $file_name){ + $file_pathinfo = pathinfo($file_name); + if ($file_pathinfo['basename'] == $attrs['href']){ + //This file will be parsed later + continue; + } + + if (in_array($file_pathinfo['extension'], $supported_media_type)){ + //copy medias over. + $this->copyMedia(array($file_name), $xml->items); + } + } + + for ($loopcounter=0; $loopcounter<$xml->item_num; $loopcounter++){ + //Create POST values. + unset($test_obj); //clear cache + $test_obj['required'] = 1; + $test_obj['preset_num'] = 0; + $test_obj['category_id'] = 0; + $test_obj['question'] = $xml->question[$loopcounter]; + $test_obj['feedback'] = $xml->feedback[$loopcounter]; + $test_obj['groups'] = $xml->groups[$loopcounter]; + $test_obj['property'] = intval($xml->attributes[$loopcounter]['render_fib']['property']); + $test_obj['choice'] = array(); + $test_obj['answers'] = array(); + + //assign choices + $i = 0; + + //trim values + if (is_array($xml->answers[$loopcounter])){ + array_walk($xml->answers[$loopcounter], 'trim_value'); + } + //TODO: The groups is 1-0+ choices. So we should loop thru groups, not choices. + if (is_array($xml->choices[$loopcounter])){ + foreach ($xml->choices[$loopcounter] as $choiceNum=>$choiceOpt){ + if (sizeof($test_obj['groups'] )>0) { + if (!empty($xml->answers[$loopcounter])){ + foreach ($xml->answers[$loopcounter] as $ansNum=>$ansOpt){ + if ($choiceNum == $ansOpt){ + //Not exactly efficient, worst case N^2 + $test_obj['answers'][$ansNum] = $i; + } + } + } + } else { + //save answer(s) + if (is_array($xml->answers[$loopcounter]) && in_array($choiceNum, $xml->answers[$loopcounter])){ + $test_obj['answers'][] = $i; + } + } + $test_obj['choice'][] = $choiceOpt; + $i++; + } + } + + // unset($qti_import); + $this->constructParams($test_obj); +//debug($xml->getQuestionType($loopcounter), 'lp_'.$loopcounter); + //Create questions + $this->getQuestionType($xml->getQuestionType($loopcounter)); + + //save question id + $qids[] = $this->qid; + + //Dependency handling + if (!empty($attrs['dependency'])){ + $xml_items = array_merge($xml_items, $xml->items); + } + } + + //assign title + if ($xml->title != ''){ + $this->title = $xml->title; + } + + //assign marks/weights + $this->weights = $xml->weights; + + $xml->close(); + } elseif ($attrs['type'] == 'webcontent') { + //webcontent, copy it over. + $this->copyMedia($attrs['file'], $xml_items); + } + } +//debug($qids, 'qids'); + return $qids; + } + + /** + * This function is to import a test and returns the test id. + * @param string custmom test title + * + * @return int test id + */ + function importTest($title='') { + global $msg, $db; + + $missing_fields = array(); + $test_obj['title'] = ($title=='')?$this->title:$title; + $test_obj['description'] = ''; + $test_obj['num_questions'] = 0; + $test_obj['num_takes'] = 0; + $test_obj['content_id'] = 0; + $test_obj['passpercent'] = 0; + $test_obj['passscore'] = 0; + $test_obj['passfeedback'] = 0; + $test_obj['failfeedback'] = 0; + $test_obj['num_takes'] = 0; + $test_obj['anonymous'] = 0; + $test_obj['allow_guests'] = $_POST['allow_guests'] ? 1 : 0; + $test_obj['instructions'] = ''; + $test_obj['display'] = 0; + $test_obj['result_release'] = 0; + $test_obj['random'] = 0; + + // currently these options are ignored for tests: + $test_obj['format'] = intval($test_obj['format']); + $test_obj['order'] = 1; //intval($test_obj['order']); + $test_obj['difficulty'] = 0; //intval($test_obj['difficulty']); /* avman */ + + //Title of the test is empty, could be from question database export or some other system's export. + //Either prompt for a title, or generate a random title + if ($test_obj['title'] == '') { + if ($this->title != '') { + $test_obj['title'] = $this->title; + } else { +// $test_obj['title'] = 'random title'; + + //set marks to 0 if no title? + $this->weights = array(); + } + } + + /* + if ($test_obj['random'] && !$test_obj['num_questions']) { + $missing_fields[] = _AT('num_questions_per_test'); + } + + if ($test_obj['pass_score']==1 && !$test_obj['passpercent']) { + $missing_fields[] = _AT('percentage_score'); + } + + if ($test_obj['pass_score']==2 && !$test_obj['passscore']) { + $missing_fields[] = _AT('points_score'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + */ + + $day_start = intval(date('j')); + $month_start= intval(date('n')); + $year_start = intval(date('Y')); + $hour_start = intval(date('G')); + $min_start = intval(date('i')); + + $day_end = $day_start; + $month_end = $month_start; + $year_end = $year_start; //as of Oct 21,09. Check http://www.atutor.ca/atutor/mantis/view.php?id=3961 + $hour_end = $hour_start; + $min_end = $min_start; + + if (!checkdate($month_start, $day_start, $year_start)) { + $msg->addError('START_DATE_INVALID'); + } + + if (!checkdate($month_end, $day_end, $year_end)) { + $msg->addError('END_DATE_INVALID'); + } + + if (mktime($hour_end, $min_end, 0, $month_end, $day_end, $year_end) < + mktime($hour_start, $min_start, 0, $month_start, $day_start, $year_start)) { + $msg->addError('END_DATE_INVALID'); + } + + if (!$msg->containsErrors()) { + if (strlen($month_start) == 1){ + $month_start = "0$month_start"; + } + if (strlen($day_start) == 1){ + $day_start = "0$day_start"; + } + if (strlen($hour_start) == 1){ + $hour_start = "0$hour_start"; + } + if (strlen($min_start) == 1){ + $min_start = "0$min_start"; + } + + if (strlen($month_end) == 1){ + $month_end = "0$month_end"; + } + if (strlen($day_end) == 1){ + $day_end = "0$day_end"; + } + if (strlen($hour_end) == 1){ + $hour_end = "0$hour_end"; + } + if (strlen($min_end) == 1){ + $min_end = "0$min_end"; + } + + $start_date = "$year_start-$month_start-$day_start $hour_start:$min_start:00"; + $end_date = "$year_end-$month_end-$day_end $hour_end:$min_end:00"; + + //If title exceeded database defined length, truncate it. + $test_obj['title'] = validate_length($test_obj['title'], 100); + + $sql_params = array ( $_SESSION['course_id'], + $test_obj['title'], + $test_obj['description'], + $test_obj['format'], + $start_date, + $end_date, + $test_obj['order'], + $test_obj['num_questions'], + $test_obj['instructions'], + $test_obj['content_id'], + $test_obj['passscore'], + $test_obj['passpercent'], + $test_obj['passfeedback'], + $test_obj['failfeedback'], + $test_obj['result_release'], + $test_obj['random'], + $test_obj['difficulty'], + $test_obj['num_takes'], + $test_obj['anonymous'], + '', + $test_obj['allow_guests'], + $test_obj['display']); + + $sql = vsprintf(AT_SQL_TEST, $sql_params); + $result = mysql_query($sql, $db); + $tid = mysql_insert_id($db); + //debug($qti_import->weights, 'weights'); + } + return $tid; + } + + + /* + * Match the XML files to the actual files found in the content, then copy the media + * over to the content folder based on the actual links. *The XML file names might not be right. + * @param array The list of file names provided by the manifest's resources + * @param array The list of relative files that is used in the question contents. Default empty. + */ + function copyMedia($files, $xml_items = array()){ + global $msg; + foreach($files as $file_num => $file_loc){ + //skip test xml files + if (preg_match('/tests\_[0-9]+\.xml/', $file_loc)){ + continue; + } + + $new_file_loc =''; + + /** + Use the items list to handle and check which path it is from, so then it won't blindly truncate 'resource/' from the path + - For any x in xml_files, any y in new_file_loc, any k in the set of strings; such that k concat x = y, then use y, else use x + - BUG: Same filename fails. If resource/folder1/file1.jpg, and resource/file1.jpg, both will get replaced with file1.jpg + */ + if(!empty($xml_items)){ + foreach ($xml_items as $xk=>$xv){ + if (($pos = strpos($file_loc, $xv))!==false){ + //address the bug mentioned aboe. + //check if there is just one level of directory in this extra path. + //based on the assumption that most installation are just using 'resources/' or '{FOLDER_NAME}/' + $shortened = substr($file_loc, 0, $pos); + $num_of_occurrences = explode('/', $shortened); + if (sizeof($num_of_occurrences) == 2){ + $new_file_loc = $xv; + break; + } + } + } + } + + if ($new_file_loc==''){ + $new_file_loc = $file_loc; + } + + //Check if the file_loc has been changed, if not, don't move it, let ims class to handle it + //we only want to touch the files that the test/surveys use + if ($new_file_loc!=$file_loc){ + //check if new folder is there, if not, create it. + createDir(AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$new_file_loc ); + + //copy files over + // if (rename(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$file_loc, + // AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$package_base_name.'/'.$new_file_loc) === false) { + //overwrite files + if (file_exists(AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$new_file_loc)){ + unlink(AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$new_file_loc); + } + if (file_exists(AT_CONTENT_DIR.'import/'.$_SESSION['course_id'].'/'.$file_loc)){ + if (copy(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id'].'/'.$file_loc, + AT_CONTENT_DIR .$_SESSION['course_id'].'/'.$new_file_loc) === false) { + //TODO: Print out file already exist error. + if (!$msg->containsErrors()) { + // $msg->addError('FILE_EXISTED'); + } + } + } + } + } + } +} +?> \ No newline at end of file diff --git a/mods/_core/imsqti/classes/QTIParser.class.php b/mods/_core/imsqti/classes/QTIParser.class.php new file mode 100644 index 000000000..18b3f549c --- /dev/null +++ b/mods/_core/imsqti/classes/QTIParser.class.php @@ -0,0 +1,466 @@ +qti_type = $qti_type; + $this->parser = xml_parser_create(); + + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($this->parser, 'startElement', 'endElement'); + xml_set_character_data_handler($this->parser, 'characterData'); + } + + // public + // @return true if parsed successfully, false otherwise + function parse($xml_data) { + $this->element_path = array(); + $this->character_data = ''; + xml_parse($this->parser, $xml_data, TRUE); + + //Loop thru each item and replace if existed + foreach ($this->answers_for_matching as $afm_k => $afk_v){ + if (!empty($this->answers_for_matching[$afm_k])){ + $this->answers[$afm_k] = $afk_v; + } + } + + if(in_array('questestinterop', $this->element_path) || + in_array('assessment', $this->element_path)){ + //this is a v2.1+ package + return false; + } else { + return true; + } + } + + // private + function startElement($parser, $name, $attributes) { + global $msg; +// debug($attributes, $name ); + //save attributes. + switch($name) { + case 'section': + $this->title = $attributes['title']; + break; + case 'response_lid': + if ($this->response_type[$this->item_num] <= 0) { + $this->response_type[$this->item_num] = AT_QTI_REPONSE_LID; + } + case 'response_grp': + if ($this->response_type[$this->item_num] <= 0) { + $this->response_type[$this->item_num] = AT_QTI_REPONSE_GRP; + } + case 'response_str': + $this->attributes[$this->item_num][$name]['ident'] = $attributes['ident']; + $this->attributes[$this->item_num][$name]['rcardinality'] = $attributes['rcardinality']; + if ($this->response_type[$this->item_num] <= 0) { + $this->response_type[$this->item_num] = AT_QTI_REPONSE_STR; + } + break; + case 'response_label': + if(!isset($this->choices[$this->item_num][$attributes['ident']])){ + if (!is_array($this->response_label[$this->item_num])){ + $this->response_label[$this->item_num] = array(); + } + array_push($this->response_label[$this->item_num], $attributes['ident']); + } + break; + case 'varequal': + $this->attributes[$this->item_num][$name]['respident'] = $attributes['respident']; + break; + case 'setvar': + $this->attributes[$this->item_num][$name]['varname'] = $attributes['varname']; + break; + case 'render_choice': + $this->attributes[$this->item_num][$name]['shuffle'] = $attributes['shuffle']; + $this->attributes[$this->item_num][$name]['minnumber'] = $attributes['minnumber']; + $this->attributes[$this->item_num][$name]['maxnumber'] = $attributes['maxnumber']; + break; + case 'render_fib': + $rows = intval($attributes['rows']); + $property = 1; + + //1,2,3,4 according to tools/tests/create_question_long.php + if ($rows == 1){ + $property = 2; + } elseif ($rows > 1 && $rows <= 5){ + $property = 3; + } elseif ($rows > 5){ + $property = 4; + } + $this->attributes[$this->item_num][$name]['property'] = $property; + break; + case 'matimage': + $this->attributes[$this->item_num][$name]['imagtype'] = $attributes['imagtype']; + $this->attributes[$this->item_num][$name]['uri'] = $attributes['uri']; + break; + case 'mataudio': + $this->attributes[$this->item_num][$name]['audiotype'] = $attributes['audiotype']; + $this->attributes[$this->item_num][$name]['uri'] = $attributes['uri']; + break; + case 'matvideo': + $this->attributes[$this->item_num][$name]['videotype'] = $attributes['videotype']; + $this->attributes[$this->item_num][$name]['uri'] = $attributes['uri']; + break; + case 'matapplet': + $this->attributes[$this->item_num][$name]['uri'] = $attributes['uri']; + $this->attributes[$this->item_num][$name]['width'] = intval($attributes['width']); + $this->attributes[$this->item_num][$name]['height'] = intval($attributes['height']); + break; + case 'setvar': + $this->attributes[$this->item_num][$name]['varname'] = $attributes['varname']; + $this->attributes[$this->item_num][$name]['action'] = $attributes['action']; + break; + case 'itemproc_extension': + if (preg_match('/imsqti_xmlv1p2\/imscc_xmlv1p0(.*)/', $this->qti_type)){ + $msg->addError('QTI_WRONG_PACKAGE'); + } + break; + } + array_push($this->element_path, $name); + } + + // private + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) { + global $msg; + //check element path + $current_pos = count($this->element_path) - 1; + $last_element = $this->element_path[$current_pos - 1]; + + switch($name) { + case 'item': + $this->item_num++; + break; + case 'mattext': + $this->mat_content[$this->item_num] .= $this->reconstructRelativePath($this->character_data); + break; + case 'matimage': + $this->mat_content[$this->item_num] .= 'Image Not loaded:'.$this->attributes[$this->item_num][$name]['uri'].''; + break; + case 'mataudio': + $this->mat_content[$this->item_num] .= '<bgsound src="'.$this->attributes[$this->item_num][$name]['uri'].'">'; + break; + case 'matvideo': + if ($this->attributes[$this->item_num][$name]['videotype'] == 'type/swf'){ + $this->mat_content[$this->item_num] .= ''; + } elseif ($this->attributes[$this->item_num][$name]['videotype'] == 'type/mov'){ + $this->mat_content[$this->item_num] .= ''; + } + break; + case 'matapplet': + (($this->attributes[$this->item_num][$name]['width'] != 0)? $width = $this->attributes[$this->item_num][$name]['width'] : $width = 460); + (($this->attributes[$this->item_num][$name]['height'] != 0)? $height = $this->attributes[$this->item_num][$name]['height'] : $height = 160); + $this->mat_content[$this->item_num] .= ''; + break; + case 'material': + //check who is mattext's ancestor, started from the most known inner layer + if (in_array('response_label', $this->element_path)){ + if(!in_array($this->mat_content, $this->choices)){ + //This is one of the choices. + if (!empty($this->response_label[$this->item_num])){ + $this->choices[$this->item_num][array_pop($this->response_label[$this->item_num])] = $this->mat_content[$this->item_num]; + } + } + } elseif (in_array('response_grp', $this->element_path) || in_array('response_lid', $this->element_path)){ + //for matching, where there are groups + //keep in mind that Respondus handles this by using response_lid + $this->groups[$this->item_num][] = $this->reconstructRelativePath($this->mat_content[$this->item_num]); +// debug($this->character_data, 'harris - groups'); + } elseif (in_array('presentation', $this->element_path)){ + $this->question[$this->item_num] = $this->reconstructRelativePath($this->mat_content[$this->item_num]); + } elseif (in_array('itemfeedback', $this->element_path)){ + $this->feedback[$this->item_num] = $this->mat_content[$this->item_num]; + } + //once material is closed, reset the mat_content variable. + $this->mat_content[$this->item_num] = ''; + break; + case 'varequal': + //stores the answers (either correct or incorrect) into a stack + $this->temp_answer[$this->attributes[$this->item_num][$name]['respident']]['name'][] = $this->character_data; + //responses handling, remember to save the answers or match them up + if (!is_array($this->answers[$this->item_num])){ + $this->answers[$this->item_num] = array(); + } + array_push($this->answers[$this->item_num], $this->reconstructRelativePath($this->character_data)); + break; + case 'setvar': + $this->temp_answer[$this->attributes[$this->item_num]['varequal']['respident']]['value'][] = $this->character_data; + $this->temp_answer[$this->attributes[$this->item_num]['varequal']['respident']]['attribute'][] = $this->attributes[$this->item_num]['setvar']['varname']; + break; + case 'respcondition': + if (empty($this->temp_answer)) { + break; + } + + //closing this tag means a selection of choices have ended. Assign the correct answer in this case. + $tv = $this->temp_answer[$this->attributes[$this->item_num]['varequal']['respident']]; +// debug($tv, 'harris'.$this->item_num); +// debug($this->answers_for_matching[$this->item_num], 'answers'); + + //If matching, then attribute = 'Respondus_correct'; otherwise it is 'que_score' + if ($this->getQuestionType($this->item_num) == 5){ + if ($tv['answerAdded']!=true && !empty($tv['attribute'])){ + foreach ($tv['attribute'] as $att_id => $att_value){ + //Handles Respondus' (and blakcboard, angels, etc) responses schemas + if (strtolower($att_value)=='respondus_correct'){ + //Then this is the right answer + if (!is_array($this->answers_for_matching[$this->item_num])){ + $this->answers_for_matching[$this->item_num] = array(); + } + //The condition here is to check rather the answers have been duplicated, otherwise the indexing won't be right. + //sizeof[answers] != sizeof[questions], then the index matching is wrong. + //Created a problem though, which is then many-to-1 matching fails, cuz answers will be repeated. + //Sep 2,08, Fixed by adding a flag into the array + // if (!in_array($tv['name'][$att_id], $this->answers_for_matching[$this->item_num])){ + array_push($this->answers_for_matching[$this->item_num], $tv['name'][$att_id]); + $this->temp_answer[$this->attributes[$this->item_num]['varequal']['respident']]['answerAdded'] = true; + + //add mark + $this->weights[$this->item_num] = floatval($tv['value'][$att_id]); + // } + break; + } + } + } + } else { + $pos = sizeof($tv['value']) - 1; //position of the last entry of the "temp answer's value" array + //Retrieve the last entry of the "temp answer's value" array + $current_answer = $tv['value'][$pos]; + if (floatval($current_answer) > 0){ + if (!is_array($this->answers_for_matching[$this->item_num])){ + $this->answers_for_matching[$this->item_num] = array(); + } +// if (!in_array($tv['name'][$val_id], $this->answers_for_matching[$this->item_num])){ + array_push($this->answers_for_matching[$this->item_num], $tv['name'][$pos]); + + //add mark + $this->weights[$this->item_num] += floatval($current_answer); +// } + } + } + break; + case 'fieldlabel': + $this->field_label[$this->item_num] = $this->character_data; + break; + case 'fieldentry': + $this->field_entry[$this->item_num][$this->field_label[$this->item_num]] = $this->character_data; + break; + case 'qmd_itemtype': + //Deprecated as of QTI 1.2. + if (empty($this->field_entry[$this->item_num][$name])){ + $this->field_entry[$this->item_num][$name] = $this->character_data; + } + break; + default: + break; + } +// debug($this->element_path, "Ele Path"); + + //pop stack and reset character data, o/w it will stack up + array_pop($this->element_path); + $this->character_data = ''; + } + + // private + function characterData($parser, $data){ + global $addslashes; + if (trim($data)!=''){ + $this->character_data .= $addslashes(preg_replace('/[\t\0\x0B(\r\n)]*/', '', $data)); +// $this->character_data .= trim($data); + } + } + + /* + * This function returns the question type of this XML. + * @access public + * @param the item_num + * @return 1-8, in the order of m/c, t/f, open eneded, likert, s match, order, m/a, g match + false for not found. + */ + function getQuestionType($item_num){ + switch ($this->field_entry[$item_num]['qmd_questiontype']){ + case 'Multiple-choice': + //1, 4 + //likert have no answers + if (empty($this->answers)){ + return 4; + } + return 1; + break; + case 'True/false': + return 2; + break; + case 'FIB-string': + return 3; + break; + case 'Drag-and-drop': + return 5; + break; + case 'Multiple-response': + return 7; + break; + } + + switch ($this->field_entry[$item_num]['qmd_itemtype']){ + case 'Matching': + //matching + return 5; + break; + } + + //handles CC packages + switch ($this->field_entry[$item_num]['cc_profile']){ + case 'cc.multiple_choice.v0p1': + return 1; + break; + case 'cc.true_false.v0p1': + return 2; + break; + case 'cc.fib.v0p1': + return 3; + break; + case 'cc.multiple_response.v0p1': + return 7; + break; + } + + + //Check if this is an ordering, or matching + $response_obj; + switch ($this->response_type[$item_num]){ + case AT_QTI_REPONSE_LID: + $response_obj = $this->attributes[$item_num]['response_lid']; + break; + case AT_QTI_REPONSE_GRP: + $response_obj = $this->attributes[$item_num]['response_grp']; + break; + case AT_QTI_REPONSE_STR: + $response_obj = $this->attributes[$item_num]['response_str']; + return 3; //no need to parse the rcardinality? + break; + } + if ($response_obj['rcardinality'] == 'Ordered'){ + return 6; + } elseif ($response_obj['rcardinality'] == 'Multiple'){ + //TODO Multiple answers, Simple matching and Graphical matching + if (empty($this->field_entry[$item_num])){ + return 7; + } + return 5; + } elseif ($response_obj['rcardinality'] == 'Single'){ + return 1; //assume mc + } + + //None found. + return false; + } + + + //set relative path + //must be used before calling parse. Otherwise it will be null. + //private + function setRelativePath($path){ + if ($path != ''){ + if ($path[-1] != '/'){ + $path .= '/'; + } + $this->relative_path = $path; + } + } + + + //private + //when importing, the path of the images are all changed. Have to parse them out and add the extra path in. + //No longer needed to reconstruct, just needed to save the path, as of Aug 25th, 08. Decided to overwrite files if the same name exist. + function reconstructRelativePath($path){ + //match img tag, all. +// if (preg_match_all('/\])*\ssrc\=[\\\\]?\"([^\\\\^\"]+)[\\\\]?\".*\/?\>/i', $path, $matches) > 0){ +//fixes multiple image tags within a $path + if (preg_match_all('/\ 0){ + foreach ($matches[2] as $k=>$v){ + if(strpos($v, 'http://')===false && !in_array($v, $this->items)) { + $this->items[] = $v; //save the url of this media. + // $path = str_replace($v, $this->relative_path.$v, $path); + } + } + return $path; + } elseif (preg_match_all('/\])*\ssrc\=[\\\\]?\"([^\\\\^\"]+)[\\\\]?\".*/i', $path, $matches) > 0){ + foreach ($matches[2] as $k=>$v){ + if(strpos($v, 'http://')===false && !in_array($v, $this->items)) { + $this->items[] = $v; //save the url of this media. + // $path = str_replace($v, $this->relative_path.$v, $path); + } + } + return $path; + } else { + return $path; + } + } + + + //public + function close(){ + //Free the XML parser + unset($this->response_label); + unset($this->field_label); + unset($this->temp_answer); + xml_parser_free($this->parser); + } + +} + +?> \ No newline at end of file diff --git a/mods/_core/imsqti/lib/qti.inc.php b/mods/_core/imsqti/lib/qti.inc.php new file mode 100644 index 000000000..f151e63cc --- /dev/null +++ b/mods/_core/imsqti/lib/qti.inc.php @@ -0,0 +1,80 @@ +$attrs){ + if ($attrs['type'] == 'imsqti_xmlv1p1' || $attrs['type'] == 'imsqti_item_xmlv2p1'){ + //loop through the file array + foreach($attrs['file'] as $file_id => $file_name){ + $file_pathinfo = pathinfo($file_name); +// if ($file_pathinfo['basename'] == $attrs['href']){ +// //This file will be parsed later +// continue; +// } + + if (in_array($file_pathinfo['extension'], $supported_media_type)){ + //check media + if (file_exists(AT_CONTENT_DIR . $_SESSION['course_id'] . '/' . $file_name)){ + $existing_files[] = $file_name; + } + } + } + } + } + return $existing_files; +} +?> \ No newline at end of file diff --git a/mods/_core/languages/classes/Language.class.php b/mods/_core/languages/classes/Language.class.php new file mode 100644 index 000000000..8f0280be4 --- /dev/null +++ b/mods/_core/languages/classes/Language.class.php @@ -0,0 +1,220 @@ +db = $db; + + if (is_array($language_row)) { + $this->code = $language_row['language_code']; + $this->characterSet = $language_row['char_set']; + $this->direction = $language_row['direction']; + $this->regularExpression = $language_row['reg_exp']; + $this->nativeName = $language_row['native_name']; + $this->englishName = $language_row['english_name']; + $this->status = $language_row['status']; + $this->atutor_version = isset($language_row['version']) ? $language_row['version'] : VERSION; + + } else if (is_object($language_row)) { + $this->cloneThis($language_row); + } + } + + // private + // copies the properties from $from to $this Object + function cloneThis($from) { + $vars = get_object_vars($from); + foreach ($vars as $key => $value) { + $this->$key = $value; + } + } + + // returns whether or not the $search_string matches the regular expression + function isMatchHttpAcceptLanguage($search_string) { + return preg_match('/^(' . $this->regularExpression . ')(;q=[0-9]\\.[0-9])?$/', $search_string); + } + + // returns boolean whether or not $search_string is in HTTP_USER_AGENT + function isMatchHttpUserAgent($search_string) { + return preg_match('/(\(|\[|;[\s])(' . $this->regularExpression . ')(;|\]|\))/', $search_string); + + } + + function getCode() { + return $this->code; + } + + function getCharacterSet() { + return $this->characterSet; + } + + function getDirection() { + return $this->direction; + } + + function getRegularExpression() { + return $this->regularExpression; + } + + function getAtutorVersion() { + return $this->atutor_version; + } + + function getTranslatedName() { + if ($this->code == $_SESSION['lang']) { + return $this->nativeName; + } + // this code has to be translated: + return _AT('lang_' . str_replace('-', '_', $this->code)); + } + + function getNativeName() { + return $this->nativeName; + } + + function getEnglishName() { + return $this->englishName; + } + + function getStatus() { + return $this->status; + } + + + // public + function sendContentTypeHeader() { + header('Content-Type: text/html; charset=' . $this->characterSet); + } + + // public + function saveToSession() { + $_SESSION['lang'] = $this->code; + } + + /* + * public + * @param member_id or login for members and admin respectively + * @param 1 for admin, 0 for members, all other integers are ignored. + */ + function saveToPreferences($id, $is_admin) { + global $db; + if ($id) { + if ($is_admin === 0) { + $sql = "UPDATE ".TABLE_PREFIX."members SET language='".$this->code."', creation_date=creation_date, last_login=last_login WHERE member_id=$id"; + } elseif ($is_admin === 1) { + $sql = "UPDATE ".TABLE_PREFIX."admins SET language='".$this->code."', last_login=last_login WHERE login='$id'"; + } + mysql_query($sql,$db); + } + } + + // public + // returns whether or not this language is right-to-left + // possible langues are: arabic, farsi, hebrew, urdo + function isRTL() { + if ($this->direction == 'rtl') { + return true; + } // else: + + return false; + } + + // public + // can be called staticly + function getParentCode($code = '') { + if (!$code && isset($this)) { + $code = $this->code; + } + $peices = explode(AT_LANGUAGE_LOCALE_SEP, $code, 2); + return $peices[0]; + } + + // public + // can be called staticly + function getLocale($code = '') { + if (!$code && isset($this)) { + $code = $this->code; + } + $peices = explode(AT_LANGUAGE_LOCALE_SEP, $code, 2); + return $peices[1]; + } + + + // public + function getTerm($term) { + $sql = "SELECT *, UNIX_TIMESTAMP(L.revised_date) AS revised_date_unix FROM ".TABLE_PREFIX."language_text L WHERE L.language_code='".$this->getCode()."' AND L.variable='_template' AND L.term='$term'"; + + $result = mysql_query($sql, $this->db); + $row = mysql_fetch_assoc($result); + return $row; + } + + function getXML($part=FALSE) { + if (!$part) { + $xml = ' + + + + + + + + + + + + + ]>'; + } + + $xml .= ' + '.VERSION.' + '.$this->characterSet.' + '.$this->direction.' + '.$this->regularExpression.' + '.$this->nativeName.' + '.$this->englishName.' + '.$this->status.' + '; + + return $xml; + } +} +?> \ No newline at end of file diff --git a/mods/_core/languages/classes/LanguageEditor.class.php b/mods/_core/languages/classes/LanguageEditor.class.php new file mode 100644 index 000000000..8a76ca888 --- /dev/null +++ b/mods/_core/languages/classes/LanguageEditor.class.php @@ -0,0 +1,435 @@ +msg = $msg; + + $this->addslashes = $addslashes; + + if (isset($myLang)) { + $this->Language($myLang); + } + $this->missingTerms = array(); + } + + /** + * Inserts a new language def'n into the database. + * @access public + * @param array $row The language def'n fields as an assoc array. + * @return boolean Returns TRUE if the def'n was inserted correctly, + * or FALSE, otherwise. + * call staticly only! + */ + function addLanguage($row, $db) { + global $addslashes; + global $msg; + + $row['code'] = trim($row['code']); + $row['locale'] = trim($row['locale']); + $row['charset'] = trim($row['charset']); + $row['reg_exp'] = trim($row['reg_exp']); + $row['native_name'] = trim($row['native_name']); + $row['english_name'] = trim($row['english_name']); + + $missing_fields = array(); + + if ($row['code'] == '') { + $missing_fields[] = _AT('lang_code'); + } + if ($row['charset'] == '') { + $missing_fields[] = _AT('charset'); + } + if ($row['reg_exp'] == '') { + $missing_fields[] = _AT('reg_exp'); + } + if ($row['native_name'] == '') { + $missing_fields[] = _AT('name_in_language'); + } + if ($row['english_name'] == '') { + $missing_fields[] = _AT('name_in_english'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $row['code'] = $addslashes($row['code']); + $row['locale'] = $addslashes($row['locale']); + $row['charset'] = $addslashes($row['charset']); + $row['direction'] = $addslashes($row['direction']); + $row['reg_exp'] = $addslashes($row['reg_exp']); + $row['native_name'] = $addslashes($row['native_name']); + $row['english_name'] = $addslashes($row['english_name']); + + if (!empty($row['locale'])) { + $row['code'] .= AT_LANGUAGE_LOCALE_SEP . strtolower($row['locale']); + } + + $sql = "INSERT INTO ".TABLE_PREFIX."languages VALUES ('$row[code]', '$row[charset]', '$row[direction]', '$row[reg_exp]', '$row[native_name]', '$row[english_name]', 3)"; + + if (mysql_query($sql, $db)) { + return TRUE; + } else { + return FALSE; + } + } + + return FALSE; + } + + // public + // $row = the language info array + // $new_exists whether the new code+locale exists already + // returns true or false, depending on success if db update + // can be called staticly + function updateLanguage($row, $new_exists) { + $missing_fields = array(); + + if ($row['code'] == '') { + $missing_fields[] = _AT('lang_code'); + } + if ($row['charset'] == '') { + $missing_fields[] = _AT('charset'); + } + if ($row['reg_exp'] == '') { + $missing_fields[] = _AT('reg_exp'); + } + if ($row['native_name'] == '') { + $missing_fields[] = _AT('name_in_language'); + } + if ($row['english_name'] == '') { + $missing_fields[] = _AT('name_in_english'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + + if (!$this->msg->containsErrors()) { + global $addslashes; + global $db; + + $row['code'] = strtolower($addslashes($row['code'])); + if (!empty($row['locale'])) { + $row['code'] .= AT_LANGUAGE_LOCALE_SEP . strtolower($addslashes($row['locale'])); + } + $row['charset'] = strtolower($addslashes($row['charset'])); + $row['direction'] = strtolower($addslashes($row['direction'])); + $row['reg_exp'] = strtolower($addslashes($row['reg_exp'])); + $row['native_name'] = $addslashes($row['native_name']); + $row['english_name'] = $addslashes($row['english_name']); + if (isset($row['status'])) { + $row['status'] = intval($row['status']); + $status_sql = ', status='.$row['status']; + } else { + $status_sql = ''; + } + + if ($row['old_code'] == $row['code']) { + $sql = "UPDATE ".TABLE_PREFIX."languages SET char_set='$row[charset]', direction='$row[direction]', reg_exp='$row[reg_exp]', native_name='$row[native_name]', english_name='$row[english_name]' $status_sql WHERE language_code='$row[code]'"; + mysql_query($sql, $db); + + return TRUE; + } else if ($new_exists) { + $this->msg->addError('LANG_EXISTS'); + return FALSE; + } else { + $sql = "UPDATE ".TABLE_PREFIX."languages SET language_code='$row[code]', char_set='$row[charset]', direction='$row[direction]', reg_exp='$row[reg_exp]', native_name='$row[native_name]', english_name='$row[english_name]' $status_sql WHERE language_code='$row[old_code]'"; + mysql_query($sql, $db); + + $sql = "UPDATE ".TABLE_PREFIX."language_text SET language_code='$row[code]' WHERE language_code='$row[old_code]'"; + mysql_query($sql, $db); + + return TRUE; + } + + } + return FALSE; + } + + function deleteLanguage() { + $sql = "DELETE FROM ".TABLE_PREFIX."languages WHERE language_code='$this->code'"; + mysql_query($sql, $this->db); + + $sql = "DELETE FROM ".TABLE_PREFIX."language_text WHERE language_code='$this->code'"; + mysql_query($sql, $this->db); + + $sql = "UPDATE ".TABLE_PREFIX."members SET language='".DEFAULT_LANGUAGE."', creation_date=creation_date, last_login=last_login WHERE language='$this->code'"; + mysql_query($sql, $this->db); + + $sql = "UPDATE ".TABLE_PREFIX."courses SET primary_language='".DEFAULT_LANGUAGE."' WHERE primary_language='$this->code'"; + mysql_query($sql, $this->db); + + cache_purge('system_langs', 'system_langs'); + } + + // public + function updateTerm($variable, $term, $text) { + $addslashes = $this->addslashes; + + $variable = $addslashes($variable); + $term = $addslashes($term); + $text = $addslashes($text); + $code = $addslashes($this->getCode()); + + $sql = "UPDATE ".TABLE_PREFIX."language_text SET text='$text', revised_date=NOW() WHERE language_code='$code' AND variable='$variable' AND term='$term'"; + + /* + if (mysql_query($sql, $this->db)) { + return TRUE; + } else { + debug(mysql_error($this->db)); + return FALSE; + } + */ + } + + // public + function insertTerm($variable, $key, $text, $context) { + $addslashes = $this->addslashes; + + $variable = $addslashes($variable); + $key = $addslashes($key); + $text = $addslashes($text); + $code = $addslashes($this->getCode()); + $context = $addslashes($context); + + $sql = "INSERT INTO ".TABLE_PREFIX."language_text VALUES('$code', '$variable', '$key', '$text', NOW(), '$context')"; + } + + // public + function showMissingTermsFrame(){ + global $_base_path, $addslashes; + //$terms = array_slice($this->missingTerms, 0, 20); + $terms = $this->missingTerms; + $terms = serialize($terms); + $terms = urlencode($terms); + + echo '
        +
        + + + + + + + + + \ No newline at end of file diff --git a/mods/_core/languages/language_import.php b/mods/_core/languages/language_import.php new file mode 100644 index 000000000..dbb59f8fe --- /dev/null +++ b/mods/_core/languages/language_import.php @@ -0,0 +1,103 @@ +import($_POST['language']); + header('Location: language_import.php'); + exit; +} else if (isset($_POST['submit']) && (!is_uploaded_file($_FILES['file']['tmp_name']) || !$_FILES['file']['size'])) { + $msg->addError('LANG_IMPORT_FAILED'); +} else if (isset($_POST['submit']) && !$_FILES['file']['name']) { + $msg->addError('IMPORTFILE_EMPTY'); +} else if (isset($_POST['submit']) && is_uploaded_file($_FILES['file']['tmp_name'])) { + $languageManager->import($_FILES['file']['tmp_name']); + header('Location: ./language_import.php'); + exit; +} + +?> + + +
        +
        +
        +

        +
        + +
        +
        + +
        + +
        + +
        +
        +
        + + +
        +
        +
        + +
        + +
        + getNumLanguages()) { + $found = false; + foreach ($remoteLanguageManager->getAvailableLanguages() as $codes){ + $language = current($codes); + if (!$languageManager->exists($language->getCode()) && ($language->getStatus() == AT_LANG_STATUS_PUBLISHED)) { + if (!$found) { + echo '
        '; + echo '
        '; + } else { + echo _AT('none_found'); + echo '
        '; + } + } else { + echo _AT('cannot_find_remote_languages'); + echo ''; + } + ?> + +
        + + + \ No newline at end of file diff --git a/mods/_core/languages/language_term.php b/mods/_core/languages/language_term.php new file mode 100644 index 000000000..78f347939 --- /dev/null +++ b/mods/_core/languages/language_term.php @@ -0,0 +1,102 @@ + + + +
        + + + +
        +
        +

        + +

        + + + +

        +

        + + + + +
        + +
        + 10) { + echo ''._AT('global_more_than_10_pages').''; + } else { + echo '
          '; + while ($page_row = mysql_fetch_assoc($result)) { + echo '
        • '.$page_row['page'] . '
        • '; + } + echo '
        '; + } + ?> +
        + +
        + + + + +
        +
        +
        + +

        + + + \ No newline at end of file diff --git a/mods/_core/languages/language_translate.php b/mods/_core/languages/language_translate.php new file mode 100644 index 000000000..198e6441b --- /dev/null +++ b/mods/_core/languages/language_translate.php @@ -0,0 +1,72 @@ +liveImport($addslashes($_POST['import_lang'])); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +$button_state = ''; +if (!defined('AT_DEVEL_TRANSLATE') || !AT_DEVEL_TRANSLATE) { + $button_state = 'disabled="disabled"'; +} + +?> + +
        +
        +
        +

        +
        + +
        +

        +
        + +
        + /> +
        +
        +
        + + +
        +
        +
        + Import partial language from the live ATutor language database to your local installation for translating. +
        +
        + printDropdown($_SESSION['lang'], 'import_lang', 'import_lang'); + ?> +
        + +
        + +
        +
        +
        + + + \ No newline at end of file diff --git a/mods/_core/languages/missing_language.php b/mods/_core/languages/missing_language.php new file mode 100644 index 000000000..15e4dcb0c --- /dev/null +++ b/mods/_core/languages/missing_language.php @@ -0,0 +1,38 @@ + -1) { exit; } + +if (isset($_POST['submit'])) { + unset($_POST['g']); + unset($_POST['submit']); + $langEditor->updateTerms($_POST); +} + +$params = array(); +if ($_POST['filter_new']) { + $params['new'] = true; +} +if ($_POST['filter_update']) { + $params['update'] = true; +} +$langEditor->setFilter($params); + + +$langEditor->printTerms($_GET['terms']); + + +?> \ No newline at end of file diff --git a/mods/_core/languages/module.php b/mods/_core/languages/module.php new file mode 100644 index 000000000..783938d08 --- /dev/null +++ b/mods/_core/languages/module.php @@ -0,0 +1,41 @@ +getAdminPrivilege()); + +if (admin_authenticate(AT_ADMIN_PRIV_LANGUAGES, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + if (admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + $this->_pages['admin/config_edit.php']['children'] = array('mods/_core/languages/language.php'); + $this->_pages['mods/_core/languages/language.php']['parent'] = 'admin/config_edit.php'; + } else { + $this->_pages[AT_NAV_ADMIN] = array('mods/_core/languages/language.php'); + $this->_pages['mods/_core/languages/language.php']['parent'] = AT_NAV_ADMIN; + } + + //admin + $this->_pages['mods/_core/languages/language.php']['title_var'] = 'languages'; + $this->_pages['mods/_core/languages/language.php']['guide'] = 'admin/?p=languages.php'; + $this->_pages['mods/_core/languages/language.php']['children'] = array('mods/_core/languages/language_import.php', 'mods/_core/languages/language_editor.php', 'mods/_core/languages/language_translate.php'); + + $this->_pages['mods/_core/languages/language_add.php']['title_var'] = 'add_language'; + $this->_pages['mods/_core/languages/language_add.php']['parent'] = 'mods/_core/languages/language.php'; + + $this->_pages['mods/_core/languages/language_edit.php']['title_var'] = 'edit_language'; + $this->_pages['mods/_core/languages/language_edit.php']['parent'] = 'mods/_core/languages/language.php'; + + $this->_pages['mods/_core/languages/language_delete.php']['title_var'] = 'delete_language'; + $this->_pages['mods/_core/languages/language_delete.php']['parent'] = 'mods/_core/languages/language.php'; + + $this->_pages['mods/_core/languages/language_import.php']['title_var'] = 'import'; + $this->_pages['mods/_core/languages/language_import.php']['parent'] = 'mods/_core/languages/language.php'; + + $this->_pages['mods/_core/languages/language_translate.php']['title_var'] = 'translate'; + $this->_pages['mods/_core/languages/language_translate.php']['parent'] = 'mods/_core/languages/language.php'; + + $this->_pages['mods/_core/languages/language_editor.php']['title_var'] = 'editor'; + $this->_pages['mods/_core/languages/language_editor.php']['parent'] = 'mods/_core/languages/language.php'; + + $this->_pages['mods/_core/languages/language_term.php']['title_var'] = 'editor'; +} +?> \ No newline at end of file diff --git a/mods/_core/languages/module.xml b/mods/_core/languages/module.xml new file mode 100644 index 000000000..dfa17047f --- /dev/null +++ b/mods/_core/languages/module.xml @@ -0,0 +1,23 @@ + + + Languages + Allows administrators to manage languages. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + + create + + 2005-09-19 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/languages/module_cron.php b/mods/_core/languages/module_cron.php new file mode 100644 index 000000000..5b9ff4061 --- /dev/null +++ b/mods/_core/languages/module_cron.php @@ -0,0 +1,28 @@ +getAvailableLanguages(); + + foreach ($languages as $codes) { + $language = current($codes); + if (($language->getStatus() == AT_LANG_STATUS_PUBLISHED) && !$languageManager->exists($language->getCode())) { + // language does not exist + + $remoteLanguageManager->import($language->getCode()); + } + } +} + +?> \ No newline at end of file diff --git a/mods/_core/languages/translate.php b/mods/_core/languages/translate.php new file mode 100644 index 000000000..e42bbf637 --- /dev/null +++ b/mods/_core/languages/translate.php @@ -0,0 +1,44 @@ + + + + +
        + + + + + + + + + + + +

        : |

        +
        diff --git a/mods/_core/languages/translate_atutor.php b/mods/_core/languages/translate_atutor.php new file mode 100644 index 000000000..59a4cf25a --- /dev/null +++ b/mods/_core/languages/translate_atutor.php @@ -0,0 +1,83 @@ + + + + + + ATutor Translator Site + + + + + + + +' . _AT('close_window') . ''; +echo '

        ATutor Translator Site

        '; + +$variables = array('_template','_msgs','_module'); + +$atutor_test = ''; + +$_SESSION['status'] = 2; +$_USER_ADMIN = $_SESSION['status']; + +$sql = "SELECT english_name, char_set FROM ".TABLE_PREFIX."languages WHERE language_code = '$_SESSION[language]'"; +$result = mysql_query($sql, $db); +$row = mysql_fetch_assoc($result); + +?> +
          +

        1. + + + + + +
          Translate
          + From English - iso-8859-1 to +
          +
        2. +
          + + + + + \ No newline at end of file diff --git a/mods/_core/languages/translator.php b/mods/_core/languages/translator.php new file mode 100644 index 000000000..2c02d98a8 --- /dev/null +++ b/mods/_core/languages/translator.php @@ -0,0 +1,605 @@ +Choose the New and Updated filters to display only language that has not been translated, or language that needs to be modified
          '; + + echo '
          Filter
          '; + echo '
          + + ,

          '; + echo ''; +} +?> + + +
        3. Choose Template, Msgs, or Modules to display a list of language variables. Click on a variable name to display its associated language. + +
          +
        4. + +
        5. +
          + +
          +
        6. +
        +
        + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        New
        Variable:
        Term:
        Context:
        text:
        +
        +The source language was not found for that item (try using the English source).

        '; + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + + if ($_SESSION['language'] == 'en') { + $row2 = $row; + } else { + $sql = "SELECT text FROM ".TABLE_PREFIX."language_text WHERE term='$_REQUEST[k]' AND variable='$_REQUEST[v]' AND language_code='$_SESSION[language]'"; + } + + $result = mysql_query($sql, $db); + $row2 = mysql_fetch_array($result); + +function trans_form($page) { + global $row0; + global $row; + global $row2; + global $langs; + global $success_error; + global $db; + global $_USER_ADMIN; + global $addslashes; + global $stripslashes; +?> +
        + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Edit
        Context:'; + } else { + if ($row['context'] == '') { + echo 'None specified.'; + } else { + echo $row['context']; + } + } ?> 
        Pages: 10) { + echo 'Global (more than 10 pages)'; + } else { + while ($page_row = mysql_fetch_array($result)) { + echo $page_row['page'] . '
        '; + } + } + + ?> +
        text:
        text:
        + +      + +
        +
        + + '; + + echo '
      • View All Terms'; + + if ($_REQUEST['page'] == 'all') { + echo ''; + display_all_terms($_REQUEST['v'], $_REQUEST['k'], $_REQUEST['f'], $_REQUEST['n'], $_REQUEST['u']); + } + echo '
      • '; + + echo '
      • View Unused Terms'; + + if ($_REQUEST['page'] == 'none') { + echo ''; + display_unused_terms($_REQUEST['v'], $_REQUEST['k'], $_REQUEST['f'], $_REQUEST['n'], $_REQUEST['u']); + } + echo '
      • '; + + $sql0 = "SELECT DISTINCT page FROM ".TABLE_PREFIX."language_pages ORDER BY page"; + $result0 = mysql_query($sql0, $db); + + while ($row0 = mysql_fetch_assoc($result0)) { + + if ($_REQUEST['page'] == $row0['page']) { + display_page_terms($_REQUEST['v'], $_REQUEST['k'], $_REQUEST['f'], $_REQUEST['n'], $_REQUEST['u'], $row0['page']); + } + else { + echo '
      • '.$row0['page'].'
      • '; + } + } + echo '
      '; + } else if (!$_REQUEST['search_term'] && ($_REQUEST['v'] == $variables[1])){ + //displaying messages + display_all_terms($_REQUEST['v'], $_REQUEST['k'], $_REQUEST['f'], $_REQUEST['n'], $_REQUEST['u']); + } else if (!$_REQUEST['search_term'] && ($_REQUEST['v'] == $variables[2])){ + display_all_terms($_REQUEST['v'], $_REQUEST['k'], $_REQUEST['f'], $_REQUEST['n'], $_REQUEST['u']); + } else if ($_REQUEST['search_term']) { + display_search_terms($_REQUEST['v'], $_REQUEST['k'], $_REQUEST['f'], $_REQUEST['n'], $_REQUEST['u']); + } + + +function delete_term($variable, $term) { + global $db; + + $sql = "DELETE FROM ".TABLE_PREFIX."language_text WHERE variable='$variable' AND term='$term'"; + $result = mysql_query($sql, $db); + + $sql3 = "DELETE FROM ".TABLE_PREFIX."language_pages WHERE term='$term'"; + $result3 = mysql_query($sql3, $db); + + unset($_REQUEST['k']); + echo '
      Success: deleted.
      '; +} + +function update_term($text, $context, $variable, $term) { + global $addslashes, $db; + + $term = $addslashes(trim($term)); + $text = $addslashes(trim($text)); + $context = $addslashes(trim($context)); + + if ($_SESSION['language'] == 'en') { + $sql = "UPDATE ".TABLE_PREFIX."language_text SET text='$text', revised_date=NOW(), context='$context' WHERE variable='$variable' AND term='$term' AND language_code='en'"; + } + + else { + $sql = "REPLACE INTO ".TABLE_PREFIX."language_text VALUES ('$_SESSION[language]', '$variable', '$term', '$text', NOW(), '')"; + + $trans = get_html_translation_table(HTML_ENTITIES); + $trans = array_flip($trans); + $sql = strtr($sql, $trans); + } + + $result = mysql_query($sql, $db); + + if (!$result) { + echo mysql_error($db); + echo '
      Error: changes not saved!
      '; + $success_error = '
      Error: changes not saved!
      '; + return $success_error; + } + else { + echo '
      Success: changes saved.
      '; + $success_error = '
      Success: changes saved.
      '; + return $success_error; + } +} + +function add_term($text, $context, $variable, $term) { + global $addslashes, $db; + + $term = $addslashes(trim($term)); + $text = $addslashes(trim($text)); + $context = $addslashes(trim($context)); + + $sql = "INSERT INTO ".TABLE_PREFIX."language_text VALUES ('en', '$variable', '$term', '$text', NOW(), '$context')"; + $result = mysql_query($sql, $db); + + if (!$result) { + echo '
      Error: that term already exists!
      '; + $success_error = ''; + } else { + echo '
      Success: term added.
      '; + $success_error = '
      Success: term added.
      '; + return $success_error; + } +} + +function display_page_terms ($variable, $term1, $lang_code, $new, $updated, $page) { + global $db; + + echo '
    • '; + echo ''.$page.''; + + $sql1 = "SELECT term FROM ".TABLE_PREFIX."language_pages WHERE page='$page' ORDER BY term"; + $result1 = mysql_query($sql1, $db); + + $term_list = array(); + + while ($row1 = mysql_fetch_assoc($result1)) { + + if ($_SESSION['language'] != 'en') { + $sql = "SELECT term, revised_date+0 AS r_date FROM ".TABLE_PREFIX."language_text WHERE variable='$variable' AND language_code='$_SESSION[language]' AND term='$row1[term]' ORDER BY term"; + $result = mysql_query($sql, $db); + + while ($row = mysql_fetch_assoc($result)) { + $t_keys[$row['term']] = $row['r_date']; + } + } + $term_list[] = $row1['term']; + } + + echo '
        '; + + foreach ($term_list as $term) { + + if ($_REQUEST['f'] == 'en') { + $sql = "SELECT *, revised_date+0 AS r_date FROM ".TABLE_PREFIX."language_text WHERE variable='$_REQUEST[v]' AND language_code='en' AND term='$term'"; + } else { + $sql = "SELECT * FROM ".TABLE_PREFIX."language_text WHERE variable='$_REQUEST[v]' AND language_code='$_REQUEST[f]' AND term='$term'"; + } + + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + if (mysql_num_rows($result) == 0) continue; + + if ($_SESSION['language'] != 'en') { + if ($new && $updated) { + if ((!($t_keys[$row['term']] == '')) && (!(($t_keys[$row['term']] < $row['r_date']) && $t_keys[$row['term']]))) { + continue; + } + } else if ($new) { + if (!($t_keys[$row['term']] == '')) { + continue; + } + } else if ($updated) { + if (!(($t_keys[$row['term']] < $row['r_date']) && $t_keys[$row['term']])) { + continue; + } + } + } + + if ($term == $term1) { + trans_form($page); + echo '
      • '; + } else { + echo '
      • '; + } + echo ''; + + if ($_SESSION['language'] != 'en') { + if ($t_keys[$row['term']] == '') { + echo '*New* '; + } else if ($t_keys[$term] < $row['r_date']) { + echo '*Updated* '; + } + } + + if ($term != $term1) { + echo ''; + echo $term; + echo ''; + } else { + echo $term; + } + echo ''; + echo '
      • '; + } + echo '
      '; + echo '
    • '; +} + +function display_all_terms ($variable, $term1, $lang_code, $new, $updated) { + global $db; + + if ($_SESSION['language'] != 'en') { + $sql = "SELECT term, revised_date+0 AS r_date FROM ".TABLE_PREFIX."language_text WHERE variable='$variable' AND language_code='$_SESSION[language]' ORDER BY term"; + $result = mysql_query($sql, $db); + + $t_keys = array(); + while ($row = mysql_fetch_assoc($result)) { + $t_keys[$row['term']] = $row['r_date']; + } + } + + if ($lang_code == 'en') { + $sql = "SELECT *, revised_date+0 AS r_date FROM ".TABLE_PREFIX."language_text WHERE variable='$variable' AND language_code='en' ORDER BY term"; + } else { + $sql = "SELECT * FROM ".TABLE_PREFIX."language_text WHERE variable='$variable' AND language_code='$lang_code' ORDER BY term"; + } + $result = mysql_query($sql, $db); + + echo '
        '; + while ($row = mysql_fetch_assoc($result)) { + if ($_SESSION['language'] != 'en') { + if ($new && $updated) { + if ((!($t_keys[$row['term']] == '')) && (!(($t_keys[$row['term']] < $row['r_date']) && $t_keys[$row['term']]))) { + continue; + } + } else if ($new) { + if (!($t_keys[$row['term']] == '')) { + continue; + } + } else if ($updated) { + if (!(($t_keys[$row['term']] < $row['r_date']) && $t_keys[$row['term']])) { + continue; + } + } + } + + + if ($row['term'] == $term1) { + trans_form('all'); + echo '
      • '; + + } else { + echo '
      • '; + } + echo ''; + if ($_SESSION['language'] != 'en') { + if ($t_keys[$row['term']] == '') { + echo '*New* '; + } else if ($t_keys[$row['term']] < $row['r_date']) { + echo '*Updated* '; + } + } + + if ($row['term'] != $term1) { + echo ''; + echo $row['term']; + echo ''; + } else { + echo $row['term']; + } + echo ''; + echo '
      • '; + } + echo '
      '; +} + +function display_unused_terms ($variable, $term1, $lang_code, $new, $updated) { + global $db; + + if ($_SESSION['language'] != 'en') { + $sql = "SELECT term, revised_date+0 AS r_date FROM ".TABLE_PREFIX."language_text WHERE variable='$variable' AND language_code='$_SESSION[language]' ORDER BY term"; + $result = mysql_query($sql, $db); + + $t_keys = array(); + while ($row = mysql_fetch_assoc($result)) { + $t_keys[$row['term']] = $row['r_date']; + } + } + + if ($lang_code == 'en') { + $sql = "SELECT lt.*, lt.revised_date+0 AS r_date FROM ".TABLE_PREFIX."language_text lt LEFT JOIN ".TABLE_PREFIX."language_pages lp ON lt.term = lp.term WHERE lp.term IS NULL AND lt.variable='$variable' AND lt.language_code='en' ORDER BY lt.term"; + } else { + $sql = "SELECT lt.* FROM ".TABLE_PREFIX."language_text lt LEFT JOIN ".TABLE_PREFIX."language_pages lp ON lt.term = NULL WHERE lt.variable='$variable' AND lt.language_code='$lang_code' ORDER BY lt.term"; + } + $result = mysql_query($sql, $db); + + echo '
        '; + while ($row = mysql_fetch_assoc($result)) { + if ($_SESSION['language'] != 'en') { + if ($new && $updated) { + if ((!($t_keys[$row['term']] == '')) && (!(($t_keys[$row['term']] < $row['r_date']) && $t_keys[$row['term']]))) { + continue; + } + } else if ($new) { + if (!($t_keys[$row['term']] == '')) { + continue; + } + } else if ($updated) { + if (!(($t_keys[$row['term']] < $row['r_date']) && $t_keys[$row['term']])) { + continue; + } + } + } + + + if ($row['term'] == $term1) { + trans_form('none'); + echo '
      • '; + + } else { + echo '
      • '; + } + echo ''; + if ($_SESSION['language'] != 'en') { + if ($t_keys[$row['term']] == '') { + echo '*New* '; + } else if ($t_keys[$row['term']] < $row['r_date']) { + echo '*Updated* '; + } + } + + if ($row['term'] != $term1) { + echo ''; + echo $row['term']; + echo ''; + } else { + echo $row['term']; + } + echo ''; + echo '
      • '; + } + echo '
      '; +} + + +function display_search_terms ($variable, $term1, $lang_code, $new, $updated) { + global $db, $addslashes, $stripslashes; + + $_REQUEST['search_term'] = $addslashes($_REQUEST['search_term']); + + $sql = "SELECT term, revised_date+0 AS r_date FROM ".TABLE_PREFIX."language_text WHERE (term LIKE '%$_REQUEST[search_term]%' OR CAST(text AS CHAR) LIKE '%$_REQUEST[search_term]%') AND (language_code='$_SESSION[language]' OR language_code='en') GROUP BY term ORDER BY term"; + $result = mysql_query($sql, $db); + + $t_keys = array(); + while ($row = mysql_fetch_assoc($result)) { + $t_keys[$row['term']] = $row['r_date']; + } + + $sql = "SELECT *, revised_date+0 AS r_date FROM ".TABLE_PREFIX."language_text WHERE (term LIKE '%$_REQUEST[search_term]%' OR CAST(text AS CHAR) LIKE '%$_REQUEST[search_term]%') AND (language_code='en' OR language_code='$_SESSION[language]') GROUP BY term ORDER BY term"; + $result = mysql_query($sql, $db); + + if (mysql_num_rows($result) == 0) { + echo '
      • No results found.
      '; + } else { + echo '
        '; + while ($row = mysql_fetch_assoc($result)) { + if ($_SESSION['language'] != 'en') { + if ($new && $updated) { + if ((!($t_keys[$row['term']] == '')) && (!(($t_keys[$row['term']] < $row['r_date']) && $t_keys[$row['term']]))) { + continue; + } + } else if ($new) { + if (!($t_keys[$row['term']] == '')) { + continue; + } + } else if ($updated) { + if (!(($t_keys[$row['term']] < $row['r_date']) && $t_keys[$row['term']])) { + continue; + } + } + } + + + if ($row['term'] == $term1) { + trans_form('search'); + echo '
      • '; + + } else { + echo '
      • '; + } + echo ''; + if ($_SESSION['language'] != 'en') { + if ($t_keys[$row['term']] == '') { + echo '*New* '; + } else if ($t_keys[$row['term']] < $row['r_date']) { + echo '*Updated* '; + } + } + + if ($row['term'] != $term1) { + echo ''; + echo $row['term']; + echo ''; + } else { + echo $row['term']; + } + echo ''; + echo '
      • '; + } + echo '
      '; + } +} +?> \ No newline at end of file diff --git a/mods/_core/modules/add_new.php b/mods/_core/modules/add_new.php new file mode 100644 index 000000000..a54c0b3ea --- /dev/null +++ b/mods/_core/modules/add_new.php @@ -0,0 +1,80 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +$module_list = $moduleFactory->getModules(AT_MODULE_STATUS_UNINSTALLED | AT_MODULE_STATUS_MISSING | AT_MODULE_STATUS_PARTIALLY_UNINSTALLED, AT_MODULE_TYPE_EXTRA); +$keys = array_keys($module_list); +natsort($keys); + +?> + +
      +
      + +
      +
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
      + +
      /getDescription($_SESSION['lang']); ?>
      +
      + + \ No newline at end of file diff --git a/mods/_core/modules/classes/Module.class.php b/mods/_core/modules/classes/Module.class.php new file mode 100644 index 000000000..3ec61f1e2 --- /dev/null +++ b/mods/_core/modules/classes/Module.class.php @@ -0,0 +1,835 @@ +_modules = array(); + + if ($auto_load == TRUE) { + // initialise enabled modules + $sql = "SELECT dir_name, privilege, admin_privilege, status, cron_interval, cron_last_run FROM ". TABLE_PREFIX . "modules WHERE status=".AT_MODULE_STATUS_ENABLED; + $result = mysql_query($sql, $db); + while($row = mysql_fetch_assoc($result)) { + $module = new Module($row); + $this->_modules[$row['dir_name']] = $module; + $module->load(); + } + } + } + + // public + // status := enabled | disabled | uninstalled | missing + // type := core | standard | extra + // sort := true | false (by name only) + // the results of this method are not cached. call sparingly. + function getModules($status, $type = 0, $sort = FALSE) { + global $db; + + $modules = array(); + $all_modules = array(); + + if ($type == 0) { + $type = AT_MODULE_TYPE_CORE | AT_MODULE_TYPE_STANDARD | AT_MODULE_TYPE_EXTRA; + } + + $sql = "SELECT dir_name, privilege, admin_privilege, status, cron_interval, cron_last_run FROM ". TABLE_PREFIX . "modules"; + $result = mysql_query($sql, $db); + + while($row = mysql_fetch_assoc($result)) { + if (!isset($this->_modules[$row['dir_name']])) { + $module = new Module($row); + } else { + $module = $this->_modules[$row['dir_name']]; + } + $all_modules[$row['dir_name']] = $module; + } + + // small performance addition: + if ($status & AT_MODULE_STATUS_UNINSTALLED) { + $dir = opendir(AT_MODULE_PATH); + while (false !== ($dir_name = readdir($dir))) { + if (($dir_name == '.') + || ($dir_name == '..') + || ($dir_name == '.svn') + || ($dir_name == AT_MODULE_DIR_CORE) + || ($dir_name == AT_MODULE_DIR_STANDARD)) { + continue; + } + + if (is_dir(AT_MODULE_PATH . $dir_name) && !isset($all_modules[$dir_name])) { + $module = new Module($dir_name); + $all_modules[$dir_name] = $module; + } + } + closedir($dir); + } + + $keys = array_keys($all_modules); + foreach ($keys as $dir_name) { + $module =$all_modules[$dir_name]; + if ($module->checkStatus($status) && $module->checkType($type)) { + $modules[$dir_name] = $module; + } + } + + if ($sort) { + uasort($modules, array($this, 'compare')); + } + + return $modules; + } + + // public. + function & getModule($module_dir) { + if (!isset($this->_modules[$module_dir])) { + global $db; + $sql = "SELECT dir_name, privilege, admin_privilege, status FROM ". TABLE_PREFIX . "modules WHERE dir_name='$module_dir'"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $module = new Module($row); + } else { + $module = new Module($module_dir); + } + $this->_modules[$module_dir] =& $module; + } + return $this->_modules[$module_dir]; + } + + // private + // used for sorting modules + function compare($a, $b) { + return strnatcasecmp($a->getName(), $b->getName()); + } +} + +/** +* Module +* +* @access public +* @author Joel Kronenberg +* @package Module +*/ +class Module { + // private + var $_moduleObj; + var $_directoryName; + var $_status; // core|enabled|disabled + var $_privilege; // priv bit(s) | 0 (in dec form) + var $_admin_privilege; // priv bit(s) | 0 (in dec form) + var $_display_defaults; // bit(s) + var $_pages; + var $_type; // core, standard, extra + var $_properties; // array from xml + var $_cron_interval; // cron interval + var $_cron_last_run; // cron last run date stamp + + // constructor + function Module($row) { + if (is_array($row)) { + $this->_directoryName = $row['dir_name']; + $this->_status = $row['status']; + $this->_privilege = $row['privilege']; + $this->_admin_privilege = $row['admin_privilege']; + $this->_display_defaults= isset($row['display_defaults']) ? $row['display_defaults'] : 0; + $this->_cron_interval = $row['cron_interval']; + $this->_cron_last_run = $row['cron_last_run']; + + if (strpos($row['dir_name'], AT_MODULE_DIR_CORE) === 0) { + $this->_type = AT_MODULE_TYPE_CORE; + } else if (strpos($row['dir_name'], AT_MODULE_DIR_STANDARD) === 0) { + $this->_type = AT_MODULE_TYPE_STANDARD; + } else { + $this->_type = AT_MODULE_TYPE_EXTRA; + } + } else { + $this->_directoryName = $row; + $this->_status = AT_MODULE_STATUS_UNINSTALLED; + $this->_privilege = 0; + $this->_admin_privilege = 0; + $this->_display_defaults= 0; + $this->_type = AT_MODULE_TYPE_EXTRA; // standard/core are installed by default + } + } + + // statuses + function checkStatus($status) { return (bool) ($status & $this->_status); } + function isPartiallyUninstalled() { return ($this->_status == AT_MODULE_STATUS_PARTIALLY_UNINSTALLED) ? true : false; } + function isUninstalled() { return ($this->_status == AT_MODULE_STATUS_UNINSTALLED) ? true : false; } + function isEnabled() { return ($this->_status == AT_MODULE_STATUS_ENABLED) ? true : false; } + function isDisabled() { return ($this->_status == AT_MODULE_STATUS_DISABLED) ? true : false; } + function isMissing() { return ($this->_status == AT_MODULE_STATUS_MISSING) ? true : false; } + + // types + function checkType($type) { return (bool) ($type & $this->_type); } + function isCore() { return ($this->_type == AT_MODULE_TYPE_CORE) ? true : false; } + function isStandard() { return ($this->_type == AT_MODULE_TYPE_STANDARD) ? true : false; } + function isExtra() { return ($this->_type == AT_MODULE_TYPE_EXTRA) ? true : false; } + + // privileges + function getPrivilege() { return $this->_privilege; } + function getAdminPrivilege() { return $this->_admin_privilege; } + + function load() { + if (is_file(AT_MODULE_PATH . $this->_directoryName.'/module.php')) { + global $_modules, $_pages, $_stacks, $_list, $_tool; // $_list is for sublinks on "detail view" + + require(AT_MODULE_PATH . $this->_directoryName.'/module.php'); + + if (isset($this->_pages)) { + $_pages = array_merge_recursive((array) $_pages, $this->_pages); + } + + //side menu items + if (isset($this->_stacks)) { + $count = 0; + $_stacks = array_merge((array)$_stacks, $this->_stacks); + } + + // sublinks on "detail view" + if(isset($this->_list)) { + $_list = array_merge((array)$_list, $this->_list); + } + + //TODO***********BOLOGNA***********REMOVE ME***********/ + //tool manager (content editing) + if(isset($this->_tool)) { + $_tool = array_merge((array)$_tool, $this->_tool); + } + + //student tools + if (isset($_student_tool)) { + $this->_student_tool =& $_student_tool; + $_modules[] = $this->_student_tool; + } + + //group tools + if (isset($_group_tool)) { + $this->_group_tool =& $_group_tool; + } + } + } + + // private + function _initModuleProperties() { + if (!isset($this->_properties)) { + require_once(dirname(__FILE__) . '/ModuleParser.class.php'); + $moduleParser = new ModuleParser(); + $moduleParser->parse(@file_get_contents(AT_MODULE_PATH . $this->_directoryName.'/module.xml')); + if ($moduleParser->rows[0]) { + $this->_properties = $moduleParser->rows[0]; + } else { + $this->_properties = array(); + $this->setIsMissing(); // the xml file may not be found -> the dir may be missing. + } + } + } + + /** + * Get the properties of this module as found in the module.xml file + * @access public + * @param array $properties_list list of property names + * @return array associative array of property/value pairs + * @author Joel Kronenberg + */ + function getProperties($properties_list) { + $this->_initModuleProperties(); + + if (!$this->_properties) { + return; + } + $properties_list = array_flip($properties_list); + foreach ($properties_list as $property => $garbage) { + $properties_list[$property] = $this->_properties[$property]; + } + return $properties_list; + } + /** + * Get a single property as found in the module.xml file + * @access public + * @param string $property name of the property to return + * @return string the value of the property + * @author Joel Kronenberg + */ + function getProperty($property) { + $this->_initModuleProperties(); + + if (!$this->_properties) { + return; + } + + return $this->_properties[$property]; + } + + function getCronInterval() { + return $this->_cron_interval; + + } + + function getName() { + if ($this->isUninstalled()) { + $name = $this->getProperty('name'); + return current($name); + } + return _AT(basename($this->_directoryName)); + } + + function getDescription($lang = 'en') { + $this->_initModuleProperties(); + + if (!$this->_properties) { + return; + } + + if (isset($this->_properties['description'][$lang])) { + return $this->_properties['description'][$lang]; + } + $description = current($this->_properties['description']); + return $description; + } + + function getChildPage($page) { + if (!is_array($this->_pages)) { + return; + } + foreach ($this->_pages as $tmp_page => $item) { + if (!empty($item['parent']) && $item['parent'] == $page) { + return $tmp_page; + } + } + } + + /** + * Checks whether or not this module can be backed-up + * @access public + * @return boolean true if this module can be backed-up, false otherwise + * @author Joel Kronenberg + */ + function isBackupable() { + return is_file(AT_MODULE_PATH . $this->_directoryName.'/module_backup.php'); + } + + function createGroup($group_id) { + if (is_file(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php')) { + require_once(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php'); + $fn_name = basename($this->_directoryName) .'_create_group'; + $fn_name($group_id); + } + } + + function deleteGroup($group_id) { + $fn_name = basename($this->_directoryName) .'_delete_group'; + + if (!function_exists($fn_name) && is_file(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php')) { + require_once(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php'); + } + if (function_exists($fn_name)) { + $fn_name($group_id); + } + } + + function getGroupTool() { + if (!isset($this->_group_tool)) { + return; + } + + return $this->_group_tool; + } + + function isGroupable() { + return is_file(AT_MODULE_PATH . $this->_directoryName.'/module_groups.php'); + } + + /** + * Backup this module for a given course + * @access public + * @param int $course_id ID of the course to backup + * @param object $zipfile a reference to a zipfile object + * @author Joel Kronenberg + */ + function backup($course_id, &$zipfile) { + static $CSVExport; + + if (!isset($CSVExport)) { + require_once(AT_INCLUDE_PATH . 'classes/CSVExport.class.php'); + $CSVExport = new CSVExport(); + } + $now = time(); + + if ($this->isBackupable()) { + require(AT_MODULE_PATH . $this->_directoryName . '/module_backup.php'); + if (isset($sql)) { + foreach ($sql as $file_name => $table_sql) { + $content = $CSVExport->export($table_sql, $course_id); + if ($content) { + $zipfile->add_file($content, $file_name . '.csv', $now); + } + } + } + + if (isset($dirs)) { + foreach ($dirs as $dir => $path) { + $path = str_replace('?', $course_id, $path); + + $zipfile->add_dir($path , $dir); + } + } + } + } + + /** + * Restores this module into the given course + * @access public + * @param int $course_id ID of the course to restore into + * @param string $version version number of the ATutor installation used to make this backup + * @param string $import_dir the path to the import directory + * @author Joel Kronenberg + */ + function restore($course_id, $version, $import_dir) { + static $CSVImport; + if (!file_exists(AT_MODULE_PATH . $this->_directoryName.'/module_backup.php')) { + return; + } + + if (!isset($CSVImport)) { + require_once(AT_INCLUDE_PATH . 'classes/CSVImport.class.php'); + $CSVImport = new CSVImport(); + } + + require(AT_MODULE_PATH . $this->_directoryName.'/module_backup.php'); + + if (isset($sql)) { + foreach ($sql as $table_name => $table_sql) { + $CSVImport->import($table_name, $import_dir, $course_id, $version); + } + } + if ($this->_directoryName == '_core/content') + { + if (version_compare($version, '1.6.4', '<')) { + $this->convertContent164($course_id); + } + } + + if (isset($dirs)) { + foreach ($dirs as $src => $dest) { + $dest = str_replace('?', $course_id, $dest); + copys($import_dir.$src, $dest); + } + } + } + + /** + * Delete this module's course content. If $groups is specified then it will + * delete all content for the groups specified. + * @access public + * @param int $course_id ID of the course to delete + * @param array $groups Array of groups to delete + * @author Joel Kronenberg + */ + function delete($course_id, $groups) { + if (is_file(AT_MODULE_PATH . $this->_directoryName.'/module_delete.php')) { + require(AT_MODULE_PATH . $this->_directoryName.'/module_delete.php'); + if (function_exists(basename($this->_directoryName).'_delete')) { + $fnctn = basename($this->_directoryName).'_delete'; + $fnctn($course_id); + } + } + if ($groups) { + foreach ($groups as $group_id) { + $this->deleteGroup($group_id); + } + } + } + + /** + * Enables the installed module + * @access public + * @author Joel Kronenberg + */ + function enable() { + global $db; + + $sql = 'UPDATE '. TABLE_PREFIX . 'modules SET status='.AT_MODULE_STATUS_ENABLED.' WHERE dir_name="'.$this->_directoryName.'"'; + $result = mysql_query($sql, $db); + } + + /** + * Sets the status to missing if the module dir doesn't exist. + * @access public + * @param boolean $force whether or not to force the module to be missing (used for bundled extra modules upon upgrade) + * @author Joel Kronenberg + */ + function setIsMissing($force = false) { + global $db; + // if the directory doesn't exist then set the status to MISSING + if ($force || !is_dir(AT_MODULE_PATH . $this->_directoryName)) { + $sql = 'UPDATE '. TABLE_PREFIX . 'modules SET status='.AT_MODULE_STATUS_MISSING.' WHERE dir_name="'.$this->_directoryName.'"'; + $result = mysql_query($sql, $db); + } + } + + /** + * Disables the installed module + * @access public + * @author Joel Kronenberg + */ + function disable() { + global $db; + + // remove any privileges admins, students + if ($this->_privilege > 1) { + $sql = 'UPDATE '. TABLE_PREFIX . 'course_enrollment SET `privileges`=`privileges`-'.$this->_privilege.' WHERE `privileges` > 1 AND (`privileges` & '.$this->_privilege.')<>0'; + $result = mysql_query($sql, $db); + } + + if ($this->_admin_privilege > 1) { + $sql = 'UPDATE '. TABLE_PREFIX . 'admins SET `privileges`=`privileges`-'.$this->_admin_privilege.' WHERE `privileges` > 1 AND (`privileges` & '.$this->_admin_privilege.')<>0'; + $result = mysql_query($sql, $db); + } + + $sql = 'UPDATE '. TABLE_PREFIX . 'modules SET status='.AT_MODULE_STATUS_DISABLED.' WHERE dir_name="'.$this->_directoryName.'"'; + $result = mysql_query($sql, $db); + + if (function_exists(basename($this->_directoryName).'_disable')) { + $fn_name = basename($this->_directoryName).'_disable'; + $fn_name(); + } + } + + /** + * Installs the module + * @access public + * @author Joel Kronenberg + */ + function install() { + global $msg; + + // should check if this module is already installed... + + if (file_exists(AT_MODULE_PATH . $this->_directoryName . '/module_install.php')) { + require(AT_MODULE_PATH . $this->_directoryName . '/module_install.php'); + } + + if (!$msg->containsErrors()) { + global $db; + + $sql = "SELECT MAX(`privilege`) AS `privilege`, MAX(admin_privilege) AS admin_privilege FROM ".TABLE_PREFIX."modules"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + if (($_course_privilege === TRUE) || ((string) $_course_privilege == 'new')) { + $priv = $row['privilege'] * 2; + } else if ($_course_privilege == AT_PRIV_ADMIN) { + $priv = AT_PRIV_ADMIN; + } else { + $priv = 0; + } + + if (($_admin_privilege === TRUE) || ((string) $_admin_privilege == 'new')) { + $admin_priv = $row['admin_privilege'] * 2; + } else { + $admin_priv = AT_ADMIN_PRIV_ADMIN; + } + + if (isset($_cron_interval)) { + $_cron_interval = abs($_cron_interval); + } else { + $_cron_interval = 0; + } + + $sql = 'INSERT INTO '. TABLE_PREFIX . 'modules VALUES ("'.$this->_directoryName.'", '.AT_MODULE_STATUS_DISABLED.', '.$priv.', '.$admin_priv.', '.$_cron_interval.', 0)'; + mysql_query($sql, $db); + if (mysql_affected_rows($db) != 1) { + // in case this module has to be re-installed (because it was Missing) + $sql = 'UPDATE '. TABLE_PREFIX . 'modules SET status='.AT_MODULE_STATUS_DISABLED.' WHERE dir_name="'.$this->_directoryName.'"'; + mysql_query($sql, $db); + } + } + } + + /** + * Uninstalls the module + * @access public + * @author Cindy Qi Li + */ + function uninstall($del_data='') { + global $msg; + + if (file_exists(AT_MODULE_PATH . $this->_directoryName . '/module_uninstall.php') && $del_data == 1) + { + require(AT_MODULE_PATH . $this->_directoryName . '/module_uninstall.php'); + } + + if (!$msg->containsErrors()) + { + require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + + if (!clr_dir(AT_MODULE_PATH . $this->_directoryName)) + $msg->addError(array('MODULE_UNINSTALL', '
    • '.AT_MODULE_PATH . $this->_directoryName.' can not be removed. Please manually remove it.
    • ')); + } + + if (!$msg->containsErrors()) + { + global $db; + + $sql = "DELETE FROM ". TABLE_PREFIX . "modules WHERE dir_name = '".$this->_directoryName."'"; + mysql_query($sql, $db); + } + + if ($msg->containsErrors()) + { + global $db; + + $sql = "UPDATE ". TABLE_PREFIX . "modules SET status=".AT_MODULE_STATUS_PARTIALLY_UNINSTALLED." WHERE dir_name='".$this->_directoryName."'"; + mysql_query($sql, $db); + } + } + + function getStudentTools() { + if (!isset($this->_student_tool)) { + return FALSE; + } + + return $this->_student_tool; + } + + + function runCron() { + if ( ($this->_cron_last_run + ($this->_cron_interval * 60)) < time()) { + if (is_file(AT_MODULE_PATH . $this->_directoryName.'/module_cron.php')) { + require(AT_MODULE_PATH . $this->_directoryName.'/module_cron.php'); + if (function_exists(basename($this->_directoryName).'_cron')) { + $fnctn = basename($this->_directoryName).'_cron'; + $fnctn(); + } + } + $this->updateCronLastRun(); + } + } + + // i'm private! update the last time the cron was run + function updateCronLastRun() { + global $db; + + $sql = "UPDATE ".TABLE_PREFIX."modules SET cron_last_run=".time()." WHERE dir_name='$this->_directoryName'"; + mysql_query($sql, $db); + + } + + + /** + * Get the latest news from the Module. + * @access public + * @author Harris Wong + * @date Feb 25, 2010 + */ + function getNews(){ + global $msg, $enrolled_courses, $db; + + if (!isset($enrolled_courses)){ + $sql = 'SELECT E.approved, E.last_cid, C.* FROM AT_course_enrollment E, AT_courses C WHERE E.member_id='.$_SESSION['member_id'].' AND E.course_id=C.course_id ORDER BY C.title'; + $result = mysql_query($sql, $db); + if ($result) { + while($row = mysql_fetch_assoc($result)){ + $enrolled_courses = $enrolled_courses . $row['course_id'] . ', '; + } + $enrolled_courses = substr($enrolled_courses, 0, -2); + + if ($enrolled_courses != ''){ + $enrolled_courses = '(' . $enrolled_courses . ')'; + } + } + } + + if (file_exists(AT_MODULE_PATH . $this->_directoryName . '/module_news.php')) { + require(AT_MODULE_PATH . $this->_directoryName . '/module_news.php'); + if (function_exists(basename($this->_directoryName).'_news')) { + $fnctn = basename($this->_directoryName).'_news'; + return $fnctn($course_id); + } + } + } + + private function convertContent164($course_id) { + global $db; + + /* convert all content nodes to the IMS standard. (adds null nodes for all top pages) */ + /* 1. Convert db to a tree */ + $sql = 'SELECT * FROM '.TABLE_PREFIX.'content where course_id='.$course_id; + + $result = mysql_query($sql, $db); + $content_array = array(); + + while ($row = mysql_fetch_assoc($result)){ + $content_array[$row['content_parent_id']][$row['ordering']] = $row['content_id']; + } + $tree = $this->buildTree($content_array[0], $content_array); + + /* 2. Restructure the tree */ + $tree = $this->rebuild($tree); + + /* 3. Update the Db based on this new tree */ + $this->reconstruct($tree, '', 0, TABLE_PREFIX); + } + + /** + * Construct a tree based on table entries + * @param array current node, (current parent) + * @param mixed a set of parents, where each parents is in the format of [parent]=>children + * should remain the same throughout the recursion. + * @return A tree structure representation of the content entries. + * @author Harris Wong + */ + private function buildTree($current, $content_array){ + $folder = array(); + foreach($current as $order=>$content_id){ + //if has children + if (isset($content_array[$content_id])){ + $wrapper[$content_id] = $this->buildTree($content_array[$content_id], $content_array); + } + + //no children. + if ($wrapper){ + $folder['order_'.$order] = $wrapper; + unset($wrapper); + } else { + $folder['order_'.$order] = $content_id; + } + } + return $folder; + } + + + /** + * Transverse the content tree structure, and reconstruct it with the IMS spec. + * This tree has the structure of [order=>array(id)], so the first layer is its order, second is the id + * if param merge is true, if node!=null, merge it to top layer, and + offset to all others + * @param mixed Tree from the buildTree() function, or sub-tree + * @param mixed the current tree. + * @return A new content tree that meets the IMS specification. + * @author Harris Wong + */ + private function rebuild($tree, $node=''){ + $order_offset = 0; + $folder = array(); + if (!is_array($tree)){ + return $tree; + } + if ($node!=''){ + $tree['order_0'] = $node; + $order_offset += 1; + } + //go through the tree + foreach($tree as $k=>$v){ + if (preg_match('/order\_([\d]+)/', $k, $match)==1){ + //if this is the order layer + $folder['order_'.($match[1]+$order_offset)] = $this->rebuild($v); + } else { + //if this is the content layer + if(is_array($v)){ + $folder[$k] = $this->rebuild($v, $k); + } + } + } + return $folder; + } + + /** + * Transverse the tree and update/insert entries based on the updated structure. + * @param array The tree from rebuild(), and the subtree from the recursion. + * @param int the ordering of this subtree respect to its parent. + * @param int parent content id + * @return null (nothing to return, it updates the db only) + */ + private function reconstruct($tree, $order, $content_parent_id, $table_prefix){ + global $db; + + //a content page. + if (!is_array($tree)){ + $sql = 'UPDATE '.$table_prefix."content SET ordering=$order, content_parent_id=$content_parent_id WHERE content_id=$tree"; + if (!mysql_query($sql, $db)){ + //throw error + echo mysql_error(); + } + return; + } + foreach ($tree as $k=>$v){ + if (preg_match('/order\_([\d]+)/', $k, $match)==1){ + //order layer + $this->reconstruct($v, $match[1], $content_parent_id, $table_prefix); //inherit the previous layer id + } else { + //content folder layer + $sql = 'SELECT * FROM '.$table_prefix."content WHERE content_id=$k"; + $result = mysql_query($sql, $db); + $old_content_row = mysql_fetch_assoc($result); + $sql = 'INSERT INTO '.$table_prefix.'content (course_id, content_parent_id, ordering, last_modified, revision, formatting, release_date, keywords, content_path, title, use_customized_head, allow_test_export, content_type) VALUES (' + .$old_content_row['course_id'] . ', ' + .$content_parent_id . ', ' + .$order . ', ' + .'\''. $old_content_row['last_modified'] . '\', ' + .$old_content_row['revision'] . ', ' + .$old_content_row['formatting'] . ', ' + .'\''. $old_content_row['release_date'] . '\', ' + .'\''. $old_content_row['keywords'] . '\', ' + .'\''. $old_content_row['content_path'] . '\', ' + .'\''. $old_content_row['title'] . '\', ' + .$old_content_row['use_customized_head'] . ', ' + .$old_content_row['allow_test_export'] . ', ' + . '1)'; + + if (mysql_query($sql, $db)){ + $folder_id = mysql_insert_id(); + $this->reconstruct($v, '', $folder_id, $table_prefix); + } else { + //throw error + echo mysql_error(); + } + } + } + } +} +?> \ No newline at end of file diff --git a/mods/_core/modules/classes/ModuleListParser.class.php b/mods/_core/modules/classes/ModuleListParser.class.php new file mode 100644 index 000000000..c07abfa16 --- /dev/null +++ b/mods/_core/modules/classes/ModuleListParser.class.php @@ -0,0 +1,140 @@ +parser = xml_parser_create(''); + + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($this->parser, 'startElement', 'endElement'); + xml_set_character_data_handler($this->parser, 'characterData'); + } + + // public + function parse($xml_data) { + $this->element_path = array(); + $this->module_rows = array(); + $this->character_data = ''; + $this->row_num = 0; + $this->history_num = 0; + xml_parse($this->parser, $xml_data, TRUE); + } + + // private + function startElement($parser, $name, $attributes) + { + array_push($this->element_path, $name); + } + + // private + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) { + if ($this->element_path == array('module_list', 'module', 'name')) + { + $this->module_rows[$this->row_num]['name'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'atutor_version')) + { + $this->module_rows[$this->row_num]['atutor_version'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'description')) + { + $this->module_rows[$this->row_num]['description'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'history')) + { + $this->history_num = 0; + } + else if ($this->element_path === array('module_list', 'module', 'history', 'release')) + { + $this->history_num++; + } + else if ($this->element_path === array('module_list', 'module', 'history', 'release', 'version')) + { + $this->module_rows[$this->row_num]['history'][$this->history_num]['version'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'history', 'release', 'filename')) + { + $this->module_rows[$this->row_num]['history'][$this->history_num]['filename'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'history', 'release', 'location')) + { + $this->module_rows[$this->row_num]['history'][$this->history_num]['location'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'history', 'release', 'install_folder')) + { + $this->module_rows[$this->row_num]['history'][$this->history_num]['install_folder'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'history', 'release', 'date')) + { + $this->module_rows[$this->row_num]['history'][$this->history_num]['date'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'history', 'release', 'state')) + { + $this->module_rows[$this->row_num]['history'][$this->history_num]['state'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'history', 'release', 'maintainer')) + { + $this->module_rows[$this->row_num]['history'][$this->history_num]['maintainer'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module', 'history', 'release', 'notes')) + { + $this->module_rows[$this->row_num]['history'][$this->history_num]['notes'] = trim($this->character_data); + } + else if ($this->element_path === array('module_list', 'module')) + { + $this->row_num++; + } + + array_pop($this->element_path); + $this->character_data = ''; + } + + // private + function characterData($parser, $data){ + $this->character_data .= $data; + } + + // public + function getNumOfModules() + { + return count($this->module_rows); + } + + // public + function getParsedArray() + { + return $this->module_rows; + } +} + +?> \ No newline at end of file diff --git a/mods/_core/modules/classes/ModuleParser.class.php b/mods/_core/modules/classes/ModuleParser.class.php new file mode 100644 index 000000000..1bdff4893 --- /dev/null +++ b/mods/_core/modules/classes/ModuleParser.class.php @@ -0,0 +1,130 @@ +element_path = array(); + $this->rows = array(); + $this->character_data = ''; + $this->row_num = 0; + $this->maintainer_num = 0; + + $this->parser = xml_parser_create(''); + + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($this->parser, 'startElement', 'endElement'); + xml_set_character_data_handler($this->parser, 'characterData'); + xml_parse($this->parser, $xml_data, TRUE); + } + + // public + function getModule($row_num) { + return new Module($this->rows[$row_num]); + } + + // public + function getNewModule($row_num) { + //return new LanguageEditor($this->language_rows[$row_num]); + } + + // private + function startElement($parser, $name, $attributes) { + array_push($this->element_path, $name); + + $this->attributes = $attributes; + } + + // private + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) { + if ($this->element_path == array('module', 'name')) { + if (isset($this->attributes['lang'])) { + $this->rows[$this->row_num]['name'][$this->attributes['lang']] = trim($this->character_data); + } else { + $this->rows[$this->row_num]['name'][] = trim($this->character_data); + } + + } else if ($this->element_path === array('module', 'description')) { + if (isset($this->attributes['lang'])) { + $this->rows[$this->row_num]['description'][$this->attributes['lang']] = trim($this->character_data); + } else { + $this->rows[$this->row_num]['description'][] = trim($this->character_data); + } + + } else if ($this->element_path === array('module', 'url')) { + $this->rows[$this->row_num]['url'] = trim($this->character_data); + + } else if ($this->element_path === array('module', 'license')) { + $this->rows[$this->row_num]['license'] = trim($this->character_data); + + } else if ($this->element_path === array('module', 'maintainers', 'maintainer', 'name')) { + $this->rows[$this->row_num]['maintainers'][$this->maintainer_num]['name'] = trim($this->character_data); + + } else if ($this->element_path === array('module', 'maintainers', 'maintainer', 'email')) { + $this->rows[$this->row_num]['maintainers'][$this->maintainer_num]['email'] = trim($this->character_data); + + $this->maintainer_num++; + + } else if ($this->element_path === array('module', 'release', 'version')) { + $this->rows[$this->row_num]['version'] = trim($this->character_data); + + } else if ($this->element_path === array('module', 'release', 'date')) { + $this->rows[$this->row_num]['date'] = trim($this->character_data); + + } else if ($this->element_path === array('module', 'release', 'state')) { + $this->rows[$this->row_num]['state'] = trim($this->character_data); + + } else if ($this->element_path === array('module', 'release', 'notes')) { + $this->rows[$this->row_num]['notes'] = trim($this->character_data); + + } else if ($this->element_path === array('module')) { + $this->row_num++; + } + array_pop($this->element_path); + $this->character_data = ''; + } + + // private + function characterData($parser, $data){ + $this->character_data .= $data; + } + +} + + +?> \ No newline at end of file diff --git a/mods/_core/modules/create.php b/mods/_core/modules/create.php new file mode 100644 index 000000000..8a98d357e --- /dev/null +++ b/mods/_core/modules/create.php @@ -0,0 +1,137 @@ + +
      +
      +
      +
      +
      + +
      + +
      +
      +
      + + +[[ this form is used to generate the module.xml file which must be packaged with each module ]] + +
      +
      +

      + +
      + *
      + +
      + +
      +
      + +
      + +
      + +
        +
      1. +
      2. + +
      3. +
      4. +
      +
      + +
      +
      + +
      + +
      +
      + +
      + + +

      + +
      +
      + +
      + +
      +
      + , + +
      + +
      +
      + +
      + +
      +
      + , + , + +
      + +
      +
      + +
      + +
      + +
      +
      +
      + + \ No newline at end of file diff --git a/mods/_core/modules/default_mods.php b/mods/_core/modules/default_mods.php new file mode 100644 index 000000000..9d721be4c --- /dev/null +++ b/mods/_core/modules/default_mods.php @@ -0,0 +1,256 @@ +addFeedback('CANCELLED'); + header('Location: ../courses.php'); + exit; +} + +if (isset($_POST['up'])) { + $up = key($_POST['up']); + $_new_modules = array(); + if (isset($_POST['main'])) { + foreach ($_POST['main'] as $m) { + if ($m == $up) { + $last_m = array_pop($_new_modules); + $_new_modules[] = $m; + $_new_modules[] = $last_m; + } else { + $_new_modules[] = $m; + } + } + + $_POST['main'] = $_new_modules; + } + if (isset($_POST['home'])) { + $_new_modules = array(); + foreach ($_POST['home'] as $m) { + if ($m == $up) { + $last_m = array_pop($_new_modules); + $_new_modules[] = $m; + $_new_modules[] = $last_m; + } else { + $_new_modules[] = $m; + } + } + $_POST['home'] = $_new_modules; + } + + $_POST['submit'] = TRUE; +} else if (isset($_POST['down'])) { + $_new_modules = array(); + + $down = key($_POST['down']); + + if (isset($_POST['main'])) { + foreach ($_POST['main'] as $m) { + if ($m == $down) { + $found = TRUE; + continue; + } + $_new_modules[] = $m; + if ($found) { + $_new_modules[] = $down; + $found = FALSE; + } + } + + $_POST['main'] = $_new_modules; + } + + if (isset($_POST['home'])) { + $_new_modules = array(); + foreach ($_POST['home'] as $m) { + if ($m == $down) { + $found = TRUE; + continue; + } + $_new_modules[] = $m; + if ($found) { + $_new_modules[] = $down; + $found = FALSE; + } + } + + $_POST['home'] = $_new_modules; + } + + $_POST['submit'] = TRUE; +} + +if (isset($_POST['submit'])) { + if (isset($_POST['main'])) { + $_POST['main'] = array_unique($_POST['main']); + $_POST['main'] = array_filter($_POST['main']); // remove empties + $main_defaults = implode('|', $_POST['main']); + + } else { + $main_defaults = ''; + } + + if (isset($_POST['home'])) { + $_POST['home'] = array_unique($_POST['home']); + $_POST['home'] = array_filter($_POST['home']); // remove empties + $home_defaults = implode('|', $_POST['home']); + } else { + $home_defaults = ''; + } + + if (!($_config_defaults['main_defaults'] == $main_defaults) && (strlen($main_defaults) < 256)) { + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('main_defaults', '$main_defaults')"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='main_defaults_2'"; + } else if (!($_config_defaults['main_defaults'] == $main_defaults) && (strlen($main_defaults) > 255)) { + // we don't have to worry about chopping in the middle since they'll be combined anyway + $main_defaults_1 = substr($main_defaults, 0, 255); + $main_defaults_2 = substr($main_defaults, 255); + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('main_defaults', '$main_defaults_1')"; + $result = mysql_query($sql, $db); + + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('main_defaults_2', '$main_defaults_2')"; + } else if ($_config_defaults['main_defaults'] == $main_defaults) { + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='main_defaults' OR name='name_defaults_2'"; + } + $result = mysql_query($sql, $db); + + + if (!($_config_defaults['home_defaults'] == $home_defaults) && (strlen($home_defaults) < 256)) { + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('home_defaults', '$home_defaults')"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='home_defaults_2'"; + + } else if (!($_config_defaults['home_defaults'] == $home_defaults) && (strlen($home_defaults) > 255)) { + // we don't have to worry about chopping in the middle since they'll be combined anyway + $home_defaults_1 = substr($home_defaults, 0, 255); + $home_defaults_2 = substr($home_defaults, 255); + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('home_defaults', '$home_defaults_1')"; + $result = mysql_query($sql, $db); + + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('home_defaults_2', '$home_defaults_2')"; + + } else if ($_config_defaults['home_defaults'] == $home_defaults) { + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='home_defaults' OR name='home_defaults_2'"; + } + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} + + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$main_defaults = explode('|', $_config['main_defaults']); +$home_defaults = explode('|', $_config['home_defaults']); + +$main_defaults = array_filter($main_defaults); // remove empties +$home_defaults = array_filter($home_defaults); // remove empties +?> +
      + + + + + + + + + + + + + + +getModules(AT_MODULE_STATUS_ENABLED); +$keys = array_keys($module_list); + +foreach ($keys as $dir_name) { + $module =& $module_list[$dir_name]; + + if ($module->getStudentTools()) { + $student_tools[] = $module->getStudentTools(); + } +} + +$count = 0; + +//main mods +$_current_modules = $main_defaults; +$num_main = count($_current_modules); +//main and home merged +$_current_modules = array_merge($_current_modules, array_diff($home_defaults, $main_defaults)); +$num_modules = count($_current_modules); +//all other mods +$_current_modules = array_merge($_current_modules, array_diff($student_tools, $_current_modules)); + + +foreach ($_current_modules as $tool) : + $count++; +?> + + + + + + + +
      + + +
      + + + + + + + + + + + + + +   + + 1)): ?> + + + + + + + + + + +
      +
      + + \ No newline at end of file diff --git a/mods/_core/modules/default_side.php b/mods/_core/modules/default_side.php new file mode 100644 index 000000000..8acfd8117 --- /dev/null +++ b/mods/_core/modules/default_side.php @@ -0,0 +1,99 @@ +addFeedback('CANCELLED'); + header('Location: ../courses.php'); + exit; +} + +if (isset($_POST['submit'])) { + + $side_menu = ''; + $_stack_names = array(); + + foreach($_stacks as $name=>$file) { + $_stack_names[] = $name; + } + + $_POST['stack'] = array_unique($_POST['stack']); + $_POST['stack'] = array_intersect($_POST['stack'], $_stack_names); + + foreach($_POST['stack'] as $dropdown) { + if($dropdown != '') { + $side_menu .= $dropdown . '|'; + } + } + $side_menu = substr($side_menu, 0, -1); + + if (!($_config_defaults['side_defaults'] == $side_menu) && (strlen($side_menu) < 256)) { + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('side_defaults', '$side_menu')"; + } else if ($_config_defaults['side_defaults'] == $side_menu) { + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='side_defaults'"; + } + + $result = mysql_query($sql, $db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location:'. $_SERVER[PHP_SELF]); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
      +
      +
      +

      +
      + +
      + '; + echo ''; + foreach ($_stacks as $name=>$info) { + if (isset($info['title'])) { + $title = $info['title']; + } else { + $title = _AT($info['title_var']); + } + echo ''; + } + echo ''; + echo '
      '; + } ?> +
      + +
      + + +
      +
      +
      + + \ No newline at end of file diff --git a/mods/_core/modules/details.php b/mods/_core/modules/details.php new file mode 100644 index 000000000..6ead9eaef --- /dev/null +++ b/mods/_core/modules/details.php @@ -0,0 +1,185 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/add_new.php'); + exit; +} else if (isset($_POST['mod']) && isset($_POST['submit_yes'])) { + $module = $moduleFactory->getModule($_POST['mod']); + $module->load(); + $module->install(); + + if ($msg->containsErrors()) { + header('Location: '.AT_BASE_HREF.'mods/_core/modules/details.php?mod='.$addslashes($_POST['mod']).SEP.'new=1'); + } else { + $msg->addFeedback('MOD_INSTALLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/index.php'); + } + exit; +} else if (isset($_GET['submit'])) { + $args = ''; + + if (isset($_GET['enabled']) && $_GET['enabled']) { $args .= 'enabled=1'; } + if (isset($_GET['disabled']) && $_GET['disabled']) { $args .= SEP.'disabled=1'; } + if (isset($_GET['missing']) && $_GET['missing']) { $args .= SEP.'missing=1'; } + if (isset($_GET['core']) && $_GET['core']) { $args .= SEP.'core=1'; } + if (isset($_GET['standard']) && $_GET['standard']) { $args .= SEP.'standard=1'; } + if (isset($_GET['extra']) && $_GET['extra']) { $args .= SEP.'extra=1'; } + + header('Location: index.php?'. $args); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$moduleParser = new ModuleParser(); + +$_REQUEST['mod'] = str_replace(array('.','..'), '', $_REQUEST['mod']); + +if (!file_exists('../../../mods/'.$_GET['mod'].'/module.xml')) { +?> +
      + + +
      +
      +

      +
      + +
      + +
      + +
      + + + + +
      + +
      +
      +parse(file_get_contents('../../../mods/'.$_GET['mod'].'/module.xml')); + +$module = $moduleFactory->getModule($_GET['mod']); + +$properties = $module->getProperties(array('maintainers', 'url', 'date', 'license', 'state', 'notes', 'version')); +?> +
      + + + + + + + + + + +
      +
      +

      getName(); ?>

      +
      + +
      +
      + getDescription($_SESSION['lang'])); ?> +
      + +
      +
      +
        + +
      • + +
      +
      + +
      +
      + +
      + +
      +
      + +
      + +
      +
      + +
      + +
      +
      + +
      + +
      +
      + +
      + +
      +
      + +
      + + _pages)): ?> +
      + +
      + + _pages); ?> + +
      +
        + + +
      • + +
      + +
      + + + +
      + +
      + +
      +
      + + addConfirm(array('ADD_MODULE', $_REQUEST['mod']), $hidden_vars); + $msg->printConfirm(); + ?> + + + \ No newline at end of file diff --git a/mods/_core/modules/index.php b/mods/_core/modules/index.php new file mode 100644 index 000000000..a7e3ad2f8 --- /dev/null +++ b/mods/_core/modules/index.php @@ -0,0 +1,270 @@ +getModule($_GET['mod_dir']); + if (!$module->isEnabled() && !$module->isCore() && !$module->isMissing() && !$module->isPartiallyUninstalled()) { + $module->enable(); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + + header('Location: '.$_SERVER['PHP_SELF'] . '?' . $args); + exit; +} else if (isset($_GET['mod_dir'], $_GET['disable'])) { + $module = $moduleFactory->getModule($_GET['mod_dir']); + if ($module->isCore()) { + // core modules cannot be disabled! + $msg->addError('DISABLE_CORE_MODULE'); + } else if ($module->isMissing()) { + $msg->addError('DISABLE_MISSING_MODULE'); + } else if ($module->isPartiallyUninstalled()) { + $msg->addError('DISABLE_PARTIALLY_UNINSTALLED_MODULE'); + } else if ($module->isEnabled()) { + $module->disable(); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + header('Location: '.$_SERVER['PHP_SELF'] . '?' . $args); + exit; +} else if (isset($_GET['mod_dir'], $_GET['details'])) { + header('Location: details.php?mod='.$_GET['mod_dir'] . SEP . $args); + exit; + +} else if (isset($_GET['mod_dir'], $_GET['uninstall'])) { + $module = $moduleFactory->getModule($_GET['mod_dir']); + + $module_folder = '../../../mods/'.$_GET['mod_dir']; + // check if the module has been un-installed + if (!file_exists($module_folder)) + { + $msg->addError('ALREADY_UNINSTALLED'); + } + + // only extra modules can be uninstalled + if (!$module->isExtra()) { + $msg->addError('ONLY_UNINSTALL_EXTRA_MODULE'); + } + // check if the module is installed via "Available Extra Modules" + // which are the modules can be un-installed + else if (!file_exists($module_folder.'/module_uninstall.php') || !is_writable($module_folder)) + { + $msg->addError('CANNOT_UNINSTALL_MANUAL_MODULE'); + } + + if (!$msg->containsErrors()) + { + header('Location: module_uninstall_step_1.php?mod=' . urlencode($_GET['mod_dir']) . SEP.'args='.urlencode($args)); + exit; + } + +} else if (isset($_GET['mod_dir'], $_GET['export'])) { + $module = $moduleFactory->getModule($_GET['mod_dir']); + + $module_folder = '../../../mods/'.$_GET['mod_dir']; + // check if the module has been un-installed + if (!file_exists($module_folder)) + { + $msg->addError('ITEM_NOT_FOUND'); + } + + // only extra modules can be uninstalled + if (!$module->isExtra()) { + $msg->addError('ONLY_EXPORT_EXTRA_MODULE'); + } + + if (!$msg->containsErrors()) + { + require(AT_INCLUDE_PATH.'classes/zipfile.class.php'); /* for zipfile */ + + $zipfile = new zipfile(); + $zipfile->add_dir('../../../mods/'.$_GET['mod_dir'].'/', $_GET['mod_dir'].'/'); + $zipfile->close(); + $zipfile->send_file($_GET['mod_dir']); + exit; + } + +} else if (isset($_GET['disable']) || isset($_GET['enable']) || isset($_GET['details']) || isset($_GET['uninstall']) || isset($_GET['export'])) { + $msg->addError('NO_ITEM_SELECTED'); + header('Location: '.$_SERVER['PHP_SELF'] . '?' . $args); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$module_status_bits = $module_type_bits = 0; + +if ($_GET['enabled']) { $module_status_bits += AT_MODULE_STATUS_ENABLED; } +if ($_GET['disabled']) { $module_status_bits += AT_MODULE_STATUS_DISABLED; } +if ($_GET['missing']) { $module_status_bits += AT_MODULE_STATUS_MISSING; } +if ($_GET['partially_uninstalled']) { $module_status_bits += AT_MODULE_STATUS_PARTIALLY_UNINSTALLED; } + +if ($_GET['core']) { $module_type_bits += AT_MODULE_TYPE_CORE; } +if ($_GET['standard']) { $module_type_bits += AT_MODULE_TYPE_STANDARD; } +if ($_GET['extra']) { $module_type_bits += AT_MODULE_TYPE_EXTRA; } + +if ($module_status_bits == 0) { + $module_status_bits = AT_MODULE_STATUS_DISABLED | AT_MODULE_STATUS_ENABLED | AT_MODULE_STATUS_MISSING | AT_MODULE_STATUS_PARTIALLY_UNINSTALLED; + $_GET['enabled'] = $_GET['disabled'] = $_GET['missing'] = $_GET['partially_uninstalled'] = 1; +} + +if ($module_type_bits == 0) { + $module_type_bits = AT_MODULE_TYPE_STANDARD + AT_MODULE_TYPE_EXTRA; + $_GET['standard'] = $_GET['extra'] = 1; +} + + +$module_list = $moduleFactory->getModules($module_status_bits, $module_type_bits, $sort = TRUE); +$keys = array_keys($module_list); +?> +
      +
      +
      +

      +
      + +
      +
      + /> + + /> + + /> +
      + + +
      +
      + /> + + /> + + /> + + /> +
      + +
      + + +
      +
      +
      + +
      + + + + + + + + + + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
      + + + + + +
      isCore()) { + echo ''._AT('core').''; + } else if ($module->isStandard()) { + echo _AT('standard'); + } else { + echo _AT('extra'); + } + ?>isEnabled()) { + echo _AT('enabled'); + } else if ($module->isMissing()) { + echo ''._AT('missing').''; + } else if ($module->isPartiallyUninstalled()) { + echo _AT('partially_uninstalled'); + } else { + echo ''._AT('disabled').''; + } + ?> + getCronInterval()): ?> + getCronInterval()); ?> + + - + + /
      +
      + + \ No newline at end of file diff --git a/mods/_core/modules/install_modules.php b/mods/_core/modules/install_modules.php new file mode 100644 index 000000000..9d61ff965 --- /dev/null +++ b/mods/_core/modules/install_modules.php @@ -0,0 +1,370 @@ +addInfo(array('CANNOT_CONNECT_MOD_SERVER')); +} +else +{ + // get module list + $module_folder = $update_server . '/modules/'; + + $module_list_xml = @file_get_contents($module_folder . 'module_list.xml'); + + if ($module_list_xml) + { + $moduleListParser = new ModuleListParser(); + $moduleListParser->parse($module_list_xml); + $module_list_array = $moduleListParser->getParsedArray(); + } + // end of get module list + + $module_content_folder = AT_CONTENT_DIR . "module/"; + + if (!is_dir($module_content_folder)) mkdir($module_content_folder); +} +// end of get module list + +$module_content_folder = AT_CONTENT_DIR . "module/"; + +if (!is_dir($module_content_folder)) mkdir($module_content_folder); + +// Installation process +if ((isset($_POST['install']) || isset($_POST["download"]) || isset($_POST["version_history"])) && !isset($_POST["id"])) +{ + $msg->addError('NO_ITEM_SELECTED'); +} +else if (isset($_POST['install']) || isset($_POST["download"]) || isset($_POST["version_history"]) || isset($_POST["install_upload"])) +{ + if ($_POST['version_history']) + { + header('Location: '.AT_BASE_HREF.'mods/_core/modules/version_history.php?id='.$_POST["id"]); + exit; + } + + // install and download + if ($_POST["install_upload"]) + $module_zip_file = $_FILES['modulefile']['tmp_name']; + else + $module_zip_file = $module_folder . $module_list_array[$_POST["id"]]['history'][0]['location'].$module_list_array[$_POST["id"]]['history'][0]['filename']; + + $file_content = file_get_contents($module_zip_file); + + if (!$file_content & ($_POST['install'] || $_POST['download'])) + { + $msg->addError('FILE_NOT_EXIST'); + } + else + { + if ($_POST['install'] || $_POST['install_upload']) + { + clear_dir($module_content_folder); + + // download zip file from update.atutor.ca and write into module content folder + if ($_POST["install_upload"]) + $local_module_zip_file = $module_content_folder . $_FILES['modulefile']['name']; + else + $local_module_zip_file = $module_content_folder. $module_list_array[$_POST["id"]]['history'][0]['filename']; + + $fp = fopen($local_module_zip_file, "w"); + fwrite($fp, $file_content); + fclose($fp); + + // unzip uploaded file to module's content directory + include_once(AT_INCLUDE_PATH . '/classes/pclzip.lib.php'); + + $archive = new PclZip($local_module_zip_file); + + if ($archive->extract(PCLZIP_OPT_PATH, $module_content_folder) == 0) + { + clear_dir($module_content_folder); + $msg->addError('CANNOT_UNZIP'); + } + + if (!$msg->containsErrors()) + { + // find unzip module folder name + clearstatcache(); + + if ($dh = opendir($module_content_folder)) + { + while (($module_folder = readdir($dh)) !== false) + { + if ($module_folder <> "." && $module_folder <> ".." && is_dir($module_content_folder.$module_folder)) break; + } + + closedir($dh); + } + + if ($module_folder == "." || $module_folder == ".." || !isset($module_folder)) + $msg->addError('EMPTY_ZIP_FILE'); + } + + // check if the same module exists in "mods" folder. If exists, it has been installed + if (!$msg->containsErrors()) + { + if (is_dir("../../../mods/". $module_folder)) + $msg->addError('ALREADY_INSTALLED'); + } + + if (!$msg->containsErrors()) + { + header('Location: module_install_step_1.php?mod='.urlencode($module_folder).SEP.'new=1'); + exit; + } + } + + if ($_POST['download']) + { + $id = intval($_POST['id']); + + header('Content-Type: application/x-zip'); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="'.htmlspecialchars($module_list_array[$id]['history'][0]['filename']).'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: '.strlen($file_content)); + + echo $file_content; + exit; + } + } +} + +if (isset($_POST['mod'])) { + $dir_name = str_replace(array('.','..'), '', $_POST['mod']); + + if (isset($_POST['install_manually'])) { + header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_install_step_2.php?mod='.urlencode($dir_name).SEP.'new=1'.SEP.'mod_in=1'); + exit; + } + +} else if (isset($_POST['install_manually'])) { + $msg->addError('NO_ITEM_SELECTED'); +} + +$module_list = $moduleFactory->getModules(AT_MODULE_STATUS_UNINSTALLED | AT_MODULE_STATUS_MISSING | AT_MODULE_STATUS_PARTIALLY_UNINSTALLED, AT_MODULE_TYPE_EXTRA); +$keys = array_keys($module_list); +natsort($keys); + +require (AT_INCLUDE_PATH.'header.inc.php'); + +$msg->printAll(); + +?> + +
      + +
      +
      + +
      + + +
      + +
      + + +
      +
      + +
      + + 0) +{ +?> +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
      + +
      /getDescription($_SESSION['lang']); ?>
      +
      +
      + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
      + + + +
      />
      +
      + + + + diff --git a/mods/_core/modules/module.php b/mods/_core/modules/module.php new file mode 100644 index 000000000..b1e417cb4 --- /dev/null +++ b/mods/_core/modules/module.php @@ -0,0 +1,48 @@ +getAdminPrivilege()); + +//admin pages +//if (admin_authenticate(AT_ADMIN_PRIV_RSS, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + + $this->_pages['mods/_core/modules/index.php']['title_var'] = 'modules'; + $this->_pages['mods/_core/modules/index.php']['parent'] = AT_NAV_ADMIN; + $this->_pages['mods/_core/modules/index.php']['guide'] = 'admin/?p=modules.php'; + $this->_pages['mods/_core/modules/index.php']['children'] = array('mods/_core/modules/install_modules.php'); + + $this->_pages['mods/_core/modules/details.php']['title_var'] = 'details'; + $this->_pages['mods/_core/modules/details.php']['parent'] = 'mods/_core/modules/index.php'; + + $this->_pages['mods/_core/modules/module_uninstall_step_1.php']['title_var'] = 'module_uninstall'; + $this->_pages['mods/_core/modules/module_uninstall_step_1.php']['parent'] = 'mods/_core/modules/index.php'; + + $this->_pages['mods/_core/modules/module_uninstall_step_2.php']['title_var'] = 'module_uninstall'; + $this->_pages['mods/_core/modules/module_uninstall_step_2.php']['parent'] = 'mods/_core/modules/index.php'; + + $this->_pages['mods/_core/modules/module_uninstall_step_3.php']['title_var'] = 'module_uninstall'; + $this->_pages['mods/_core/modules/module_uninstall_step_3.php']['parent'] = 'mods/_core/modules/index.php'; + + $this->_pages['mods/_core/modules/install_modules.php']['title_var'] = 'install_modules'; + $this->_pages['mods/_core/modules/install_modules.php']['parent'] = 'mods/_core/modules/index.php'; + $this->_pages['mods/_core/modules/install_modules.php']['guide'] = 'admin/?p=modules.php'; + + $this->_pages['mods/_core/modules/version_history.php']['title_var'] = 'version_history'; + $this->_pages['mods/_core/modules/version_history.php']['parent'] = 'mods/_core/modules/install_modules.php'; + + $this->_pages['mods/_core/modules/module_install_step_1.php']['title_var'] = 'details'; + $this->_pages['mods/_core/modules/module_install_step_1.php']['parent'] = 'mods/_core/modules/install_modules.php'; + + $this->_pages['mods/_core/modules/module_install_step_2.php']['title_var'] = 'details'; + $this->_pages['mods/_core/modules/module_install_step_2.php']['parent'] = 'mods/_core/modules/install_modules.php'; + + $this->_pages['mods/_core/modules/module_install_step_3.php']['title_var'] = 'details'; + $this->_pages['mods/_core/modules/module_install_step_3.php']['parent'] = 'mods/_core/modules/install_modules.php'; + + $this->_pages['mods/_core/modules/confirm.php']['title_var'] = 'confirm'; + $this->_pages['mods/_core/modules/confirm.php']['parent'] = 'mods/_core/modules/add_new.php'; +//} + +?> \ No newline at end of file diff --git a/mods/_core/modules/module.template.php b/mods/_core/modules/module.template.php new file mode 100644 index 000000000..0cd5c1828 --- /dev/null +++ b/mods/_core/modules/module.template.php @@ -0,0 +1,24 @@ + + + {NAME} + {DESCRIPTION} + {MAINTAINERS} + {URL} + {LICENSE} + + {VERSION} + {USER_PRIVILEGE} + {DATE} + {STATE} + {NOTES} + +'; + +$maintainer_xml = "\n\t\t".' + {NAME} + {EMAIL} + '; + +?> \ No newline at end of file diff --git a/mods/_core/modules/module.xml b/mods/_core/modules/module.xml new file mode 100644 index 000000000..58d6be6e5 --- /dev/null +++ b/mods/_core/modules/module.xml @@ -0,0 +1,19 @@ + + + Module Manager + Setup and manage modules added to or enabled on this ATutor system. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + 2010-11-02 + stable + + + \ No newline at end of file diff --git a/mods/_core/modules/module_install_step_1.php b/mods/_core/modules/module_install_step_1.php new file mode 100644 index 000000000..eaf043c73 --- /dev/null +++ b/mods/_core/modules/module_install_step_1.php @@ -0,0 +1,53 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/install_modules.php'); + exit; +} +else if (is_writable($module_folder) || (is_writable($module_folder) && isset($_POST['submit_yes']))) +{ + header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_install_step_2.php?mod='.$mod.SEP.'new='.$new.SEP.'permission_granted='.$_POST["permission_granted"]); + exit; +} + +if (!is_writable($module_folder)) +{ + unset($hidden_vars); + $hidden_vars['mod'] = $mod; + $hidden_vars['new'] = $new; + $hidden_vars['permission_granted'] = 1; + + require(AT_INCLUDE_PATH.'header.inc.php'); + + $msg->addConfirm(array('GRANT_WRITE_PERMISSION', realpath($module_folder)), $hidden_vars, _AT("continue"), _AT("cancel")); + $msg->printConfirm(); + + require(AT_INCLUDE_PATH.'footer.inc.php'); +} + +?> \ No newline at end of file diff --git a/mods/_core/modules/module_install_step_2.php b/mods/_core/modules/module_install_step_2.php new file mode 100644 index 000000000..3425cffea --- /dev/null +++ b/mods/_core/modules/module_install_step_2.php @@ -0,0 +1,232 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/install_modules.php'); + } + + exit; +} +else if (isset($_POST['submit_yes'])) +{ + // install module + $module = $moduleFactory->getModule($_POST['mod']); + $module->load(); + $module->install(); + + if ($msg->containsErrors()) + { + header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_install_step_2.php?mod='.$addslashes($mod).SEP.'new=1'.SEP.'permission_granted='.$permission_granted); + } + else + { + if ($_POST['permission_granted']==1) + { + header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_install_step_3.php?installed=1'); + } + else + { + $msg->addFeedback('MOD_INSTALLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/index.php'); + } + } + exit; +} else if (isset($_GET['submit'])) { + $args = ''; + + if (isset($_GET['enabled']) && $_GET['enabled']) { $args .= 'enabled=1'; } + if (isset($_GET['disabled']) && $_GET['disabled']) { $args .= SEP.'disabled=1'; } + if (isset($_GET['missing']) && $_GET['missing']) { $args .= SEP.'missing=1'; } + if (isset($_GET['core']) && $_GET['core']) { $args .= SEP.'core=1'; } + if (isset($_GET['standard']) && $_GET['standard']) { $args .= SEP.'standard=1'; } + if (isset($_GET['extra']) && $_GET['extra']) { $args .= SEP.'extra=1'; } + + header('Location: index.php?'. $args); + exit; +} + +// copy module from content folder into mods folder +if (isset($mod) && !isset($_GET['mod_in'])) +{ + copys($module_content_folder.$mod, '../../../mods/'.$mod); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$moduleParser = new ModuleParser(); + +if (!file_exists('../../../mods/'.$mod.'/module.xml')) { +?> +
      + + + +
      +
      +

      +
      + +
      + +
      + +
      + + + + +
      + +
      +
      +parse(file_get_contents('../../../mods/'.$mod.'/module.xml')); + +$module = $moduleFactory->getModule($mod); + +$properties = $module->getProperties(array('maintainers', 'url', 'date', 'license', 'state', 'notes', 'version')); +?> +
      + + + + + + + + + + + +
      +
      +

      getName(); ?>

      +
      + +
      +
      + getDescription($_SESSION['lang'])); if ($readme <> '') echo '
      '._AT('view_readme').''; ?> +
      + +
      +
      +
        + +
      • + +
      +
      + +
      +
      + +
      + +
      +
      + +
      + +
      +
      + +
      + +
      +
      + +
      + +
      +
      + +
      + +
      +
      + +
      + + _pages)): ?> +
      + +
      + + _pages); ?> + +
      +
        + + +
      • + +
      + +
      + + + +
      + +
      + +
      +
      + + addConfirm(array('ADD_MODULE', $mod), $hidden_vars); + $msg->printConfirm(); + ?> + + + \ No newline at end of file diff --git a/mods/_core/modules/module_install_step_3.php b/mods/_core/modules/module_install_step_3.php new file mode 100644 index 000000000..af7e0c632 --- /dev/null +++ b/mods/_core/modules/module_install_step_3.php @@ -0,0 +1,60 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/install_modules.php'); + } + if ($installed == 1) + { + $msg->addFeedback('MOD_INSTALLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/index.php'); + } + + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +unset($hidden_vars); +$hidden_vars['cancelled'] = $cancelled; +$hidden_vars['installed'] = $installed; + +$msg->addConfirm(array('REMOVE_WRITE_PERMISSION', realpath($module_folder)), $hidden_vars, _AT("continue"), '', true); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); + + + +?> \ No newline at end of file diff --git a/mods/_core/modules/module_uninstall_step_1.php b/mods/_core/modules/module_uninstall_step_1.php new file mode 100644 index 000000000..381add95e --- /dev/null +++ b/mods/_core/modules/module_uninstall_step_1.php @@ -0,0 +1,53 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/index.php?'.urlencode($_POST['args'])); + exit; +} +else if (is_writable($mods_folder) || (is_writable($mods_folder) && isset($_POST['submit_yes']))) +{ + header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_uninstall_step_2.php?mod='.$mod.SEP.'args='.urlencode($args).SEP.'permission_granted='.$_POST["permission_granted"]); + exit; +} + +if (!is_writable($mods_folder)) +{ + unset($hidden_vars); + $hidden_vars['mod'] = $mod; + $hidden_vars['args'] = $args; + $hidden_vars['permission_granted'] = 1; + + require(AT_INCLUDE_PATH.'header.inc.php'); + + $msg->addConfirm(array('GRANT_WRITE_PERMISSION', realpath($mods_folder)), $hidden_vars, _AT("continue"), _AT("cancel")); + $msg->printConfirm(); + + require(AT_INCLUDE_PATH.'footer.inc.php'); +} + +?> \ No newline at end of file diff --git a/mods/_core/modules/module_uninstall_step_2.php b/mods/_core/modules/module_uninstall_step_2.php new file mode 100644 index 000000000..0307c03dd --- /dev/null +++ b/mods/_core/modules/module_uninstall_step_2.php @@ -0,0 +1,76 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/index.php?'.urlencode($_POST["args"])); + } + + exit; +} +else if (isset($_POST['submit_yes'], $_POST['mod'])) +{ + $module = $moduleFactory->getModule($_POST['mod']); + $module->uninstall($_POST['del_data']); + + if ($_POST['permission_granted']==1) + { + header('Location: '.AT_BASE_HREF.'mods/_core/modules/module_uninstall_step_3.php?uninstalled=1'.SEP.'args='.urlencode($_POST["args"])); + } + else + { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_core/modules/index.php?'.$_POST['args']); + } + + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
      + + + + +
      +
      +
      + +
      + +
      + + +
      +
      +
      + + \ No newline at end of file diff --git a/mods/_core/modules/module_uninstall_step_3.php b/mods/_core/modules/module_uninstall_step_3.php new file mode 100644 index 000000000..54ba11554 --- /dev/null +++ b/mods/_core/modules/module_uninstall_step_3.php @@ -0,0 +1,70 @@ +addFeedback('CANCELLED'); + } + + if ($uninstalled == 1) + { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + + header('Location: '.AT_BASE_HREF.'mods/_core/modules/index.php?'.$_POST['args']); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +unset($hidden_vars); +$hidden_vars['cancelled'] = $cancelled; +$hidden_vars['uninstalled'] = $uninstalled; +$hidden_vars['args'] = $args; +$hidden_vars['mod'] = $mod; + +$msg->addConfirm(array('REMOVE_WRITE_PERMISSION', realpath($mods_folder)), $hidden_vars, _AT("continue"), '', true); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_core/modules/version_history.php b/mods/_core/modules/version_history.php new file mode 100644 index 000000000..59e652d1f --- /dev/null +++ b/mods/_core/modules/version_history.php @@ -0,0 +1,173 @@ +addError($infos); + + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printAll(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +if (isset($_GET["id"])) $id = intval($_GET["id"]); +else if (isset($_POST["id"])) $id = intval($_POST["id"]); + +// get module list +$module_folder = "http://" . $update_server . '/modules/'; + +$module_list_xml = @file_get_contents($module_folder . 'module_list.xml'); + +if ($module_list_xml) +{ + $moduleListParser = new ModuleListParser(); + $moduleListParser->parse($module_list_xml); + $module_list_array = $moduleListParser->getParsedArray(); +} +// end of get module list + +$module_content_folder = AT_CONTENT_DIR . "module"; + +//debug($_POST["vid"]); +//exit; +// download process +if (isset($_POST["download"]) && !isset($_POST["vid"])) +{ + $msg->addError('NO_ITEM_SELECTED'); +} +else if ($_POST['download']) +{ + $vid = intval($_POST['vid']); + $file_content = @file_get_contents($module_folder . $module_list_array[$id]['history'][$vid]['location'].$module_list_array[$id]['history'][$vid]['filename']); + + if (!$file_content) + { + $msg->addError('FILE_NOT_EXIST'); + } + else + { + header('Content-Type: application/x-zip'); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="'.htmlspecialchars($module_list_array[$id]['history'][$vid]['filename']).'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: '.strlen($file_content)); + + echo $file_content; + exit; + } +} + +require (AT_INCLUDE_PATH.'header.inc.php'); + +$msg->printErrors(); +?> + +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
      + + +
      + +
      + +
      +
      + + diff --git a/mods/_core/properties/access.php b/mods/_core/properties/access.php new file mode 100644 index 000000000..2b1528f91 --- /dev/null +++ b/mods/_core/properties/access.php @@ -0,0 +1,162 @@ +addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} else if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} else if (isset($_POST['submit'])) { + $auth = intval($_POST['auth']); + + //expiry date + if (intval($_POST['expiry_date'])) { + $day_expire = intval($_POST['day_expire']); + $month_expire = intval($_POST['month_expire']); + $year_expire = intval($_POST['year_expire']); + $hour_expire = intval($_POST['hour_expire']); + $min_expire = intval($_POST['min_expire']); + + if (strlen($month_expire) == 1){ + $month_expire = "0$month_expire"; + } + if (strlen($day_expire) == 1){ + $day_expire = "0$day_expire"; + } + if (strlen($hour_expire) == 1){ + $hour_expire = "0$hour_expire"; + } + if (strlen($min_expire) == 1){ + $min_expire = "0$min_expire"; + } + $expiry_date = "$year_expire-$month_expire-$day_expire $hour_expire:$min_expire:00"; + } else { + $expiry_date = 0; + } + + $sql = "UPDATE ".TABLE_PREFIX."course_access SET `expiry_date`='$expiry_date', enabled=$auth WHERE course_id=".$_SESSION['course_id']; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($system_courses[$_SESSION['course_id']]['access'] == 'public') { + // if this course is public, then we can't use this feature + echo '
      '; + $msg->printInfos('ACCESS_PUBLIC'); + echo '
      '; + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$sql = "SELECT password, expiry_date+0 AS expiry_date, enabled FROM ".TABLE_PREFIX."course_access WHERE course_id=".$_SESSION['course_id']; +$result = mysql_query($sql, $db); + +if ($row = mysql_fetch_assoc($result)) { + $enabled = $row['enabled']; + $password = $row['password']; + $expiry = $row['expiry_date']; +} else { + $enabled = 0; + $password = strtoupper(substr(md5(rand()), 3, 8)); + $expiry = 0; + $sql = "INSERT INTO ".TABLE_PREFIX."course_access VALUES ('$password', {$_SESSION['course_id']},'0000-00-00 00:00:00', 0)"; + $result = mysql_query($sql, $db); +} +$url = AT_BASE_HREF.'acl.php?'.$password; + +?> +
      +
      +
      +
      + +
      +
      +
      + +
      +
      + +
      +
      +
      + +
      +
      +
      +
      + /> /> +
      + +
      +
      + + + />
      + + /> + +
      + +
      + + +
      +
      +
      +
      + \ No newline at end of file diff --git a/mods/_core/properties/admin/delete_course.php b/mods/_core/properties/admin/delete_course.php new file mode 100644 index 000000000..d89657b3c --- /dev/null +++ b/mods/_core/properties/admin/delete_course.php @@ -0,0 +1,51 @@ +addFeedback('CANCELLED'); + header('Location: ../../courses/admin/courses.php'); + exit; +} else if (isset($_POST['step']) && ($_POST['step'] == 2) && isset($_POST['submit_yes'])) { + require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + require(AT_INCLUDE_PATH.'../mods/_core/properties/lib/delete_course.inc.php'); + + delete_course($course, $entire_course = true, $rel_path = '../'); // delete the course + cache_purge('system_courses','system_courses'); // purge the system_courses cache (if successful) + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: ../../courses/admin/courses.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (!isset($_POST['step'])) { + $hidden_vars['step'] = 1; + $hidden_vars['course'] = $course; + $msg->addConfirm(array('DELETE_COURSE_1', $system_courses[$course]['title']), $hidden_vars); + $msg->printConfirm(); +} else if ($_POST['step'] == 1) { + $hidden_vars['step'] = 2; + $hidden_vars['course'] = $course; + $msg->addConfirm(array('DELETE_COURSE_2', $system_courses[$course]['title']), $hidden_vars); + $msg->printConfirm(); +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/properties/admin/edit_course.php b/mods/_core/properties/admin/edit_course.php new file mode 100644 index 000000000..f3b6955f7 --- /dev/null +++ b/mods/_core/properties/admin/edit_course.php @@ -0,0 +1,46 @@ +addFeedback('CANCELLED'); + header('Location: ../../courses/admin/courses.php'); + exit; +} else if (isset($_POST['submit'])) { + require(AT_INCLUDE_PATH.'../mods/_core/courses/lib/course.inc.php'); + $errors = add_update_course($_POST, TRUE); + + if (is_numeric($errors)) { + $msg->addFeedback('COURSE_PROPERTIES'); + header('Location: '.AT_BASE_HREF.'mods/_core/courses/admin/courses.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$msg->printAll(); + +$course = intval($_REQUEST['course']); +$isadmin = TRUE; + + +require(AT_INCLUDE_PATH.'../mods/_core/courses/html/course_properties.inc.php'); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/properties/course_properties.php b/mods/_core/properties/course_properties.php new file mode 100644 index 000000000..bb00b7e8e --- /dev/null +++ b/mods/_core/properties/course_properties.php @@ -0,0 +1,70 @@ +addFeedback('CANCELLED'); + header('Location: ../../../tools/index.php'); + exit; + + +}else if($_POST['submit']){ + require(AT_INCLUDE_PATH.'../mods/_core/properties/lib/course.inc.php'); + $_POST['instructor'] = $_SESSION['member_id']; + + $errors = add_update_course($_POST); + + if (is_numeric($errors)) { + $msg->addFeedback('COURSE_PROPERTIES'); + header('Location: '.AT_BASE_HREF.'tools/index.php'); + exit; + } + +//}else if(($_POST['setvisual'] && !$_POST['settext']) || $_GET['setvisual'])){ +} else if (($_POST['setvisual'] || $_POST['settext'])){ + //header('Location: '.$_SESSION['PHP_SELF'].''); + //exit; +} else if (isset($_POST['course'])) { + require(AT_INCLUDE_PATH.'mods/_core/properties/lib/course.inc.php'); + $_POST['instructor'] = $_SESSION['member_id']; + + $errors = add_update_course($_POST); + + if (is_numeric($errors)) { + $msg->addFeedback('COURSE_PROPERTIES'); + header('Location: '.AT_BASE_HREF.'tools/index.php'); + exit; + } +} + +$onload = 'document.course_form.title.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +require(AT_INCLUDE_PATH.'../mods/_core/courses/html/course_properties.inc.php'); + +require(AT_INCLUDE_PATH.'footer.inc.php'); + + +?> \ No newline at end of file diff --git a/mods/_core/properties/delete_course.php b/mods/_core/properties/delete_course.php new file mode 100644 index 000000000..e2127d4e2 --- /dev/null +++ b/mods/_core/properties/delete_course.php @@ -0,0 +1,49 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/properties/course_properties.php'); + exit; +} else if (isset($_POST['step']) && ($_POST['step'] == 2) && isset($_POST['submit_yes'])) { + require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + require(AT_INCLUDE_PATH.'../mods/_core/properties/lib/delete_course.inc.php'); + + delete_course($_SESSION['course_id'], $entire_course = true); // delete the course + cache_purge('system_courses','system_courses'); // purge the system_courses cache (if successful) + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'bounce.php?course=0'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (!isset($_POST['step'])) { + $hidden_vars['step'] = 1; + $msg->addConfirm(array('DELETE_COURSE_1', $system_courses[$_SESSION['course_id']]['title']), $hidden_vars); + $msg->printConfirm(); +} else if ($_POST['step'] == 1) { + $hidden_vars['step'] = 2; + $msg->addConfirm(array('DELETE_COURSE_2', $system_courses[$_SESSION['course_id']]['title']), $hidden_vars); + $msg->printConfirm(); +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/properties/lib/course.inc.php b/mods/_core/properties/lib/course.inc.php new file mode 100644 index 000000000..569b42fc7 --- /dev/null +++ b/mods/_core/properties/lib/course.inc.php @@ -0,0 +1,430 @@ +addError(array('EMPTY_FIELDS', $missing_fields)); + } + + $_POST['access'] = $addslashes($_POST['access']); + $_POST['title'] = $addslashes($_POST['title']); + $_POST['description'] = $addslashes($_POST['description']); + $_POST['hide'] = $addslashes($_POST['hide']); + $_POST['pri_lang'] = $addslashes($_POST['pri_lang']); + $_POST['created_date'] = $addslashes($_POST['created_date']); + $_POST['copyright'] = $addslashes($_POST['copyright']); + $_POST['icon'] = $addslashes($_POST['icon']); + $_POST['banner'] = $addslashes($_POST['banner']); + $_POST['course_dir_name'] = $addslashes($_POST['course_dir_name']); + + $_POST['course'] = intval($_POST['course']); + $_POST['notify'] = intval($_POST['notify']); + $_POST['hide'] = intval($_POST['hide']); + $_POST['instructor']= intval($_POST['instructor']); + $_POST['category_parent'] = intval($_POST['category_parent']); + $_POST['rss'] = intval($_POST['rss']); + + // Course directory name (aka course slug) + if ($_POST['course_dir_name'] != ''){ + //validate the course_dir_name, allow only alphanumeric, dash, underscore. + if (preg_match('/^[\w][\w\d\-\_]+$/', $_POST['course_dir_name'])==0){ + $msg->addError('COURSE_DIR_NAME_INVALID'); + } + + //check if the course_dir_name is already being used + $sql = 'SELECT COUNT(course_id) as cnt FROM '.TABLE_PREFIX."courses WHERE course_id!=$_POST[course] AND course_dir_name='$_POST[course_dir_name]'"; + $result = mysql_query($sql); + $num_of_dir = mysql_fetch_assoc($result); + if (intval($num_of_dir['cnt']) > 0){ + $msg->addError('COURSE_DIR_NAME_IN_USE'); + } + } + + // Custom icon + if ($_FILES['customicon']['name'] != ''){ + // Use custom icon instead if it exists + $_POST['icon'] = $addslashes($_FILES['customicon']['name']); + } + if ($_FILES['customicon']['error'] == UPLOAD_ERR_FORM_SIZE){ + // Check if filesize is too large for a POST + $msg->addError(array('FILE_MAX_SIZE', $_config['prof_pic_max_file_size'] . ' ' . _AT('bytes'))); + } + if ($_POST['release_date']) { + $day_release = intval($_POST['day_release']); + $month_release = intval($_POST['month_release']); + $year_release = intval($_POST['year_release']); + $hour_release = intval($_POST['hour_release']); + $min_release = intval($_POST['min_release']); + + if (!checkdate($month_release, $day_release, $year_release)) { //or date is in the past + $msg->addError('RELEASE_DATE_INVALID'); + } + + if (strlen($month_release) == 1){ + $month_release = "0$month_release"; + } + if (strlen($day_release) == 1){ + $day_release = "0$day_release"; + } + if (strlen($hour_release) == 1){ + $hour_release = "0$hour_release"; + } + if (strlen($min_release) == 1){ + $min_release = "0$min_release"; + } + $release_date = "$year_release-$month_release-$day_release $hour_release:$min_release:00"; + } else { + $release_date = "0000-00-00 00:00:00"; + } + + if ($_POST['end_date']) { + $day_end = intval($_POST['day_end']); + $month_end = intval($_POST['month_end']); + $year_end = intval($_POST['year_end']); + $hour_end = intval($_POST['hour_end']); + $min_end = intval($_POST['min_end']); + + if (!checkdate($month_end, $day_end, $year_end)) { //or date is in the past + $msg->addError('END_DATE_INVALID'); + } + + if (strlen($month_end) == 1){ + $month_end = "0$month_end"; + } + if (strlen($day_end) == 1){ + $day_end = "0$day_end"; + } + if (strlen($hour_end) == 1){ + $hour_end = "0$hour_end"; + } + if (strlen($min_end) == 1){ + $min_end = "0$min_end"; + } + $end_date = "$year_end-$month_end-$day_end $hour_end:$min_end:00"; + } else { + $end_date = "0000-00-00 00:00:00"; + } + + $initial_content_info = explode('_', $_POST['initial_content'], 2); + //admin + $course_quotas = ''; + if ($isadmin) { + $instructor = $_POST['instructor']; + $quota = intval($_POST['quota']); + $quota_entered = intval($_POST['quota_entered']); + $filesize = intval($_POST['filesize']); + $filesize_entered= intval($_POST['filesize_entered']); + + //if they checked 'other', set quota=entered value, if it is empty or negative, set to default (-2) + if ($quota == '2') { + if ($quota_entered=='' || empty($quota_entered) || $quota_entered<0 ) { + $quota = AT_COURSESIZE_DEFAULT; + } else { + $quota = floatval($quota_entered); + $quota = megabytes_to_bytes($quota); + } + } + + //if they checked 'other', set filesize=entered value, if it is empty or negative, set to default + if ($filesize=='2') { + if ($filesize_entered=='' || empty($filesize_entered) || $filesize_entered<0 ) { + $filesize = AT_FILESIZE_DEFAULT; + $msg->addFeedback('COURSE_DEFAULT_FSIZE'); + } else { + $filesize = floatval($filesize_entered); + $filesize = megabytes_to_bytes($filesize); + } + } + + $course_quotas = "max_quota='$quota', max_file_size='$filesize',"; + + } else { + $instructor = $_SESSION['member_id']; + if (!$_POST['course']) { + $course_quotas = "max_quota=".AT_COURSESIZE_DEFAULT.", max_file_size=".AT_FILESIZE_DEFAULT.","; + $row = $Backup->getRow($initial_content_info[0], $initial_content_info[1]); + + if ((count($initial_content_info) == 2) + && ($system_courses[$initial_content_info[1]]['member_id'] == $_SESSION['member_id'])) { + + if ($MaxCourseSize < $row['contents']['file_manager']) { + $msg->addError('RESTORE_TOO_BIG'); + } + } else { + $initial_content_info = intval($_POST['initial_content']); + } + + } else { + unset($initial_content_info); + $course_quotas = "max_quota='{$system_courses[$_POST[course]][max_quota]}', max_file_size='{$system_courses[$_POST[course]][max_file_size]}',"; + } + } + + if ($msg->containsErrors()) { + return FALSE; + } + + //display defaults + if (!$_POST['course']) { + $menu_defaults = ",home_links='$_config[home_defaults]', main_links='$_config[main_defaults]', side_menu='$_config[side_defaults]'"; + } else { + $menu_defaults = ',home_links=\''.$system_courses[$_POST['course']]['home_links'].'\', main_links=\''.$system_courses[$_POST['course']]['main_links'].'\', side_menu=\''.$system_courses[$_POST['course']]['side_menu'].'\''; + } + + $sql = "REPLACE INTO ".TABLE_PREFIX."courses SET course_id=$_POST[course], member_id='$_POST[instructor]', access='$_POST[access]', title='$_POST[title]', description='$_POST[description]', course_dir_name='$_POST[course_dir_name]', cat_id='$_POST[category_parent]', content_packaging='$_POST[content_packaging]', notify=$_POST[notify], hide=$_POST[hide], $course_quotas primary_language='$_POST[pri_lang]', created_date='$_POST[created_date]', rss=$_POST[rss], copyright='$_POST[copyright]', icon='$_POST[icon]', banner='$_POST[banner]', release_date='$release_date', end_date='$end_date' $menu_defaults"; + + $result = mysql_query($sql, $db); + if (!$result) { + echo mysql_error($db); + echo 'DB Error'; + exit; + } + $_SESSION['is_admin'] = 1; + $new_course_id = $_SESSION['course_id'] = mysql_insert_id($db); + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_REPLACE, 'courses', mysql_affected_rows($db), $sql); + } + + if ($isadmin) { + //get current instructor and unenroll from course if different from POST instructor + $old_instructor = $system_courses[$_POST['course']]['member_id']; + + if ($old_instructor != $_POST['instructor']) { + //remove old from course enrollment + $sql = "DELETE FROM ".TABLE_PREFIX."course_enrollment WHERE course_id=".$_POST['course']." AND member_id=".$old_instructor; + $result = mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'course_enrollment', mysql_affected_rows($db), $sql); + } + } + + //enroll new instructor + $sql = "INSERT INTO ".TABLE_PREFIX."course_enrollment VALUES ($_POST[instructor], $new_course_id, 'y', 0, '"._AT('instructor')."', 0)"; + $result = mysql_query($sql, $db); + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_REPLACE, 'course_enrollment', mysql_affected_rows($db), $sql); + } + + // create the course content directory + $path = AT_CONTENT_DIR . $new_course_id . '/'; + @mkdir($path, 0700); + @copy(AT_CONTENT_DIR . 'index.html', AT_CONTENT_DIR . $new_course_id . '/index.html'); + + // create the course backup directory + $path = AT_BACKUP_DIR . $new_course_id . '/'; + @mkdir($path, 0700); + @copy(AT_CONTENT_DIR . 'index.html', AT_BACKUP_DIR . $new_course_id . '/index.html'); + + /* insert some default content: */ + + if (!$_POST['course_id'] && ($_POST['initial_content'] == '1')) { + $contentManager = new ContentManager($db, $new_course_id); + $contentManager->initContent( ); + + $cid = $contentManager->addContent($new_course_id, 0, 1,_AT('welcome_to_atutor'), + addslashes(_AT('this_is_content')), + '', '', 1, date('Y-m-d H:00:00')); + + $announcement = _AT('default_announcement'); + + $sql = "INSERT INTO ".TABLE_PREFIX."news VALUES (NULL, $new_course_id, $instructor, NOW(), 1, '"._AT('welcome_to_atutor')."', '$announcement')"; + $result = mysql_query($sql,$db); + + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_INSERT, 'news', mysql_affected_rows($db), $sql); + } + + /** + * removed - #3098 + // create forum for Welcome Course + $sql = "INSERT INTO ".TABLE_PREFIX."forums VALUES (NULL, '"._AT('forum_general_discussion')."', '', 0, 0, NOW())"; + $result = mysql_query($sql,$db); + + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_INSERT, 'forums', mysql_affected_rows($db), $sql); + } + + $sql = "INSERT INTO ".TABLE_PREFIX."forums_courses VALUES (LAST_INSERT_ID(), $new_course_id)"; + $result = mysql_query($sql,$db); + + if ($isadmin) { + write_to_log(AT_ADMIN_LOG_INSERT, 'forums_courses', mysql_affected_rows($db), $sql); + } + ***/ + + } else if (!$_POST['course'] && (count($initial_content_info) == 2)){ + + $Backup->setCourseID($new_course_id); + $Backup->restore($material = TRUE, 'append', $initial_content_info[0], $initial_content_info[1]); + } + + // custom icon, have to be after directory is created +// $_FILES['customicon'] = $_POST['customicon']; //copy to $_FILES. + if($_FILES['customicon']['tmp_name'] != ''){ + $_POST['comments'] = trim($_POST['comments']); + + $owner_id = $_SESSION['course_id']; + $owner_type = "1"; + if ($_FILES['customicon']['error'] == UPLOAD_ERR_INI_SIZE) { + $msg->addError(array('FILE_TOO_BIG', get_human_size(megabytes_to_bytes(substr(ini_get('upload_max_filesize'), 0, -1))))); + } else if (!isset($_FILES['customicon']['name']) || ($_FILES['customicon']['error'] == UPLOAD_ERR_NO_FILE) || ($_FILES['customicon']['size'] == 0)) { + $msg->addError('FILE_NOT_SELECTED'); + + } else if ($_FILES['customicon']['error'] || !is_uploaded_file($_FILES['customicon']['tmp_name'])) { + $msg->addError('FILE_NOT_SAVED'); + } + + if (!$msg->containsErrors()) { + $_POST['description'] = $addslashes(trim($_POST['description'])); + $_FILES['customicon']['name'] = addslashes($_FILES['customicon']['name']); + + if ($_POST['comments']) { + $num_comments = 1; + } else { + $num_comments = 0; + } + + $path = AT_CONTENT_DIR.$owner_id."/custom_icons/"; + + if (!is_dir($path)) { + @mkdir($path); + } + + // if we can upload custom course icon, it means GD is enabled, no need to check extension again. + $gd_info = gd_info(); + $supported_images = array(); + if ($gd_info['GIF Create Support']) { + $supported_images[] = 'gif'; + } + if ($gd_info['JPG Support']) { + $supported_images[] = 'jpg'; + } + if ($gd_info['PNG Support']) { + $supported_images[] = 'png'; + } + + // check if this is a supported file type + $filename = $stripslashes($_FILES['customicon']['name']); + $path_parts = pathinfo($filename); + $extension = strtolower($path_parts['extension']); + $image_attributes = getimagesize($_FILES['customicon']['tmp_name']); + + if ($extension == 'jpeg') { + $extension = 'jpg'; + } + + // resize the original but don't backup a copy. + $width = $image_attributes[0]; + $height = $image_attributes[1]; + $original_img = $_FILES['customicon']['tmp_name']; + $thumbnail_img = $path . $_FILES['customicon']['name']; + + if ($width > $height && $width>79) { + $thumbnail_height = intval(79 * $height / $width); + $thumbnail_width = 79; + if (!resize_image($original_img, $thumbnail_img, $height, $width, $thumbnail_height, $thumbnail_width, $extension)){ + $msg->addError('FILE_NOT_SAVED'); + } + } else if ($width <= $height && $height > 79) { + $thumbnail_height= 100; + $thumbnail_width = intval(100 * $width / $height); + if (!resize_image($original_img, $thumbnail_img, $height, $width, $thumbnail_height, $thumbnail_width, $extension)){ + $msg->addError('FILE_NOT_SAVED'); + } + } else { + // no resizing, just copy the image. + // it's too small to resize. + copy($original_img, $thumbnail_img); + } + + } else { + $msg->addError('FILE_NOT_SAVED'); + + } + //header('Location: index.php'.$owner_arg_prefix.'folder='.$parent_folder_id); + //exit; + } + //---------------------------------------- + + /* delete the RSS feeds just in case: */ + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $new_course_id . '/RSS1.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $_POST['course'] . '/RSS1.0.xml'); + } + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $new_course_id . '/RSS2.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $new_course_id . '/RSS2.0.xml'); + } + + if ($isadmin) { + $_SESSION['course_id'] = -1; + } + + $_SESSION['course_title'] = $stripslashes($_POST['title']); + return $new_course_id; +} + +?> diff --git a/mods/_core/properties/lib/delete_course.inc.php b/mods/_core/properties/lib/delete_course.inc.php new file mode 100644 index 000000000..38f9f878b --- /dev/null +++ b/mods/_core/properties/lib/delete_course.inc.php @@ -0,0 +1,70 @@ +getModules(AT_MODULE_STATUS_ENABLED | AT_MODULE_STATUS_DISABLED); + $keys = array_keys($module_list); + + //loop through mods and call delete function + foreach ($keys as $module_name) { + if ($module_name == '_core/groups') { + continue; + } + if ($module_name == '_core/enrolment') { + continue; + } + $module =& $module_list[$module_name]; + + if (($material === TRUE) || isset($material[$module_name])) { + $module->delete($course, $groups); + } + } + + // groups and enrollment must be deleted last because that info is used by other modules + + if (($material === TRUE) || isset($material['_core/groups'])) { + $module =& $moduleFactory->getModule('_core/groups'); + $module->delete($course, $groups); + } + if (($material === TRUE) || isset($material['_core/enrolment'])) { + $module =& $moduleFactory->getModule('_core/enrolment'); + $module->delete($course, $groups); + } + + if ($material === TRUE) { + // delete actual course + $sql = "DELETE FROM ".TABLE_PREFIX."courses WHERE course_id=$course"; + $result = mysql_query($sql, $db); + } +} +?> \ No newline at end of file diff --git a/mods/_core/properties/module.php b/mods/_core/properties/module.php new file mode 100644 index 000000000..c4281cf8a --- /dev/null +++ b/mods/_core/properties/module.php @@ -0,0 +1,46 @@ +getPrivilege()); +} + +//admin pages +$this->_pages['mods/_core/properties/admin/edit_course.php']['title_var'] = 'course_properties'; +$this->_pages['mods/_core/properties/admin/edit_course.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + +$this->_pages['mods/_core/properties/admin/delete_course.php']['title_var'] = 'delete_course'; +$this->_pages['mods/_core/properties/admin/delete_course.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + + +//instructor pages +$this->_pages['mods/_core/properties/course_properties.php']['title_var'] = 'properties'; +$this->_pages['mods/_core/properties/course_properties.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_core/properties/course_properties.php']['children'] = array('mods/_core/properties/delete_course.php', 'mods/_core/properties/access.php'); +$this->_pages['mods/_core/properties/course_properties.php']['guide'] = 'instructor/?p=properties.php'; + + $this->_pages['mods/_core/properties/delete_course.php']['title_var'] = 'delete_course'; + $this->_pages['mods/_core/properties/delete_course.php']['parent'] = 'mods/_core/properties/course_properties.php'; + + $this->_pages['mods/_core/properties/access.php']['title_var'] = 'authenticated_access'; + $this->_pages['mods/_core/properties/access.php']['parent'] = 'mods/_core/properties/course_properties.php'; + $this->_pages['mods/_core/properties/access.php']['guide'] = 'instructor/?p=authenticated_access.php'; + +?> \ No newline at end of file diff --git a/mods/_core/properties/module.xml b/mods/_core/properties/module.xml new file mode 100644 index 000000000..21f8865d4 --- /dev/null +++ b/mods/_core/properties/module.xml @@ -0,0 +1,23 @@ + + + Course Properties + Allows instructors and administrators to delete a course, or manage its various properties such as title, description, access level, course icon and more. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + existing + + + 2005-08-22 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/properties/module_delete.php b/mods/_core/properties/module_delete.php new file mode 100644 index 000000000..87053bcaa --- /dev/null +++ b/mods/_core/properties/module_delete.php @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/mods/_core/themes/classes/ThemeListParser.class.php b/mods/_core/themes/classes/ThemeListParser.class.php new file mode 100644 index 000000000..ae87ba394 --- /dev/null +++ b/mods/_core/themes/classes/ThemeListParser.class.php @@ -0,0 +1,148 @@ +parser = xml_parser_create(''); + + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($this->parser, 'startElement', 'endElement'); + xml_set_character_data_handler($this->parser, 'characterData'); + } + + // public + function parse($xml_data) { + $this->element_path = array(); + $this->theme_rows = array(); + $this->character_data = ''; + $this->row_num = 0; + $this->history_num = 0; + xml_parse($this->parser, $xml_data, TRUE); + } + + // private + function startElement($parser, $name, $attributes) + { + array_push($this->element_path, $name); + } + + // private + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) { + if ($this->element_path == array('theme_list', 'theme', 'name')) + { + $this->theme_rows[$this->row_num]['name'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'atutor_version')) + { + $this->theme_rows[$this->row_num]['atutor_version'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'description')) + { + $this->theme_rows[$this->row_num]['description'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history')) + { + $this->history_num = 0; + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release')) + { + $this->history_num++; + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'version')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['version'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'atutor_version')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['atutor_version'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'filename')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['filename'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'location')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['location'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'screenshot_file')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['screenshot_file'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'install_folder')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['install_folder'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'date')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['date'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'state')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['state'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'maintainer')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['maintainer'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme', 'history', 'release', 'notes')) + { + $this->theme_rows[$this->row_num]['history'][$this->history_num]['notes'] = trim($this->character_data); + } + else if ($this->element_path === array('theme_list', 'theme')) + { + $this->row_num++; + } + + array_pop($this->element_path); + $this->character_data = ''; + } + + // private + function characterData($parser, $data){ + $this->character_data .= $data; + } + + // public + function getNumOfThemes() + { + return count($this->theme_rows); + } + + // public + function getParsedArray() + { + return $this->theme_rows; + } +} + +?> \ No newline at end of file diff --git a/mods/_core/themes/classes/ThemeParser.class.php b/mods/_core/themes/classes/ThemeParser.class.php new file mode 100644 index 000000000..530208c2d --- /dev/null +++ b/mods/_core/themes/classes/ThemeParser.class.php @@ -0,0 +1,87 @@ +parser = xml_parser_create(); + + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($this->parser, 'startElement', 'endElement'); + xml_set_character_data_handler($this->parser, 'characterData'); + } + + // public + function parse($xml_data) { + $this->element_path = array(); + $this->theme_rows = array(); + $this->character_data = ''; + xml_parse($this->parser, $xml_data, TRUE); + } + + // private + function startElement($parser, $name, $attributes) { + array_push($this->element_path, $name); + } + + // private + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) { + if ($this->element_path == array('theme', 'dir_name')) { + $this->theme_rows['dir_name'] = trim($this->character_data); + + } else if ($this->element_path == array('theme', 'title')) { + $this->theme_rows['title'] = trim($this->character_data); + + } else if ($this->element_path == array('theme', 'version')) { + $this->theme_rows['version'] = trim($this->character_data); + + } else if ($this->element_path == array('theme', 'type')) { + $this->theme_rows['type'] = trim($this->character_data); + + } else if ($this->element_path == array('theme', 'last_updated')) { + $this->theme_rows['last_updated'] = trim($this->character_data); + + } else if ($this->element_path == array('theme', 'extra_info')) { + $this->theme_rows['extra_info'] = trim($this->character_data); + + } + + array_pop($this->element_path); + $this->character_data = ''; + } + + // private + function characterData($parser, $data){ + $this->character_data .= $data; + } + +} + +?> \ No newline at end of file diff --git a/mods/_core/themes/delete.php b/mods/_core/themes/delete.php new file mode 100644 index 000000000..384e26ff3 --- /dev/null +++ b/mods/_core/themes/delete.php @@ -0,0 +1,40 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + require_once(AT_INCLUDE_PATH.'../mods/_core/themes/lib/themes.inc.php'); + delete_theme($_POST['tc']); + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$hidden_vars['tc'] = $_GET['theme_code']; + +$msg->addConfirm(array('DELETE_THEME', $_GET['theme_code']), $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/themes/import.php b/mods/_core/themes/import.php new file mode 100644 index 000000000..7bb1067d6 --- /dev/null +++ b/mods/_core/themes/import.php @@ -0,0 +1,204 @@ +addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; +} + +/** +* Imports a theme from a URL or Zip file to Atutor +* @access private +* @author Shozub Qureshi +*/ +function import_theme() { + global $db; + global $msg; + + if (isset($_POST['url']) && ($_POST['url'] != 'http://') ) { + if ($content = @file_get_contents($_POST['url'])) { + + // save file to /themes/ + $filename = pathinfo($_POST['url']); + $filename = $filename['basename']; + $full_filename = AT_CONTENT_DIR . '/' . $filename; + + if (!$fp = fopen($full_filename, 'w+b')) { + //Cannot open file ($filename)"; + $errors = array('CANNOT_OPEN_FILE', $filename); + $msg->addError($errors); + header('Location: index.php'); + exit; + } + + if (fwrite($fp, $content, strlen($content) ) === FALSE) { + //"Cannot write to file ($filename)"; + $errors = array('CANNOT_WRITE_FILE', $filename); + $msg->addError($errors); + header('Location: index.php'); + exit; + } + fclose($fp); + } + $_FILES['file']['name'] = $filename; + $_FILES['file']['tmp_name'] = $full_filename; + $_FILES['file']['size'] = strlen($content); + unset($content); + $url_parts = pathinfo($_POST['url']); + $package_base_name_url = $url_parts['basename']; + } + $ext = pathinfo($_FILES['file']['name']); + $ext = $ext['extension']; + + //error in the file + if ($_FILES['file']['error'] == 1) { + $errors = array('FILE_MAX_SIZE', ini_get('upload_max_filesize')); + $msg->addError($errors); + header('Location: index.php'); + exit; + } + + //If file has no name or no address or if the extension is not .zip + if (!$_FILES['file']['name'] + || (!is_uploaded_file($_FILES['file']['tmp_name']) && !$_POST['url'])) { + + $msg->addError('FILE_NOT_SELECTED'); + header('Location: index.php'); + exit; + } + + if (($ext != 'zip')) { + $msg->addError('IMPORT_NOT_PROPER_FORMAT'); + header('Location: index.php'); + exit; + } + + //check if file size is ZERO + if ($_FILES['file']['size'] == 0) { + $msg->addError('IMPORTFILE_EMPTY'); + header('Location: index.php'); + exit; + } + + // new directory name is the filename minus the extension + $fldrname = substr($_FILES['file']['name'], 0, -4); + $fldrname = str_replace(' ', '_', $fldrname); + $import_path = '../../../themes/' . $fldrname; + + //check if Folder by that name already exists + if (is_dir($import_path)) { + $i = 1; + while (is_dir($import_path . '_' . $i)) { + $i++; + } + $fldrname = $fldrname . '_' . $i; + $import_path = $import_path . '_' . $i; + } + + //if folder does not exist previously + if (!@mkdir($import_path, 0700)) { + $msg->addError('IMPORTDIR_FAILED'); + header('Location: index.php'); + exit; + } + + // unzip file and save into directory in themes + $archive = new PclZip($_FILES['file']['tmp_name']); + + //extract contents to importpath/foldrname + if (!$archive->extract($import_path)) { + $errors = array('IMPORT_ERROR_IN_ZIP', $archive->errorInfo(true)); + clr_dir($import_path); + $msg->addError($errors); + header('Location: index.php'); + exit; + } + + $handle = opendir($import_path); + while ($file = readdir($handle)) { + if (is_dir($import_path.'/'.$file) && $file != '.' && $file != '..') { + $folder = $file; + } + } + + //copy contents from importpath/foldrname to importpath + copys($import_path.'/'.$folder, $import_path); + + //delete importpath/foldrname + clr_dir($import_path.'/'.$folder); + + $theme_xml = @file_get_contents($import_path . '/theme_info.xml'); + + //Check if XML file exists (if it doesnt send error and clear directory) + if ($theme_xml == false) { + /** Next version 1.4.4, require themes.xml + $msg->addError('MISSING_THEMEXML'); + + // clean up + clr_dir($import_path); + + header('Location: index.php'); + exit; + */ + $version = '1.4.x'; + $extra_info = 'unspecified'; + } else { + //parse information + $xml_parser = new ThemeParser(); + $xml_parser->parse($theme_xml); + + $version = $xml_parser->theme_rows['version']; + $extra_info = $xml_parser->theme_rows['extra_info']; + $type = $xml_parser->theme_rows['type']; + } + + $title = str_replace('_', ' ', $fldrname); + $last_updated = date('Y-m-d'); + $status = '1'; + + //if version number is not compatible with current Atutor version, set theme as disabled + if ($version != VERSION) { + $status = '0'; + } + + //save information in database + $sql = "INSERT INTO ".TABLE_PREFIX."themes VALUES ('$title', '$version', '$fldrname', '$type', '$last_updated', '$extra_info', '$status')"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_INSERT, 'themes', mysql_affected_rows($db), $sql); + + if (!$result) { + $msg->addError('IMPORT_FAILED'); + header('Location: index.php'); + exit; + } + + if (isset($_POST['url'])) { + @unlink($full_filename); + } +} + +?> \ No newline at end of file diff --git a/mods/_core/themes/index.php b/mods/_core/themes/index.php new file mode 100644 index 000000000..7350d8308 --- /dev/null +++ b/mods/_core/themes/index.php @@ -0,0 +1,178 @@ +addWarning($warnings); + } + enable_theme($theme); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} else if (isset($_GET['disable'], $_GET['theme_dir'])) { + disable_theme($theme); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} else if (isset($_GET['preview'], $_GET['theme_dir'])) { + $_SESSION['prefs']['PREF_THEME'] = $_GET['theme_dir']; + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} else if (isset($_GET['disable']) || isset($_GET['enable']) || isset($_GET['default']) || isset($_GET['delete']) || isset($_GET['export'])) { + $msg->addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> + + +
      +
      + +
      +
      + +
      +
      +
      +

      +
      + +
      +
      + +
      + +
      +
      + +
      + +
      + +
      +
      +
      +
      +
      '; +$sql = "SELECT * FROM " . TABLE_PREFIX . "themes WHERE type='".MOBILE_DEVICE."' ORDER BY title ASC"; +$result = mysql_query($sql, $db); +print_data_table($result, MOBILE_DEVICE); +?> + + +


      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
      + + + + + + +
      + + + '._AT('default').''; + } + ?> + / + <?php echo _AT('theme_screenshot'); ?> + + <?php echo _AT('theme_screenshot'); ?> + +
      +
      + \ No newline at end of file diff --git a/mods/_core/themes/install_themes.php b/mods/_core/themes/install_themes.php new file mode 100644 index 000000000..612e13fcf --- /dev/null +++ b/mods/_core/themes/install_themes.php @@ -0,0 +1,332 @@ +addError($infos); + + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printAll(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +// get theme list +$theme_folder = $update_server . '/themes/'; +$local_theme_folder = "../../../themes/"; + +$theme_list_xml = @file_get_contents($theme_folder . 'theme_list.xml'); + +if ($theme_list_xml) +{ + $themeListParser = new ThemeListParser(); + $themeListParser->parse($theme_list_xml); + $theme_list_array = $themeListParser->getParsedArray(); +} +// end of get theme list + +$theme_content_folder = AT_CONTENT_DIR . "theme/"; + +// create theme content dir if not exists +if (!is_dir($theme_content_folder)) mkdir($theme_content_folder); + +// Installation process +if ((isset($_POST['install']) || isset($_POST["download"]) || isset($_POST["version_history"])) && !isset($_POST["id"])) +{ + $msg->addError('NO_ITEM_SELECTED'); +} +else if (isset($_POST['install']) || isset($_POST["download"]) || isset($_POST["version_history"]) || isset($_POST["import"])) +{ + if ($_POST['version_history']) + { + header('Location: '.AT_BASE_HREF.'mods/_core/themes/version_history.php?id='.$_POST["id"]); + exit; + } + + // install and download + if ($_POST["import"]) + { + if (isset($_POST['url']) && ($_POST['url'] != 'http://') ) + { + $file_content = file_get_contents($_POST['url']); + $filename = pathinfo($_POST['url']); + $filename = $filename['basename']; + } + else + { + $file_content = file_get_contents($_FILES['themefile']['tmp_name']); + $filename = $_FILES['themefile']['name']; + } + } + else + { + $file_content = file_get_contents($theme_folder . $theme_list_array[$_POST["id"]]['history'][0]['location'].$theme_list_array[$_POST["id"]]['history'][0]['filename']); + } + + if (!$file_content & ($_POST['install'] || $_POST['download'])) + { + $msg->addError('FILE_NOT_EXIST'); + } + else + { + if ($_POST['install'] || $_POST['import']) + { + clear_dir($theme_content_folder); + + // download zip file from update.atutor.ca and write into theme content folder + if ($_POST["import"]) + $local_theme_zip_file = $theme_content_folder . $filename; + else + $local_theme_zip_file = $theme_content_folder. $theme_list_array[$_POST["id"]]['history'][0]['filename']; + + $fp = fopen($local_theme_zip_file, "w"); + fwrite($fp, $file_content); + fclose($fp); + + // unzip uploaded file to theme's content directory + include_once(AT_INCLUDE_PATH . '/classes/pclzip.lib.php'); + + $archive = new PclZip($local_theme_zip_file); + + if ($archive->extract(PCLZIP_OPT_PATH, $theme_content_folder) == 0) + { + clear_dir($theme_content_folder); + $msg->addError('CANNOT_UNZIP'); + } + + if (!$msg->containsErrors()) + { + // find unzip theme folder name + clearstatcache(); + + if ($dh = opendir($theme_content_folder)) + { + while (($this_theme_folder = readdir($dh)) !== false) + { + if ($this_theme_folder <> "." && $this_theme_folder <> ".." && is_dir($theme_content_folder.$this_theme_folder)) break; + } + + closedir($dh); + } + + if ($this_theme_folder == "." || $this_theme_folder == ".." || !isset($this_theme_folder)) + $msg->addError('EMPTY_ZIP_FILE'); + } + + // check if the same theme exists in "themes" folder. If exists, it has been installed + if (!$msg->containsErrors()) + { + if (is_dir($local_theme_folder. $this_theme_folder)) + $msg->addError('ALREADY_INSTALLED'); + } + + if (!$msg->containsErrors()) + { + header('Location: theme_install_step_1.php?theme='.urlencode($this_theme_folder).SEP.'title='.urlencode($theme_list_array[$_POST["id"]]["name"])); + exit; + } + } + + if ($_POST['download']) + { + $id = intval($_POST['id']); + + header('Content-Type: application/x-zip'); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="'.htmlspecialchars($theme_list_array[$id]['history'][0]['filename']).'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: '.strlen($file_content)); + + echo $file_content; + exit; + } + } +} + +require (AT_INCLUDE_PATH.'header.inc.php'); + +$msg->printErrors(); + +?> + +
      +
      +
      +

      +
      + +
      + +
      + +
      + +
      +
      + +
      + +
      + +
      +
      +
      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
      + + + +
      /> + " border="1" alt="" /> + +
      +
      + + + + diff --git a/mods/_core/themes/lib/theme_template.inc.php b/mods/_core/themes/lib/theme_template.inc.php new file mode 100644 index 000000000..383fded64 --- /dev/null +++ b/mods/_core/themes/lib/theme_template.inc.php @@ -0,0 +1,16 @@ + + + + + + {TITLE} + {VERSION} + {TYPE} + {LAST_UPDATED} + {EXTRA_INFO} + + +'; +?> \ No newline at end of file diff --git a/mods/_core/themes/lib/themes.inc.php b/mods/_core/themes/lib/themes.inc.php new file mode 100644 index 000000000..747a2b212 --- /dev/null +++ b/mods/_core/themes/lib/themes.inc.php @@ -0,0 +1,307 @@ +addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); +} + +function disable_theme ($theme_dir) { + global $msg, $db; + + $sql = "SELECT status FROM ".TABLE_PREFIX."themes WHERE dir_name = '$theme_dir'"; + $result = mysql_query ($sql, $db); + $row = mysql_fetch_array($result); + $status = intval($row['status']); + + //If default theme, then it cannot be disabled + if ($status == 2) { + $msg->addError('THEME_NOT_DISABLED'); + return; + } else { + $sql = "UPDATE ".TABLE_PREFIX."themes SET status = '0' WHERE dir_name = '$theme_dir'"; + $result = mysql_query($sql, $db); + + $feedback = array('THEME_DISABLED', $theme_dir); + $msg->addFeedback($feedback); + + write_to_log(AT_ADMIN_LOG_UPDATE, 'themes', mysql_affected_rows($db), $sql); + } +} + +function set_theme_as_default ($theme_dir, $type) { + global $msg, $db; + + //unset current default theme + if ($type == MOBILE_DEVICE) { + $default_status = 3; + } else { + $default_status = 2; + } + $sql = "UPDATE ".TABLE_PREFIX."themes SET status = 1 WHERE status = ".$default_status; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_UPDATE, 'themes', mysql_affected_rows($db), $sql); + + //set to default + $sql = "UPDATE ".TABLE_PREFIX."themes SET status = ".$default_status." WHERE dir_name = '$theme_dir'"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + $feedback = array('THEME_DEFAULT', $theme_dir); + $msg->addFeedback($feedback); + $_SESSION['prefs']['PREF_THEME'] = $theme_dir; + + write_to_log(AT_ADMIN_LOG_UPDATE, 'themes', mysql_affected_rows($db), $sql); +} + +function delete_theme ($theme_dir) { + global $msg, $db; + + //check status + $sql = "SELECT status FROM ".TABLE_PREFIX."themes WHERE dir_name='$theme_dir'"; + $result = mysql_query ($sql, $db); + $row = mysql_fetch_assoc($result); + $status = intval($row['status']); + + //can't delete original default or current default theme + if (($theme_dir == 'default') || ($status == 2)) { + $msg->addError('THEME_NOT_DELETED'); + return FALSE; + + } else { //disable, clear directory and delete theme from db + + require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); /* for clr_dir() */ + if ($status != 0) { + disable_theme($theme_dir); + $msg->deleteFeedback('THEME_DISABLED'); + } + + $dir = '../../../themes/' . $theme_dir; + //chmod($dir, 0777); + @clr_dir($dir); + + $sql1 = "DELETE FROM ".TABLE_PREFIX."themes WHERE dir_name = '$theme_dir'"; + $result1 = mysql_query ($sql1, $db); + + write_to_log(AT_ADMIN_LOG_DELETE, 'themes', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + return TRUE; + } +} + +function export_theme($theme_dir) { + require(AT_INCLUDE_PATH.'classes/zipfile.class.php'); /* for zipfile */ + require(AT_INCLUDE_PATH.'classes/XML/XML_HTMLSax/XML_HTMLSax.php'); /* for XML_HTMLSax */ + require('theme_template.inc.php'); /* for theme XML templates */ + + global $db; + + //identify current theme and then searches db for relavent info + $sql = "SELECT * FROM ".TABLE_PREFIX."themes WHERE dir_name = '$theme_dir'"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $dir = $row['dir_name'] . '/'; + $title = $row['title']; + $version = $row['version']; + $type = $row['type']; + $last_updated = $row['last_updated']; + $extra_info = $row['extra_info']; + + + + //generate 'theme_info.xml' file based on info + $info_xml = str_replace(array('{TITLE}', '{VERSION}', '{TYPE}', '{LAST_UPDATED}', '{EXTRA_INFO}'), + array($title, $version, $type, $last_updated, $extra_info), + $theme_template_xml); + + //zip together all the contents of the folder along with the XML file + $zipfile = new zipfile(); + $zipfile->create_dir($dir); + + //update installation folder + $dir1 = '../../../themes/' . $dir; + + $zipfile->add_file($info_xml, $dir . 'theme_info.xml'); + + /* zip other required files */ + $zipfile->add_dir($dir1, $dir); + + /*close & send*/ + $zipfile->close(); + //Name the Zip file and sends to user for download + $zipfile->send_file(str_replace(array(' ', ':'), '_', $title)); +} + +?> \ No newline at end of file diff --git a/mods/_core/themes/module.php b/mods/_core/themes/module.php new file mode 100644 index 000000000..6cb643213 --- /dev/null +++ b/mods/_core/themes/module.php @@ -0,0 +1,42 @@ +getAdminPrivilege()); + +if (admin_authenticate(AT_ADMIN_PRIV_THEMES, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + if (admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + $this->_pages['admin/config_edit.php']['children'] = array('mods/_core/themes/index.php'); + $this->_pages['mods/_core/themes/index.php']['parent'] = 'admin/config_edit.php'; + } else { + $this->_pages[AT_NAV_ADMIN] = array('mods/_core/themes/index.php'); + $this->_pages['mods/_core/themes/index.php']['parent'] = AT_NAV_ADMIN; + } + + + //admin + $this->_pages['mods/_core/themes/index.php']['title_var'] = 'themes'; + $this->_pages['mods/_core/themes/index.php']['guide'] = 'admin/?p=themes.php'; + $this->_pages['mods/_core/themes/index.php']['children'] = array('mods/_core/themes/install_themes.php'); + $this->_pages['mods/_core/themes/install_themes.php']['guide'] = 'admin/?p=importing_themes.php'; + + $this->_pages['mods/_core/themes/delete.php']['title_var'] = 'delete'; + $this->_pages['mods/_core/themes/delete.php']['parent'] = 'mods/_core/themes/index.php'; + + $this->_pages['mods/_core/themes/install_themes.php']['title_var'] = 'install_themes'; + $this->_pages['mods/_core/themes/install_themes.php']['parent'] = 'mods/_core/themes/index.php'; + + $this->_pages['mods/_core/themes/theme_install_step_1.php']['title_var'] = 'details'; + $this->_pages['mods/_core/themes/theme_install_step_1.php']['parent'] = 'mods/_core/themes/install_themes.php'; + + $this->_pages['mods/_core/themes/theme_install_step_2.php']['title_var'] = 'details'; + $this->_pages['mods/_core/themes/theme_install_step_2.php']['parent'] = 'mods/_core/themes/install_themes.php'; + + $this->_pages['mods/_core/themes/theme_install_step_3.php']['title_var'] = 'details'; + $this->_pages['mods/_core/themes/theme_install_step_3.php']['parent'] = 'mods/_core/themes/install_themes.php'; + + $this->_pages['mods/_core/themes/version_history.php']['title_var'] = 'version_history'; + $this->_pages['mods/_core/themes/version_history.php']['parent'] = 'mods/_core/themes/install_themes.php'; + +} +?> \ No newline at end of file diff --git a/mods/_core/themes/module.xml b/mods/_core/themes/module.xml new file mode 100644 index 000000000..4533e751b --- /dev/null +++ b/mods/_core/themes/module.xml @@ -0,0 +1,23 @@ + + + Themes + Allows administrators themes. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + + create + + 2005-09-19 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/themes/theme_install_step_1.php b/mods/_core/themes/theme_install_step_1.php new file mode 100644 index 000000000..6d7b70ff4 --- /dev/null +++ b/mods/_core/themes/theme_install_step_1.php @@ -0,0 +1,53 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/themes/install_themes.php'); + exit; +} +else if (is_writable($theme_folder) || (is_writable($theme_folder) && isset($_POST['submit_yes']))) +{ + header('Location: '.AT_BASE_HREF.'mods/_core/themes/theme_install_step_2.php?theme='.$theme.SEP.'permission_granted='.$_POST["permission_granted"].SEP.'title='.$title); + exit; +} + +if (!is_writable($theme_folder)) +{ + unset($hidden_vars); + $hidden_vars['theme'] = $theme; + $hidden_vars['title'] = $title; + $hidden_vars['permission_granted'] = 1; + + require(AT_INCLUDE_PATH.'header.inc.php'); + + $msg->addConfirm(array('GRANT_WRITE_PERMISSION', realpath($theme_folder)), $hidden_vars, _AT("continue"), _AT("cancel")); + $msg->printConfirm(); + + require(AT_INCLUDE_PATH.'footer.inc.php'); +} + +?> \ No newline at end of file diff --git a/mods/_core/themes/theme_install_step_2.php b/mods/_core/themes/theme_install_step_2.php new file mode 100644 index 000000000..1d7d220b3 --- /dev/null +++ b/mods/_core/themes/theme_install_step_2.php @@ -0,0 +1,99 @@ +parse($theme_xml); + + $version = $xml_parser->theme_rows['version']; + $type = $xml_parser->theme_rows['type']; + $extra_info = $xml_parser->theme_rows['extra_info']; + } + + if ($title == '') $title = str_replace('_', ' ', $theme); + $last_updated = date('Y-m-d'); + $status = '1'; + + //if version number is not compatible with current Atutor version, set theme as disabled + if ($version != VERSION) $status = '0'; + + //save information in database + $sql = "INSERT INTO ".TABLE_PREFIX."themes (title, version, dir_name, type, last_updated, extra_info, status) ". + "VALUES ('$title', '$version', '$theme', '$type', '$last_updated', '$extra_info', '$status')"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_INSERT, 'themes', mysql_affected_rows($db), $sql); +} + +if (!$result) // error occurs +{ + clr_dir("../../themes/".$theme); + + if ($_GET['permission_granted']==1) + { + header('Location: '.AT_BASE_HREF.'mods/_core/themes/theme_install_step_3.php?error=1'); + } + else + { + $msg->addError('IMPORT_FAILED'); + header('Location: '.AT_BASE_HREF.'mods/_core/themes/install_themes.php'); + } +} +else // successful +{ + if ($_GET['permission_granted']==1) + { + header('Location: '.AT_BASE_HREF.'mods/_core/themes/theme_install_step_3.php?installed=1'); + } + else + { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_core/themes/index.php'); + } +} +?> \ No newline at end of file diff --git a/mods/_core/themes/theme_install_step_3.php b/mods/_core/themes/theme_install_step_3.php new file mode 100644 index 000000000..e84628ded --- /dev/null +++ b/mods/_core/themes/theme_install_step_3.php @@ -0,0 +1,60 @@ +addError('IMPORT_FAILED'); + header('Location: '.AT_BASE_HREF.'mods/_core/themes/install_themes.php'); + } + if ($installed == 1) + { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_core/themes/index.php'); + } + + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +unset($hidden_vars); +$hidden_vars['installed'] = $installed; +$hidden_vars['error'] = $error; + +$msg->addConfirm(array('REMOVE_WRITE_PERMISSION', realpath($theme_folder)), $hidden_vars, _AT("continue"), '', true); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); + + + +?> \ No newline at end of file diff --git a/mods/_core/themes/version_history.php b/mods/_core/themes/version_history.php new file mode 100644 index 000000000..262d797b3 --- /dev/null +++ b/mods/_core/themes/version_history.php @@ -0,0 +1,175 @@ +addError($infos); + + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printAll(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +if (isset($_GET["id"])) $id = intval($_GET["id"]); +else if (isset($_POST["id"])) $id = intval($_POST["id"]); + +// get theme list +$theme_folder = "http://" . $update_server . '/themes/'; + +$theme_list_xml = @file_get_contents($theme_folder . 'theme_list.xml'); + +if ($theme_list_xml) +{ + $themeListParser = new ThemeListParser(); + $themeListParser->parse($theme_list_xml); + $theme_list_array = $themeListParser->getParsedArray(); +} +// end of get theme list + +$theme_content_folder = AT_CONTENT_DIR . "theme"; + +//debug($_POST["vid"]); +//exit; +// download process +if (isset($_POST["download"]) && !isset($_POST["vid"])) +{ + $msg->addError('NO_ITEM_SELECTED'); +} +else if ($_POST['download']) +{ + $vid = intval($_POST['vid']); + $file_content = @file_get_contents($theme_folder . $theme_list_array[$id]['history'][$vid]['location'].$theme_list_array[$id]['history'][$vid]['filename']); + + if (!$file_content) + { + $msg->addError('FILE_NOT_EXIST'); + } + else + { + header('Content-Type: application/x-zip'); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="'.htmlspecialchars($theme_list_array[$id]['history'][$vid]['filename']).'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: '.strlen($file_content)); + + echo $file_content; + exit; + } +} + +require (AT_INCLUDE_PATH.'header.inc.php'); + +$msg->printErrors(); +?> + +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
      + + +
      + +
      + +
      +
      + + diff --git a/mods/_core/tool_manager/forums_tool.php b/mods/_core/tool_manager/forums_tool.php new file mode 100644 index 000000000..d81843762 --- /dev/null +++ b/mods/_core/tool_manager/forums_tool.php @@ -0,0 +1,55 @@ + + + + + + +$row['forum_id'], 'title' => $row['title'], 'path' => $path, 'image' => AT_BASE_HREF.'images/home-forums_sm.png'); + } + return $content_list; +} else { + $msg->addInfo('NO_FORUMS'); + $msg->printInfos(); + return; +} + +?> \ No newline at end of file diff --git a/mods/_core/tool_manager/index.php b/mods/_core/tool_manager/index.php new file mode 100644 index 000000000..2d3d81ea4 --- /dev/null +++ b/mods/_core/tool_manager/index.php @@ -0,0 +1,79 @@ + +
      +
      +
      + +


      +printFeedbacks(); + +$sql = "SELECT forum_id FROM ".TABLE_PREFIX."content_forums_assoc WHERE content_id='$cid'"; +if(isset($tool_list)) {?> +
      + + + + + + + + + + + + + + + +
       
      + /> +   +
      +


      + + + +
      + + +
      +
      + diff --git a/mods/_core/tool_manager/module.php b/mods/_core/tool_manager/module.php new file mode 100644 index 000000000..d2cb4c037 --- /dev/null +++ b/mods/_core/tool_manager/module.php @@ -0,0 +1,10 @@ +getPrivilege()); + +$this->_pages['mods/_core/tool_manager/index.php']['title_var'] = 'tool_manager'; +$this->_pages['mods/_core/tool_manager/index.php']['parent'] = 'tools/index.php'; + +?> \ No newline at end of file diff --git a/mods/_core/tool_manager/module.xml b/mods/_core/tool_manager/module.xml new file mode 100644 index 000000000..c0e35e9bc --- /dev/null +++ b/mods/_core/tool_manager/module.xml @@ -0,0 +1,23 @@ + + + Tool Manager + Tools sub-content can be made available to students by linking them into content pages. + + + CRIAD + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + + + 2009-06-24 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/users/admin_delete.php b/mods/_core/users/admin_delete.php new file mode 100644 index 000000000..9a42b1f1e --- /dev/null +++ b/mods/_core/users/admin_delete.php @@ -0,0 +1,164 @@ +addError('NODELETE_USER'); + header('Location: '.AT_BASE_HREF.'users.php'); + exit;*/ + return; + } + + $sql = "DELETE FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'course_enrollment', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_accessed WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'forums_accessed', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_subscriptions WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'forums_subscriptions', mysql_affected_rows($db), $sql); + + + /****/ + /* delete forum threads block: */ + /* delete the thread replies: */ + $sql = "SELECT COUNT(*) AS cnt, parent_id, forum_id FROM ".TABLE_PREFIX."forums_threads WHERE member_id=$id AND parent_id<>0 GROUP BY parent_id"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + /* update the forum posts counter */ + $sql = "UPDATE ".TABLE_PREFIX."forums SET num_posts=num_posts - $row[cnt], last_post=last_post WHERE forum_id=$row[forum_id]"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_UPDATE, 'forums', mysql_affected_rows($db), $sql); + + /* update the topics reply counter */ + $sql = "UPDATE ".TABLE_PREFIX."forums_threads SET num_comments=num_comments-$row[cnt], last_comment=last_comment, date=date WHERE post_id=$row[parent_id]"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_UPDATE, 'forums_threads', mysql_affected_rows($db), $sql); + } + + /* delete threads this member started: */ + $sql = "SELECT post_id, forum_id, num_comments FROM ".TABLE_PREFIX."forums_threads WHERE member_id=$id AND parent_id=0"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + /* update the forum posts and topics counters */ + $num_posts = $row['num_comments'] + 1; + $sql = "UPDATE ".TABLE_PREFIX."forums SET num_topics=num_topics-1, num_posts=num_posts - $num_posts, last_post=last_post WHERE forum_id=$row[forum_id]"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_UPDATE, 'forums', mysql_affected_rows($db), $sql); + + /* delete the replies */ + $sql = "DELETE FROM ".TABLE_PREFIX."forums_threads WHERE parent_id=$row[post_id]"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'forums_threads', mysql_affected_rows($db), $sql); + } + /* delete the actual threads */ + $sql = "DELETE FROM ".TABLE_PREFIX."forums_threads WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'forums_threads', mysql_affected_rows($db), $sql); + + /* end delete forum threads block. */ + /****/ + + $sql = "DELETE FROM ".TABLE_PREFIX."instructor_approvals WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'instructor_approvals', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."messages WHERE from_member_id=$id OR to_member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'messages', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."polls_members WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'polls_members', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."tests_answers WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'tests_answers', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."tests_results WHERE member_id='$id'"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'tests_results', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."users_online WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'users_online', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."members WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'members', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."member_track WHERE member_id=$id"; + mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'member_track', mysql_affected_rows($db), $sql); + + // delete personal files from file storage + fs_delete_workspace(WORKSPACE_PERSONAL, $id); + + + return; +} + +$ids = explode(',', $_REQUEST['id']); + +if (isset($_POST['submit_yes'])) { + + foreach($ids as $id) { + delete_user(intval($id)); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + if (isset($_POST['ml']) && $_REQUEST['ml']) { + header('Location: '.AT_BASE_HREF.'mods/_core/users/master_list.php'); + } else { + header('Location: '.AT_BASE_HREF.'mods/_core/users/users.php'); + } + exit; +} else if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + if (isset($_POST['ml']) && $_REQUEST['ml']) { + header('Location: '.AT_BASE_HREF.'mods/_core/users/master_list.php'); + } else { + header('Location: '.AT_BASE_HREF.'mods/_core/users/users.php'); + } + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +$names = get_login($ids); +$names_html = '
        '.html_get_list($names).'
      '; +$hidden_vars['id'] = implode(',', array_keys($names)); +$hidden_vars['ml'] = intval($_REQUEST['ml']); + +$confirm = array('DELETE_USER', $names_html); +$msg->addConfirm($confirm, $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_core/users/admin_deny.php b/mods/_core/users/admin_deny.php new file mode 100644 index 000000000..ba313bd06 --- /dev/null +++ b/mods/_core/users/admin_deny.php @@ -0,0 +1,150 @@ +addFeedback('PROFILE_UPDATED_ADMIN'); + + /* notify the users that they have been denied: */ + $sql = "SELECT email, first_name, last_name FROM ".TABLE_PREFIX."members WHERE member_id=".$_POST['id']; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_array($result)) { + $to_email = $row['email']; + + $message = _AT('instructor_request_deny', AT_BASE_HREF)." \n"; + if ($_POST['msg_option'] == $other_option) { + $message.=addslashes($_POST['other_msg']); + } else if ($_POST['msg_option']) { + $message.= "\n".$msg_options[$_POST['msg_option']]; + } + + if ($to_email != '') { + + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + $mail = new ATutorMailer; + + $mail->From = $_config['contact_email']; + $mail->AddAddress($to_email); + $mail->Subject = _AT('instructor_request'); + $mail->Body = $message; + + if(!$mail->Send()) { + //echo 'There was an error sending the message'; + $msg->printErrors('SENDING_ERROR'); + exit; + } + + unset($mail); + } + } + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + Header('Location: index.php'); + exit; +} else if ($_POST['cancel']) { + $msg->addFeedback('CANCELLED'); + header('Location: users.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT email, first_name, last_name FROM ".TABLE_PREFIX."members WHERE member_id=".$request_id; +$result = mysql_query($sql, $db); + +if ($row = mysql_fetch_array($result)) { + $username = ''; + if ($row['first_name']!="") { + $username .= $row['first_name'].' '; + } + + if ($row['last_name']!="") { + $username .= $row['last_name'].' '; + } + $username .= $row['email']; +} else { + require(AT_INCLUDE_PATH.'header.inc.php'); + echo _AT('no_user_found'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +?> + +
      + + + +
      +
      +
    "; + ?> + + + +
    +
    + +
    '; + + $num_msgs = count($msg_options) - 1; + for ($i = 1; $i<$num_msgs; $i++) { + echo '
    '; + } + + echo ''; + ?> + +
    +
    + +
    + + +
    + + + + \ No newline at end of file diff --git a/mods/_core/users/admin_email.php b/mods/_core/users/admin_email.php new file mode 100644 index 000000000..cfaacf192 --- /dev/null +++ b/mods/_core/users/admin_email.php @@ -0,0 +1,131 @@ +addFeedback('CANCELLED'); + + header('Location: users.php#feedback'); + exit; +} else if ($_POST['submit']) { + $missing_fields = array(); + + $_POST['subject'] = trim($_POST['subject']); + $_POST['body'] = trim($_POST['body']); + + if (($_POST['to'] == '') || ($_POST['to'] == 0)) { + $missing_fields[] = _AT('to'); + } + + if ($_POST['subject'] == '') { + $missing_fields[] = _AT('subject'); + } + + if ($_POST['body'] == '') { + $missing_fields[] = _AT('body'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + if (!$msg->containsErrors()) { + if ($_POST['to'] == 1) { + // choose all instructors + $sql = "SELECT * FROM ".TABLE_PREFIX."members WHERE status = ".AT_STATUS_INSTRUCTOR; + } else if ($_POST['to'] == 2) { + // choose all students + $sql = "SELECT * FROM ".TABLE_PREFIX."members WHERE status = ".AT_STATUS_STUDENT; + } else { + // choose all members + $sql = "SELECT * FROM ".TABLE_PREFIX."members WHERE status = ".AT_STATUS_INSTRUCTOR." OR status = ".AT_STATUS_STUDENT; + } + + $result = mysql_query($sql,$db); + + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + $mail = new ATutorMailer; + + while ($row = mysql_fetch_assoc($result)) { + $mail->AddBCC($row['email']); + } + + + $mail->From = $_config['contact_email']; + $mail->FromName = $_config['site_name']; + $mail->AddAddress($_config['contact_email']); + $mail->Subject = $stripslashes($_POST['subject']); + $mail->Body = $stripslashes($_POST['body']); + + if(!$mail->Send()) { + //echo 'There was an error sending the message'; + $msg->printErrors('SENDING_ERROR'); + exit; + } + unset($mail); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: users.php'); + exit; + } +} + +$title = _AT('admin_email'); + +$onload = 'document.form.subject.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."members ORDER BY login"; +$result = mysql_query($sql,$db); +$row = mysql_fetch_array($result); +if ($row['cnt'] == 0) { + $msg->printErrors('NO_MEMBERS'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +?> +
    + + +
    +
    + *
    + + /> + /> +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/users/admins/create.php b/mods/_core/users/admins/create.php new file mode 100644 index 000000000..ef91b4e90 --- /dev/null +++ b/mods/_core/users/admins/create.php @@ -0,0 +1,228 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + $missing_fields = array(); + + /* login validation */ + if ($_POST['login'] == '') { + $missing_fields[] = _AT('login_name'); + } else { + /* check for special characters */ + if (!(preg_match("/^[a-zA-Z0-9_]([a-zA-Z0-9_])*$/i", $_POST['login']))) { + $msg->addError('LOGIN_CHARS'); + } else { + $result = mysql_query("SELECT * FROM ".TABLE_PREFIX."members WHERE login='$_POST[login]'",$db); + if (mysql_num_rows($result) != 0) { + $msg->addError('LOGIN_EXISTS'); + } + + $result = mysql_query("SELECT * FROM ".TABLE_PREFIX."admins WHERE login='$_POST[login]'",$db); + if (mysql_num_rows($result) != 0) { + $msg->addError('LOGIN_EXISTS'); + } + } + } + + /* password check: password is verified front end by javascript. here is to handle the errors from javascript */ + if ($_POST['password_error'] <> "") + { + $pwd_errors = explode(",", $_POST['password_error']); + + foreach ($pwd_errors as $pwd_error) + { + if ($pwd_error == "missing_password") + $missing_fields[] = _AT('password'); + else + $msg->addError($pwd_error); + } + } + + /* email validation */ + if ($_POST['email'] == '') { + $missing_fields[] = _AT('email'); + } else if (!preg_match("/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,6}$/i", $_POST['email'])) { + $msg->addError('EMAIL_INVALID'); + } + $result = mysql_query("SELECT * FROM ".TABLE_PREFIX."members WHERE email LIKE '$_POST[email]'",$db); + if (mysql_num_rows($result) != 0) { + $valid = 'no'; + $msg->addError('EMAIL_EXISTS'); + } + + $priv = 0; + if (isset($_POST['priv_admin'])) { + // overrides all above. + $priv = AT_ADMIN_PRIV_ADMIN; + } else if (isset($_POST['privs'])) { + foreach ($_POST['privs'] as $value) { + $priv += intval($value); + } + } + $_POST['privs'] = $priv; + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $_POST['login'] = $addslashes($_POST['login']); + $password = $addslashes($_POST['form_password_hidden']); + $_POST['real_name'] = $addslashes($_POST['real_name']); + $_POST['email'] = $addslashes($_POST['email']); + + $admin_lang = $_config['default_language']; + + $sql = "INSERT INTO ".TABLE_PREFIX."admins + (login, + password, + real_name, + email, + language, + `privileges`, + last_login) + VALUES ('$_POST[login]', + '$password', + '$_POST[real_name]', + '$_POST[email]', + '$admin_lang', + $priv, + 0)"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $sql = "INSERT INTO ".TABLE_PREFIX."admins + (login, + password, + real_name, + email, + language, + `privileges`, + last_login) + VALUES ('$_POST[login]', + '********', + '$_POST[real_name]', + '$_POST[email]', + '$admin_lang', + $priv, + 0)"; + + write_to_log(AT_ADMIN_LOG_INSERT, 'admins', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ADMIN_CREATED'); + header('Location: index.php'); + exit; + } + $_POST['login'] = $stripslashes($_POST['login']); + $_POST['real_name'] = $stripslashes($_POST['real_name']); + $_POST['email'] = $stripslashes($_POST['email']); +} + +$onload = 'document.form.login.focus();'; +require(AT_INCLUDE_PATH.'header.inc.php'); +?> + + +
    + + + +
    +
    + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    +
    + +
    + +
    + *
    + +
    + +
    +
    + />

    + + getModules(AT_MODULE_STATUS_ENABLED, 0, TRUE); + $keys = array_keys($module_list); + ?> + + + + getAdminPrivilege() > 1)) { continue; } ?> + getAdminPrivilege())) { echo 'checked="checked"'; } ?> />
    + +
    + +
    + + +
    +
    +
    + + + + + + diff --git a/mods/_core/users/admins/delete.php b/mods/_core/users/admins/delete.php new file mode 100644 index 000000000..673276c57 --- /dev/null +++ b/mods/_core/users/admins/delete.php @@ -0,0 +1,59 @@ +addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + $_POST['login'] = $addslashes($_POST['login']); + + $sql = "DELETE FROM ".TABLE_PREFIX."admins WHERE login='$_POST[login]'"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_DELETE, 'admins', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ADMIN_DELETED'); + header('Location: index.php'); + exit; +} +?> + +addError('CANNOT_DELETE_OWN_ACCOUNT'); + $msg->printErrors(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$sql = "SELECT * FROM ".TABLE_PREFIX."admins WHERE login='$_GET[login]'"; +$result = mysql_query($sql, $db); +if (!($row = mysql_fetch_assoc($result))) { + echo _AT('no_user_found'); +} else { + $hidden_vars['login'] = $_GET['login']; + $confirm = array('DELETE_ADMIN', $row['login']); + $msg->addConfirm($confirm, $hidden_vars); + $msg->printConfirm(); +} +?> + \ No newline at end of file diff --git a/mods/_core/users/admins/detail_log.php b/mods/_core/users/admins/detail_log.php new file mode 100644 index 000000000..097dd3088 --- /dev/null +++ b/mods/_core/users/admins/detail_log.php @@ -0,0 +1,86 @@ + +
    + + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    + + + +
    + +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/users/admins/edit.php b/mods/_core/users/admins/edit.php new file mode 100644 index 000000000..8a719f525 --- /dev/null +++ b/mods/_core/users/admins/edit.php @@ -0,0 +1,155 @@ +addError('ADMIN_EDIT_OWN_ACCOUNT'); + header('Location: index.php'); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php'); + exit; +} else if (isset($_POST['submit'])) { + $missing_fields = array(); + + /* email validation */ + if ($_POST['email'] == '') { + $missing_fields[] = _AT('email'); + } else if (!preg_match("/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,6}$/i", $_POST['email'])) { + $msg->addError('EMAIL_INVALID'); + } + $result = mysql_query("SELECT * FROM ".TABLE_PREFIX."members WHERE email LIKE '$_POST[email]'",$db); + if (mysql_num_rows($result) != 0) { + $valid = 'no'; + $msg->addError('EMAIL_EXISTS'); + } + + $priv = 0; + + if (isset($_POST['priv_admin'])) { + // overrides all above. + $priv = AT_ADMIN_PRIV_ADMIN; + } else if (isset($_POST['privs'])) { + foreach ($_POST['privs'] as $value) { + $priv += intval($value); + } + } + $_POST['privs'] = $priv; + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $_POST['login'] = $addslashes($_POST['login']); + $_POST['real_name'] = $addslashes($_POST['real_name']); + $_POST['email'] = $addslashes($_POST['email']); + + $sql = "UPDATE ".TABLE_PREFIX."admins SET real_name='$_POST[real_name]', email='$_POST[email]', `privileges`=$priv, last_login=last_login WHERE login='$_POST[login]'"; + $result = mysql_query($sql, $db); + + $sql = "UPDATE ".TABLE_PREFIX."admins SET real_name='$_POST[real_name]', email='$_POST[email]', `privileges`=$priv WHERE login='$_POST[login]'"; + + write_to_log(AT_ADMIN_LOG_UPDATE, 'admins', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php'); + exit; + } + $_POST['login'] = $stripslashes($_POST['login']); + $_POST['real_name'] = $stripslashes($_POST['real_name']); + $_POST['email'] = $stripslashes($_POST['email']); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$_GET['login'] = $addslashes($_REQUEST['login']); + +$sql = "SELECT * FROM ".TABLE_PREFIX."admins WHERE login='$_GET[login]'"; +$result = mysql_query($sql, $db); +if (!($row = mysql_fetch_assoc($result))) { + $msg->addError('USER_NOT_FOUND'); + $msg->printErrors(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +if (!isset($_POST['submit'])) { + $_POST = $row; + if (query_bit($row['privileges'], AT_ADMIN_PRIV_ADMIN)) { + $_POST['priv_admin'] = 1; + } + $_POST['privs'] = intval($row['privileges']); +} + +?> +
    + +
    +
    +

    +
    + +
    +
    + +
    + +
    + *
    + +
    + +
    +
    + />

    + + getModules(AT_MODULE_STATUS_ENABLED, 0, TRUE); + $keys = array_keys($module_list); + ?> + + + + getAdminPrivilege() > 1)) { continue; } ?> + getAdminPrivilege())) { echo 'checked="checked"'; } ?> />
    + +
    + +
    + /> + +
    +
    +
    + + + + \ No newline at end of file diff --git a/mods/_core/users/admins/index.php b/mods/_core/users/admins/index.php new file mode 100644 index 000000000..a462f6455 --- /dev/null +++ b/mods/_core/users/admins/index.php @@ -0,0 +1,138 @@ +addError('NO_ITEM_SELECTED'); +} + +$id = $_GET['id']; +$L = $_GET['L']; +require(AT_INCLUDE_PATH.'header.inc.php'); + + +$orders = array('asc' => 'desc', 'desc' => 'asc'); +$cols = array('login' => 1, 'real_name' => 1, 'email' => 1, 'last_login' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'login'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'login'; +} else { + // no order set + $order = 'asc'; + $col = 'login'; +} + +?> + +
    + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    + + + + +
    0) { + echo _AT('active_admin'); + } else { + echo _AT('inactive_admin'); + } + ?>
    +
    + + \ No newline at end of file diff --git a/mods/_core/users/admins/log.php b/mods/_core/users/admins/log.php new file mode 100644 index 000000000..7726c007b --- /dev/null +++ b/mods/_core/users/admins/log.php @@ -0,0 +1,99 @@ +'._AT('no_log_found_').''; + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + + $num_results = $row[0]; + $results_per_page = 50; + $num_pages = max(ceil($num_results / $results_per_page), 1); + $page = intval($_GET['p']); + if (!$page) { + $page = 1; + } + $count = (($page-1) * $results_per_page) + 1; + + echo '
    '; + echo '
      '; + for ($i=1; $i<=$num_pages; $i++) { + echo '
    • '; + if ($i == $page) { + echo ''.$i.''; + } else { + echo ''.$i.''; + } + echo '
    • '; + } + echo '
    '; + echo '
    '; + + $offset = ($page-1)*$results_per_page; + + $sql = "SELECT * FROM ".TABLE_PREFIX."admin_log $login_where ORDER BY `time` DESC LIMIT $offset, $results_per_page"; + $result = mysql_query($sql, $db); +?> + + + + + + + + + + + 0) : ?> + + + + + + + + + + + + + + + +
    + + \ No newline at end of file diff --git a/mods/_core/users/admins/my_edit.php b/mods/_core/users/admins/my_edit.php new file mode 100644 index 000000000..2fb105db2 --- /dev/null +++ b/mods/_core/users/admins/my_edit.php @@ -0,0 +1,94 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'admin/index.php'); + exit; +} else if (isset($_POST['submit'])) { + $missing_fields = array(); + + /* email validation */ + if ($_POST['email'] == '') { + $missing_fields[] = _AT('email'); + } else if (!preg_match("/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,6}$/i", $_POST['email'])) { + $msg->addError('EMAIL_INVALID'); + } + $result = mysql_query("SELECT * FROM ".TABLE_PREFIX."members WHERE email LIKE '$_POST[email]'",$db); + if (mysql_num_rows($result) != 0) { + $valid = 'no'; + $msg->addError('EMAIL_EXISTS'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { +// $_POST['password'] = $addslashes($_POST['password']); + $_POST['real_name'] = $addslashes($_POST['real_name']); + $_POST['email'] = $addslashes($_POST['email']); + + $sql = "UPDATE ".TABLE_PREFIX."admins SET real_name='$_POST[real_name]', email='$_POST[email]', last_login=last_login WHERE login='$_SESSION[login]'"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'admin/index.php'); + exit; + } + $_POST['real_name'] = $stripslashes($_POST['real_name']); + $_POST['email'] = $stripslashes($_POST['email']); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT real_name, email FROM ".TABLE_PREFIX."admins WHERE login='$_SESSION[login]'"; +$result = mysql_query($sql, $db); +if (!($row = mysql_fetch_assoc($result))) { + $msg->addError('USER_NOT_FOUND'); + $msg->printErrors(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +if (!isset($_POST['submit'])) { + $_POST = $row; +// $_POST['confirm_password'] = $_POST['password']; +} + +?> +
    +
    +
    +
    + +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/users/admins/my_password.php b/mods/_core/users/admins/my_password.php new file mode 100644 index 000000000..a65ebc619 --- /dev/null +++ b/mods/_core/users/admins/my_password.php @@ -0,0 +1,77 @@ +printInfos($info); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'admin/index.php'); + exit; +} + +if (isset($_POST['submit'])) { + if (!empty($_POST['form_old_password_hidden'])) { + //check if old password entered is correct + $sql = "SELECT password FROM ".TABLE_PREFIX."admins WHERE login='$_SESSION[login]'"; + $result = mysql_query($sql,$db); + if ($row = mysql_fetch_assoc($result)) { + if ($row['password'] != $_POST['form_old_password_hidden']) { + $msg->addError('WRONG_PASSWORD'); + Header('Location: my_password.php'); + exit; + } + } + } else { + $msg->addError(array('EMPTY_FIELDS', _AT('password'))); + header('Location: my_password.php'); + exit; + } + + // new password check + if ($_POST['password_error'] <> "") + { + $pwd_errors = explode(",", $_POST['password_error']); + + foreach ($pwd_errors as $pwd_error) + { + if ($pwd_error == "missing_password") + $missing_fields[] = _AT('password'); + else + $msg->addError($pwd_error); + } + } + + if (!$msg->containsErrors()) { + $password = addslashes($_POST['form_password_hidden']); + + $sql = "UPDATE ".TABLE_PREFIX."admins SET password='$password', last_login=last_login WHERE login='$_SESSION[login]'"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('PASSWORD_CHANGED'); + header('Location: '.AT_BASE_HREF.'admin/index.php'); + exit; + } +} + +/* template starts here */ +$savant->display('users/password_change.tmpl.php'); + +?> \ No newline at end of file diff --git a/mods/_core/users/admins/password.php b/mods/_core/users/admins/password.php new file mode 100644 index 000000000..ce3ba86a8 --- /dev/null +++ b/mods/_core/users/admins/password.php @@ -0,0 +1,126 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/admins/index.php'); + exit; +} else if (isset($_POST['submit'])) { + /* password check: password is verified front end by javascript. here is to handle the errors from javascript */ + if ($_POST['password_error'] <> "") + { + $pwd_errors = explode(",", $_POST['password_error']); + + foreach ($pwd_errors as $pwd_error) + { + if ($pwd_error == "missing_password") + $missing_fields[] = _AT('password'); + else + $msg->addError($pwd_error); + } + } + + if (!$msg->containsErrors()) { + $password = $addslashes($_POST['form_password_hidden']); + + $sql = "UPDATE ".TABLE_PREFIX."admins SET password='$password', last_login=last_login WHERE login='$_POST[login]'"; + $result = mysql_query($sql, $db); + + $sql = "UPDATE ".TABLE_PREFIX."admins SET password='********' WHERE login='$_POST[login]'"; + write_to_log(AT_ADMIN_LOG_UPDATE, 'admins', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/admins/index.php'); + exit; + } + $_POST['login'] = $stripslashes($_POST['login']); +} + + +$_GET['login'] = $addslashes($_REQUEST['login']); + +$sql = "SELECT login FROM ".TABLE_PREFIX."admins WHERE login='$_GET[login]'"; +$result = mysql_query($sql, $db); +if (!($row = mysql_fetch_assoc($result))) { + $msg->addError('USER_NOT_FOUND'); + $msg->printErrors(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +if (!isset($_POST['submit'])) { + $_POST = $row; + + if (query_bit($row['privileges'], AT_ADMIN_PRIV_ADMIN)) { + $_POST['priv_admin'] = 1; + } + $_POST['privs'] = intval($row['privileges']); +} + +$onload = 'document.form.password1.focus();'; +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + + + + +
    + + + + +
    +
    +

    +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/users/admins/reset_log.php b/mods/_core/users/admins/reset_log.php new file mode 100644 index 000000000..9883d5190 --- /dev/null +++ b/mods/_core/users/admins/reset_log.php @@ -0,0 +1,46 @@ +addFeedback('CANCELLED'); + header('Location: ./log.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + //clean up the db + $sql = "DELETE FROM ".TABLE_PREFIX."admin_log"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_DELETE, 'admin_log', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ADMIN_LOG_RESET'); + header('Location: ./log.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +//print confirmation +$hidden_vars['all'] = TRUE; + +$confirm = array('RESET_ADMIN_LOG', $_SERVER['PHP_SELF']); +$msg->addConfirm($confirm, $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/users/create_user.php b/mods/_core/users/create_user.php new file mode 100644 index 000000000..e07ff0627 --- /dev/null +++ b/mods/_core/users/create_user.php @@ -0,0 +1,234 @@ +0",$db); + if (mysql_num_rows($result) != 0) { + $msg->addError('CREATE_MASTER_USED'); + } + } + + /* login name check */ + if ($_POST['login'] == '') { + $missing_fields[] = _AT('login_name'); + } else { + /* check for special characters */ + if (!(preg_match("/^[a-zA-Z0-9_.-]([a-zA-Z0-9_.-])*$/i", $_POST['login']))) { + $msg->addError('LOGIN_CHARS'); + } else { + $result = mysql_query("SELECT * FROM ".TABLE_PREFIX."members WHERE login='$_POST[login]'",$db); + if (mysql_num_rows($result) != 0) { + $valid = 'no'; + $msg->addError('LOGIN_EXISTS'); + } else { + $result = mysql_query("SELECT * FROM ".TABLE_PREFIX."admins WHERE login='$_POST[login]'",$db); + if (mysql_num_rows($result) != 0) { + $msg->addError('LOGIN_EXISTS'); + } + } + } + } + + /* password check: */ + $_POST['password'] = $_POST['form_password_hidden']; + + /* password check: password is verified front end by javascript. here is to handle the errors from javascript */ + if ($_POST['password_error'] <> "") + { + $pwd_errors = explode(",", $_POST['password_error']); + + foreach ($pwd_errors as $pwd_error) + { + if ($pwd_error == "missing_password") + $missing_fields[] = _AT('password'); + else + $msg->addError($pwd_error); + } + } + + /* email check */ + if ($_POST['email'] == '') { + $missing_fields[] = _AT('email'); + } else if (!preg_match("/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,6}$/i", $_POST['email'])) { + $msg->addError('EMAIL_INVALID'); + } + + $_POST['email'] = $addslashes($_POST['email']); + $result = mysql_query("SELECT member_id FROM ".TABLE_PREFIX."members WHERE email LIKE '$_POST[email]'",$db); + if (mysql_num_rows($result) != 0) { + $msg->addError('EMAIL_EXISTS'); + } + + if (!$_POST['first_name']) { + $missing_fields[] = _AT('first_name'); + } + + if (!$_POST['last_name']) { + $missing_fields[] = _AT('last_name'); + } + + $_POST['first_name'] = str_replace('<', '', $_POST['first_name']); + $_POST['second_name'] = str_replace('<', '', $_POST['second_name']); + $_POST['last_name'] = str_replace('<', '', $_POST['last_name']); + + $_POST['login'] = strtolower($_POST['login']); + + //check date of birth + $mo = intval($_POST['month']); + $day = intval($_POST['day']); + $yr = intval($_POST['year']); + + /* let's us take (one or) two digit years (ex. 78 = 1978, 3 = 2003) */ + if ($yr < date('y')) { + $yr += 2000; + } else if ($yr < 1900) { + $yr += 1900; + } + + $dob = $yr.'-'.$mo.'-'.$day; + + if ($mo && $day && $yr && !checkdate($mo, $day, $yr)) { + $msg->addError('DOB_INVALID'); + } else if (!$mo || !$day || !$yr) { + $dob = '0000-00-00'; + $yr = $mo = $day = 0; + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + if (($_POST['website']) && (!strstr($_POST['website'], '://'))) { + $_POST['website'] = 'http://' . $_POST['website']; + } + if ($_POST['website'] == 'http://') { + $_POST['website'] = ''; + } + $_POST['postal'] = strtoupper(trim($_POST['postal'])); + + if (isset($_POST['private_email'])) { + $_POST['private_email'] = 1; + } else { + $_POST['private_email'] = 0; + } + $_POST['password'] = $addslashes($_POST['password']); + $_POST['website'] = $addslashes($_POST['website']); + $_POST['first_name'] = $addslashes($_POST['first_name']); + $_POST['second_name'] = $addslashes($_POST['second_name']); + $_POST['last_name'] = $addslashes($_POST['last_name']); + $_POST['address'] = $addslashes($_POST['address']); + $_POST['postal'] = $addslashes($_POST['postal']); + $_POST['city'] = $addslashes($_POST['city']); + $_POST['province'] = $addslashes($_POST['province']); + $_POST['country'] = $addslashes($_POST['country']); + $_POST['phone'] = $addslashes($_POST['phone']); + $_POST['status'] = intval($_POST['status']); + $_POST['gender'] = $addslashes($_POST['gender']); + + $now = date('Y-m-d H:i:s'); // we use this later for the email confirmation. + + /* insert into the db. (the last 0 for status) */ + $sql = "INSERT INTO ".TABLE_PREFIX."members VALUES (NULL,'$_POST[login]','$_POST[password]','$_POST[email]','$_POST[website]','$_POST[first_name]', '$_POST[second_name]', '$_POST[last_name]', '$dob', '$_POST[gender]', '$_POST[address]','$_POST[postal]','$_POST[city]','$_POST[province]','$_POST[country]', '$_POST[phone]',$_POST[status], '$_config[pref_defaults]', '$now','$_config[default_language]', $_config[pref_inbox_notify], $_POST[private_email], '0000-00-00 00:00:00')"; + + $result = mysql_query($sql, $db); + + $m_id = mysql_insert_id($db); + if (!$result) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->addError('DB_NOT_UPDATED'); + $msg->printAll(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + + if (defined('AT_MASTER_LIST') && AT_MASTER_LIST) { + $student_id = $addslashes($_POST['student_id']); + $student_pin = md5($addslashes($_POST['student_pin'])); + if ($student_id) { + $sql = "UPDATE ".TABLE_PREFIX."master_list SET member_id=$m_id WHERE public_field='$student_id'"; + mysql_query($sql, $db); + if (mysql_affected_rows($db) == 0) { + $sql = "REPLACE INTO ".TABLE_PREFIX."master_list VALUES ('$student_id', '$student_pin', $m_id)"; + mysql_query($sql, $db); + } + } + } + + + if ($_POST['pref'] == 'access') { + $_SESSION['member_id'] = $m_id; + save_prefs(); + unset($_SESSION['member_id']); + } + + + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + $mail = new ATutorMailer(); + $mail->AddAddress($_POST['email']); + $mail->From = $_config['contact_email']; + + if (defined('AT_EMAIL_CONFIRMATION') && AT_EMAIL_CONFIRMATION && ($_POST['status'] == AT_STATUS_UNCONFIRMED)) { + $code = substr(md5($_POST['email'] . $now . $m_id), 0, 10); + $confirmation_link = AT_BASE_HREF . 'confirm.php?id='.$m_id.SEP.'m='.$code; + + /* send the email confirmation message: */ + $mail->Subject = $_config['site_name'] . ': ' . _AT('email_confirmation_subject'); + $body .= _AT('admin_new_account_confirm', $_config['site_name'], $confirmation_link)."\n\n"; + + } else { + $mail->Subject = $_config['site_name'].": "._AT('account_information'); + $body .= _AT('admin_new_account', $_config['site_name'])."\n\n"; + } + $body .= _AT('web_site') .' : '.AT_BASE_HREF."\n"; + $body .= _AT('login_name') .' : '.$_POST['login'] . "\n"; +// $body .= _AT('password') .' : '.$_POST['password'] . "\n"; + $mail->Body = $body; + $mail->Send(); + + $msg->addFeedback('PROFILE_CREATED_ADMIN'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/users.php'); + exit; + } +} + +$onload = 'document.form.login.focus();'; + +$savant->assign('languageManager', $languageManager); + +if (!isset($_POST['status'])) { + if (defined('AT_EMAIL_CONFIRMATION') && AT_EMAIL_CONFIRMATION) { + $_POST['status'] = AT_STATUS_UNCONFIRMED; + } else { + $_POST['status'] = AT_STATUS_STUDENT; + } +} + +$savant->display('registration.tmpl.php'); + +?> \ No newline at end of file diff --git a/mods/_core/users/default_preferences.php b/mods/_core/users/default_preferences.php new file mode 100644 index 000000000..323e99174 --- /dev/null +++ b/mods/_core/users/default_preferences.php @@ -0,0 +1,146 @@ +addFeedback('CANCELLED'); + header('Location: users.php'); + exit; +} + +if (isset($_POST['submit']) || isset($_POST["set_default"])) { + if (isset($_POST['submit'])) + { + /* custom prefs */ + // atutor settings (tab 0) + $pref_defaults['PREF_NUMBERING'] = intval($_POST['numbering']); + if (isset($_POST['theme']) && $_POST['theme'] != '') { + $pref_defaults['PREF_THEME'] = $addslashes($_POST['theme']); + } + if (isset($_POST['mobile_theme']) && $_POST['mobile_theme'] != '') { + $pref_defaults['PREF_MOBILE_THEME'] = $addslashes($_POST['mobile_theme']); + } + $pref_defaults['PREF_TIMEZONE'] = $addslashes($_POST['time_zone']); + $pref_defaults['PREF_JUMP_REDIRECT'] = intval($_POST['use_jump_redirect']); + $pref_defaults['PREF_FORM_FOCUS'] = intval($_POST['form_focus']); + $pref_defaults['PREF_CONTENT_EDITOR'] = intval($_POST['content_editor']); + $pref_defaults['PREF_SHOW_GUIDE'] = intval($_POST['show_guide']); + + // display settings (tab 1) + $pref_defaults['PREF_FONT_FACE'] = $addslashes($_POST['fontface']); + $pref_defaults['PREF_FONT_TIMES'] = $addslashes($_POST['font_times']); + $pref_defaults['PREF_FG_COLOUR'] = $addslashes($_POST['fg']); + $pref_defaults['PREF_BG_COLOUR'] = $addslashes($_POST['bg']); + $pref_defaults['PREF_HL_COLOUR'] = $addslashes($_POST['hl']); + + // content settings (tab 2) + $pref_defaults['PREF_USE_ALTERNATIVE_TO_TEXT'] = intval($_POST['use_alternative_to_text']); + $pref_defaults['PREF_ALT_TO_TEXT'] = $addslashes($_POST['preferred_alt_to_text']); + $pref_defaults['PREF_ALT_TO_TEXT_APPEND_OR_REPLACE'] = $addslashes($_POST['alt_to_text_append_or_replace']); + $pref_defaults['PREF_ALT_TEXT_PREFER_LANG'] = $addslashes($_POST['alt_text_prefer_lang']); + $pref_defaults['PREF_USE_ALTERNATIVE_TO_AUDIO'] = intval($_POST['use_alternative_to_audio']); + $pref_defaults['PREF_ALT_TO_AUDIO'] = $addslashes($_POST['preferred_alt_to_audio']); + $pref_defaults['PREF_ALT_TO_AUDIO_APPEND_OR_REPLACE'] = $addslashes($_POST['alt_to_audio_append_or_replace']); + $pref_defaults['PREF_ALT_AUDIO_PREFER_LANG'] = $addslashes($_POST['alt_audio_prefer_lang']); + $pref_defaults['PREF_USE_ALTERNATIVE_TO_VISUAL'] = intval($_POST['use_alternative_to_visual']); + $pref_defaults['PREF_ALT_TO_VISUAL'] = $addslashes($_POST['preferred_alt_to_visual']); + $pref_defaults['PREF_ALT_TO_VISUAL_APPEND_OR_REPLACE'] = $addslashes($_POST['alt_to_visual_append_or_replace']); + $pref_defaults['PREF_ALT_VISUAL_PREFER_LANG'] = $addslashes($_POST['alt_visual_prefer_lang']); + + // tool settings (tab 3) + $pref_defaults['PREF_DICTIONARY'] = intval($_POST['dictionary_val']); + $pref_defaults['PREF_THESAURUS'] = intval($_POST['thesaurus_val']); + $pref_defaults['PREF_NOTE_TAKING'] = intval($_POST['note_taking_val']); + $pref_defaults['PREF_CALCULATOR'] = intval($_POST['calculator_val']); + $pref_defaults['PREF_ABACUS'] = intval($_POST['abacus_val']); + $pref_defaults['PREF_ATLAS'] = intval($_POST['atlas_val']); + $pref_defaults['PREF_ENCYCLOPEDIA'] = intval($_POST['encyclopedia_val']); + + // control settings (tab 4) + $pref_defaults['PREF_SHOW_CONTENTS'] = intval($_POST['show_contents']); + $pref_defaults['PREF_SHOW_NEXT_PREVIOUS_BUTTONS'] = intval($_POST['show_next_previous_buttons']); + $pref_defaults['PREF_SHOW_BREAD_CRUMBS'] = intval($_POST['show_bread_crumbs']); + + $pref_defaults = serialize($pref_defaults); + + $mnot = intval($_POST["mnot"]); + $auto_login = $addslashes($_POST["auto"]); + } + else + { + $pref_defaults = $_config_defaults['pref_defaults']; + $mnot = $_config_defaults['pref_inbox_notify']; + $auto_login = $_config_defaults['pref_is_auto_login']; + } + + if (!($_config_defaults['pref_defaults'] == $pref_defaults)) { + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES ('pref_defaults','$pref_defaults')"; + } else if ($_config_defaults['pref_defaults'] == $pref_defaults) { + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='pref_defaults'"; + } + $result = mysql_query($sql, $db); + + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES ('pref_inbox_notify','".$mnot."')"; + $result = mysql_query($sql, $db); + + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES ('pref_is_auto_login','".$auto_login."')"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; +} + +// set defaults with the $_config_defaults and overwrite the configs that are defined in table `config` +$pref_defaults = unserialize($_config_defaults['pref_defaults']); + +if (is_array(unserialize($_config['pref_defaults']))) + foreach (unserialize($_config['pref_defaults']) as $name => $value) + $pref_defaults[$name] = $value; + +assign_session_prefs($pref_defaults); + +$sql = "SELECT value FROM ".TABLE_PREFIX."config WHERE name='pref_inbox_notify'"; +$result = mysql_query($sql, $db); +if (mysql_num_rows($result) > 0) +{ + $row_notify = mysql_fetch_assoc($result); + $notify = $row_notify['value']; +} +else + $notify = $_config_defaults['pref_inbox_notify']; + +$sql = "SELECT value FROM ".TABLE_PREFIX."config WHERE name='pref_is_auto_login'"; +$result = mysql_query($sql, $db); +if (mysql_num_rows($result) > 0) +{ + $row_is_auto_login = mysql_fetch_assoc($result); + $auto_login = $row_is_auto_login["value"]; +} +else + $auto_login = $_config_defaults['pref_is_auto_login']; + +$languages = $languageManager->getAvailableLanguages(); + +$savant->assign('notify', $notify); +$savant->assign('languages', $languages); +$savant->assign('is_auto_login', $auto_login); + +$savant->display('users/preferences.tmpl.php'); + +?> diff --git a/mods/_core/users/edit_user.php b/mods/_core/users/edit_user.php new file mode 100644 index 000000000..bd38830c0 --- /dev/null +++ b/mods/_core/users/edit_user.php @@ -0,0 +1,263 @@ +0 AND member_id<>$id",$db); + if (mysql_num_rows($result) != 0) { + $msg->addError('CREATE_MASTER_USED'); + } + } + + /* email check */ + if ($_POST['email'] == '') { + $missing_fields[] = _AT('email'); + } else if (!preg_match("/^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,6}$/i", $_POST['email'])) { + $msg->addError('EMAIL_INVALID'); + } + $result = mysql_query("SELECT * FROM ".TABLE_PREFIX."members WHERE email LIKE '$_POST[email]' AND member_id <> $id",$db); + + if (mysql_num_rows($result) != 0) { + $valid = 'no'; + $msg->addError('EMAIL_EXISTS'); + } + + if (!$_POST['first_name']) { + $missing_fields[] = _AT('first_name'); + } + + if (!$_POST['last_name']) { + $missing_fields[] = _AT('last_name'); + } + + $_POST['first_name'] = str_replace('<', '', $_POST['first_name']); + $_POST['second_name'] = str_replace('<', '', $_POST['second_name']); + $_POST['last_name'] = str_replace('<', '', $_POST['last_name']); + + // check if first+last is unique + /* + * http://www.atutor.ca/atutor/mantis/view.php?id=3760 + if ($_POST['first_name'] && $_POST['last_name']) { + $first_name_sql = $addslashes($_POST['first_name']); + $last_name_sql = $addslashes($_POST['last_name']); + $second_name_sql = $addslashes($_POST['second_name']); + + $sql = "SELECT member_id FROM ".TABLE_PREFIX."members WHERE first_name='$first_name_sql' AND second_name='$second_name_sql' AND last_name='$last_name_sql' AND member_id<>$id LIMIT 1"; + $result = mysql_query($sql, $db); + if (mysql_fetch_assoc($result)) { + $msg->addError('FIRST_LAST_NAME_UNIQUE'); + } + } + */ + + + //check date of birth + $mo = intval($_POST['month']); + $day = intval($_POST['day']); + $yr = intval($_POST['year']); + + /* let's us take (one or) two digit years (ex. 78 = 1978, 3 = 2003) */ + if ($yr < date('y')) { + $yr += 2000; + } else if ($yr < 1900) { + $yr += 1900; + } + + $dob = $yr.'-'.$mo.'-'.$day; + + if ($mo && $day && $yr && !checkdate($mo, $day, $yr)) { + $msg->addError('DOB_INVALID'); + } else if (!$mo || !$day || !$yr) { + $dob = '0000-00-00'; + $yr = $mo = $day = 0; + } + + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + if (isset($_POST['profile_pic_delete'])) { + profile_image_delete($id); + } + if (($_POST['website']) && (!strstr($_POST['website'], "://"))) { + $_POST['website'] = "http://".$_POST['website']; + } + if ($_POST['website'] == 'http://') { + $_POST['website'] = ''; + } + $_POST['postal'] = strtoupper(trim($_POST['postal'])); + + if (isset($_POST['private_email'])) { + $_POST['private_email'] = 1; + } else { + $_POST['private_email'] = 0; + } + + //$_POST['password'] = $addslashes($_POST['password']); + $_POST['website'] = $addslashes($_POST['website']); + $_POST['first_name'] = $addslashes($_POST['first_name']); + $_POST['second_name'] = $addslashes($_POST['second_name']); + $_POST['last_name'] = $addslashes($_POST['last_name']); + $_POST['address'] = $addslashes($_POST['address']); + $_POST['postal'] = $addslashes($_POST['postal']); + $_POST['city'] = $addslashes($_POST['city']); + $_POST['province'] = $addslashes($_POST['province']); + $_POST['country'] = $addslashes($_POST['country']); + $_POST['phone'] = $addslashes($_POST['phone']); + $_POST['status'] = intval($_POST['status']); + $_POST['old_status'] = intval($_POST['old_status']); + $_POST['gender'] = $addslashes($_POST['gender']); + + /* insert into the db. (the last 0 for status) */ + $sql = "UPDATE ".TABLE_PREFIX."members SET email = '$_POST[email]', + website = '$_POST[website]', + first_name = '$_POST[first_name]', + second_name= '$_POST[second_name]', + last_name = '$_POST[last_name]', + dob = '$dob', + gender = '$_POST[gender]', + address = '$_POST[address]', + postal = '$_POST[postal]', + city = '$_POST[city]', + province = '$_POST[province]', + country = '$_POST[country]', + phone = '$_POST[phone]', + status = $_POST[status], + language = '$_SESSION[lang]', + private_email = $_POST[private_email], + creation_date=creation_date, + last_login=last_login + WHERE member_id = $id"; + $result = mysql_query($sql, $db); + if (!$result) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->addError('DB_NOT_UPDATED'); + $msg->printAll(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + + + if (defined('AT_MASTER_LIST') && AT_MASTER_LIST) { + $_POST['student_id'] = $addslashes($_POST['student_id']); + $student_pin = sha1($addslashes($_POST['student_pin'])); + + //if changed, delete old stud id + if (!empty($_POST['old_student_id']) && $_POST['old_student_id'] != $_POST['student_id']) { + $sql = "DELETE FROM ".TABLE_PREFIX."master_list WHERE public_field=".$_POST['old_student_id']." AND member_id=$id"; + $result = mysql_query($sql, $db); + } + //if new is set + if (!empty($_POST['student_id']) && $_POST['old_student_id'] != $_POST['student_id']) { + $sql = "REPLACE INTO ".TABLE_PREFIX."master_list VALUES ('$_POST[student_id]', '', $id)"; + $result = mysql_query($sql, $db); + } + } + + + if (defined('AT_EMAIL_CONFIRMATION') && AT_EMAIL_CONFIRMATION && ($_POST['status'] == AT_STATUS_UNCONFIRMED) && ($_POST['old_status'] != AT_STATUS_UNCONFIRMED)) { + + $sql = "SELECT email, creation_date FROM ".TABLE_PREFIX."members WHERE member_id=$id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $code = substr(md5($row['email'] . $row['creation_date']. $id), 0, 10); + $confirmation_link = AT_BASE_HREF . 'confirm.php?id='.$id.SEP.'m='.$code; + + /* send the email confirmation message: */ + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + $mail = new ATutorMailer(); + + $mail->AddAddress($row['email']); + $mail->From = $_config['contact_email']; + $mail->Subject = $_config['site_name'] . ' - ' . _AT('email_confirmation_subject'); + $mail->Body = _AT('email_confirmation_message', $_config['site_name'], $confirmation_link); + + $mail->Send(); + } + + $msg->addFeedback('PROFILE_UPDATED_ADMIN'); + if (isset($_POST['ml']) && $_REQUEST['ml']) { + header('Location: '.AT_BASE_HREF.'mods/_core/users/master_list.php'); + } else { + header('Location: '.AT_BASE_HREF.'mods/_core/users/users.php'); + } + exit; + } +} + +$id = intval($_REQUEST['id']); + +if (empty($_POST)) { + $sql = "SELECT * FROM ".TABLE_PREFIX."members WHERE member_id = $id"; + $result = mysql_query($sql, $db); + if (!($row = mysql_fetch_assoc($result))) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->addError('USER_NOT_FOUND'); + $msg->printAll(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + + $_POST = $row; + list($_POST['year'],$_POST['month'],$_POST['day']) = explode('-', $row['dob']); + //$_POST['password2'] = $_POST['password']; + $_POST['old_status'] = $_POST['status']; + + if (admin_authenticate(AT_ADMIN_PRIV_USERS, TRUE) && defined('AT_MASTER_LIST') && AT_MASTER_LIST) { + $sql = "SELECT public_field FROM ".TABLE_PREFIX."master_list WHERE member_id=$id"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $_POST['old_student_id'] = $row['public_field']; + $_POST['student_id'] = $row['public_field']; + } + } +} + +$savant->assign('languageManager', $languageManager); + +if (isset($_REQUEST['ml']) && $_REQUEST['ml']) { + // redirect back to the master list + $savant->assign('ml', 1); +} else { + $savant->assign('ml', 0); +} + + +/* HAVE TO SEND MEMBER_ID THROUGH FORM AS A HIDDEN POST VARIABLE!!! */ +/* PUT IN IF LOOP THAT LETS YOU SEE STATUS RADIO BUTTONS */ +$savant->display('registration.tmpl.php'); + +?> \ No newline at end of file diff --git a/mods/_core/users/index.php b/mods/_core/users/index.php new file mode 100644 index 000000000..8eb1d2a7d --- /dev/null +++ b/mods/_core/users/index.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/mods/_core/users/instructor_requests.php b/mods/_core/users/instructor_requests.php new file mode 100644 index 000000000..40b6b57c1 --- /dev/null +++ b/mods/_core/users/instructor_requests.php @@ -0,0 +1,128 @@ +From = $_config['contact_email']; + $mail->AddAddress($to_email); + $mail->Subject = _AT('instructor_request'); + $mail->Body = $tmp_message; + + if(!$mail->Send()) { + //echo 'There was an error sending the message'; + $msg->addError('SENDING_ERROR'); + } + + unset($mail); + } + } + + $msg->addFeedback('PROFILE_UPDATED_ADMIN'); +} else if (!empty($_GET) && !$_GET['submit']) { + $msg->addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT M.login, M.first_name, M.last_name, M.email, M.member_id, A.* FROM ".TABLE_PREFIX."members M, ".TABLE_PREFIX."instructor_approvals A WHERE A.member_id=M.member_id ORDER BY M.login"; +$result = mysql_query($sql, $db); +$num_pending = mysql_num_rows($result); +?> + +
    + + + + + + + + + + + + + + + + + +'; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + echo ''; + + echo ''; + } while ($row = mysql_fetch_assoc($result)); + } else { + echo ''; + } +?> + +
     
    + +
    '.AT_print($row['first_name'], 'members.first_name').''.AT_print($row['last_name'], 'members.last_name').''.AT_print($row['email'], 'members.email').''.AT_print($row['notes'], 'instructor_approvals.notes').'
    '._AT('none_found').'
    +
    + + \ No newline at end of file diff --git a/mods/_core/users/lib/pref_functions.inc.php b/mods/_core/users/lib/pref_functions.inc.php new file mode 100644 index 000000000..083a1e67c --- /dev/null +++ b/mods/_core/users/lib/pref_functions.inc.php @@ -0,0 +1,392 @@ + + + +'; + +function get_user_style() +{ + global $user_style_template; + + if (($_SESSION["prefs"]["PREF_FONT_FACE"] == "") + && ($_SESSION["prefs"]["PREF_FONT_TIMES"] == 0 || $_SESSION["prefs"]["PREF_FONT_TIMES"] == 0.8) + && ($_SESSION["prefs"]["PREF_FG_COLOUR"] == "") + && ($_SESSION["prefs"]["PREF_BG_COLOUR"] == "") + && ($_SESSION["prefs"]["PREF_HL_COLOUR"] == "")) + { + return ""; + } + else + { + if ($_SESSION["prefs"]["PREF_FONT_FACE"] <> "") + $font = "font-family: ". $_SESSION["prefs"]["PREF_FONT_FACE"] .";"; + + if ($_SESSION["prefs"]["PREF_FONT_TIMES"] <> 0 && $_SESSION["prefs"]["PREF_FONT_TIMES"] <> 0.8) + $font_size = "font-size: ". $_SESSION["prefs"]["PREF_FONT_TIMES"] ."em;"; + + if ($_SESSION["prefs"]["PREF_FG_COLOUR"] <> "") + $fg_color = "color: #". $_SESSION["prefs"]["PREF_FG_COLOUR"] .";"; + + if ($_SESSION["prefs"]["PREF_BG_COLOUR"] <> "") + $bg_color = "background-color: #". $_SESSION["prefs"]["PREF_BG_COLOUR"] .";"; + + if ($_SESSION["prefs"]["PREF_HL_COLOUR"] <> "") + $hl_color = "background-color: #". $_SESSION["prefs"]["PREF_HL_COLOUR"] .";"; + + return str_replace(array("{FONT}", "{FONT_SIZE}", "{FG_COLOR}", "{BG_COLOR}", "{HL_COLOR}"), + array($font, $font_size, $fg_color, $bg_color, $hl_color), + $user_style_template); + } +} + +?> \ No newline at end of file diff --git a/mods/_core/users/lib/pref_tab_functions.inc.php b/mods/_core/users/lib/pref_tab_functions.inc.php new file mode 100644 index 000000000..974fc69dc --- /dev/null +++ b/mods/_core/users/lib/pref_tab_functions.inc.php @@ -0,0 +1,267 @@ + + + + + + + + + + + + + +
    + + <?php echo _AT('usaved_changes_made'); ?> + '; ?> + + +   + + <?php echo _AT('usaved_changes_made'); ?> + + + '; ?> +   
    + tag +function output_language_options($languages, $selected_lang) +{ + foreach ($languages as $codes) + { + $language = current($codes); + + $lang_code = $language->getCode(); + $lang_native_name = $language->getNativeName(); + $lang_english_name = $language->getEnglishName() +?> + + $value) { + $temp_prefs[$pref_name] = $value; + } + + /* custom prefs */ + // atutor settings (tab 0) + if (isset($_POST['numbering'])) $temp_prefs['PREF_NUMBERING'] = intval($_POST['numbering']); + if (isset($_POST['theme'])) $temp_prefs['PREF_THEME'] = $temp_prefs['PREF_THEME_ORIG'] = $addslashes($_POST['theme']); + if (isset($_POST['mobile_theme'])) $temp_prefs['PREF_MOBILE_THEME'] = $addslashes($_POST['mobile_theme']); + if (isset($_POST['time_zone'])) $temp_prefs['PREF_TIMEZONE'] = $addslashes($_POST['time_zone']); + if (isset($_POST['use_jump_direct'])) $temp_prefs['PREF_JUMP_REDIRECT'] = intval($_POST['use_jump_redirect']); + if (isset($_POST['form_focus'])) $temp_prefs['PREF_FORM_FOCUS'] = intval($_POST['form_focus']); + if (isset($_POST['content_editor'])) $temp_prefs['PREF_CONTENT_EDITOR'] = intval($_POST['content_editor']); + if (isset($_POST['show_guide']))$temp_prefs['PREF_SHOW_GUIDE'] = intval($_POST['show_guide']); + + // display settings (tab 1) + if (isset($_POST['fontface'])) $temp_prefs['PREF_FONT_FACE'] = $addslashes($_POST['fontface']); + if (isset($_POST['font_times'])) $temp_prefs['PREF_FONT_TIMES'] = $addslashes($_POST['font_times']); + if (isset($_POST['fg'])) $temp_prefs['PREF_FG_COLOUR'] = $addslashes($_POST['fg']); + if (isset($_POST['bg'])) $temp_prefs['PREF_BG_COLOUR'] = $addslashes($_POST['bg']); + if (isset($_POST['hl'])) $temp_prefs['PREF_HL_COLOUR'] = $addslashes($_POST['hl']); + + // content settings (tab 2) + if (isset($_POST['use_alternative_to_text'])) $temp_prefs['PREF_USE_ALTERNATIVE_TO_TEXT'] = intval($_POST['use_alternative_to_text']); + if (isset($_POST['preferred_alt_to_text'])) $temp_prefs['PREF_ALT_TO_TEXT'] = $addslashes($_POST['preferred_alt_to_text']); + if (isset($_POST['alt_to_text_append_or_replace'])) $temp_prefs['PREF_ALT_TO_TEXT_APPEND_OR_REPLACE'] = $addslashes($_POST['alt_to_text_append_or_replace']); + if (isset($_POST['alt_text_prefer_lang'])) $temp_prefs['PREF_ALT_TEXT_PREFER_LANG'] = $addslashes($_POST['alt_text_prefer_lang']); + if (isset($_POST['use_alternative_to_audio'])) $temp_prefs['PREF_USE_ALTERNATIVE_TO_AUDIO'] = intval($_POST['use_alternative_to_audio']); + if (isset($_POST['preferred_alt_to_audio'])) $temp_prefs['PREF_ALT_TO_AUDIO'] = $addslashes($_POST['preferred_alt_to_audio']); + if (isset($_POST['alt_to_audio_append_or_replace'])) $temp_prefs['PREF_ALT_TO_AUDIO_APPEND_OR_REPLACE'] = $addslashes($_POST['alt_to_audio_append_or_replace']); + if (isset($_POST['alt_audio_prefer_lang'])) $temp_prefs['PREF_ALT_AUDIO_PREFER_LANG'] = $addslashes($_POST['alt_audio_prefer_lang']); + if (isset($_POST['use_alternative_to_visual'])) $temp_prefs['PREF_USE_ALTERNATIVE_TO_VISUAL'] = intval($_POST['use_alternative_to_visual']); + if (isset($_POST['preferred_alt_to_visual'])) $temp_prefs['PREF_ALT_TO_VISUAL'] = $addslashes($_POST['preferred_alt_to_visual']); + if (isset($_POST['alt_to_visual_append_or_replace'])) $temp_prefs['PREF_ALT_TO_VISUAL_APPEND_OR_REPLACE'] = $addslashes($_POST['alt_to_visual_append_or_replace']); + if (isset($_POST['alt_visual_prefer_lang'])) $temp_prefs['PREF_ALT_VISUAL_PREFER_LANG'] = $addslashes($_POST['alt_visual_prefer_lang']); + + // tool settings (tab 3) + if (isset($_POST['dictionary_val'])) $temp_prefs['PREF_DICTIONARY'] = intval($_POST['dictionary_val']); + if (isset($_POST['thesaurus_val'])) $temp_prefs['PREF_THESAURUS'] = intval($_POST['thesaurus_val']); + if (isset($_POST['note_taking_val'])) $temp_prefs['PREF_NOTE_TAKING'] = intval($_POST['note_taking_val']); + if (isset($_POST['calculator_val'])) $temp_prefs['PREF_CALCULATOR'] = intval($_POST['calculator_val']); + if (isset($_POST['abacus_val'])) $temp_prefs['PREF_ABACUS'] = intval($_POST['abacus_val']); + if (isset($_POST['atlas_val'])) $temp_prefs['PREF_ATLAS'] = intval($_POST['atlas_val']); + if (isset($_POST['encyclopedia_val'])) $temp_prefs['PREF_ENCYCLOPEDIA'] = intval($_POST['encyclopedia_val']); + + // control settings (tab 4) + if (isset($_POST['show_contents'])) $temp_prefs['PREF_SHOW_CONTENTS'] = intval($_POST['show_contents']); + if (isset($_POST['show_next_previous_buttons'])) $temp_prefs['PREF_SHOW_NEXT_PREVIOUS_BUTTONS'] = intval($_POST['show_next_previous_buttons']); + if (isset($_POST['show_bread_crumbs'])) $temp_prefs['PREF_SHOW_BREAD_CRUMBS'] = intval($_POST['show_bread_crumbs']); + + return $temp_prefs; +} + +/** + * Assigns default preferences to a preferences array + * + * @return an array of preferences + */ +function assignDefaultPrefs() { +global $db, $_config_defaults; + $sql = "SELECT value FROM ".TABLE_PREFIX."config WHERE name='pref_defaults'"; + $result = mysql_query($sql, $db); + + if (mysql_num_rows($result) > 0) + { + $row_defaults = mysql_fetch_assoc($result); + $default = $row_defaults["value"]; + + $temp_prefs = unserialize($default); + + // Many new preferences are introduced in 1.6.2 that are missing in old admin + // default preference string. Solve this case by completing settings on new + // preferences with $_config_defaults + foreach (unserialize($_config_defaults['pref_defaults']) as $name => $value) { + if (!isset($temp_prefs[$name])) $temp_prefs[$name] = $value; + } + } + else { + $temp_prefs = unserialize($_config_defaults['pref_defaults']); + } + // PREF_THEME is replaced by PREF_THEME_ORIG when the page is accessed from a mobile device + // and PREF_THEME is put back from PREF_THEME_ORIG in save_prefs(). @see vitals.inc.php + $temp_prefs['PREF_THEME_ORIG'] = $temp_prefs['PREF_THEME']; + + return $temp_prefs; +} + +/** + * gets the default preference for inbox notification (disabled usually) + * + * @return the value of the inbox notification preference + */ +function assignDefaultMnot() { +global $db, $_config_defaults; + $sql = "SELECT value FROM ".TABLE_PREFIX."config WHERE name='pref_inbox_notify'"; + $result = mysql_query($sql, $db); + if (mysql_num_rows($result) > 0) + { + $row_notify = mysql_fetch_assoc($result); + $mnot = $row_notify["value"]; + } + else + $mnot = $_config_defaults['pref_inbox_notify']; + return $mnot; +} + +/** + * gets the default preference for auto login preference (disabled usually) + * + * @return the value of the auto login preference + */ +function assignDefaultAutologin() { +global $db, $_config_defaults;; + $sql = "SELECT value FROM ".TABLE_PREFIX."config WHERE name='pref_is_auto_login'"; + $result = mysql_query($sql, $db); + if (mysql_num_rows($result) > 0) + { + $row_is_auto_login = mysql_fetch_assoc($result); + $auto_login = $row_is_auto_login["value"]; + } + else + $auto_login = $_config_defaults['pref_is_auto_login']; + return $auto_login; + +} + +/** + * Either sets the auto login cookies or expires them depending on the input + * + * @param string $toDo - 'enable' if the autologin cookies are to be set, and + * 'disable' if the auto login cookies are to be expired. + * + * @return string - either 'enable' if the cookies were set, or 'disable' otherwise. + */ +function setAutoLoginCookie($toDo) { +global $db; + + //set default values for disabled auto login cookies + $parts = parse_url(AT_BASE_HREF); + $path = $parts['path']; + $time = time() - 172800; + $password = ""; + $login = ""; + $is_auto_login = 'disable'; + + //if enable auto login, set actual cookie values + if ($toDo == 'enable') { + $time = time() + 172800; + $sql = "SELECT password FROM ".TABLE_PREFIX."members WHERE member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $password = $row["password"]; + $login = $_SESSION['login']; + } + + //set cookies and boolean value indicating cookies have been set.ies + $is_cookie_login_set = ATutor.setcookie('ATLogin', $login, $time, $path); + $is_cookie_pass_set = ATutor.setcookie('ATPass', $password, $time, $path); + if ($is_cookie_login_set && $is_cookie_pass_set) $is_auto_login = $toDo; + return $is_auto_login; +} + +/** + * Sets $is_auto_login to 'enable' if both auto login cookies are set, otherwise + * returns 'disable' + * + * @return string + */ +function checkAutoLoginCookie() { + $is_auto_login = 'disable'; + if (isset($_COOKIE['ATLogin']) && isset($_COOKIE['ATPass'])) { + $is_auto_login = 'enable'; + } + return $is_auto_login; +} + +?> \ No newline at end of file diff --git a/mods/_core/users/master_list.php b/mods/_core/users/master_list.php new file mode 100644 index 000000000..e232f5e86 --- /dev/null +++ b/mods/_core/users/master_list.php @@ -0,0 +1,340 @@ +addInfo('MASTER_LIST_DISABLED'); + $msg->printInfos(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + + +if (isset($_POST['submit'])) { + if ($_FILES['file']['error'] == 1) { + $errors = array('FILE_MAX_SIZE', ini_get('upload_max_filesize')); + $msg->addError($errors); + header('Location: '.$_SERVER['PHP_SELF']); + exit; + } + + if (!$_FILES['file']['name'] || (!is_uploaded_file($_FILES['file']['tmp_name']))) { + $msg->addError('FILE_NOT_SELECTED'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; + } + + $fp = fopen($_FILES['file']['tmp_name'], 'r'); + if ($fp) { + $existing_accounts = array(); + $number_of_updates = 0; + + if ($_POST['override'] > 0) { + /* Delete all the un-created accounts. (There is no member to delete or disable). */ + $sql = "DELETE FROM ".TABLE_PREFIX."master_list WHERE member_id=0"; + $result = mysql_query($sql, $db); + + /* Get all the created accounts. (They will be disabled or deleted if not in the new list). */ + $sql = "SELECT public_field, member_id FROM ".TABLE_PREFIX."master_list"; + $result = mysql_query($sql, $db); + $num_affected += mysql_affected_rows($db); + if ($num_affected > 0) { + $number_of_updated += $num_affected; + } + while ($row = mysql_fetch_assoc($result)) { + $existing_accounts[$row['public_field']] = $row['member_id']; + } + } + $sql = ''; + while (($row = fgetcsv($fp, 1000, ',')) !== FALSE) { + if (count($row) != 2) { + continue; + } + if (!$existing_accounts[$row[0]]) { + $row[0] = addslashes($row[0]); + $row[1] = md5($row[1]); // this may be hashed + + $sql = "INSERT INTO ".TABLE_PREFIX."master_list VALUES ('$row[0]', '$row[1]', 0)"; + mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_INSERT, 'master_list', mysql_affected_rows($db), $sql); + $num_affected = mysql_affected_rows($db); + if ($num_affected > 0) { + $number_of_updated += $num_affected; + } + } + unset($existing_accounts[$row[0]]); + } + fclose($fp); + + if (($_POST['override'] == 1) && $existing_accounts) { + // disable missing accounts + $existing_accounts = implode(',', $existing_accounts); + + $sql = "UPDATE ".TABLE_PREFIX."members SET status=".AT_STATUS_DISABLED.", creation_date=creation_date, last_login=last_login WHERE member_id IN ($existing_accounts)"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_UPDATE, 'members', mysql_affected_rows($db), $sql); + + // un-enrol disabled accounts + $sql = "DELETE FROM ".TABLE_PREFIX."course_enrollment WHERE member_id IN ($existing_accounts)"; + $result = mysql_query($sql, $db); + + $num_affected = mysql_affected_rows($db); + if ($num_affected > 0) { + $number_of_updated += $num_affected; + } + write_to_log(AT_ADMIN_LOG_DELETE, 'course_enrollment', mysql_affected_rows($db), $sql); + + } else if ($_POST['override'] == 2) { + // delete missing accounts + } + + if ($number_of_updated > 0) { + $msg->addFeedback('MASTER_LIST_UPLOADED'); + } else { + $msg->addFeedback('MASTER_LIST_NO_CHANGES'); + } + header('Location: '.$_SERVER['PHP_SELF']); + } + + exit; +} else if (isset($_GET['edit'], $_GET['id'])) { + if (substr($_GET['id'], 0, 1) != '-') { + header('Location: '.AT_BASE_HREF.'mods/_core/users/edit_user.php?id='.$_GET['id'] . SEP . 'ml=1'); + } else { + header('Location: '.AT_BASE_HREF.'mods/_core/users/master_list_edit.php?id='.substr($_GET['id'], 1) . SEP . 'ml=1'); + } + exit; +} else if (isset($_GET['delete'], $_GET['id'])) { + if (substr($_GET['id'], 0, 1) != '-') { + header('Location: '.AT_BASE_HREF.'mods/_core/users/admin_delete.php?id='.$_GET['id'] . SEP . 'ml=1'); + } else { + header('Location: '.AT_BASE_HREF.'mods/_core/users/master_list_delete.php?id='.substr($_GET['id'], 1) . SEP . 'ml=1'); + } + exit; +} else if (isset($_GET['delete']) || isset($_GET['edit'])) { + $msg->addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +if ($_GET['reset_filter']) { + unset($_GET); +} +?> + +
    +
    +
    +

    +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    +
    + +0 '; + } + $page_string .= SEP.'status='.$_GET['status']; +} else { + $status = '1'; +} + +if ($_GET['search']) { + $_GET['search'] = trim($_GET['search']); + $page_string .= SEP.'search='.urlencode($_GET['search']); + $search = $addslashes($_GET['search']); + + $search = explode(',', $search); + + $sql = ''; + foreach ($search as $term) { + $term = trim($term); + $term = str_replace(array('%','_'), array('\%', '\_'), $term); + if ($term) { + if (strpos($term, '-') === FALSE) { + $term = '%'.$term.'%'; + $sql .= "(M.public_field LIKE '$term') OR "; + } else { + // range search + $range = explode('-', $term, 2); + $range[0] = trim($range[0]); + $range[1] = trim($range[1]); + if (is_numeric($range[0]) && is_numeric($range[1])) { + $sql .= "(M.public_field >= $range[0] AND M.public_field <= $range[1]) OR "; + } else { + $sql .= "(M.public_field >= '$range[0]' AND M.public_field <= '$range[1]') OR "; + } + } + } + } + $sql = '('.substr($sql, 0, -3).')'; + $search = $sql; +} else { + $search = '1'; +} + +$sql = "SELECT COUNT(member_id) AS cnt FROM ".TABLE_PREFIX."master_list M WHERE $status AND $search"; + +$result = mysql_query($sql, $db); +$row = mysql_fetch_assoc($result); + +$num_results = $row['cnt']; + +$results_per_page = 100; +$num_pages = max(ceil($num_results / $results_per_page), 1); +$page = intval($_GET['p']); +if (!$page) { + $page = 1; +} +$offset = ($page-1)*$results_per_page; + +$sql = "SELECT M.*, B.login, B.first_name, B.second_name, B.last_name FROM ".TABLE_PREFIX."master_list M LEFT JOIN ".TABLE_PREFIX."members B USING (member_id) WHERE $status AND $search ORDER BY M.public_field LIMIT $offset, $results_per_page"; +$result = mysql_query($sql, $db); +?> + +
    +
    +
    +

    +
    + +
    +
    + /> + + /> + + /> +
    + +
    +
    + +
    + +
    + + +
    +
    +
    + +
    +
      + +
    • + + + + + +
    • + +
    +
    + + +
    + + + + + + + + + + + + + + 0): ?> + + + + + + + + + + + + + + + + + + + + + + +
     
    +
    + \ No newline at end of file diff --git a/mods/_core/users/master_list_delete.php b/mods/_core/users/master_list_delete.php new file mode 100644 index 000000000..8eee93309 --- /dev/null +++ b/mods/_core/users/master_list_delete.php @@ -0,0 +1,49 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/master_list.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + $_POST['id'] = $addslashes($_POST['id']); + + $sql = "DELETE FROM ".TABLE_PREFIX."master_list WHERE public_field='$_POST[id]'"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_DELETE, 'master_list', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/master_list.php'); + exit; +} +require(AT_INCLUDE_PATH.'header.inc.php'); ?> +addConfirm($confirm, $hidden_vars); + $msg->printConfirm(); +} +?> + \ No newline at end of file diff --git a/mods/_core/users/master_list_edit.php b/mods/_core/users/master_list_edit.php new file mode 100644 index 000000000..84bdb6cf6 --- /dev/null +++ b/mods/_core/users/master_list_edit.php @@ -0,0 +1,74 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/master_list.php'); + exit; +} else if (isset($_POST['submit'])) { + $_POST['public_field'] = trim($_POST['public_field']); + if ($_POST['public_field'] == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('student_id'))); + } + + if (!$msg->containsErrors()) { + $_POST['public_field'] = $addslashes($_POST['public_field']); + + $sql = "UPDATE ".TABLE_PREFIX."master_list SET public_field='$_POST[public_field]' WHERE public_field='$_POST[id]'"; + $result = mysql_query($sql, $db); + + write_to_log(AT_ADMIN_LOG_UPDATE, 'master_list', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: '.AT_BASE_HREF.'mods/_core/users/master_list.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT * FROM ".TABLE_PREFIX."master_list WHERE public_field='$_REQUEST[id]'"; +$result = mysql_query($sql, $db); +if (!($row = mysql_fetch_assoc($result))) { + $msg->addError('USER_NOT_FOUND'); + $msg->printErrors(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} else { + $_POST = $row; +} +?> +
    + +
    +
    + *
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/users/module.php b/mods/_core/users/module.php new file mode 100644 index 000000000..3bd70ea02 --- /dev/null +++ b/mods/_core/users/module.php @@ -0,0 +1,90 @@ +getAdminPrivilege()); + +// for admin +if (admin_authenticate(AT_ADMIN_PRIV_USERS, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + + $this->_pages[AT_NAV_ADMIN] = array('mods/_core/users/users.php'); + + $this->_pages['mods/_core/users/users.php']['title_var'] = 'users'; + $this->_pages['mods/_core/users/users.php']['parent'] = AT_NAV_ADMIN; + $this->_pages['mods/_core/users/users.php']['guide'] = 'admin/?p=users.php'; + $this->_pages['mods/_core/users/users.php']['children'] = array('mods/_core/users/create_user.php', 'mods/_core/users/instructor_requests.php', 'mods/_core/users/master_list.php', 'mods/_core/users/admin_email.php'); + + if (admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + $this->_pages['mods/_core/users/users.php']['children'][] = 'mods/_core/users/admins/index.php'; + + $this->_pages['mods/_core/users/admins/index.php']['title_var'] = 'administrators'; + $this->_pages['mods/_core/users/admins/index.php']['parent'] = 'mods/_core/users/users.php'; + $this->_pages['mods/_core/users/admins/index.php']['guide'] = 'admin/?p=administrators.php'; + $this->_pages['mods/_core/users/admins/index.php']['children'] = array('mods/_core/users/admins/create.php', 'mods/_core/users/admins/log.php'); + + $this->_pages['mods/_core/users/admins/log.php']['title_var'] = 'admin_log'; + $this->_pages['mods/_core/users/admins/log.php']['parent'] = 'mods/_core/users/admins/index.php'; + $this->_pages['mods/_core/users/admins/log.php']['children'] = array('mods/_core/users/admins/reset_log.php'); + + $this->_pages['mods/_core/users/admins/reset_log.php']['title_var'] = 'reset_log'; + $this->_pages['mods/_core/users/admins/reset_log.php']['parent'] = 'mods/_core/users/admins/log.php'; + + $this->_pages['mods/_core/users/admins/detail_log.php']['title_var'] = 'details'; + $this->_pages['mods/_core/users/admins/detail_log.php']['parent'] = 'mods/_core/users/admins/log.php'; + + $this->_pages['mods/_core/users/admins/create.php']['title_var'] = 'create_admin'; + $this->_pages['mods/_core/users/admins/create.php']['parent'] = 'mods/_core/users/admins/index.php'; + + $this->_pages['mods/_core/users/admins/edit.php']['title_var'] = 'edit_admin'; + $this->_pages['mods/_core/users/admins/edit.php']['parent'] = 'mods/_core/users/admins/index.php'; + + $this->_pages['mods/_core/users/admins/password.php']['title_var'] = 'password'; + $this->_pages['mods/_core/users/admins/password.php']['parent'] = 'mods/_core/users/admins/index.php'; + + $this->_pages['mods/_core/users/admins/delete.php']['title_var'] = 'delete_admin'; + $this->_pages['mods/_core/users/admins/delete.php']['parent'] = 'mods/_core/users/admins/index.php'; + } + + $this->_pages['mods/_core/users/admin_email.php']['title_var'] = 'admin_email'; + $this->_pages['mods/_core/users/admin_email.php']['parent'] = 'mods/_core/users/users.php'; + $this->_pages['mods/_core/users/admin_email.php']['guide'] = 'admin/?p=email_users.php'; + + $this->_pages['mods/_core/users/create_user.php']['title_var'] = 'create_user'; + $this->_pages['mods/_core/users/create_user.php']['parent'] = 'mods/_core/users/users.php'; + + $this->_pages['mods/_core/users/default_preferences.php']['title_var'] = 'default_preferences'; + $this->_pages['mods/_core/users/default_preferences.php']['parent'] = 'admin/config_edit.php'; + $this->_pages['mods/_core/users/default_preferences.php']['guide'] = 'admin/?p=default_preferences.php'; + $this->_pages['admin/config_edit.php']['children'] = array('mods/_core/users/default_preferences.php'); + + $this->_pages['mods/_core/users/password_user.php']['title_var'] = 'password'; + $this->_pages['mods/_core/users/password_user.php']['parent'] = 'mods/_core/users/users.php'; + + $this->_pages['mods/_core/users/instructor_requests.php']['title_var'] = 'instructor_requests'; + $this->_pages['mods/_core/users/instructor_requests.php']['parent'] = 'mods/_core/users/users.php'; + $this->_pages['mods/_core/users/instructor_requests.php']['guide'] = 'admin/?p=instructor_requests.php'; + + $this->_pages['mods/_core/users/admin_deny.php']['title_var'] = 'deny_instructor_request'; + $this->_pages['mods/_core/users/admin_deny.php']['parent'] = 'mods/_core/users/instructor_requests.php'; + + $this->_pages['mods/_core/users/master_list.php']['title_var'] = 'master_student_list'; + $this->_pages['mods/_core/users/master_list.php']['parent'] = 'mods/_core/users/users.php'; + $this->_pages['mods/_core/users/master_list.php']['guide'] = 'admin/?p=master_student_list.php'; + + $this->_pages['mods/_core/users/master_list_edit.php']['title_var'] = 'edit'; + $this->_pages['mods/_core/users/master_list_edit.php']['parent'] = 'mods/_core/users/master_list.php'; + + $this->_pages['mods/_core/users/master_list_delete.php']['title_var'] = 'delete'; + $this->_pages['mods/_core/users/master_list_delete.php']['parent'] = 'mods/_core/users/master_list.php'; + + $this->_pages['mods/_core/users/edit_user.php']['title_var'] = 'edit_user'; + $this->_pages['mods/_core/users/edit_user.php']['parent'] = 'mods/_core/users/users.php'; + + $this->_pages['mods/_core/users/admin_delete.php']['title_var'] = 'delete_user'; + $this->_pages['mods/_core/users/admin_delete.php']['parent'] = 'mods/_core/users/users.php'; + + $this->_pages['mods/_core/users/user_status.php']['title_var'] = 'status'; + $this->_pages['mods/_core/users/user_status.php']['parent'] = 'mods/_core/users/users.php'; + +} +?> \ No newline at end of file diff --git a/mods/_core/users/module.xml b/mods/_core/users/module.xml new file mode 100644 index 000000000..6e71574af --- /dev/null +++ b/mods/_core/users/module.xml @@ -0,0 +1,23 @@ + + + Users + Allows administrators to manage the users on a system. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + + create + + 2005-09-12 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_core/users/password_user.php b/mods/_core/users/password_user.php new file mode 100644 index 000000000..085934230 --- /dev/null +++ b/mods/_core/users/password_user.php @@ -0,0 +1,145 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/users.php'); + exit; +} else if (isset($_POST['submit'])) { + /* password check: password is verified front end by javascript. here is to handle the errors from javascript */ + if ($_POST['password_error'] <> "") + { + $pwd_errors = explode(",", $_POST['password_error']); + + foreach ($pwd_errors as $pwd_error) + { + if ($pwd_error == "missing_password") + $missing_fields[] = _AT('password'); + else + $msg->addError($pwd_error); + } + } + + if (!$msg->containsErrors()) { + $_POST['id'] = intval($_POST['id']); + + $sql = "UPDATE ".TABLE_PREFIX."members SET password= '$_POST[form_password_hidden]', creation_date=creation_date, last_login=last_login WHERE member_id=$_POST[id]"; + $result = mysql_query($sql, $db); + + $sql = "SELECT login, email FROM ".TABLE_PREFIX."members WHERE member_id=$_POST[id]"; + $result = mysql_query($sql,$db); + if ($row = mysql_fetch_assoc($result)) { + $r_login = $row['login']; + $r_email = $row['email']; + + $tmp_message = _AT('password_change_msg')."\n\n"; + $tmp_message .= _AT('web_site').' : '.AT_BASE_HREF."\n"; + $tmp_message .= _AT('login_name').' : '.$r_login."\n"; + + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + $mail = new ATutorMailer; + + $mail->From = $_config['contact_email']; + $mail->AddAddress($r_email); + $mail->Subject = $_config['site_name'] . ': ' . _AT('password_changed'); + $mail->Body = $tmp_message; + + if(!$mail->Send()) { + $msg->printErrors('SENDING_ERROR'); + exit; + } + + } + + $msg->addFeedback('PROFILE_UPDATED_ADMIN'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/users.php'); + exit; + } + $_GET['id'] = $_POST['id']; +} + + +$onload = 'document.form.password.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$id = intval($_GET['id']); + +$sql = "SELECT login FROM ".TABLE_PREFIX."members WHERE member_id=$id"; +$result = mysql_query($sql, $db); + +if (!$row = mysql_fetch_assoc($result)) { + $msg->printErrors('USER_NOT_FOUND'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +?> + + + + +
    + + + + +
    +
    +

    +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/users/user_enrollment.php b/mods/_core/users/user_enrollment.php new file mode 100644 index 000000000..85a618752 --- /dev/null +++ b/mods/_core/users/user_enrollment.php @@ -0,0 +1,210 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/users.php'); + exit; +} else if (isset($_POST['enrolled_unenroll'])) { + $_POST['id'] = intval($_POST['id']); + + if (!is_array($_POST['enrolled'])) { + $msg->addError('NO_ITEM_SELECTED'); + } else { + $cids = implode(',', $_POST['enrolled']); + $sql = "DELETE FROM ".TABLE_PREFIX."course_enrollment WHERE member_id={$_POST['id']} AND course_id IN ($cids)"; + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_SERVER['PHP_SELF'] . '?id='.$_POST['id']); + exit; + } +} else if (isset($_POST['pending_remove'])) { + $_POST['id'] = intval($_POST['id']); + + if (!is_array($_POST['pending'])) { + $msg->addError('NO_ITEM_SELECTED'); + } else { + $cids = implode(',', $_POST['pending']); + $sql = "DELETE FROM ".TABLE_PREFIX."course_enrollment WHERE member_id={$_POST['id']} AND course_id IN ($cids)"; + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_SERVER['PHP_SELF'] . '?id='.$_POST['id']); + exit; + } +} else if (isset($_POST['pending_enroll'])) { + $_POST['id'] = intval($_POST['id']); + + if (!is_array($_POST['pending'])) { + $msg->addError('NO_ITEM_SELECTED'); + } else { + $cids = implode(',', $_POST['pending']); + $sql = "UPDATE ".TABLE_PREFIX."course_enrollment SET approved='y' WHERE member_id={$_POST['id']} AND course_id IN ($cids)"; + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_SERVER['PHP_SELF'] . '?id='.$_POST['id']); + exit; + } +} else if (isset($_POST['not_enrolled_enroll'])) { + $_POST['id'] = intval($_POST['id']); + + if (!is_array($_POST['not_enrolled'])) { + $msg->addError('NO_ITEM_SELECTED'); + } else { + foreach ($_POST['not_enrolled'] as $cid) { + $sql = "INSERT INTO ".TABLE_PREFIX."course_enrollment VALUES ({$_POST['id']}, $cid, 'y', 0, '', 0)"; + mysql_query($sql, $db); + } + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_SERVER['PHP_SELF'] . '?id='.$_POST['id']); + exit; + } +} + +$id = intval($_GET['id']); + +// add the user's name to the page heading: +$_pages['mods/_core/users/user_enrollment.php']['title'] = _AT('enrollment').': '.get_display_name($id); + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT login FROM ".TABLE_PREFIX."members WHERE member_id=$id"; +$result = mysql_query($sql, $db); + +if (!$row = mysql_fetch_assoc($result)) { + $msg->printErrors('USER_NOT_FOUND'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$enrollment = array(); +$sql = "SELECT * FROM ".TABLE_PREFIX."course_enrollment WHERE member_id=$id"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $enrollment[$row['course_id']] = $row; +} + +$instruct = array(); +$enrolled = array(); +$pending = array(); +$not_enrolled = array(); + +foreach ($system_courses as $cid => $course) { + if ($course['member_id'] == $id) { + $instruct[] = $cid; + } else if (isset($enrollment[$cid]) && $enrollment[$cid]['approved'] == 'y') { + $enrolled[] = $cid; + } else if (isset($enrollment[$cid]) && $enrollment[$cid]['approved'] == 'n') { + $pending[] = $cid; + } else { + $not_enrolled[] = $cid; + } +} + +?> + +
    + +
    +
    +

    + +
      + +
    • + +
    + + + +
    +
    + +
    +
    + +
    +
    +

    + +
      + +
    • + +
    + + + +
    +
    + + + + +
    + +
    + +
    +
    +

    + +
      + +
    • + +
    + + + +
    +
    + + + + + +
    +
    + +
    +
    +

    + +
      + +
    • + +
    + + + +
    +
    + + + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_core/users/user_status.php b/mods/_core/users/user_status.php new file mode 100644 index 000000000..d7de54c07 --- /dev/null +++ b/mods/_core/users/user_status.php @@ -0,0 +1,63 @@ +addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/users.php'); + exit; + +} else if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_core/users/users.php'); + exit; +} + + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$names = get_login($ids); +$names_html = '
      '.html_get_list($names).'
    '; +$status_name = get_status_name($status); + +$hidden_vars['ids'] = implode(',', array_keys($names)); +$hidden_vars['status'] = $status; + +$confirm = array('EDIT_STATUS', $status_name, $names_html); +$msg->addConfirm($confirm, $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_core/users/users.php b/mods/_core/users/users.php new file mode 100644 index 000000000..221cb1e88 --- /dev/null +++ b/mods/_core/users/users.php @@ -0,0 +1,411 @@ + 1) ) { + $msg->addError('SELECT_ONE_ITEM'); +} else if (isset($_GET['edit'], $_GET['id'])) { + header('Location: edit_user.php?id='.$_GET['id'][0]); + exit; +} else if (isset($_GET['password'], $_GET['id'])) { + header('Location: password_user.php?id='.$_GET['id'][0]); + exit; +} else if (isset($_GET['enrollment'], $_GET['id'])) { + header('Location: user_enrollment.php?id='.$_GET['id'][0]); + exit; +} else if ( isset($_GET['apply']) && isset($_GET['id']) && $_GET['change_status'] >= -1) { + $ids = implode(',', $_GET['id']); + $status = intval($_GET['change_status']); + if ($status == -1) { + header('Location: admin_delete.php?id='.$ids); + exit; + } else { + header('Location: user_status.php?ids='.$ids.'&status='.$status); + exit; + } +} else if ( (isset($_GET['apply']) || isset($_GET['apply_all'])) && $_GET['change_status'] < -1) { + $msg->addError('NO_ACTION_SELECTED'); +} else if (isset($_GET['apply']) || isset($_GET['edit']) || isset($_GET['delete']) || isset($_GET['password'])) { + $msg->addError('NO_ITEM_SELECTED'); +} + +if ($_GET['reset_filter']) { + unset($_GET); +} + +$page_string = ''; +$orders = array('asc' => 'desc', 'desc' => 'asc'); +$cols = array('login' => 1, 'public_field' => 1, 'first_name' => 1, 'second_name' => 1, 'last_name' => 1, 'email' => 1, 'status' => 1, 'last_login' => 1, 'creation_date' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'login'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'login'; +} else { + // no order set + $order = 'asc'; + $col = 'login'; +} +if (isset($_GET['status']) && ($_GET['status'] != '')) { + $_GET['status'] = intval($_GET['status']); + $status = '=' . intval($_GET['status']); + $page_string .= SEP.'status'.$status; +} else { + $status = '<>-1'; + $_GET['status'] = ''; +} + +if (isset($_GET['last_login_days'], $_GET['last_login_have']) && ($_GET['last_login_have'] >= 0) && $_GET['last_login_days']) { + $have = intval($_GET['last_login_have']); + $days = intval($_GET['last_login_days']); + $page_string .= SEP.'last_login_have='.$have; + $page_string .= SEP.'last_login_days='.$days; + + if ($have) { + $ll = " >= TO_DAYS(NOW())-$days)"; + } else { + $ll = " < TO_DAYS(NOW())-$days OR last_login+0=0)"; + } + $last_login_days = '(TO_DAYS(last_login)'.$ll; +} else { + $last_login_days = '1'; +} + +if (isset($_GET['include']) && $_GET['include'] == 'one') { + $checked_include_one = ' checked="checked"'; + $page_string .= SEP.'include=one'; +} else { + $_GET['include'] = 'all'; + $checked_include_all = ' checked="checked"'; + $page_string .= SEP.'include=all'; +} + +if ($_GET['search']) { + $page_string .= SEP.'search='.urlencode($stripslashes($_GET['search'])); + $search = $addslashes($_GET['search']); + $search = explode(' ', $search); + + if ($_GET['include'] == 'all') { + $predicate = 'AND '; + } else { + $predicate = 'OR '; + } + + $sql = ''; + foreach ($search as $term) { + $term = trim($term); + $term = str_replace(array('%','_'), array('\%', '\_'), $term); + if ($term) { + $term = '%'.$term.'%'; + $sql .= "((M.first_name LIKE '$term') OR (M.second_name LIKE '$term') OR (M.last_name LIKE '$term') OR (M.email LIKE '$term') OR (M.login LIKE '$term')) $predicate"; + } + } + $sql = '('.substr($sql, 0, -strlen($predicate)).')'; + $search = $sql; +} else { + $search = '1'; +} + +if ($_GET['searchid']) { + $_GET['searchid'] = trim($_GET['searchid']); + $page_string .= SEP.'searchid='.urlencode($_GET['searchid']); + $searchid = $addslashes($_GET['searchid']); + + $searchid = explode(',', $searchid); + + $sql = ''; + foreach ($searchid as $term) { + $term = trim($term); + $term = str_replace(array('%','_'), array('\%', '\_'), $term); + if ($term) { + if (strpos($term, '-') === FALSE) { + $term = '%'.$term.'%'; + $sql .= "(L.public_field LIKE '$term') OR "; + } else { + // range search + $range = explode('-', $term, 2); + $range[0] = trim($range[0]); + $range[1] = trim($range[1]); + if (is_numeric($range[0]) && is_numeric($range[1])) { + $sql .= "(L.public_field >= $range[0] AND L.public_field <= $range[1]) OR "; + } else { + $sql .= "(L.public_field >= '$range[0]' AND L.public_field <= '$range[1]') OR "; + } + } + } + } + $sql = '('.substr($sql, 0, -3).')'; + $searchid = $sql; +} else { + $searchid = '1'; +} + +if (defined('AT_MASTER_LIST') && AT_MASTER_LIST) { + $sql = "SELECT COUNT(M.member_id) AS cnt FROM ".TABLE_PREFIX."members M LEFT JOIN (SELECT * FROM ".TABLE_PREFIX."master_list WHERE member_id <> 0) L USING (member_id) WHERE M.status $status AND $search AND $searchid AND $last_login_days"; +} else { + $sql = "SELECT COUNT(member_id) AS cnt FROM ".TABLE_PREFIX."members M WHERE status $status AND $search AND $last_login_days"; +} + +$result = mysql_query($sql, $db); +$row = mysql_fetch_assoc($result); +$num_results = $row['cnt']; + +$results_per_page = 50; +$num_pages = max(ceil($num_results / $results_per_page), 1); +$page = intval($_GET['p']); +if (!$page) { + $page = 1; +} +$count = (($page-1) * $results_per_page) + 1; +$offset = ($page-1)*$results_per_page; + +if ( isset($_GET['apply_all']) && $_GET['change_status'] >= -1) { + $offset = 0; + $results_per_page = 999999; +} + +if (defined('AT_MASTER_LIST') && AT_MASTER_LIST) { + $sql = "SELECT M.member_id, M.login, M.first_name, M.second_name, M.last_name, M.email, M.status, M.last_login+0 AS last_login, M.creation_date, L.public_field FROM ".TABLE_PREFIX."members M LEFT JOIN (SELECT * FROM ".TABLE_PREFIX."master_list WHERE member_id <> 0) L USING (member_id) WHERE M.status $status AND $search AND $searchid AND $last_login_days ORDER BY $col $order LIMIT $offset, $results_per_page"; +} else { + $sql = "SELECT M.member_id, M.login, M.first_name, M.second_name, M.last_name, M.email, M.status, M.last_login+0 AS last_login, M.creation_date FROM ".TABLE_PREFIX."members M WHERE M.status $status AND $search AND $last_login_days ORDER BY $col $order LIMIT $offset, $results_per_page"; +} + +$result = mysql_query($sql, $db); + +if ( isset($_GET['apply_all']) && $_GET['change_status'] >= -1) { + $ids = ''; + while ($row = mysql_fetch_assoc($result)) { + $ids .= $row['member_id'].','; + } + $ids = substr($ids,0,-1); + $status = intval($_GET['change_status']); + + if ($status==-1) { + header('Location: admin_delete.php?id='.$ids); + exit; + } else { + header('Location: user_status.php?ids='.$ids.'&status='.$status); + exit; + } +} +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    +
    +
    +

    +
    + +
    +
    + /> + + /> + + /> + + /> + + /> +
    + +
    +
    + + +
    + : + /> + /> +
    + + +
    +
    + +
    + + +
    +
    + :
    + +
    + +
    + + +
    +
    +
    + + + +
    + + + + + + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0): ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + | + + + + +
    + + + + + +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/announcements/add_news.php b/mods/_standard/announcements/add_news.php new file mode 100644 index 000000000..ac669a0c0 --- /dev/null +++ b/mods/_standard/announcements/add_news.php @@ -0,0 +1,148 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/announcements/index.php'); + exit; +} + +if ((!$_POST['setvisual'] && $_POST['settext']) || !$_GET['setvisual']){ + $onload = 'document.form.title.focus();'; +} + +if (isset($_POST['add_news'])&& isset($_POST['submit'])) { + $_POST['formatting'] = intval($_POST['formatting']); + $_POST['title'] = trim($_POST['title']); + $_POST['body_text'] = trim($_POST['body_text']); + + $missing_fields = array(); + + if (!$_POST['body_text']) { + $missing_fields[] = _AT('body'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors() && (!isset($_POST['setvisual']) || isset($_POST['submit']))) { + + $_POST['formatting'] = intval($_POST['formatting']); + $_POST['title'] = $addslashes($_POST['title']); + $_POST['body_text'] = $addslashes($_POST['body_text']); + + //The following checks if title length exceed 100, defined by DB structure + $_POST['title'] = validate_length($_POST['title'], 100); + + $sql = "INSERT INTO ".TABLE_PREFIX."news VALUES (NULL, $_SESSION[course_id], $_SESSION[member_id], NOW(), $_POST[formatting], '$_POST[title]', '$_POST[body_text]')"; + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + /* update announcement RSS: */ + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS1.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS1.0.xml'); + } + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS2.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS2.0.xml'); + } + + header('Location: '.AT_BASE_HREF.'mods/_standard/announcements/index.php'); + exit; + } +} + +if (!isset($_REQUEST['setvisual']) && !isset($_REQUEST['settext'])) { + if ($_SESSION['prefs']['PREF_CONTENT_EDITOR'] == 1) { + $_POST['formatting'] = 1; + $_REQUEST['settext'] = 0; + $_REQUEST['setvisual'] = 0; + + } else if ($_SESSION['prefs']['PREF_CONTENT_EDITOR'] == 2) { + $_POST['formatting'] = 1; + $_POST['settext'] = 0; + $_POST['setvisual'] = 1; + + } else { // else if == 0 + $_POST['formatting'] = 0; + $_REQUEST['settext'] = 0; + $_REQUEST['setvisual'] = 0; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (($_POST['setvisual'] && !$_POST['settext']) || $_GET['setvisual']) { + load_editor(); +} +$msg->printErrors(); + +?> +
    + + +
    +
    +
    +
    + +
    + +
    +
    + onclick="javascript: document.form.setvisual.disabled=true;" /> + + + onclick="javascript: document.form.setvisual.disabled=false;"/> + + + '; + echo ''; + } else { + echo ''; + } + ?> +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/announcements/delete_news.php b/mods/_standard/announcements/delete_news.php new file mode 100644 index 000000000..78c55e106 --- /dev/null +++ b/mods/_standard/announcements/delete_news.php @@ -0,0 +1,67 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/announcements/index.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + $_POST['form_news_id'] = intval($_POST['form_news_id']); + + $sql = "DELETE FROM ".TABLE_PREFIX."news WHERE news_id=$_POST[form_news_id] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + + /* update announcement RSS: */ + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS1.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS1.0.xml'); + } + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS2.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS2.0.xml'); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_standard/announcements/index.php'); + exit; +} + +$_section[0][0] = _AT('delete_announcement'); + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $_GET['aid'] = intval($_GET['aid']); + + $sql = "SELECT * FROM ".TABLE_PREFIX."news WHERE news_id=$_GET[aid] AND course_id=$_SESSION[course_id]"; + + $result = mysql_query($sql,$db); + if (mysql_num_rows($result) == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); + } else { + $row = mysql_fetch_assoc($result); + + $hidden_vars['delete_news'] = TRUE; + $hidden_vars['form_news_id'] = $row['news_id']; + + $confirm = array('DELETE_NEWS', AT_print(htmlentities_utf8($row['title']), 'news.title')); + $msg->addConfirm($confirm, $hidden_vars); + + $msg->printConfirm(); + } + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/announcements/edit_news.php b/mods/_standard/announcements/edit_news.php new file mode 100644 index 000000000..981c74e4a --- /dev/null +++ b/mods/_standard/announcements/edit_news.php @@ -0,0 +1,156 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/announcements/index.php'); + exit; +} else if ($_POST['edit_news']) { + $_POST['title'] = trim($_POST['title']); + $_POST['body_text'] = trim($_POST['body_text']); + $_POST['aid'] = intval($_POST['aid']); + $_POST['formatting'] = intval($_POST['formatting']); + + if (($_POST['title'] == '') && ($_POST['body_text'] == '')) { + $msg->addErros('ANN_BOTH_EMPTY'); + } + + if (!$msg->containsErrors() && isset($_POST['submit'])) { + $_POST['title'] = $addslashes($_POST['title']); + $_POST['body_text'] = $addslashes($_POST['body_text']); + //Check if the title has exceeded the DB length, 100 + $_POST['title'] = validate_length($_POST['title'], 100); + + $sql = "UPDATE ".TABLE_PREFIX."news SET title='$_POST[title]', body='$_POST[body_text]', formatting=$_POST[formatting], date=date WHERE news_id=$_POST[aid] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql,$db); + + /* update announcement RSS: */ + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS1.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS1.0.xml'); + } + if (file_exists(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS2.0.xml')) { + @unlink(AT_CONTENT_DIR . 'feeds/' . $_SESSION['course_id'] . '/RSS2.0.xml'); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_standard/announcements/index.php'); + exit; + } +} + +if (!isset($_REQUEST['setvisual']) && !isset($_REQUEST['settext'])) { + if ($_SESSION['prefs']['PREF_CONTENT_EDITOR'] == 1) { + $_POST['formatting'] = 1; + $_REQUEST['settext'] = 0; + $_REQUEST['setvisual'] = 0; + + } else if ($_SESSION['prefs']['PREF_CONTENT_EDITOR'] == 2) { + $_POST['formatting'] = 1; + $_POST['settext'] = 0; + $_POST['setvisual'] = 1; + + } else { // else if == 0 + $_POST['formatting'] = 0; + $_REQUEST['settext'] = 0; + $_REQUEST['setvisual'] = 0; + } +} + +if ((!$_POST['setvisual'] && $_POST['settext']) || !$_GET['setvisual']){ + $onload = 'document.form.title.focus();'; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (($_POST['setvisual'] && !$_POST['settext']) || $_GET['setvisual']) { + load_editor(); +} + +if (isset($_GET['aid'])) { + $aid = intval($_GET['aid']); +} else { + $aid = intval($_POST['aid']); +} + +if ($aid == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$sql = "SELECT * FROM ".TABLE_PREFIX."news WHERE news_id=$aid AND course_id=$_SESSION[course_id]"; +$result = mysql_query($sql,$db); +if (!($row = mysql_fetch_array($result))) { + $msg->printErrors('ITEM_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +$_POST['formatting'] = intval($row['formatting']); + +?> + + +
    + + + +
    +
    + *
    + +
    + +
    +
    + onclick="javascript: document.form.setvisual.disabled=true;" />, + + onclick="javascript: document.form.setvisual.disabled=false;" /> + '; + echo ''; + } else { + echo ''; + } + ?> +
    + +
    + *
    + +
    + +
    + + +
    + + +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/announcements/index.php b/mods/_standard/announcements/index.php new file mode 100644 index 000000000..421211099 --- /dev/null +++ b/mods/_standard/announcements/index.php @@ -0,0 +1,95 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$orders = array('asc' => 'desc', 'desc' => 'asc'); +$cols = array('title' => 1, 'date' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'date'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'date'; +} else { + // no order set + $order = 'desc'; + $col = 'date'; +} + +$sql = "SELECT news_id, title, date FROM ".TABLE_PREFIX."news WHERE course_id=$_SESSION[course_id] ORDER BY $col $order"; +$result = mysql_query($sql, $db); + +?> +
    + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    +
    + + \ No newline at end of file diff --git a/mods/_standard/announcements/module.php b/mods/_standard/announcements/module.php new file mode 100644 index 000000000..287df03c1 --- /dev/null +++ b/mods/_standard/announcements/module.php @@ -0,0 +1,21 @@ +getPrivilege()); + +$this->_pages['mods/_standard/announcements/index.php']['title_var'] = 'announcements'; +$this->_pages['mods/_standard/announcements/index.php']['guide'] = 'instructor/?p=announcements.php'; +$this->_pages['mods/_standard/announcements/index.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_standard/announcements/index.php']['children'] = array('mods/_standard/announcements/add_news.php'); + + $this->_pages['mods/_standard/announcements/add_news.php']['title_var'] = 'add_announcement'; + $this->_pages['mods/_standard/announcements/add_news.php']['parent'] = 'mods/_standard/announcements/index.php'; + + $this->_pages['mods/_standard/announcements/edit_news.php']['title_var'] = 'edit_announcement'; + $this->_pages['mods/_standard/announcements/edit_news.php']['parent'] = 'mods/_standard/announcements/index.php'; + + $this->_pages['mods/_standard/announcements/delete_news.php']['title_var'] = 'delete_announcement'; + $this->_pages['mods/_standard/announcements/delete_news.php']['parent'] = 'mods/_standard/announcements/index.php'; + +?> \ No newline at end of file diff --git a/mods/_standard/announcements/module.xml b/mods/_standard/announcements/module.xml new file mode 100644 index 000000000..7bcc3de1d --- /dev/null +++ b/mods/_standard/announcements/module.xml @@ -0,0 +1,23 @@ + + + Announcements + Announcements are useful for posting important information to students on the course Home page. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + + + 2005-08-25 + stable + This is a standard module. + + \ No newline at end of file diff --git a/mods/_standard/announcements/module_backup.php b/mods/_standard/announcements/module_backup.php new file mode 100644 index 000000000..5fb48e09b --- /dev/null +++ b/mods/_standard/announcements/module_backup.php @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/mods/_standard/announcements/module_delete.php b/mods/_standard/announcements/module_delete.php new file mode 100644 index 000000000..a5d85a712 --- /dev/null +++ b/mods/_standard/announcements/module_delete.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/mods/_standard/announcements/module_news.php b/mods/_standard/announcements/module_news.php new file mode 100644 index 000000000..0de91007e --- /dev/null +++ b/mods/_standard/announcements/module_news.php @@ -0,0 +1,41 @@ + + */ +function announcements_news() { + global $db, $enrolled_courses, $system_courses; + $news = array(); + + if ($enrolled_courses == ''){ + return $news; + } + + $sql = 'SELECT * FROM '.TABLE_PREFIX.'news WHERE course_id IN '.$enrolled_courses.' ORDER BY date DESC'; + $result = mysql_query($sql, $db); + if($result){ + while($row = mysql_fetch_assoc($result)){ + $news[] = array('time'=>$row['date'], + 'object'=>$row, + 'alt'=>_AT('announcements'), + 'course'=>$system_courses[$row['course_id']]['title'], + 'thumb'=>'images/flag_blue.png', + 'link'=>$row['body']); + } + } + return $news; +} + +?> \ No newline at end of file diff --git a/mods/_standard/assignments/add_assignment.php b/mods/_standard/assignments/add_assignment.php new file mode 100644 index 000000000..b3a118a92 --- /dev/null +++ b/mods/_standard/assignments/add_assignment.php @@ -0,0 +1,374 @@ +addFeedback('ASSIGNMENT_NOT_FOUND'); + header('Location: index_instructor.php'); + exit; + } + + // get values of existing assignment from database + $title = $row['title']; + $assign_to = $row['assign_to']; + $multi_submit = $row['multi_submit']; + + $array1 = explode (' ', $row['date_due'], 2); + $array_date_due = explode ('-', $array1[0],3); + $array_time_due = explode (':', $array1[1]); + $dueyear = $array_date_due[0]; + $duemonth = $array_date_due[1]; + $dueday = $array_date_due[2]; + $duehour = $array_time_due[0]; + $dueminute = $array_time_due[1]; + + if ($dueyear == '0000'){ + $has_due_date = 'false'; + } else { + $has_due_date = 'true'; + } + + // use date from database + $array2 = explode (' ', $row['date_cutoff'], 2); + $array_date_cutoff = explode ('-', $array2[0],3); + $array_time_cutoff = explode (':', $array2[1]); + $cutoffyear = $array_date_cutoff[0]; + $cutoffmonth = $array_date_cutoff[1]; + $cutoffday = $array_date_cutoff[2]; + $cutoffhour = $array_time_cutoff[0]; + $cutoffminute = $array_time_cutoff[1]; + + if ($cutoffyear == '0000'){ + $late_submit = '0'; // allow late submissions always + } else if ($row['date_cutoff'] == $row['date_due']){ + $late_submit = '1'; // allow late submissions never + // use today's date as default + $cutoffday = $today['mday']; + $cutoffmonth = $today['mon']; + $cutoffyear = $today['year']; + $cutoffhour = $today['hours']; + $cutoffminute = $today['minutes']; + // round the minute to the next highest multiple of 5 + $cutoffminute = round($cutoffminute / '5' ) * '5' + '5'; + if ($cutoffminute > '55'){ $cutoffminute = '55'; } + } else { + $late_submit = '2'; // allow late submissions until (date) + } +} +else if (isset($_POST['cancel'])) { + // cancel, nothing happened + $msg->addFeedback('CANCELLED'); + header('Location: index_instructor.php'); + exit; +} +else if (isset($_POST['submit'])) { + // user has submitted form to update database + $id = intval ($_POST['id']); + + if ($_POST['multi_submit'] == 'on'){ + $multi_submit = '1'; + } + + // get values from form that was just submitted + $title = $addslashes($_POST['title']); + $assign_to = intval($_POST['assign_to']); + $has_due_date = $addslashes($_POST['has_due_date']); + $late_submit = intval($_POST['late_submit']); + + $dueday = intval($_POST['day_due']); + $duemonth = intval($_POST['month_due']); + $dueyear = intval($_POST['year_due']); + $duehour = intval($_POST['hour_due']); + $dueminute = intval($_POST['min_due']); + + $cutoffday = intval($_POST['day_cutoff']); + $cutoffmonth = intval($_POST['month_cutoff']); + $cutoffyear = intval($_POST['year_cutoff']); + $cutoffhour = intval($_POST['hour_cutoff']); + $cutoffminute = intval($_POST['min_cutoff']); + + // ensure title is not empty + if (trim($title) == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('title'))); + } else { + //ensure the title does not exceed db length, 60 + $title = validate_length($title, 60); + } + + // If due date is set and user has selected 'accept late submission until' + // then ensure cutoff date is greater or equal to due date. + if (($has_due_date == 'true') && ($late_submit == '2')){ + if ($cutoffyear < $dueyear){ + $msg->addError('CUTOFF_DATE_WRONG'); + } else if ($cutoffyear == $dueyear){ + if ($cutoffmonth < $duemonth){ + $msg->addError('CUTOFF_DATE_WRONG'); + } else if ($cutoffmonth == $duemonth){ + if ($cutoffday < $dueday){ + $msg->addError('CUTOFF_DATE_WRONG'); + } else if ($cutoffday == $dueday){ + if ($cutoffhour < $duehour){ + $msg->addError('CUTOFF_DATE_WRONG'); + } else if ($cutoffhour == $duehour) { + if ($cutoffminute < $dueminute){ + $msg->addError('CUTOFF_DATE_WRONG'); + } + } + } + } + } + } + + if (!$msg->containsErrors()) { + $multi_submit = 0; + + // create the date strings + $date_due = '0'; + $date_cutoff = '0'; + + // note: if due date is NOT set then ignore the late submission date + if ($has_due_date == 'true'){ + $date_due = $dueyear. '-' .str_pad ($duemonth, 2, "0", STR_PAD_LEFT). '-' .str_pad ($dueday, 2, "0", STR_PAD_LEFT). ' '.str_pad ($duehour, 2, "0", STR_PAD_LEFT). ':' .str_pad ($dueminute, 2, "0", STR_PAD_LEFT) . ':00'; + } + + if ($late_submit == '1'){ // never accept late submissions + $date_cutoff = $date_due; // cutoff date will be same as due date + } else if ($late_submit == '2'){ // accept late submissions until date + $date_cutoff = $cutoffyear. '-' .str_pad ($cutoffmonth, 2, "0", STR_PAD_LEFT). '-' .str_pad ($cutoffday, 2, "0", STR_PAD_LEFT). ' '.str_pad ($cutoffhour, 2, "0", STR_PAD_LEFT). ':' .str_pad ($cutoffminute, 2, "0", STR_PAD_LEFT) . ':00'; + } + + // Are we creating a new assignment or updating an existing assignment? + if ($id == '0'){ + // creating a new assignment + $sql = "INSERT INTO ".TABLE_PREFIX."assignments VALUES (NULL, $_SESSION[course_id], + '$title', + '$assign_to', + '$date_due', + '$date_cutoff', + '$multi_submit' + )"; + + $result = mysql_query($sql,$db); + $msg->addFeedback('ASSIGNMENT_ADDED'); + } else { // updating an existing assignment + $assign_to = 'assign_to'; + + $sql = "UPDATE ".TABLE_PREFIX."assignments SET title='$title', assign_to=$assign_to, date_due='$date_due', date_cutoff='$date_cutoff' WHERE assignment_id='$id' AND course_id=$_SESSION[course_id]"; + + $result = mysql_query($sql,$db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + header('Location: index_instructor.php'); + exit; + } +} else { // creating a new assignment + $title = ''; + $assign_to = '0'; + $multi_submit = '1'; + $has_due_date = 'false'; + $late_submit = '0'; // 0 == always, 1 == never, 2 = until (date) + + $dueday = $today['mday']; + $duemonth = $today['mon']; + $dueyear = $today['year']; + $duehour = '12'; + $dueminute = '0'; + + $cutoffday = $today['mday']; + $cutoffmonth = $today['mon']; + $cutoffyear = $today['year']; + $cutoffhour = '12'; + $cutoffminute = '0'; +} + +// ensure the dates are valid +if ($dueyear == '0'){ + // use today's date as default + $dueday = $today['mday']; + $duemonth = $today['mon']; + $dueyear = $today['year']; + $duehour = $today['hours']; + $dueminute = $today['minutes']; + // round the minute to the next highest multiple of 5 + $dueminute = round($dueminute / '5' ) * '5' + '5'; + if ($dueminute > '55'){ $dueminute = '55'; } +} +if ($cutoffyear == '0'){ + // use today's date as default + $cutoffday = $today['mday']; + $cutoffmonth = $today['mon']; + $cutoffyear = $today['year']; + $cutoffhour = $today['hours']; + $cutoffminute = $today['minutes']; + // round the minute to the next highest multiple of 5 + $cutoffminute = round($cutoffminute / '5' ) * '5' + '5'; + if ($cutoffminute > '55'){ $cutoffminute = '55'; } +} + +$onload = 'document.form.title.focus();'; + +// enable/disable date controls +if ($has_due_date == 'false'){ + $onload .= ' disable_dates (true, \'_due\');'; +} + +if ($late_submit != '2'){ + $onload .= ' disable_dates (true, \'_cutoff\');'; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> + +
    + +
    +
    +
    + *
    + +
    + +
    +
    + + + + + +
    + +
    +
    + + onfocus="disable_dates (true, '_due');" /> +
    + + + onfocus="disable_dates (false, '_due');" /> + + + +
    + +
    +
    + + onfocus="disable_dates (true, '_cutoff');" /> + +
    + + + onfocus="disable_dates (true, '_cutoff');" /> + +
    + + + onfocus="disable_dates (false, '_cutoff');" /> + + + + +
    + + + +
    + ***/ + ?> + +
    + + +
    + + + +
    + + + + \ No newline at end of file diff --git a/mods/_standard/assignments/delete_assignment.php b/mods/_standard/assignments/delete_assignment.php new file mode 100644 index 000000000..49329e8eb --- /dev/null +++ b/mods/_standard/assignments/delete_assignment.php @@ -0,0 +1,58 @@ +addFeedback('CANCELLED'); + Header('Location: index_instructor.php'); + exit; +} +else if (isset($_POST['submit_yes'])) { + $_POST['assignment_id'] = intval($_POST['assignment_id']); + + // delete the assignment from the table + $sql = "DELETE FROM ".TABLE_PREFIX."assignments WHERE course_id=$_SESSION[course_id] AND assignment_id=$_POST[assignment_id]"; + $result = mysql_query($sql, $db); + + // delete all the files for this assignment + require(AT_INCLUDE_PATH.'../mods/_standard/file_storage/file_storage.inc.php'); + fs_delete_workspace(WORKSPACE_ASSIGNMENT, $_POST['assignment_id']); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index_instructor.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$_GET['id'] = intval($_GET['id']); + +$sql = "SELECT title FROM ".TABLE_PREFIX."assignments WHERE course_id=$_SESSION[course_id] AND assignment_id=$_GET[id]"; +$result = mysql_query($sql, $db); + +if ($row = mysql_fetch_assoc($result)){ + $hidden_vars['assignment_id'] = $_GET['id']; + $confirm = array('DELETE_ASSIGNMENT', htmlentities_utf8($row['title'])); + $msg->addConfirm($confirm, $hidden_vars); + $msg->printConfirm(); +} +else { + $msg->addError('ASSIGNMENT_NOT_FOUND'); +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/assignments/edit_assignment.php b/mods/_standard/assignments/edit_assignment.php new file mode 100644 index 000000000..d0b62973e --- /dev/null +++ b/mods/_standard/assignments/edit_assignment.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/mods/_standard/assignments/index.php b/mods/_standard/assignments/index.php new file mode 100644 index 000000000..0da7e943e --- /dev/null +++ b/mods/_standard/assignments/index.php @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/mods/_standard/assignments/index_instructor.php b/mods/_standard/assignments/index_instructor.php new file mode 100644 index 000000000..6f6e0763b --- /dev/null +++ b/mods/_standard/assignments/index_instructor.php @@ -0,0 +1,134 @@ +addInfo('ASSIGNMENT_FS_SUBMISSIONS'); +require(AT_INCLUDE_PATH.'header.inc.php'); + +// sort order of table +$orders = array('ASC' => 'DESC', 'DESC' => 'ASC'); +$cols = array('title' => 1, 'date_due' => 1); +$sort = 'title'; +$order = 'ASC'; +if (isset($_GET['sort'])){ + $sort = isset($cols[$_GET['sort']]) ? $_GET['sort'] : 'title'; +} +if (isset($_GET['order'])){ + $order = $addslashes($_GET['order']); + if (($order != 'ASC') && ($order != 'DESC')){ + $order = 'ASC'; + } +} +$sql = "SELECT * FROM ".TABLE_PREFIX."assignments WHERE course_id=$_SESSION[course_id] ORDER BY $sort $order"; +$result = mysql_query($sql, $db); +?> + + + +
    + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    + + + + +
    />
    +
    + + + \ No newline at end of file diff --git a/mods/_standard/assignments/module.php b/mods/_standard/assignments/module.php new file mode 100644 index 000000000..333bbbac9 --- /dev/null +++ b/mods/_standard/assignments/module.php @@ -0,0 +1,35 @@ +getPrivilege()); + +/******* + * instructor Manage section: + */ +$this->_pages['mods/_standard/assignments/index_instructor.php']['title_var'] = 'assignments'; +$this->_pages['mods/_standard/assignments/index_instructor.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_standard/assignments/index_instructor.php']['children'] = array('mods/_standard/assignments/add_assignment.php'); +$this->_pages['mods/_standard/assignments/index_instructor.php']['guide'] = 'instructor/?p=assignments.php'; + + $this->_pages['mods/_standard/assignments/add_assignment.php']['title_var'] = 'add_assignment'; + $this->_pages['mods/_standard/assignments/add_assignment.php']['parent'] = 'mods/_standard/assignments/index_instructor.php'; + + $this->_pages['mods/_standard/assignments/edit_assignment.php']['title_var'] = 'edit'; + $this->_pages['mods/_standard/assignments/edit_assignment.php']['parent'] = 'mods/_standard/assignments/index_instructor.php'; + + $this->_pages['mods/_standard/assignments/delete_assignment.php']['title_var'] = 'delete'; + $this->_pages['mods/_standard/assignments/delete_assignment.php']['parent'] = 'mods/_standard/assignments/index_instructor.php'; + + +?> \ No newline at end of file diff --git a/mods/_standard/assignments/module.sql b/mods/_standard/assignments/module.sql new file mode 100644 index 000000000..4427a4de5 --- /dev/null +++ b/mods/_standard/assignments/module.sql @@ -0,0 +1,33 @@ +# sql file for reading list module + +CREATE TABLE `assignments` ( + `assignment_id` MEDIUMINT(6) UNSIGNED NOT NULL AUTO_INCREMENT DEFAULT 0, + `course_id` MEDIUMINT UNSIGNED NOT NULL , + `title` VARCHAR(60) NOT NULL, + `assign_to` MEDIUMINT UNSIGNED DEFAULT 0, + `date_due` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `date_cutoff` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `multi_submit` TINYINT DEFAULT '0', + PRIMARY KEY (`assignment_id`), + INDEX (`course_id`) +) TYPE = MYISAM; + +REPLACE INTO `language_text` VALUES ('en', '_msgs','AT_CONFIRM_DELETE_ASSIGNMENT','Do you wish to delete this assignment: %s?',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_msgs','AT_ERROR_DUE_DATE_EMPTY','Due date is not set.',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_msgs','AT_ERROR_ASSIGNMENT_NOT_FOUND','Assignment not found.',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_msgs','AT_FEEDBACK_ASSIGNMENT_DELETED','Assignment Deleted',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_msgs','AT_FEEDBACK_ASSIGNMENT_UPDATED','Assignment Updated',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','assignments','Assignments',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','add_assignment','Add Assignment',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','edit_assignment','Edit Assignment',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','delete_assignment','Delete Assignment',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','due_date','Due Date',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','accept_late_submissions','Accept Late Submissions',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','allow_re_submissions','Allow Re-Submissions',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','assign_to','Assign To',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','time','Time',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','always','Always',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','until','Until',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','all_students','Everyone',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','options','Options',NOW(),''); +REPLACE INTO `language_text` VALUES ('en', '_module','specific_groups','Specific Groups',NOW(),''); diff --git a/mods/_standard/assignments/module.xml b/mods/_standard/assignments/module.xml new file mode 100644 index 000000000..5b36538f4 --- /dev/null +++ b/mods/_standard/assignments/module.xml @@ -0,0 +1,19 @@ + + + Assignments + Allows students and groups to submit assignments. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + BSD + + 0.1 + 2006-02-27 + stable + + + \ No newline at end of file diff --git a/mods/_standard/assignments/module_backup.php b/mods/_standard/assignments/module_backup.php new file mode 100644 index 000000000..2cde37203 --- /dev/null +++ b/mods/_standard/assignments/module_backup.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/mods/_standard/assignments/module_delete.php b/mods/_standard/assignments/module_delete.php new file mode 100644 index 000000000..9d88c89e0 --- /dev/null +++ b/mods/_standard/assignments/module_delete.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/mods/_standard/assignments/module_groups.php b/mods/_standard/assignments/module_groups.php new file mode 100644 index 000000000..d87691510 --- /dev/null +++ b/mods/_standard/assignments/module_groups.php @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/mods/_standard/assignments/module_news.php b/mods/_standard/assignments/module_news.php new file mode 100644 index 000000000..35e281b5e --- /dev/null +++ b/mods/_standard/assignments/module_news.php @@ -0,0 +1,41 @@ + + */ +function assignments_news() { + global $db, $enrolled_courses, $system_courses; + $news = array(); + + if ($enrolled_courses == ''){ + return $news; + } + + $sql = 'SELECT * FROM '.TABLE_PREFIX.'assignments WHERE course_id IN '.$enrolled_courses.' ORDER BY date_due DESC'; + $result = mysql_query($sql, $db); + if($result){ + while($row = mysql_fetch_assoc($result)){ + $news[] = array('time'=>$row['date_due'], + 'object'=>$row, + 'course'=>$system_courses[$row['course_id']]['title'], + 'alt'=>_AT('assignment'), + 'thumb'=>'images/home-forums_sm.png', + 'link'=>_AT('assignment_due', $row['title'], ''.AT_DATE('%F %j, %g:%i',$row['date_due']).'')); + } + } + return $news; +} + +?> diff --git a/mods/_standard/blogs/add_post.php b/mods/_standard/blogs/add_post.php new file mode 100644 index 000000000..6006a1115 --- /dev/null +++ b/mods/_standard/blogs/add_post.php @@ -0,0 +1,102 @@ +addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/blogs/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_POST['oid'], AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit'])) { + $_POST['title'] = $addslashes(trim($_POST['title'])); + $_POST['body'] = $addslashes(trim($_POST['body'])); + + if ($_POST['body'] == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('body'))); + } + + if (!$msg->containsErrors()) { + $_POST['title'] = htmlentities_utf8($_POST['title']); + $_POST['body'] = htmlentities_utf8($_POST['body']); + $_POST['private'] = abs($_POST['private']); + $sql = "INSERT INTO ".TABLE_PREFIX."blog_posts VALUES (NULL, $_SESSION[member_id], ".BLOGS_GROUP.", $_POST[oid], $_POST[private], NOW(), 0, '$_POST[title]', '$_POST[body]')"; + mysql_query($sql, $db); + + if (!isset($sub)) { + require_once(AT_INCLUDE_PATH .'classes/subscribe.class.php'); + $sub = new subscription(); + } + $sub->send_mail('blog', $_POST['oid'], mysql_insert_id()); + $msg->addFeedback('POST_ADDED_SUCCESSFULLY'); + + header('Location: '.url_rewrite('mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_POST['oid'], AT_PRETTY_URL_IS_HEADER)); + exit; + } +} + +// this will also be dynamic as the parent page changes +$_pages['mods/_standard/blogs/add_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['title_var'] = 'add'; +$_pages['mods/_standard/blogs/add_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['parent'] = 'mods/_standard/blogs/view.php'; + +$_pages['mods/_standard/blogs/add_post.php']['title_var'] = 'add'; +$_pages['mods/_standard/blogs/add_post.php']['parent'] = 'mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']; + +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['title'] = blogs_get_blog_name(BLOGS_GROUP, $_REQUEST['oid']); +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['parent'] = 'mods/_standard/blogs/index.php'; +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['children'] = array('mods/_standard/blogs/add_post.php'); + + +$onload = 'document.form.title.focus();'; +require (AT_INCLUDE_PATH.'header.inc.php'); +?> + +
    + + +
    +
    +
    + +
    +
    + *
    + +
    + +
    + <?php echo _AT('jump_codes'); ?> + + +
    + +
    + +
    + +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/blogs/delete_comment.php b/mods/_standard/blogs/delete_comment.php new file mode 100644 index 000000000..96bccb597 --- /dev/null +++ b/mods/_standard/blogs/delete_comment.php @@ -0,0 +1,68 @@ +addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; +} + +$id = abs($_REQUEST['id']); +$delete_id = abs($_REQUEST['delete_id']); + +$sql = "SELECT post_id FROM ".TABLE_PREFIX."blog_posts WHERE owner_type=$owner_type AND owner_id=$owner_id AND post_id=$id"; +$result = mysql_query($sql, $db); +if (!$row = mysql_fetch_assoc($result)) { + $msg->addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; +} + +if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/blogs/post.php?ot='.$owner_type.SEP.'oid='.$owner_id.SEP.'id='.$id, AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit_yes'])) { + + $sql = "DELETE FROM ".TABLE_PREFIX."blog_posts_comments WHERE comment_id=$delete_id AND post_id=$id"; + $result = mysql_query($sql, $db); + if (mysql_affected_rows($db) == 1) { + $sql = "UPDATE ".TABLE_PREFIX."blog_posts SET num_comments=num_comments-1, date=date WHERE owner_type=$owner_type AND owner_id=$owner_id AND post_id=$id"; + $result = mysql_query($sql, $db); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.url_rewrite('mods/_standard/blogs/post.php?ot='.$owner_type.SEP.'oid='.$owner_id.SEP.'id='.$id, AT_PRETTY_URL_IS_HEADER)); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$hidden_vars = array('id' => $id, 'ot' => $owner_type, 'oid' => $owner_id, 'delete_id' => $delete_id); +//get the comment to print into the confirm box +$sql = 'SELECT comment FROM '.TABLE_PREFIX.'blog_posts_comments WHERE comment_id='.$delete_id; +$result = mysql_query($sql, $db); +$row = mysql_fetch_assoc($result); + +$msg->addConfirm(array('DELETE', htmlentities_utf8($row['comment'])), $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/blogs/delete_post.php b/mods/_standard/blogs/delete_post.php new file mode 100644 index 000000000..6e76cc4fe --- /dev/null +++ b/mods/_standard/blogs/delete_post.php @@ -0,0 +1,71 @@ +addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; +} + +if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + $id = abs($_POST['id']); + header('Location: post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_POST['oid'].SEP.'id='.$id); + exit; +} else if (isset($_POST['submit_yes'])) { + $id = abs($_POST['id']); + + $sql = "DELETE FROM ".TABLE_PREFIX."blog_posts WHERE owner_type=".BLOGS_GROUP." AND owner_id=$_REQUEST[oid] AND post_id=$id"; + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_POST['oid']); + exit; +} + +$id = abs($_REQUEST['id']); +$sql = "SELECT title, body FROM ".TABLE_PREFIX."blog_posts WHERE owner_type=".BLOGS_GROUP." AND owner_id=$owner_id AND post_id=$id"; +$result = mysql_query($sql, $db); +$post_row = mysql_fetch_assoc($result); + +$_pages['mods/_standard/blogs/delete_post.php']['parent'] = 'mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']; + + +$_pages['mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']]['children'] = array('mods/_standard/blogs/edit_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id'], 'mods/_standard/blogs/delete_post.php'); +$_pages['mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']]['parent'] = 'mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']; +$_pages['mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']]['title'] = $post_row['title']; + + +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['title'] = blogs_get_blog_name(BLOGS_GROUP, $_REQUEST['oid']); +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['parent'] = 'mods/_standard/blogs/index.php'; +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['children'] = array('mods/_standard/blogs/add_post.php'); + +require (AT_INCLUDE_PATH.'header.inc.php'); + +$hidden_vars = array('id' => $id, 'ot' => $_REQUEST['ot'], 'oid' => $_REQUEST['oid']); +//get the post title to print into the confirm box +$sql = 'SELECT title FROM '.TABLE_PREFIX.'blog_posts WHERE post_id='.$id; +$result = mysql_query($sql, $db); +$row = mysql_fetch_assoc($result); +$msg->addConfirm(array('DELETE', $row['title']), $hidden_vars); +$msg->printConfirm(); +?> + + \ No newline at end of file diff --git a/mods/_standard/blogs/edit_post.php b/mods/_standard/blogs/edit_post.php new file mode 100644 index 000000000..544887a46 --- /dev/null +++ b/mods/_standard/blogs/edit_post.php @@ -0,0 +1,106 @@ +addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_POST['oid'], AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit'])) { + $_POST['title'] = $addslashes(trim($_POST['title'])); + $_POST['body'] = $addslashes(trim($_POST['body'])); + $id = abs($_POST['id']); + + if ($_POST['body'] == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('body'))); + } + + if (!$msg->containsErrors()) { + $_POST['title'] = htmlspecialchars($_POST['title']); + $_POST['body'] = htmlspecialchars($_POST['body']); + $_POST['private'] = abs($_POST['private']); + $sql = "UPDATE ".TABLE_PREFIX."blog_posts SET private=$_POST[private], title='$_POST[title]', body='$_POST[body]', date=date WHERE owner_type=".BLOGS_GROUP." AND owner_id=$_REQUEST[oid] AND post_id=$id"; + mysql_query($sql, $db); + + $msg->addFeedback('POST_ADDED_SUCCESSFULLY'); + + header('Location: '.url_rewrite('mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_POST['oid'].SEP.'id='.$id, AT_PRETTY_URL_IS_HEADER)); + exit; + } +} + +$id = abs($_REQUEST['id']); +$sql = "SELECT private, title, body FROM ".TABLE_PREFIX."blog_posts WHERE owner_type=".BLOGS_GROUP." AND owner_id=$_REQUEST[oid] AND post_id=$id"; +$result = mysql_query($sql, $db); +$post_row = mysql_fetch_assoc($result); + +$_pages['mods/_standard/blogs/edit_post.php']['parent'] = 'mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']; +$_pages['mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']] = $_pages['mods/_standard/blogs/post.php']; +$_pages['mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']]['children'] = array('mods/_standard/blogs/edit_post.php', 'mods/_standard/blogs/delete_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']); + +$_pages['mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']]['parent'] = 'mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']; +$_pages['mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']]['title'] = $post_row['title']; +$_pages['mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']]['children'] = array('mods/_standard/blogs/edit_post.php', 'mods/_standard/blogs/delete_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']); + +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['title'] = blogs_get_blog_name(BLOGS_GROUP, $_REQUEST['oid']); +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['parent'] = 'mods/_standard/blogs/index.php'; +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['children'] = array('mods/_standard/blogs/add_post.php'); + + +$onload = 'document.form.title.focus();'; +require (AT_INCLUDE_PATH.'header.inc.php'); + +?> + +
    + + + +
    +
    +
    + +
    +
    + *
    + +
    + +
    + <?php echo _AT('jump_codes'); ?> + + +
    + +
    + /> +
    + +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/blogs/index.php b/mods/_standard/blogs/index.php new file mode 100644 index 000000000..76d5c09cf --- /dev/null +++ b/mods/_standard/blogs/index.php @@ -0,0 +1,69 @@ +set_subscription('blog',$_SESSION['member_id'],$_GET['group_id'])){; + $msg->addFeedback('BLOG_SUBSCRIBED'); + } + } else if ($_GET['subscribe'] == "unset"){ + $sub->unset_subscription('blog',$_SESSION['member_id'],$_GET['group_id']); + $msg->addFeedback('BLOG_UNSUBSCRIBED'); + } +} + +require (AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT G.group_id, G.title, G.modules FROM ".TABLE_PREFIX."groups G INNER JOIN ".TABLE_PREFIX."groups_types T USING (type_id) WHERE T.course_id=$_SESSION[course_id] ORDER BY G.title"; +$result = mysql_query($sql, $db); + +echo '
      '; + +$blogs = false; +while ($row = mysql_fetch_assoc($result)) { + if (strpos($row['modules'], '_standard/blogs') !== FALSE) { + // retrieve the last posted date/time from this blog + $sql = "SELECT MAX(date) AS date FROM ".TABLE_PREFIX."blog_posts WHERE owner_type=".BLOGS_GROUP." AND owner_id={$row['group_id']}"; + $date_result = mysql_query($sql, $db); + if (($date_row = mysql_fetch_assoc($date_result)) && $date_row['date']) { + $last_updated = ' - ' . _AT('last_updated', AT_date(_AT('forum_date_format'), $date_row['date'], AT_DATE_MYSQL_DATETIME)); + } else { + $last_updated = ''; + } + + echo '
    1. '.$row['title'].$last_updated.''; + + // Check if subscribed and make appropriate button + if ($sub->is_subscribed('blog',$_SESSION['member_id'],$row['group_id'])){ + echo ' '._AT('blog_unsubscribe').''; + } else { + echo ' '._AT('blog_subscribe').''; + } + echo '
    2. '; + $blogs = true; + } +} +echo '
    '; + +if (!$blogs) { + echo _AT('none_found'); +} +?> + + \ No newline at end of file diff --git a/mods/_standard/blogs/module.php b/mods/_standard/blogs/module.php new file mode 100644 index 000000000..f9ce44542 --- /dev/null +++ b/mods/_standard/blogs/module.php @@ -0,0 +1,92 @@ +_list['blogs'] = array('title_var'=>'blogs','file'=>'mods/_standard/blogs/sublinks.php'); + +if (isset($_REQUEST['oid'])) { + $_pages['mods/_standard/blogs/edit_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']]['title_var'] = 'edit'; + $_pages['mods/_standard/blogs/edit_post.php']['title_var'] = 'edit'; + $_pages['mods/_standard/blogs/edit_post.php']['parent'] = 'mods/_standard/blogs/post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']; + + $_pages['mods/_standard/blogs/delete_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid'].SEP.'id='.$_REQUEST['id']]['title_var'] = 'delete'; + $_pages['mods/_standard/blogs/delete_post.php']['title_var'] = 'delete'; +} +$_pages['mods/_standard/blogs/delete_comment.php']['title_var'] = 'delete'; + + +function blogs_get_group_url($group_id) { + return 'mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$group_id; +} + +/** + * given an owner_type and owner_id + * returns false if user cannot read or write to this workspace + * returns BLOGS_AUTH_READ if the user can read + * returns BLOGS_AUTH_WRITE if the user can write + */ +function blogs_authenticate($owner_type, $owner_id) { + // ensure that this group is in the course + if ($owner_type == BLOGS_GROUP) { + if (isset($_SESSION['groups'][$owner_id])) { + return BLOGS_AUTH_RW; + } + + global $db; + $sql = "SELECT type_id FROM ".TABLE_PREFIX."groups WHERE group_id=$owner_id"; + $result = mysql_query($sql, $db); + if (!$row = mysql_fetch_assoc($result)) { + return BLOGS_AUTH_NONE; + } + + $sql = "SELECT type_id FROM ".TABLE_PREFIX."groups_types WHERE type_id=$row[type_id] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + if (!$row = mysql_fetch_assoc($result)) { + return BLOGS_AUTH_NONE; + } + + return BLOGS_AUTH_READ; + } + return BLOGS_AUTH_NONE; +} + +function blogs_get_blog_name($owner_type, $owner_id) { + if ($owner_type == BLOGS_GROUP) { + // get group name + global $db; + + $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=$owner_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + return $row['title']; + } +} +?> \ No newline at end of file diff --git a/mods/_standard/blogs/module.xml b/mods/_standard/blogs/module.xml new file mode 100644 index 000000000..22009d869 --- /dev/null +++ b/mods/_standard/blogs/module.xml @@ -0,0 +1,20 @@ + + + Blogs + Creates blogs for groups as well as all registered users. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + true + 2005-08-25 + stable + + + \ No newline at end of file diff --git a/mods/_standard/blogs/module_delete.php b/mods/_standard/blogs/module_delete.php new file mode 100644 index 000000000..484443b2c --- /dev/null +++ b/mods/_standard/blogs/module_delete.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/mods/_standard/blogs/module_groups.php b/mods/_standard/blogs/module_groups.php new file mode 100644 index 000000000..2a68efa34 --- /dev/null +++ b/mods/_standard/blogs/module_groups.php @@ -0,0 +1,29 @@ + \ No newline at end of file diff --git a/mods/_standard/blogs/module_news.php b/mods/_standard/blogs/module_news.php new file mode 100644 index 000000000..8d69a036e --- /dev/null +++ b/mods/_standard/blogs/module_news.php @@ -0,0 +1,56 @@ + + */ +function blogs_news() { + global $db, $enrolled_courses, $system_courses; + $news = array(); + + if ($enrolled_courses == ''){ + return $news; + } + + $sql = "SELECT G.group_id, G.title, G.modules, T.course_id FROM ".TABLE_PREFIX."groups G INNER JOIN ".TABLE_PREFIX."groups_types T USING (type_id) WHERE T.course_id IN $enrolled_courses ORDER BY G.title"; + + + $result = mysql_query($sql, $db); + if ($result){ + if (mysql_num_rows($result) > 0) { + while ($row = mysql_fetch_assoc($result)) { + if (strpos($row['modules'], '_standard/blogs') !== FALSE) { + // retrieve the last posted date/time from this blog + $sql = "SELECT MAX(date) AS date FROM ".TABLE_PREFIX."blog_posts WHERE owner_type=".BLOGS_GROUP." AND owner_id={$row['group_id']}"; + $date_result = mysql_query($sql, $db); + $row2 = mysql_fetch_assoc($date_result); + $last_updated = ' - ' . _AT('last_updated', AT_date(_AT('forum_date_format'), $row2['date'], AT_DATE_MYSQL_DATETIME)); + + $link_title = $row['title']; + $news[] = array('time'=>$row2['date'], + 'object'=>$row, + 'alt'=>_AT('blogs'), + 'course'=>$system_courses[$row['course_id']]['title'], + 'thumb'=>'images/home-blogs_sm.png', + 'link'=>' SUBLINK_TEXT_LEN ? ' title="'.$link_title.'"' : '') .'>'. + validate_length($link_title, SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''); + } + } + } + } + return $news; +} + +?> diff --git a/mods/_standard/blogs/post.php b/mods/_standard/blogs/post.php new file mode 100644 index 000000000..fbe0c032d --- /dev/null +++ b/mods/_standard/blogs/post.php @@ -0,0 +1,143 @@ +addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/blogs/index.php')); + exit; +} + +$id = abs($_REQUEST['id']); +$auth = ''; +if (!query_bit($owner_status, BLOGS_AUTH_WRITE)) { + $auth = 'private=0 AND '; +} +$sql = "SELECT member_id, private, date, title, body FROM ".TABLE_PREFIX."blog_posts WHERE $auth owner_type=".BLOGS_GROUP." AND owner_id=$owner_id AND post_id=$id ORDER BY date DESC"; +$result = mysql_query($sql, $db); + + +if (isset($_POST['submit']) && $_SESSION['member_id']) { + // post a comment + $_POST['body'] = $addslashes(trim($_POST['body'])); + $_POST['private'] = abs($_POST['private']); + + if ($_POST['body'] == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('comments'))); + } + + if (!$msg->containsErrors()) { + $sql = "INSERT INTO ".TABLE_PREFIX."blog_posts_comments VALUES (NULL, $id, $_SESSION[member_id], NOW(), $_POST[private], '$_POST[body]')"; + mysql_query($sql, $db); + $comments_affected_rows = mysql_affected_rows($db); + + if (!isset($sub)) { + require_once(AT_INCLUDE_PATH .'classes/subscribe.class.php'); + $sub = new subscription(); + } + $sub->send_mail('blogcomment', $owner_id, mysql_insert_id()); + + if ($comments_affected_rows == 1) { + $sql = "UPDATE ".TABLE_PREFIX."blog_posts SET num_comments=num_comments+1, date=date WHERE post_id=$id"; + mysql_query($sql, $db); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: '.url_rewrite('mods/_standard/blogs/post.php?ot='.$owner_type.SEP.'oid='.$owner_id.SEP.'id='.$id, AT_PRETTY_URL_IS_HEADER)); + exit; + } +} + +if (!$post_row = mysql_fetch_assoc($result)) { + header('Location: '.url_rewrite('mods/_standard/blogs/view.php?ot='.$owner_type.SEP.'oid='.$owner_id)); + exit; +} + +$_pages['mods/_standard/blogs/post.php']['title'] = AT_PRINT($post_row['title'], 'blog_posts.title') . ($post_row['private'] ? ' - '._AT('private') : ''); +$_pages['mods/_standard/blogs/post.php']['parent'] = 'mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$owner_id; +if (query_bit($owner_status, BLOGS_AUTH_WRITE)) { + $_pages['mods/_standard/blogs/post.php']['children'] = array('mods/_standard/blogs/edit_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$owner_id.SEP.'id='.$id, 'mods/_standard/blogs/delete_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$owner_id.SEP.'id='.$id); +} else { + $_pages['mods/_standard/blogs/post.php']['children'] = array(); +} + +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$owner_id]['title'] = blogs_get_blog_name(BLOGS_GROUP, $owner_id); +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$owner_id]['parent'] = 'mods/_standard/blogs/index.php'; + +if (query_bit($owner_status, BLOGS_AUTH_WRITE)) { + $_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$owner_id]['children'] = array('mods/_standard/blogs/add_post.php'); +} else { + $_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$owner_id]['children'] = array(); +} + + +require (AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    +

    -

    + +

    +
    + +

    + + +
    +
    +

    -

    + +

    + + +
    + +
    + +
    +
    + + + + +
    + +
    +
    + *
    + +
    + +
    + <?php echo _AT('jump_codes'); ?> + + +
    + +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/mods/_standard/blogs/sublinks.php b/mods/_standard/blogs/sublinks.php new file mode 100644 index 000000000..ba184272e --- /dev/null +++ b/mods/_standard/blogs/sublinks.php @@ -0,0 +1,45 @@ + detail view + +$sql = "SELECT G.group_id, G.title, G.modules FROM ".TABLE_PREFIX."groups G INNER JOIN ".TABLE_PREFIX."groups_types T USING (type_id) WHERE T.course_id=$_SESSION[course_id] ORDER BY G.title LIMIT $record_limit"; +$result = mysql_query($sql, $db); + +if (mysql_num_rows($result) > 0) { + while ($row = mysql_fetch_assoc($result)) { + if (strpos($row['modules'], '_standard/blogs') !== FALSE) { + // retrieve the last posted date/time from this blog + $sql = "SELECT MAX(date) AS date FROM ".TABLE_PREFIX."blog_posts WHERE owner_type=".BLOGS_GROUP." AND owner_id={$row['group_id']}"; + $date_result = mysql_query($sql, $db); + if (($date_row = mysql_fetch_assoc($date_result)) && $date_row['date']) { + $last_updated = ' - ' . _AT('last_updated', AT_date(_AT('forum_date_format'), $date_row['date'], AT_DATE_MYSQL_DATETIME)); + } else { + $last_updated = ''; + } + + $link_title = $row['title'].$last_updated; + $list[] = ' SUBLINK_TEXT_LEN ? ' title="'.$link_title.'"' : '') .'>'. + validate_length($link_title, SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''; + } + } + return $list; + +} else { + return 0; +} +?> \ No newline at end of file diff --git a/mods/_standard/blogs/view.php b/mods/_standard/blogs/view.php new file mode 100644 index 000000000..27daaf882 --- /dev/null +++ b/mods/_standard/blogs/view.php @@ -0,0 +1,94 @@ +addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; +} + +// these will all by dynamically defined on the view page +$_pages['mods/_standard/blogs/view.php']['title'] = blogs_get_blog_name(BLOGS_GROUP, $_REQUEST['oid']); +$_pages['mods/_standard/blogs/view.php']['parent'] = 'mods/_standard/blogs/index.php'; + +if (query_bit($owner_status, BLOGS_AUTH_WRITE)) { + $_pages['mods/_standard/blogs/view.php']['children'] = array('mods/_standard/blogs/add_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']); +} else { + $_pages['mods/_standard/blogs/view.php']['children'] = array(); +} + +$_pages['mods/_standard/blogs/add_post.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['title_var'] = 'add'; + +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['title'] = blogs_get_blog_name(BLOGS_GROUP, $_REQUEST['oid']); +$_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['parent'] = 'mods/_standard/blogs/index.php'; +if (query_bit($owner_status, BLOGS_AUTH_WRITE)) { + $_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['children'] = array('mods/_standard/blogs/add_post.php'); +} else { + $_pages['mods/_standard/blogs/view.php?ot='.BLOGS_GROUP.SEP.'oid='.$_REQUEST['oid']]['children'] = array(); +} + +require (AT_INCLUDE_PATH.'header.inc.php'); + +$auth = ''; +if (!query_bit($owner_status, BLOGS_AUTH_WRITE)) { + $auth = 'private=0 AND '; +} + + +if (isset($_GET['p'])) { + $page = abs($_GET['p']); +} else { + $page = 1; +} + +$num_posts_per_page = 20; +$start = ($page - 1) * $num_posts_per_page; + +$count = 0; + +$sql = "SELECT post_id, member_id, private, num_comments, date, title, body FROM ".TABLE_PREFIX."blog_posts WHERE $auth owner_type=".BLOGS_GROUP." AND owner_id=$_REQUEST[oid] ORDER BY date DESC LIMIT $start, " . ($num_posts_per_page+1); +$result = mysql_query($sql, $db); +?> + + +
    +

    + + - +

    +

    -

    + +

    + +

    +
    +
    + + $num_posts_per_page) { + + echo ''._AT('previous_posts').''; + } + ?> + +

    + + + \ No newline at end of file diff --git a/mods/_standard/calendar/module.php b/mods/_standard/calendar/module.php new file mode 100644 index 000000000..07ddcfd9c --- /dev/null +++ b/mods/_standard/calendar/module.php @@ -0,0 +1,30 @@ +getPrivilege()); + +// if this module is to be made available to students on the Home or Main Navigation +$_student_tool = 'calendar/index.php'; + +// register this module as a calendar source, must implement calendar_get_entries() +// register_hook('calendar_source', this); + +/* +- reading list (start and end dates) +- tests (start and end dates) +- assignments (due and late submission dates) +- announcements (post date) +- calendar (implements course, group, multiple calendars for system as well as the display of the calendar) + +each above module implements in module_calendar.php +mixed calendar_get_entries(int $start_timestamp, int $end_timestamp, mixed $owner_type, mixed $owner_id); + +loop through all registered modules calling their run_hook('calendar_source', $owner_type, $owner_id) method, +which then includes and runs the calendar_get_entries() function. + +the calendar display doesn't have to know which modules implement calendar_get_entries(). +*/ + + +?> \ No newline at end of file diff --git a/mods/_standard/calendar/module.xml b/mods/_standard/calendar/module.xml new file mode 100644 index 000000000..634d252e5 --- /dev/null +++ b/mods/_standard/calendar/module.xml @@ -0,0 +1,23 @@ + + + Calendar + Supports multiple calendars for site-wide, course-specific, and individuals. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + create + + 2005-08-22 + stable + This is a standard module. + + \ No newline at end of file diff --git a/mods/_standard/calendar/module_backup.php b/mods/_standard/calendar/module_backup.php new file mode 100644 index 000000000..c5faa9e12 --- /dev/null +++ b/mods/_standard/calendar/module_backup.php @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/mods/_standard/calendar/module_delete.php b/mods/_standard/calendar/module_delete.php new file mode 100644 index 000000000..a09b91811 --- /dev/null +++ b/mods/_standard/calendar/module_delete.php @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/mods/_standard/calendar/module_groups.php b/mods/_standard/calendar/module_groups.php new file mode 100644 index 000000000..2076839b3 --- /dev/null +++ b/mods/_standard/calendar/module_groups.php @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/mods/_standard/chat/MachineThatGoesBing.class b/mods/_standard/chat/MachineThatGoesBing.class new file mode 100644 index 0000000000000000000000000000000000000000..11a07eed87544fbb9cd08304a74ce9633448acb7 GIT binary patch literal 2452 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 PY+6xjDaQa3q2 literal 0 HcmV?d00001 diff --git a/mods/_standard/chat/bing.php b/mods/_standard/chat/bing.php new file mode 100644 index 000000000..9c8f5f4d8 --- /dev/null +++ b/mods/_standard/chat/bing.php @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/mods/_standard/chat/bings/.html b/mods/_standard/chat/bings/.html new file mode 100644 index 000000000..c7fd82271 --- /dev/null +++ b/mods/_standard/chat/bings/.html @@ -0,0 +1,37 @@ + + +
    + + + \ No newline at end of file diff --git a/mods/_standard/chat/bings/chime.au b/mods/_standard/chat/bings/chime.au new file mode 100644 index 0000000000000000000000000000000000000000..91c63fb457b3d600093e98ab6cc7c3b57e2d0aa8 GIT binary patch literal 23846 zcmc(ncVA*zy6@-ybKk|8xpPl<1!GlLm1P2R4ww@NCL{)E+*P0Ne%4w$0Q##Zti92r3@iAK|H!_>i~o_A`EPv@tA)J0!=9dyV~71cU;O-tyqd%Q zUe~@n_&u(4Z5Nco{+rh{UH_XG_^qe^f+`Qs?_TuCK6Y5Ay~k(dz+s(s{f4f^3MUbp z-*EE3eahMKuepRnl@9xB&iOURd|91W|DtAZRQdnk?fse${ncZBL7iVc=l9=na{8rf zeodeb-{`PkI`_BCeOZ_POn>Kx|Mayk?1x;M|LLKhAMmg0^7He5`}nsB`{3%gzv7q9 z{IWhjcbtwh9gfps9rM4z-jU=9@|Qnrzd@hYe=(eYS(1)-a)tu<*Yx?dc{s(P#~b@k zbom=}>v)U*{r1a&{qx!1QI~flxo^+c?tV_a{nz}t4jtnodo*`C%MsW=w@8O4r{&n? z&X$PTu)fV<)voUd`51;(ZC5jjw$%cbNLYjKn=JM8guT9Ls?Wr`k*86g2K{Q@4ZBfZ zzkTA?`6kkRurbP|>u<|0iobQaM*jVyoBzvZwrVz1hwGrzW@OKQ(%Iym|G+wdw6s_qT7pd)V~S z`~Lf%C=XhGjQqCeueta7eiYmv{_FDn*&hZUu>RKVlKs<)TjZ-zcmFqCPrToCczHE_ z^n2XW6yYgpVm+E}ntjG-o2Df6jD~~_52X8RB`ee{$z(uucTa#%e_O({$&VaQb@P;a zbW4vXwY|eLpz;0Vr|%klJU_gly0yKEa_N6n^icL{#pV3XuuII_E*ENZtD8?tlgG0+ zZ@>HQ?VINw%`XEVcD-agoOro%|NM2|eOhD3LuzyL!)I-c?vJH!A3bSqeCpZu*6&gG zyP)qR%^{xsAL5?&e~Nn2)0*^MUwg^3q3%=f@!ne00Cl)8iYn`;QIy?WKW2}D8rM4&7}GNv9MmI?qV{$t z`1E%8J?U?yc#ggg^H}*1?y=My;kMrt>sHhhEhPh;_A}!!Rk!w4 zSLv?8*Cj3quNPhB|1siv{!-$W`Kr@B>TTQO@aB%EAs;?Hr*ySa{rlQNsYC4%l*zVa zKiMaa|4yeaz}Bq{DD0Y`CU!}JL!@0%l+liO-^n(*_iEd<_f`9*_jaq?H>Z7s5-lBw zijWOvMs5yq0x|{`sbS-j0hHOHP~WY-guwNYjPRZD>`3FJElHrQ-%`e=x&%Dv}JlAGie+hyXF z)@AW+#KVfl43~_?BbSKADL2YnsmHUX_9st1v^@9t)a>ux{x;OD=S`fewCS1a$U7g` z@wfi2Q;or{%WvXcci&{XS2kw5Wxvt8(Oyrx2fdlP=iAu-(CeM#;ghB=7tiK)H&0NU z`^OI+?yc`V-9I&ZxV63Wbm@8P;m|`XKt%#r=R+%5QyM zPJZ+B<7M{b}s46MtI&tK?7Ek6nLc|E>Lxsek+UACW&c{W0+GZ~y53)0;nf{pHo4 zzx)2D|Lghv-@kSL>yHmyfBc*4!+(75dGF=-Pwu_`i|>EF{jZQeH~&|{pIUwp{ju}= z#Xm~EAN`N%zjXhHFfOC~R4vt;WDN%HxW_{GVBDBajabm{PFWLDo)G;MGoC3;kv z9yT$Y5iIYk4c+WlMTn&{F|47fjLeCNykyzfVcLdVU2wdmxi%fy3>rq-)>3-xhAcIF zV=R$!EM@p)OSoRiox4v$I;Nibb@zMw^z`^Wm9~3(47K{ZPkjh*TWXGSt!hqo&1$N4 zjcs0a4QL*9_igF-^#0WS-II=1FOU9@er}`9RF~zZXqTdoZECDKkR6HbWi%m>)YAa)Nd+Y$9xn2TJR0EapAsC(}-Jmv&7xMwf*t)?zU&o zB(2XM4L4If6yPEZAEMmrn-kqqKJXqbwCq39wJbc!{xtTK);0V*u6w{IOd_F%j`RhE zj7!2P3*9t7yqisxbzk~vyJ!3uJ;Q!+y}gux{*FNJ$<~M`>mSlQb3Rmh(%vt51b!HN zLTm5wj_mIAqYSo1JXe0o_R4QnK83B73@O5d=aDZhZe zeyaaOPXtBLL!bac?bV1Vy*SH$zfmgFb2_ghZ_nx~)nG)ui6zi;>V z`1CQ@z59Kf`%p8_UEUn%X?P#$$$C%wF1>~RG^sV$E2eeZJFI=iKd5VjN|i_>{f2vz ze5bktz2`ckefD}1{7k)>6p>UMoIPQTVNdPSSc_}+o=A28M_^c(wRcs|#v@LFkEey035`_%k#;t9WH z^l4V>;IpK*KChVep68*T+We>;Efl}5_rcyhP2pa>@8X_F8Z#aZzA5t9u}i_goqZcksncK3Y!(<8UWzk9j7`>X%`k3YoR@BAVAp7eFx zw_~rk-)_8IzQ_J)@LuH0t_OiHTOazpYIgN{{m$+28wjqApYFT9d;joZbCcVHk8eHi zwZHbd*Yn!%-r%dyd$X?-9&Ep1xzsjlUDMts-iv#edq2AA;9+=^!Zq;yw0pqE;Ya@M z{a*gv5{iFcPb6i!H!E;W!VbXtH^R~;=97|^rpjo$qbo_wiLu;7`G6sAb8s=fWO$rL zlMUoXu5{}HYuhHNNp1ZBQSH6KA)Vb3fxSJ^LDJrYu<`!%*s1a46xnhTQ??(&o@fX! z94QUrNlPf1y_dcTU28s39WtNb_F-R2o5a_Ie*8Zw z9tOOebbbE1+r#tir-yFM&F(Jk?;bytzVW_4`6}qa;VasMv{%(G;jiW(-5GWBeJ#EB z9>%07lZ-uoE;l=(6LarDQv$JEx*CqC_xXHU91pFfhe z`g=~a1bEJU4ENml82#v|h30k9n&!oAWj#;txbmg-Y*FL-mO^4CW@93kWm!?@^7450 z;$BLoaxp7ayUa|}Ef>(QR`|?{b#8%h`=li265kUFPByswon;PPHOos^%LR$5NpbA< zs4?0wqKdv6nvKpKnMh=fO(kSZPG+RaCJIwmW(6r5Gv{f0a($9`c8!58tTA#I7TB3e zd3nmtv?1wWQXR*eT#V12l*Ok`OvJ~H4A3G+B#9v-U1`*@HkRL1i_UMgRpq!ML7yYi^ZT}RaYuH^tuk1RM-IvE;2FdQ8-IusW<))yZ-(VGz{ z>&~Ihb(~O^+qV67T4sHDEfYQ&ErUL>EfSy5wqC!0j&2IIyPN9Y-5KE5(;n(8X^Zgg zYmN6F_?YZ9_9546`n}m}rIYqt+mY{6(_!*ucBv?gZbd*w?`%klL>7?@!69*QGCpB^ zE-GbeB_>0*Kx51+)A)<}Wd7oQifF}}S*0jVI-jnM7tWXyg)^2!)3i3Od}=i|XL2Dj zePkj8-feimWKX#7OgnhNmUvImd%Al zkxzb*gHoTYw1hs{Z;gB|X^DR}+`@b|^-1(>@zd_J-M01T`c9?yW!I|Db=RT~)-Crd z?U?fubVR?Q$_AiTtu#n$A_AMPU%FbK`O*d_eIhxRiVM5oxhf%7q0DA&uT|xo?^)Q~(~Sy-P>F+v zlqm_z@?>};S&!sZ2znd4K=oXh8o^JN)2f5d+O8P z{q$K^$FuLc+r1w3wt9OGe)RVk`$+Yiejn`?oscy-9wt@JOkcOg7=FhKQ)QiH)b(z4;F&}b!tb9C zXN=B(&zXr$mdWE17H1PP7FQCp=atEMiy9hdW;c#2*T)H`^)Xcwdy(w%tw_;`I+8Q2 zh+qyaL}m}lqp}9qf|G}r!;(f9!sExLqoSv#;-cjfNs&t<+2I?*m67{H#>o01Rb>9q zR%q6!DlC0W5s@^x91X!XK4oq>C39U(&sJ~c3U-aynNd`>t!JMoj+y$o({$bRS>o=* zNxXJ+KSn>Kk2DNu!ww~?V1Z;Qge%<(Vn~#Mi4s|GOz&uLs6-kOAnl2!40h4{M>{h8 zrrL{r=UO#BYn>_H_3do$yf&S8@~35=n6@e3koIAJYG-eNUvF2W_h5V4^Xb-WFJ(K; z3u`NUp4q1Mj&7Us3uzyuP&#`;e0w{hpZB*VdyTfRpUFPzo+&=Xczj>?9vqL07#~Rsmklw)W(TW7 z6w>`*gJcDM5m;cp#1xp(s}4+%EQUr&$0LIWhT;Rp`!f9H5}yBB@4jC}_p%S8JKsOK zN9;%I-3^GAs)C{i72(mti!qTCGs)3YGs!XX8Ai;!EGK4dDkWxXf*E5S%a7rYAICCA zH)0dV686){P}i*d9uc|z>eR91|9Dl2M!oE@#4DvD80mBrxGrg-a2LBjQ7 zeu8b0pV+Wilvux5k|dfxPv$P*>CEN54Eo9%GflNnlB}J}qnT#PY4!5+HA77UA%UYU91&X+0a`LnCpc|gvhqJXVf6Y@1$+lAJ4g_}i}yJpd;Qp`D3*0QY2HH_*xOFDmP zKRs`0Gc8lGmX@NJPfJ;sXT@(#WXD)X@*?a-0?`A5V#yAJ2+aOtRwFrgLfP zm5hY#WmbxIH7^ZUvC{Ch{4|p)KSQs`Ptz+3(zHvtNyY_MJU*8ncRC}Cub(SVD4#n| z5-yt4uoXile^r}Zw7yxu+1V-)=n(1)>{4T}88>vtXL{YS?NDQ=snzSw%Z_vn0)Shi zH(@pS8CGM!i!g(}yyzG(X+&&;s)}h=yrAPtwHbz4TOvMXi8sg?vF0gyv|)@Hc`%Y2 zele62%q!{xM46&@h!c))(YIEeWG6^+x+`cBlB2baDL{ zU8nvDor~1S?iru3o=M+e$%sF-zn@AOl?G5IdPDqXd!zicE``%Z zm!fIovvD+;jFvbnOQJ2w(rM}`R?^-SH%TnJND|5nv~2l$Le{(@C23_gleQ&e(k#=0 zgre!1_}pn-e5P!LmO8tTmb5gJPFs~RXuH$73HUTOzG0ddFOrqT7s|?G*;A)6S(E!w zX;bUbNfRqkagz&CF;lYWsM+bb$fb#daP@d*gmEG#x@J-omou>+&zRjz%A8wE&sbH` zQ+E}Rq$vyOCu@}q(Y7s@ziZCV*P09RO@=~dJuYNd9G&w8wYE}j`B8Nq4?kv;fKNC& zW^-{p#@1^w_?6i(lfJgbfH*9_&>afubywJh?urMNC)Qo^O7>28Vzo|Kw7XqWytQ4# z*;R`;2Gte&Ky}D70~Z_SDqM3`rBj`YNW?`uYvMxPT4kP2VPol*tFkPL>a6|6nhbpY zBF#EuNUNCFrNl&4tXhJaKcnB5`xd zlAxbDPB2VrY1LDEw5mxpjV(9DXU}M8Su$lps(djuby}8|Ae+jFog2@NS{g2lTp6ed z-|jbt83$P*b^Q%t?0!u|R{v^b>cDJt{NPk<%aZ z+2~`0nELZV#RGy+ZvRObeE_1`@M=uL2t>8<#n9MEc?1v@Ba_9%B4(%K<7TI6ajUZA zSd}b2P9vw&OtaiX(L!m8aHTxGKxv@otG1cBn$3b7wTE#uY9ULtBxLN&iPMcshsnlSbCPj(FR2(1&8i64i>fq&RTZ5LFcE4Miv(ve zwSd~l0n1?%Xt?YRO$keH+__9k{_fr%$F8A>a*cFW4u8<+qnQmZlUE}#Q{Y1*m<>Np1N zHMd@SgBfqB80{S9I?RWbGNnmobA1Qjt0+z$j-~%-pk9~*(%Q6Qt@&S8>?K# z*1CwURfy>O>&5haRc@ALCpXKg$<97j-@~)6@EtjLRpGPew9!tH^t>n z@5V8v)G-+oOEC#kbJ1~=Q_(Tg6S0x3(p##3{RUFk4l^xjiJegW8&ue<0F^*6C*dJsiAraE4WOG z1=0H&f_4Ygf!M%OFmrT1EPY}+f+ib}j$fLHk5!H(#c0Q&?jC1G)sC}c*T;&YcgCxu z4U?yF`bk6F>C|?DMYfY*1x{tE)LNisR+U;ktI8100ZZyk;gT{NTT*9Zt84kitLj{U z!&j~qaMfBipQJEk@u$nS%5rNHCR&zUF zb6m@-$3<)cU1d(?c0*1DV&^2keCN18u%#0gYYhl4hsZbg1i9v|l3epQD=`}+3-Ug4UQ;M>6 z%A(BurP56OLVdbeATrDthjcDl>awg_X53Uy!*h=cljA zxykFYveflyTgujKPLfv6Pc_U5(=2nsRO?KAdcFKOy=wM2y(F=Bv-K8@PnruY&=?<`s#F>`~WK_fIO4S-ay=bi{oxf3(ajLA!xKP(H z5a-HT#`)?I<6>2ZpkJ(N>7}b`dWk~G3smcc1uBJzwY%DogDVl{ zyvsFRLHYVtVcGhYP@q~D2{ft(&Ytp;v!{B&)hbPVlll-a^GzBH|56L!=X?~=fg-^v z0v8I-@M`WgP<~XyEz=*fG5t|dkx4Hi(NwSvTNM!DE7(SrxO7id&$jA|*;dVEz6~hX zT^5w@81i{g`T$S@?Fj%bDv1gWdTyan&&|CwVeBgt#y>%Jzkz>^&{+94jR~`9aO}mV zB^TSp3lTg$!idPvJFM}d4jor= zGA~w38O3YYjAEsU&Q;=2-|aFBc6JK#_Eo@oQO@B?6@n#Rv9fBERwlZlK&c0-r}I@? zu#&cqR@2x#jgHMF(6Tu=N#WHVR(Ppqv#$4q0LB#U0xI|^s2UJ-o61BN0hmeyZ&O2O z?m{F7=q3of2PzJ|UR6vNs;^jP)fG#;dsSG!hjXw!O$oro4%B6ZhXm)GBjqV)a}{Cb z>?^G-z0#VaS6XtcN`0Ke95 zbyQ$QD79SiI$~Q;qE?j@?WjrtG2ggb4pgum%7oN|EduyB&gYwOJ`Z49fd)$zzuY2X zmxC4Bs;~+hv8f{M=}A?^=}9%B{Pd()NZJyC*;rj}Hk1es5xRN=?;7f#YeOj)EW6xL z#xB>sV3%oa>@v*}8zbr=vL+JhG*A=mR1_V68m|zWmxL#BSDeI$jsO13-Wii3-S!x1-XVzA*^!N za?dt}oU@HOg3G-+4piz`DOBsIV!zxy5M3cOdpBG|-wr0icdW>5a$paLEd?Y7Z)DxmJ-zMl@-?QU2^L7E;z>| z+~Ymt)q&bjD&E+sAlTeFw{5DdfTeg}t-st<>C5-kJ7+re&M8o>2TnGiuhG=h=?oQ^ zL1Qhk;1?wpt+k}?@S?Q#@Pc0n2&@PwfDpAdM3EC^+;SVjR#a3&V2|dJ}wo0&-mFOX3 z3vQ3+H!&T>bt0%=V+}aTndTS+_;ngs+|&~km+Fmxu1;vyU6z}T#iT`jlwj&EusZy> zx|XD-{Oq`<>FcZmF&EhLVd5PRWG@ zR+TImvb+m(74H%_g~cNWXM3l-v%TZuOZ*rsBRIl}uMWgzMy`U%=c8%tm0sFdTtnn27+=H{4d(V7h|{ zjWN@8A$CB(!5VBFvFVzF9TXP|&4>~^R~(`4f+IX6EEoEKu@ocW3HLRXm>rHmQ^m3F z0r20fT6fB;-a6rt7HS|ESLv)ga6|~GgB)1+0)qv{20_KXL5L6-?VxB5u&;59U9N9H z5gj|jYq&P8jaz0oDgp!uP^X~Oa0nI`0d+{7)dqcCH4>mvW44h(G?T*A9l#TF=&3mX z9^6ELCNU6Y#WmP#Yb9p0=bE+~ZQ3x%HJ!MI{xw!%z2@`4g<|z5BHk^e#|l#AT7dYz7XB1cwMibuH9H0IVPxYRZlf#7YvZCTY*gj!DpJQrS5ImLjV0quSCF zy&a9Q^a8gcuqyn}hHNQ%V)Np#uH*)D9adqk;*{8`?TE1wv5-@22g8IMfXyK->XZ9y z^_YmjX1hA57hORm4&p=t{0tl#=+n9Fnr!$kOb{0J;bhc@b8Q3=@m}mgX}^n$UhLvF z$dW{WO@yoo7Zo1ZG4B=^n!#C$_e$)P79x>~!C?xu2oNR`r_RW`;Sj$nKEioy3tq;t zK!B?`;0wiOu;wyOjk%mtjnGwd1SjPjf&P>yIJx8zw$8>A)Y(cflF~YxtrWv*0pv7c z#R#(!)eowG9F(DIs0JV%3s|lG>|u9)Sj@$2fj^V&}$PI@~5{tuRk` z$BJvP;3gZYz*Qo5NdTIywBg_?Z{4N{vsy&_28#%@*+d{q5wF4~VheA82!DFT=ZUX4 z1>zF{K7o(8hvg3_SR{Y4zX#i+E*J(&45K?K%j_@)grVO0YrF#aPLG=gxfLd%JY!tyfizg z;glTZ<2xA0I=ImwbdVr# zMTz=22o$%OkOilCgAFb~fUfG)L=8@ZyIoPeO@!TIgR!j`fr)L!M^}Wk6^mg4Ae;Hp zZgJ=k1GF(;pno>o;RX5!7QX;0>wsE>?XU`~b#PRLVMG;F%_XWK2=DetZnxk+YmO1M z4tS+RonDJoAJ;*>UI%NNW71x4UlNQzzS^+Yl7qxDoqO|^!<0%P`>4@SEOL$Sz zYa-8i2Z-vVH-#&kaP$>R5eCwQY!lgH!q$Vm^_0$D)6plKCUTq@CcZ%op7VCcjTa?8 zCb21j2>yjeI8lcqg)da1?+T&sT>zyh%GaW0Omv=o(uTf_fgmM_WC-XEoyJ6bCqp`f zcB39byAI`=NUJExbl6jrWTN87*;c|_B~}Swl@9C^Xp*D~EUL}HzgIZ05j8rh(~fs*a* z>Hs>h`P!G+oOOx%j->K)YeO0(K~*}+I7taQTaYE0aXLv7=@mZ*&IllzMD5IO$q_jw z-KK#k#L4=H(4^xdgBC%Ls1dqE{o4sB&QX>L{?cK|Y?JVv7E+t2_yNaR)qq>7P(&vo zBYOSGjYg|Mr`52sKty&3-e`dO;AsQil100_uE%xOGVs>Zn*JIV;1?$tUBG1IB%5TSd81A8qIMnDRU+5vSUf^;{K8yUa(M?aj7gt)V-LhwnH@6u zL~3>Rn>JWMI>cZVsUG~KGpJB+0Pz707_w*#KGV*Pp9Cd4_mMgY-w3NnCX{2|0r^RA zm?WYGaGs(AsD89yQE7xqq+6~;^#{)2rDSP@$|GVWsZ4x;mtkVF9m1R1ZpCT71W}?= z=z$y_M8%oqYgsx+`%1%-uO~fuwP;IcEn&wPO8N>^$~u2syEosb8+Oe zap?Itz`itw)#CO7{fsQoYw)9*vU7r3u(hRkIIM-WOn_M6M2njwwhIeZiKAtAw8(ja z5!E@;Dbgs!a0E&+QS8Rb%w)oOt4XIH*H_9Jp z4svSFLs&9Gt462-`br~0JP!FLslcxs^@V9-$q3EsQ0)9w4aIF}MA1W}pD@0$=qV2v z6Y(4hB8RnCN6yzLzmr~G4NuAuar_uuCW`0Cb&{48>rF946!EA?zg^CBENj}W#Y!Zd z77GMz2;;l1DDqCad0Vz$gDXYayR~W~*|*Qz-70uTRQ!;k8@W(oaT6t*pCz2m z$!CeZ6mp8|x>J;2Iw0NUO@)p;CIVb0!XD4RY{8S-8&vOP4vN*3!*3W`HP}0lNhqOB zsCU2;jm$YoZQ4+xc~_H=fZ}(6#L^ME!Tt-)xe#*b^lPU(svzQTd3@y{qCz4zcMd@5 zr(^wyD96D&I?8f~y>kK@IPy!d_MFrCkSqKE?gJt41K?|oSVFSj6mqN}!LcB_pE*bS z)gku;X~OS{*dQxH{m=a*On$vNWtYzH`A(l$_Jl<9;PT+oeDn5E7ZpK!M$+PreHTR7 zhAV(Qgu}@(nk3^nvbVw{;`;5&feOo<-Yv1Hbi4N#dxR8!cEp!wQ7uFs>aagM@$Q8E z*$!AoLXB8YBK#P(J%&SEzio~XPFS?LI)HyS!Zg_hdb6H+bLWz{gd?Q&mJxIBiie^p%FBdAE_tCb77Ad(O?Dm$`M8w>y7D?<@Kv{pKlo zfnTXXr&mKxlw8}q4dY0ns5vY-BDcTqpE`7Mn-_404PFA}nnZId&dCY;77A2|?x9$!8|wBoPDm~tOYSHGz57@(1o0ZjpF#TkZyx>f8wEMq@2ubb zFF^+H{r%5o?YF(dcUB5s5w#NJ z9SLtqz6HAsZ`O}4anR>ypVesy?()B<-C0H=BK|MGjzfZ<9pzXnBCLI2UnnAuBs?ed z9G=tZbPyv*(H`9i3!6*0m5xgA+eTF8j-ex*6XUP_`8dbH|HPuA2Q`N!M_<_9uC(7S zzoP}E$|^V*#7OAz_B^r*LIn}5ViFsoOU&7K zkn!B%I6t>=^_!`mhjl`r#p2>4duf7hs6zvDn}vS){e5YcQ0c!Q)uS^qrV|kzZloW> zElYIQ5{VND6S!&QjUw`=SdEr7$#%22NvyRaclq^(ZU{lqj@+cfRo+-wP(qW$7;Xz8 lH;JFzVt1Rw9rd5B$3xMMR+5NT#Ko{8Hn)h)<|2#T|9_>+XixwE literal 0 HcmV?d00001 diff --git a/mods/_standard/chat/bings/chime.wav b/mods/_standard/chat/bings/chime.wav new file mode 100644 index 0000000000000000000000000000000000000000..9502298b4ac21b96c43125b96c1e1d9f3d9fc2da GIT binary patch literal 47672 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 BkHr80 literal 0 HcmV?d00001 diff --git a/mods/_standard/chat/bings/taras.html b/mods/_standard/chat/bings/taras.html new file mode 100644 index 000000000..e51049445 --- /dev/null +++ b/mods/_standard/chat/bings/taras.html @@ -0,0 +1,39 @@ + + + +

    bing on

    +
    + + + diff --git a/mods/_standard/chat/bings/taras.php b/mods/_standard/chat/bings/taras.php new file mode 100644 index 000000000..dfb1161a8 --- /dev/null +++ b/mods/_standard/chat/bings/taras.php @@ -0,0 +1,37 @@ + + +
    sss
    + + + diff --git a/mods/_standard/chat/chat.php b/mods/_standard/chat/chat.php new file mode 100644 index 000000000..04ea139f9 --- /dev/null +++ b/mods/_standard/chat/chat.php @@ -0,0 +1,83 @@ + + + + + + + ATutor AChat + + + 0 && $myPrefs['refresh'] == 'manual') { + //makeBingFile($chatID); +?> + + + + + + + + <p><?php echo _AT('frame_contains'); ?><br /> + * <a href="display.php?firstLoginFlag=<?php echo $_GET['firstLoginFlag']; ?>"><?php echo _AT('chat_messages') ?></a> + * <a href="options.php"><?php echo _AT('chat_options'); ?></a> + * <a href="poster.php"><?php echo _AT('chat_compose_message'); ?></a> + </p> + + + + + + + + <p><?php echo _AT('frame_contains'); ?><br /> + * <a href="display.php?firstLoginFlag=<?php echo $_GET['firstLoginFlag']; ?>"><?php echo _AT('chat_messages') ?></a> + * <a href="options.php"><?php echo _AT('chat_options'); ?></a> + * <a href="poster.php"><?php echo _AT('chat_compose_message'); ?></a> + </p> + + + + + + + + + + + <p><?php echo _AT('frame_contains'); ?><br /> + * <a href="display.php?firstLoginFlag=<?php echo $_GET['firstLoginFlag']; ?>"><?php echo _AT('chat_messages') ?></a> + * <a href="options.php"><?php echo _AT('chat_options'); ?></a> + * <a href="poster.php"><?php echo _AT('chat_compose_message'); ?></a> + </p> + + + + diff --git a/mods/_standard/chat/display.php b/mods/_standard/chat/display.php new file mode 100644 index 000000000..80445c5ac --- /dev/null +++ b/mods/_standard/chat/display.php @@ -0,0 +1,130 @@ + 0) { + postMessage(_AT('chat_system'), _AT('chat_user_logged_in', $_SESSION['login']), $topMsgNum, $bottomMsgNum); + } + +require('include/html/chat_header.inc.php'); + if ($myPrefs['refresh'] != 'manual') { +?> + + + + + + + +
    + + 1) { + $min = $topMsgNum - 10; + } + if ($myPrefs['onlyNewFlag'] > 0) { + $min = $myPrefs['lastRead'] +1; + } + if ($min <= $topMsgNum) { + echo ''; + } else { + echo '

    '._AT('chat_no_new_messages').'

    '; + } + + if ($myPrefs['newestFirstFlag'] > 0) { + for ($i = $topMsgNum; $i >= $min; $i--) { + showMessage($i, $myPrefs); + } + } else { + for ($i = $min; $i <= $topMsgNum ; $i++) { + showMessage($i, $myPrefs); + } + } + + if ($min <= $topMsgNum) { + echo '
    '; + } + + echo ''; + echo ''; + echo ''; + echo '
    '; + if ($myPrefs['navigationAidFlag'] > 0) { + echo ''._AT('chat_jump_to_message').' | '; + } + + echo ''._AT('chat_refresh_message').''; + echo '
    '; + + echo '

    '; + if ($myPrefs['refresh'] == 'manual') { + echo ' +
    '._AT('chat_compose_message').'
    '; + echo '

    '; + + echo '

    + + + '; + + echo '

    '; + echo ''; + } else { + if ($myPrefs['bingFlag'] > 0 && $topMsgNum > $myPrefs['lastRead']) { + echo ''; + } + } + + $myPrefs['lastRead'] = $topMsgNum; + $myPrefs['lastChecked'] = $topMsgNum; + writePrefs($myPrefs, $_SESSION['login']); + require('include/html/chat_footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/chat/filterHistory.php b/mods/_standard/chat/filterHistory.php new file mode 100644 index 000000000..3ab34ddc0 --- /dev/null +++ b/mods/_standard/chat/filterHistory.php @@ -0,0 +1,61 @@ + + + + + +
    + + + + + +
    +

    + 0) { + for ($i = $topMsgNum; $i >= 1; $i--) { + showMessageFiltered($i, $myPrefs, $filterChatID); + } + } else { + for ($i = 1; $i <= $topMsgNum ; $i++) { + showMessageFiltered($i, $myPrefs, $filterChatID); + } + } +?> +

    +
    + + + + + +
    + \ No newline at end of file diff --git a/mods/_standard/chat/help.php b/mods/_standard/chat/help.php new file mode 100644 index 000000000..492705773 --- /dev/null +++ b/mods/_standard/chat/help.php @@ -0,0 +1,76 @@ + + + + + +
    + + + + + +
    +

    +
    +
    +

    + + + + + +
    + + +

    +

    + + + + + +
    + + +

    + + + + + + +
    + + +


    + + + + + +
    + + + \ No newline at end of file diff --git a/mods/_standard/chat/history.php b/mods/_standard/chat/history.php new file mode 100644 index 000000000..3dce87d3b --- /dev/null +++ b/mods/_standard/chat/history.php @@ -0,0 +1,128 @@ + $topMsgNum) { + $hisTopNum = $topMsgNum; + } + if (!$hisTopNum) { + $hisTopNum = $topMsgNum; + } + + $hisBottomNum = getLower20Bound($hisTopNum, $bottomMsgNum); + + if ($hisBottomNum == 0) { + $hisBottomNum = 1; + } + $totalNum = $topMsgNum - $bottomMsgNum + 1; + + $hisTopNumUserPerspective = $hisTopNum - $bottomMsgNum + 1; + $hisBottomNumUserPerspective = $hisBottomNum - $bottomMsgNum + 1; + + if ($hisBottomNumUserPerspective < 1) { + $hisBottomNumUserPerspective = 1; + } + + $prevNumT = $hisBottomNum - 1; + $nextNumT = $hisTopNum + 20; + +require('include/html/chat_header.inc.php'); + + + if ($hisTopNum < $topMsgNum && $hisBottomNum > $bottomMsgNum) { +?> + + + + +
    | |
    + $bottomMsgNum) { +?> + + + + +
    |
    + + + + + +
    |
    + + + + + +
    +'; + + if ($myPrefs['newestFirstFlag'] > 0) { + for ($i = $hisTopNum; $i >= $hisBottomNum; $i--) { + showMessage($i, $myPrefs); + } + } else { + for ($i = $hisBottomNum; $i <= $hisTopNum ; $i++) { + showMessage($i, $myPrefs); + } + } + echo '

    '; + + if ($hisTopNum < $topMsgNum && $hisBottomNum > $bottomMsgNum) { +?> + + + + +
    Previous | Next | Return to Chat
    + $bottomMsgNum) { +?> + + + + +
    Previous | Return to Chat
    + + + + + +
    Next | Return to Chat
    + \ No newline at end of file diff --git a/mods/_standard/chat/include/html/chat_footer.inc.php b/mods/_standard/chat/include/html/chat_footer.inc.php new file mode 100644 index 000000000..691287b6e --- /dev/null +++ b/mods/_standard/chat/include/html/chat_footer.inc.php @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/mods/_standard/chat/include/html/chat_header.inc.php b/mods/_standard/chat/include/html/chat_header.inc.php new file mode 100644 index 000000000..ec4c54d30 --- /dev/null +++ b/mods/_standard/chat/include/html/chat_header.inc.php @@ -0,0 +1,16 @@ + + + + ATutor AChat + + + + + + + + class="chat"> diff --git a/mods/_standard/chat/include/html/login_footer.inc.php b/mods/_standard/chat/include/html/login_footer.inc.php new file mode 100644 index 000000000..50df04c85 --- /dev/null +++ b/mods/_standard/chat/include/html/login_footer.inc.php @@ -0,0 +1,5 @@ +

    +Developed by Joel Kronenberg at the
    +© Copyright ATRC, 2003
    + + \ No newline at end of file diff --git a/mods/_standard/chat/include/html/login_header.inc.php b/mods/_standard/chat/include/html/login_header.inc.php new file mode 100644 index 000000000..488c7aa40 --- /dev/null +++ b/mods/_standard/chat/include/html/login_header.inc.php @@ -0,0 +1,11 @@ + + + ATRC A-Chat-PHP: Login + + + + + + + +

    ATRC A-Chat-PHP

    \ No newline at end of file diff --git a/mods/_standard/chat/index.php b/mods/_standard/chat/index.php new file mode 100644 index 000000000..2d2b5ae93 --- /dev/null +++ b/mods/_standard/chat/index.php @@ -0,0 +1,134 @@ + +


    + 'desc', 'desc' => 'asc'); +$cols = array('name' => 1, 'date' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'date'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'date'; +} else { + // no order set + $order = 'desc'; + $col = 'date'; +} + +$tran_files = array(); +if (!@opendir(AT_CONTENT_DIR . 'chat/')){ + mkdir(AT_CONTENT_DIR . 'chat/', 0777); +} + +if(!file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings')){ + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'], 0777); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/', 0776); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/', 0776); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/', 0776); + @copy('admin.settings.default', AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings'); + @chmod (AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings', 0777); + +} + +if ($dir = @opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/')) { + while (($file = readdir($dir)) !== false) { + if (substr($file, -strlen('.html')) == '.html') { + $la = stat(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/'.$file); + + $file = str_replace('.html', '', $file); + $tran_files[$file] = $la['ctime']; + } + } +} + +if (count($tran_files) == 0) { + echo '

    '._AT('chat_none_found').'

    '; +} else {?> + +
    + + + + + + + + + + + + + + + + + + + + + + $date) { ?> + + + + + + + + + +
    + +
    +
    + \ No newline at end of file diff --git a/mods/_standard/chat/index.php.old b/mods/_standard/chat/index.php.old new file mode 100644 index 000000000..45d28aff5 --- /dev/null +++ b/mods/_standard/chat/index.php.old @@ -0,0 +1,134 @@ + +


    + 'desc', 'desc' => 'asc'); +$cols = array('name' => 1, 'date' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'date'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'date'; +} else { + // no order set + $order = 'desc'; + $col = 'date'; +} + +$tran_files = array(); +if (!@opendir(AT_CONTENT_DIR . 'chat/')){ + mkdir(AT_CONTENT_DIR . 'chat/', 0777); +} + +if(!file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings')){ + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'], 0777); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/', 0776); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/', 0776); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/', 0776); + @copy('admin.settings.default', AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings'); + @chmod (AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings', 0777); + +} + +if ($dir = @opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/')) { + while (($file = readdir($dir)) !== false) { + if (substr($file, -strlen('.html')) == '.html') { + $la = stat(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/'.$file); + + $file = str_replace('.html', '', $file); + $tran_files[$file] = $la['ctime']; + } + } +} + +if (count($tran_files) == 0) { + echo '

    '._AT('chat_none_found').'

    '; +} else {?> + +
    + + + + + + + + + + + + + + + + + + + + + + $date) { ?> + + + + + + + + + +
    + +
    +
    + \ No newline at end of file diff --git a/mods/_standard/chat/lib/chat.inc.php b/mods/_standard/chat/lib/chat.inc.php new file mode 100644 index 000000000..54b650c3a --- /dev/null +++ b/mods/_standard/chat/lib/chat.inc.php @@ -0,0 +1,555 @@ +'.$admin['returnT'].''; + } else { + $admin['returnLink'] = ''; + } + + return $admin; +} + +require('chat_defaults.inc.php'); +$admin = getAdminSettings(); +if ($admin === 0) { + $admin = defaultAdminSettings(); +} + +function postMessage($chatID, $message, &$topMsgNum, &$bottomMsgNum) { + global $admin; + + $topMsgNum++; + if (!is_dir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs')) { + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs'); + } + $fp = @fopen(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/'.$topMsgNum.'.message', 'w+'); + if (!$fp) { + // error + return 0; + } + + flock($fp, LOCK_EX); + if (!@fwrite($fp, $chatID."\n".$message."\n")) { + return 0; + } + flock($fp, LOCK_UN); + fclose($fp); + chmod(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/'.$topMsgNum.'.message', 0600); + + /* the transcript: */ + if ($admin['produceTran'] > 0) { + global $myPrefs; + $message = htmlspecialchars($message); + $colourT = getChatIDColour($chatID, 'whiteBlack'); + printToTran(''.stripslashes($chatID).''.stripslashes($message).''); + } +} + +function printToTran($message) { + global $admin; + $fp = fopen(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/'.$admin['tranFile'], 'a'); + if ($fp) { + fwrite($fp, $message."\n"); + }else{ + echo "nope"; + exit; + } + @fclose($fp); +} + +function howManyMessages(&$topMsgNum, &$bottomMsgNum) { + $topMsgNum = 0; + $bottomMsgNum = 0; + + if ($dir = @opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/')) { + while (($file = readdir($dir)) !== false) { + if (($file == '..') || ($file == '.')) { + continue; + } + $tempNum = substr($file, 0, -strlen('.message')); + if ($tempNum > $topMsgNum) { + $topMsgNum = $tempNum; + } + if (($tempNum < $bottomMsgNum) || ($bottomMsgNum == 0)) { + $bottomMsgNum = $tempNum; + } + } + closedir($dir); + } +} + + + +function getChatIDColour($chatID, $colours) { + $refNumT1 = strlen($chatID); + $char2T = substr($chatID, -1); + $refNumT2 = letterToNumber($char2T); + $char3T = substr($chatID, -2, 1); + $refNumT3 = letterToNumber($char3T); + $colourStr = '#'; + + if (($colours == 'blackYellow') + || ($colours == 'blueWhite') + || ($colours == 'blackWhite')) + { + if ($refNumT1%3 == 0) { + $colourStr .= 'ff'; + } else if ($refNumT1%3 == 1) { + $colourStr .= 'cc'; + } else { + $colourStr .= '99'; + } + + if ($refNumT2%3 == 0) { + $colourStr .= 'ff'; + } else if ($refNumT2%3 == 1) { + $colourStr .= 'cc'; + } else { + $colourStr .= '99'; + } + + if ($refNumT3%3 == 0) { + $colourStr .= 'ff'; + } else if ($refNumT3%3 == 1) { + $colourStr .= 'cc'; + } else { + $colourStr .= '99'; + } + } else { + if ($refNumT1%3 == 0) { + $colourStr .= '00'; + } else if ($refNumT1%3 == 1) { + $colourStr .= '33'; + } else { + $colourStr .= '66'; + } + if ($refNumT2%3 == 0) { + $colourStr .= '00'; + } else if ($refNumT2%3 == 1) { + $colourStr .= '33'; + } else { + $colourStr .= '66'; + } + if ($refNumT3%3 == 0) { + $colourStr .= '00'; + } else if ($refNumT3%3 == 1) { + $colourStr .= '33'; + } else { + $colourStr .= '66'; + } + } + return $colourStr; +} + +function letterToNumber($letter) { + $letter = strtolower($letter); + + if ($letter == '0') { return 0; } + if ($letter == '1') { return 1; } + if ($letter == '2') { return 2; } + if ($letter == '3') { return 3; } + if ($letter == '4') { return 4; } + if ($letter == '5') { return 5; } + if ($letter == '6') { return 6; } + if ($letter == '7') { return 7; } + if ($letter == '8') { return 8; } + if ($letter == '9') { return 9; } + if ($letter == 'b') { return 10; } + if ($letter == 'c') { return 11; } + if ($letter == 'd') { return 12; } + if ($letter == 'e') { return 13; } + if ($letter == 'f') { return 14; } + if ($letter == 'g') { return 15; } + if ($letter == 'h') { return 16; } + if ($letter == 'i') { return 17; } + if ($letter == 'j') { return 18; } + if ($letter == 'k') { return 19; } + if ($letter == 'l') { return 20; } + if ($letter == 'm') { return 21; } + if ($letter == 'n') { return 22; } + if ($letter == 'o') { return 23; } + if ($letter == 'p') { return 24; } + if ($letter == 'q') { return 25; } + if ($letter == 'r') { return 26; } + if ($letter == 's') { return 27; } + if ($letter == 't') { return 28; } + if ($letter == 'u') { return 30; } + if ($letter == 'v') { return 31; } + if ($letter == 'w') { return 32; } + if ($letter == 'x') { return 33; } + if ($letter == 'y') { return 34; } + if ($letter == 'z') { return 35; } + + return 36; +} + + +function printStylesheet($prefs) { + $h3SizeT = $prefs['fontSize'] + 4; + $h4SizeT = $prefs['fontSize'] + 2; + + print "\n"; +} + +function getLastAccessed($chatID) { + $tempPrefs = getPrefs($chatID, false); + return $tempPrefs['lastAccessed']; +} + +function &defaultAdminSettings() { + $admin = array(); + + //$admin['cgiURL'] = 'http://dev.atutor.ca/chat/'; + //$admin['htmlDir'] = '/usr/webserver/content/snow/chat/'; + //$admin['htmlURL'] = 'http://dev.atutor.ca/discussions/achat/'; + $admin['msgLifeSpan'] = 1800; /* 30 min */ + $admin['chatIDLifeSpan'] = 2678400; /* 1 month */ + $admin['chatSessionLifeSpan'] = 3600; /* 1 hour */ + //$admin['chatName'] = 'Accessible Chat'; + //$admin['chatIDListFlag'] = 0; + // $admin['returnL'] = 'http://dev.atutor.ca'; + //$admin['returnT'] = 'Return to the ATRC'; + //$admin['adminPass'] = 'temppass'; + + return $admin; +} +/* +function getAdminSettings() { + if (!file_exists('admin.settings')) { + return 0; + } + + $admin = array(); + + $file_prefs = file('admin.settings'); + foreach ($file_prefs as $pref) { + $pref = explode('=', $pref, 2); + $admin[$pref[0]] = trim($pref[1]); + } + + if ($admin['returnT'] && $admin['returnL']) { + $admin['returnLink'] = ''.$admin['returnT'].''; + } else { + $admin['returnLink'] = ''; + } + + return $admin; +} +*/ +function resetLastAccessed($chatID) { + $tempPrefs = getPrefs($chatID); + $tempPrefs['lastAccessed'] = 0; + writePrefs($tempPrefs, $chatID); + + /* + open(LA,">$cgiDIR"."users/$tempChatID.la") || &printError("resetLastAccessed","$!"); + flock(LA,2); + print LA "0\n"; + close(LA); + chmod (0666, "$cgiDIR"."users/$tempChatID.la"); + */ +} + + + +function cleanUp() { + global $admin; + $msgLifeSpan = $admin['msgLifeSpan']; + $chatSessionLifeSpan = $admin['chatSessionLifeSpan']; + $chatIDLifeSpan = $admin['chatIDLifeSpan']; + + $now = time(); + + if (!$msgLifeSpan || !$chatSessionLifeSpan || !$chatIDLifeSpan) { + echo 'Nope, something missing: '.$msgLifeSpan.', '.$chatSessionLifeSpan.', '.$chatIDLifeSpan.'
    '; + } else { + /* Clean up messages */ + if ($dir = @opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/')) { + while (($file = readdir($dir)) !== false) { + if (substr($file, -strlen('.message')) == '.message') { + $info = @stat(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/'.$file); + if ($now - $info['mtime'] > $msgLifeSpan) { + unlink(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/'.$file); + } + } + } + } + + /* Clean up inactive users (doesn't delete the users, just logs them out) */ + if ($dir = @opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/')) { + while (($file = readdir($dir)) !== false) { + if (substr($file, -strlen('.prefs')) == '.prefs') { + $chatName = substr($file, 0, -strlen('.prefs')); + $la = getLastAccessed($chatName); + if ($now - $la > $chatSessionLifeSpan && $la > 0) { + postMessage('system', + 'User '.$chatName.' has been logged out due to inactivity.', + $topMsgNum, + $bottomMsgNum); + resetLastAccessed($chatName); + + } + } + } + } + } +} + + +/* @See ./history.php */ +function getLower20Bound($topNum, $bottomMsgNum) { + for ($i = $topNum; ($i-$bottomMsgNum)%20 !=0; $i--) { ; } + return $i; +} + + +function showMessage($msgNum, &$prefs) { + if (file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/'.$msgNum.'.message')) { + $msg = file(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/'.$msgNum.'.message'); + + $sender = trim($msg[0]); + $msg = stripslashes(htmlspecialchars(trim($msg[1]))); + $colour = getChatIDColour($sender, $prefs['colours']); + + if ($msgNum > $prefs['lastRead']) { + echo ''.stripslashes($sender).':'.$msg.''; + } else { + echo ''.stripslashes($sender).':'.$msg.''; + } + } + +} + + +/* @See ./filterHistory.php */ +function showMessageFiltered($msgNum, &$prefs, $chatID) { + if (file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/'.$msgNum.'.message')) { + $msg = file(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/'.$msgNum.'.message'); + + $sender = trim($msg[0]); + $msg = trim($msg[1]); + + if ($sender == $chatID) { + $colour = getChatIDColour($sender, $prefs['colours']); + + if ($msgNum > $prefs['lastRead']) { + echo ''.stripslashes($sender).' : '.stripslashes($msg).''; + } else { + echo ''.stripslashes($sender).' : '.stripslashes($msg).''; + } + } + } +} + +/* @See ./prefs.php */ +function getAndWriteFormPrefs(&$prefs) { + if (isset($_POST['fontSize'])) { + $prefs['fontSize'] = $_POST['fontSize']; + } + + if (isset($_POST['fontFace'])) { + $prefs['fontFace'] = $_POST['fontFace']; + } + + if (isset($_POST['colours'])) { + $prefs['colours'] = $_POST['colours']; + } + + if (isset($_POST['navigationAidFlag'])) { + $prefs['navigationAidFlag'] = $_POST['navigationAidFlag']; + } + + if (isset($_POST['newestFirstFlag'])) { + $prefs['newestFirstFlag'] = $_POST['newestFirstFlag']; + } + + if (isset($_POST['onlyNewFlag'])) { + $prefs['onlyNewFlag'] = $_POST['onlyNewFlag']; + } + + if (isset($_POST['bingFlag'])) { + $prefs['bingFlag'] = $_POST['bingFlag']; + } + + if (isset($_POST['refresh'])) { + $prefs['refresh'] = $_POST['refresh']; + } + + writePrefs($prefs, $_SESSION['login']); +} + + +/* @See ./admin.php */ +function writeAdminSettings(&$admin) { + if (file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings')) { + chmod(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings', 0755); + } + + $fp = @fopen(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings', 'w+'); + if (!$fp) { + // error + return 0; + } + + $settings = ''; + foreach ($admin as $prefKey => $prefValue) { + $settings .= $prefKey.'='.$prefValue."\n"; + } + + flock($fp, LOCK_EX); + if (!@fwrite($fp, $settings)) { + return 0; + } + flock($fp, LOCK_UN); + chmod(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings', 0600); + + return 1; +} + +function clearOutOldChatPrefs() { + /* Clear out old user names */ + $now = time(); + $return = ''; + if ($dir = @opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/')) { + while (($file = readdir($dir)) !== false) { + if (substr($file, -strlen('.prefs')) == '.prefs') { + $chatName = substr($file, 0, -strlen('.prefs')); + $la = @stat(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/'.$file); + $la = $la['mtime']; + + if ($admin['chatIDLifeSpan'] && ($now - $la > $admin['chatIDLifeSpan'])) { + $return .= 'Automated Clean Up: Deleting old Chat ID '.$chatName.'
    '; + deleteUser($chatName); + } + } + } + } + + return $return; +} + +function deleteUser($chatName) { + @unlink(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/'.$chatName.'.prefs'); + + /* the bing file */ + @unlink ('bings/'.$chatName.'.html'); +} + + +function makeBingFile($chatName) { + global $myPrefs, $admin; + + if (($myPrefs['refresh'] == 'manual' && $myPrefs['bingFlag'] > 0)) { + + $bing = ' + +
    + + + '; + + } else if ($myPrefs['refresh'] == 'manual' && $myPrefs['bingFlag'] > 0) { + print " + + + + + \n"; + } + + $fp = @fopen('bings/'.$chatName.'.html', 'w+'); + if ($fp) { + flock($fp, LOCK_EX); + if (@fwrite($fp, $bing)) { + flock($fp, LOCK_UN); + } + } + @fclose($fp); +} + + +function securityCheck($uniqueID) { + global $myPrefs; + + if ($myPrefs['uniqueID'] == $uniqueID) { + return true; + } + return false; +} + +function printError($err1, $err2) { + print "An error has occured. Please login again
    \n"; + print "$err1
    \n"; + print "$err2
    \n"; + exit; +} + +?> diff --git a/mods/_standard/chat/lib/chat_defaults.inc.php b/mods/_standard/chat/lib/chat_defaults.inc.php new file mode 100644 index 000000000..a1963c1c2 --- /dev/null +++ b/mods/_standard/chat/lib/chat_defaults.inc.php @@ -0,0 +1,169 @@ + $prefValue) { + $prefs .= $prefKey.'='.$prefValue."\n"; + } + + flock($fp, LOCK_EX); + if (!@fwrite($fp, $prefs)) { + return 0; + } + flock($fp, LOCK_UN); + chmod(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/'.$chatID.'.prefs', 0600); + + return 1; +} + +?> diff --git a/mods/_standard/chat/logout.php b/mods/_standard/chat/logout.php new file mode 100644 index 000000000..f29389125 --- /dev/null +++ b/mods/_standard/chat/logout.php @@ -0,0 +1,57 @@ + + + + + +

    : Logout

    + +

    The will automatically save an account for you so that the next time you login with your Chat ID and Password your Preference Settings will be reloaded.

    + +

    Thank you for using the .
    +

    + + + + + + +
    Re-enter Chat
    + diff --git a/mods/_standard/chat/manage/delete_transcript.php b/mods/_standard/chat/manage/delete_transcript.php new file mode 100644 index 000000000..05a7bdff5 --- /dev/null +++ b/mods/_standard/chat/manage/delete_transcript.php @@ -0,0 +1,58 @@ +addFeedback('CANCELLED'); + Header('Location: index.php'); + exit; +} + +if ($_POST['submit_yes']) { + unlink(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/'.$m.'.html'); + + //if its the current tran, unset it + if (str_replace('.html', '', $admin['tranFile']) == $m) { + $admin['produceTran'] = 0; + writeAdminSettings($admin); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + Header('Location: index.php'); + exit; +} + +if (!file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/'.$m.'.html')) { + $msg->addError('FILE_NOT_FOUND'); + + header('Location: index.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$hidden_vars['m'] = $m; + +$msg->addConfirm(array('DELETE_TRANSCRIPT', $m), $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> diff --git a/mods/_standard/chat/manage/index.php b/mods/_standard/chat/manage/index.php new file mode 100644 index 000000000..dd81b8cd5 --- /dev/null +++ b/mods/_standard/chat/manage/index.php @@ -0,0 +1,152 @@ +addError('NO_ITEM_SELECTED'); +} + +$admin = getAdminSettings(); + +if (isset($_GET['delete'], $_GET['file'])) { + + if (($_GET['file'].'.html' == $admin['tranFile']) && ($admin['produceTran'])) { + $msg->addError('TRANSCRIPT_ACTIVE'); + } else { + header("Location:delete_transcript.php?m=".$_GET['file']); + exit; + } +} +require(AT_INCLUDE_PATH.'header.inc.php'); + +$orders = array('asc' => 'desc', 'desc' => 'asc'); +$cols = array('name' => 1, 'date' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'date'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'date'; +} else { + // no order set + $order = 'desc'; + $col = 'date'; +} + +$tran_files = array(); +if (!@opendir(AT_CONTENT_DIR . 'chat/')){ + mkdir(AT_CONTENT_DIR . 'chat/', 0777); +} + +if(!file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings')){ + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'], 0777); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/', 0776); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/', 0776); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/', 0776); + @copy('admin.settings.default', AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings'); + @chmod (AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings', 0777); + +} + +if ($dir = @opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/')) { + while (($file = readdir($dir)) !== false) { + if (substr($file, -strlen('.html')) == '.html') { + $la = stat(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/'.$file); + + $file = str_replace('.html', '', $file); + $tran_files[$file] = $la['ctime']; + } + } +} + +if (count($tran_files) == 0) { + echo '

    '._AT('chat_none_found').'

    '; +} else {?> + +
    + + + + + + + + + + + + + + + + + + + + + + + + + $date) { ?> + + + + + + + + + + + + + + + + + +
     
    + +
    +
    + \ No newline at end of file diff --git a/mods/_standard/chat/manage/start_transcript.php b/mods/_standard/chat/manage/start_transcript.php new file mode 100644 index 000000000..5dc46d453 --- /dev/null +++ b/mods/_standard/chat/manage/start_transcript.php @@ -0,0 +1,248 @@ + $prefValue) { + $settings .= $prefKey.'='.$prefValue."\n"; + } + + flock($fp, LOCK_EX); + if (!@fwrite($fp, $settings)) { + return 0; + } + flock($fp, LOCK_UN); + chmod(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings', 0600); + + return 1; +} + +function getAdminSettings() { + if (!file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings')) { + return 0; + } + + $admin = array(); + + $file_prefs = file(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings'); + foreach ($file_prefs as $pref) { + $pref = explode('=', $pref, 2); + $admin[$pref[0]] = trim($pref[1]); + } + + if ($admin['returnT'] && $admin['returnL']) { + $admin['returnLink'] = ''.$admin['returnT'].''; + } else { + $admin['returnLink'] = ''; + } + + return $admin; +} + +function defaultAdminSettings() { + $admin = array(); + + //$admin['cgiURL'] = 'http://dev.atutor.ca/chat/'; + //$admin['htmlDir'] = '/usr/webserver/content/snow/chat/'; + //$admin['htmlURL'] = 'http://dev.atutor.ca/discussions/achat/'; + $admin['msgLifeSpan'] = 1800; /* 30 min */ + $admin['chatIDLifeSpan'] = 2678400; /* 1 month */ + $admin['chatSessionLifeSpan'] = 3600; /* 1 hour */ + //$admin['chatName'] = 'Accessible Chat'; + //$admin['chatIDListFlag'] = 0; + // $admin['returnL'] = 'http://dev.atutor.ca'; + //$admin['returnT'] = 'Return to the ATRC'; + //$admin['adminPass'] = 'temppass'; + + return $admin; +} + +$admin = getAdminSettings(); +if ($admin === 0) { + $admin = defaultAdminSettings(); +} + +if (isset($_POST['submit'])) { + $admin['adminPass'] = $_POST['newAdminPass']; + $adminPass = $_POST['newAdminPass']; + $admin['chatName'] = $_POST['chatName']; + $admin['returnL'] = $_POST['returnL']; + $admin['returnT'] = $_POST['returnT']; + $admin['msgLifeSpan'] = $_POST['msgLifeSpan']; + $admin['chatSessionLifeSpan'] = $_POST['chatSessionLifeSpan']; + $admin['chatIDLifeSpan'] = $_POST['chatIDLifeSpan']; + writeAdminSettings($admin); + +} else if (isset($_POST['submit2'])) { + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'] . '/tran'); + if(file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/'.$_POST['tranFile'].'.html')){ + $warnings = array('CHAT_TRAN_EXISTS', $_POST['tranFile']); //'file already exists'; + $msg->addWarning($warnings); + } else if ($_POST['function'] == 'startTran') { + if (!(preg_match("/^[a-zA-Z0-9_]([a-zA-Z0-9_])*$/i", $_POST['tranFile']))){ + $msg->addError('CHAT_TRAN_REJECTED'); + } else { + $admin['produceTran'] = 1; + $admin['tranFile'] = $_POST['tranFile'] . '.html'; + writeAdminSettings($admin); + $tran = '

    '._AT('chat_transcript_start').' '.date('Y-M-d H:i').'

    '; + $tran .= ''; + + $fp = fopen(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/'.$admin['tranFile'], 'w+'); + + flock($fp, LOCK_EX); + if (!fwrite($fp, $tran)) { + return 0; + } + flock($fp, LOCK_UN); + + header('Location: index.php'); + exit; + } + } else if ($_POST['function'] == 'stopTran') { + $admin['produceTran'] = 0; + writeAdminSettings($admin); + + $tran = '

    '._AT('chat_transcript_end').' '.date('Y-M-d H:i').'

    '; + $fp = @fopen(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/'.$admin['tranFile'], 'a'); + + @flock($fp, LOCK_EX); + if (!@fwrite($fp, $tran)) { + return 0; + } + flock($fp, LOCK_UN); + + header('Location: index.php'); + exit; + } +} else if ($_GET['function'] == 'clearOldChatIDs') { + $return = clearOutOldChatPrefs(); +} else if ($_POST['submit3']) { + deleteUser($_POST['delName']); +} else if ($_POST['submit4']) { + if ($dir = @opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/')) { + while (($file = readdir($dir)) !== false) { + if (substr($file, -strlen('.prefs')) == '.prefs') { + $chatName = substr($file, 0, -strlen('.prefs')); + deleteUser($chatName); + } + } + } +} + +//check chat directory +if (!@opendir(AT_CONTENT_DIR . 'chat/')){ + mkdir(AT_CONTENT_DIR . 'chat/', 0777); +} + +if(!file_exists(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings')){ + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'], 0777); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/', 0776); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/msgs/', 0776); + @mkdir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/', 0776); + @copy('admin.settings.default', AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings'); + @chmod (AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/admin.settings', 0777); + +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + if ($return != '') { + echo ''.$return.''; + } + + if ($admin['msgLifeSpan'] < 650) { + $m10 = ' selected '; + } else if ($admin['msgLifeSpan'] < 950) { + $m30 = ' selected '; + } else if ($admin['msgLifeSpan'] < 1850) { + $m60 = ' selected '; + } else if ($admin['msgLifeSpan'] < 10850) { + $m180 = ' selected '; + } else { + $m1D = ' selected '; + } + + if ($admin['chatSessionLifeSpan'] < 650) { + $s10 = ' selected '; + } else if ($admin['chatSessionLifeSpan'] < 950) { + $s30 = ' selected '; + } else if ($admin['chatSessionLifeSpan'] < 1850) { + $s60 = ' selected '; + } else if ($admin['chatSessionLifeSpan'] < 10850) { + $s180 = ' selected '; + } else { + $s1D = ' selected '; + } + if ($admin['chatIDLifeSpan'] < 86450) { + $i1D = ' selected '; + } else if ($admin['chatIDLifeSpan'] < 1728050) { + $i20D = ' selected '; + } else if ($admin['chatIDLifeSpan'] < 2592050) { + $i1M = ' selected '; + } else { + $i1Y = ' selected '; + } +?> + + +
    +
    + +
    + +'; + + if ($admin['produceTran'] > 0) { + echo ''; + echo '
    '; + echo _AT('chat_current_tran').' '.str_replace('.html', '', $admin['tranFile']).'.

    '; + echo '
    '; + + echo '
    '; + echo ''; + echo '
    '; + + } else { + echo ''; + + echo '
    '; + echo _AT('chat_tran_file_name').' '; + echo ''; + echo '
    '; + + echo '
    '; + echo ''; + echo '
    '; + } + echo '
    '; + echo ''; + + require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/chat/manage/view_transcript.php b/mods/_standard/chat/manage/view_transcript.php new file mode 100644 index 000000000..cbce734aa --- /dev/null +++ b/mods/_standard/chat/manage/view_transcript.php @@ -0,0 +1,27 @@ +addError('FILE_NOT_FOUND'); + header('Location: index.php'); + exit; +} +require(AT_INCLUDE_PATH.'header.inc.php'); +@readfile($file); +echo '
    '; +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/chat/module.php b/mods/_standard/chat/module.php new file mode 100644 index 000000000..0fd4f4230 --- /dev/null +++ b/mods/_standard/chat/module.php @@ -0,0 +1,52 @@ +getPrivilege()); + +// if this module is to be made available to students on the Home or Main Navigation +$_student_tool = 'mods/_standard/chat/index.php'; + +// module sublinks +$this->_list['chat'] = array('title_var'=>'chat','file'=>'mods/_standard/chat/sublinks.php'); + +$this->_pages['mods/_standard/chat/manage/index.php']['title_var'] = 'chat'; +$this->_pages['mods/_standard/chat/manage/index.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_standard/chat/manage/index.php']['children'] = array('mods/_standard/chat/manage/start_transcript.php'); +$this->_pages['mods/_standard/chat/manage/index.php']['guide'] = 'instructor/?p=chat.php'; + + $this->_pages['mods/_standard/chat/manage/start_transcript.php']['title_var'] = 'chat_start_transcript'; + $this->_pages['mods/_standard/chat/manage/start_transcript.php']['parent'] = 'mods/_standard/chat/manage/index.php'; + + $this->_pages['mods/_standard/chat/manage/delete_transcript.php']['title_var'] = 'chat_delete_transcript'; + $this->_pages['mods/_standard/chat/manage/delete_transcript.php']['parent'] = 'mods/_standard/chat/manage/index.php'; + + $this->_pages['mods/_standard/chat/manage/view_transcript.php']['title_var'] = 'chat_transcript'; + $this->_pages['mods/_standard/chat/manage/view_transcript.php']['parent'] = 'mods/_standard/chat/manage/index.php'; + +$this->_pages['mods/_standard/chat/index.php']['title_var'] = 'chat'; +$this->_pages['mods/_standard/chat/index.php']['img'] = 'images/home-chat.png'; +$this->_pages['mods/_standard/chat/index.php']['icon'] = 'images/home-chat_sm.png'; + + $this->_pages['mods/_standard/chat/chat_frame.php']['title_var'] = 'chat'; + $this->_pages['mods/_standard/chat/chat_frame.php']['parent'] = 'mods/_standard/chat/index.php'; + + $this->_pages['mods/_standard/chat/view_transcript.php']['title_var'] = 'chat_transcript'; + $this->_pages['mods/_standard/chat/view_transcript.php']['parent'] = 'mods/_standard/chat/index.php'; + +$this->_pages['mods/_standard/chat/chat.php']['title_var'] = 'chat'; + + +?> \ No newline at end of file diff --git a/mods/_standard/chat/module.xml b/mods/_standard/chat/module.xml new file mode 100644 index 000000000..a39b1066a --- /dev/null +++ b/mods/_standard/chat/module.xml @@ -0,0 +1,23 @@ + + + Chat + AChat is an accessible chat forum for real time discussions. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + + + 2005-08-29 + stable + This is a standard module. + + \ No newline at end of file diff --git a/mods/_standard/chat/module_delete.php b/mods/_standard/chat/module_delete.php new file mode 100644 index 000000000..c4f65387e --- /dev/null +++ b/mods/_standard/chat/module_delete.php @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/mods/_standard/chat/options.php b/mods/_standard/chat/options.php new file mode 100644 index 000000000..a10ef3029 --- /dev/null +++ b/mods/_standard/chat/options.php @@ -0,0 +1,88 @@ + + + + + +
    + + + +
    |
    +

    + +
    '; + if ($dir = opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/users/')) { + while (($file = readdir($dir)) !== false) { + if (($file == '..') || ($file == '.')) { + continue; + } + + $chatName = substr($file, 0, -strlen('.prefs')); + $la = getLastAccessed($chatName); + $now = time(); + + if (($la == 0) || (!$la)) { + $la = 0; + } else if ($now - $la < $admin['chatSessionLifeSpan']) { + $colour = getChatIDColour($chatName, $myPrefs['colours']); + if ($chatName == $_SESSION['login']) { + echo '
  • '.$chatName.' ('._AT('chat_you').')
  • '; + } else if($chatName != '') { + echo '
  • '.$chatName.'
  • '; + } + } else { + resetLastAccessed($chatName); + $topMsgNum = $bottomMsgNum = 0; + howManyMessages($topMsgNum, $bottomMsgNum); + postMessage(_AT('chat_system'), + require(AT_INCLUDE_PATH.'../mods/_standard/chat/lib/chat.inc.php'), _AT('chat_user_logged_out', $chatName), + $topMsgNum, + $bottomMsgNum); + } + } + } + closedir($dir); + echo ''; + + echo ' +
    '._AT('chat_full_history').' | '._AT('chat_refresh_user_list').'
    '; + + //if ($myPrefs['navigationAidFlag'] > 0) { + echo '

    '; + echo ' +

    '._AT('chat_quick_keys').'

    '; + + echo '
    • '._AT('chat_altc').'
    • +
    • '._AT('chat_post').'
    • +
    • '._AT('chat_altr').'
    • +
    • '._AT('chat_altm').'
    • +
    • '._AT('chat_altq').'
    '; + //} +?> + diff --git a/mods/_standard/chat/poster.php b/mods/_standard/chat/poster.php new file mode 100644 index 000000000..00b073402 --- /dev/null +++ b/mods/_standard/chat/poster.php @@ -0,0 +1,46 @@ + + + + + + + + +
    +
    + +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/chat/prefs.php b/mods/_standard/chat/prefs.php new file mode 100644 index 000000000..5c9ac3d55 --- /dev/null +++ b/mods/_standard/chat/prefs.php @@ -0,0 +1,89 @@ + + + + + +

    +
    +
    + + 100) { + $mC180SelT = 'selected'; + } else if ($myPrefs['refresh'] > 30) { + $mC60SelT = 'selected'; + } else if ($myPrefs['refresh'] > 10) { + $mC20SelT = 'selected'; + } else { + $mc5SelT = 'selected'; + } + +?> +

    +

    +

    + + 0) { + $bFSelT = 'selected'; + } +?> + +

    +

    +

    + + + + + +
    +
    +
    + \ No newline at end of file diff --git a/mods/_standard/chat/prefs2.php b/mods/_standard/chat/prefs2.php new file mode 100644 index 000000000..6c837e529 --- /dev/null +++ b/mods/_standard/chat/prefs2.php @@ -0,0 +1,79 @@ + + + + + +

    +
    +
    + + + 0) { + $nFFSelT = 'selected'; + } +?> + +

    +

    +

    + + 0) { + $oNFSelT = 'selected'; + } +?> + +

    +

    +

    + + + + + +
    + +
    + \ No newline at end of file diff --git a/mods/_standard/chat/sublinks.php b/mods/_standard/chat/sublinks.php new file mode 100644 index 000000000..73b9b8a4e --- /dev/null +++ b/mods/_standard/chat/sublinks.php @@ -0,0 +1,41 @@ + detail view +$cnt = 0; // count number of sublinks pushed into $list + +if ($dir = @opendir(AT_CONTENT_DIR . 'chat/'.$_SESSION['course_id'].'/tran/')) { + while (($file = readdir($dir)) !== false) { + if ($cnt >= $record_limit) break; // quit the loop when reaching the record limit + + if (substr($file, -strlen('.html')) == '.html') { + $file = str_replace('.html', '', $file); + + $list[] = ' SUBLINK_TEXT_LEN ? ' title="'.$file.'"' : '') .'>'. + validate_length($file, SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''; + + $cnt++; + } + } +} + +if (count($list) > 0) { + return $list; +} else { + return 0; +} + + +?> \ No newline at end of file diff --git a/mods/_standard/chat/view_transcript.php b/mods/_standard/chat/view_transcript.php new file mode 100644 index 000000000..52ce99c0d --- /dev/null +++ b/mods/_standard/chat/view_transcript.php @@ -0,0 +1,33 @@ + + + +
    + +
    + +
    + + \ No newline at end of file diff --git a/mods/_standard/course_email/course_email.php b/mods/_standard/course_email/course_email.php new file mode 100644 index 000000000..522227f07 --- /dev/null +++ b/mods/_standard/course_email/course_email.php @@ -0,0 +1,231 @@ +addFeedback('CANCELLED'); + header('Location: '.$_base_href.'tools/index.php'); + exit; +} else if (isset($_POST['submit'])) { + $missing_fields = array(); + + $_POST['to_enrolled'] = trim($_POST['to_enrolled']); + $_POST['to_unenrolled'] = trim($_POST['to_unenrolled']); + $_POST['to_alumni'] = trim($_POST['to_alumni']); + $_POST['to_assistants'] = trim($_POST['to_assistants']); + + $_POST['subject'] = trim($_POST['subject']); + $_POST['body'] = trim($_POST['body']); + + if ( ($_POST['to_enrolled'] == '') && + ($_POST['to_unenrolled'] == '') && + ($_POST['to_alumni'] == '') && + ($_POST['to_assistants'] == '') && + ($_POST['groups'] == '') + ) { + $missing_fields[] = _AT('to'); + } + + if ($_POST['subject'] == '') { + $missing_fields[] = _AT('subject'); + } + + if ($_POST['body'] == '') { + $missing_fields[] = _AT('body'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $email_sql = "SELECT email, first_name, last_name, login, password FROM ".TABLE_PREFIX."course_enrollment C INNER JOIN ".TABLE_PREFIX."members M USING (member_id) WHERE C.course_id=$course AND ("; + + if ($_POST['to_unenrolled']) { + // choose all unenrolled + $email_sql .= "C.approved='n' OR "; + } + + if ($_POST['to_alumni']) { + // choose all alumni + $email_sql .= "C.approved='a' OR "; + } + + if ($_POST['to_assistants']){ + // choose all assistants + $email_sql .= "C.privileges<>0 OR "; + } + + if ($_POST['groups']) { + // specific groups + $groups = implode(',', $_POST['groups']); + + $group_members = array(); + $sql = "SELECT member_id FROM ".TABLE_PREFIX."groups_members WHERE group_id IN ($groups)"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $group_members[] = $row['member_id']; + } + $group_members = implode(',', $group_members); + if (!empty($group_members)){ + $email_sql .= "M.member_id IN ($group_members) OR "; + } else { + $email_sql .= "M.member_id IN (-1) OR "; + } + } else if ($_POST['to_enrolled']) { + // includes instructor + $email_sql .= "(C.approved='y' AND C.privileges=0) OR "; + } + + $email_sql = substr_replace($email_sql, '', -4). ')'; // strip off the last ' OR ' + $result = mysql_query($email_sql,$db); + + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + // generate email recipients + $mail_list = array(); + while ($row = mysql_fetch_assoc($result)) { + $mail_list[]=$row['email']; + $fname_list[$row['email']] = $row['first_name']; + $lname_list[$row['email']] = $row['last_name']; + $login_list[$row['email']] = $row['login']; + } + + // Get instructor ID. + $result = mysql_query("SELECT member_id FROM ".TABLE_PREFIX."courses WHERE course_id=$course",$db); + $row = mysql_fetch_assoc($result); + $instructor_id = $row['member_id']; + + // Add instructor to email list if he is not the one sending email. + if ($instructor_id != $_SESSION['member_id']) { + //$sql = "SELECT email FROM ".TABLE_PREFIX."members WHERE member_id=$instructor_id"; + $sql = "SELECT email FROM ".TABLE_PREFIX."members WHERE member_id=$instructor_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + $mail_list[]= $row['email']; + } + + // Get the sender. + $result = mysql_query("SELECT email, first_name, last_name,login,password FROM ".TABLE_PREFIX."members WHERE member_id=$_SESSION[member_id]", $db); + $row = mysql_fetch_assoc($result); + $mail_list[] = $row['email']; + // Prep the mailer. + // set some user specific variables for the body ( + // Added by Thomas Taennier (ipool) + foreach ($mail_list as $recip) { + $subject = $_POST['subject']; + $body = $_POST['body']; + $mail = new ATutorMailer; + $mail->From = $row['email']; + $mail->FromName = $row['first_name'] . ' ' . $row['last_name']; + $subject = str_replace('{AT_FNAME}', $fname_list[$recip],$subject); + $subject = str_replace('{AT_LNAME}', $lname_list[$recip],$subject); + $body = str_replace('{AT_FNAME}', $fname_list[$recip],$body); + $body = str_replace('{AT_LNAME}', $lname_list[$recip],$body); + $body = str_replace('{AT_EMAIL}', $recip,$body); + $body = str_replace('{AT_USER}', $login_list[$recip],$body); + + $mail->Subject = $subject; + $mail->AddAddress($recip); + $mail->Body = $body; + if(!$mail->Send()) { + $msg->addError('SENDING_ERROR'); + header('Location: '.$_SERVER['PHP_SELF']); + exit; + } + unset($mail); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.$_base_href.'tools/index.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."course_enrollment C, ".TABLE_PREFIX."members M WHERE C.course_id=$course AND C.member_id=M.member_id AND M.member_id<>$_SESSION[member_id] ORDER BY C.approved, M.login"; +$result = mysql_query($sql,$db); +$row = mysql_fetch_array($result); +if ($row['cnt'] == 0) { + $msg->printInfos('NO_STUDENTS'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +?> +
    + + +
    +
    +
    + * +
    + /> + /> + /> + /> + + + +

    + :
    + + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/course_email/module.php b/mods/_standard/course_email/module.php new file mode 100644 index 000000000..38abc933e --- /dev/null +++ b/mods/_standard/course_email/module.php @@ -0,0 +1,12 @@ +getPrivilege()); + +$this->_pages['mods/_standard/course_email/course_email.php']['title_var'] = 'course_email'; +$this->_pages['mods/_standard/course_email/course_email.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_standard/course_email/course_email.php']['guide'] = 'instructor/?p=course_email.php'; + + +?> \ No newline at end of file diff --git a/mods/_standard/course_email/module.xml b/mods/_standard/course_email/module.xml new file mode 100644 index 000000000..145636da2 --- /dev/null +++ b/mods/_standard/course_email/module.xml @@ -0,0 +1,23 @@ + + + Course Email + Allows instructors to mass-email all members of a course. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + + + 2005-08-25 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_standard/course_tools/module.php b/mods/_standard/course_tools/module.php new file mode 100644 index 000000000..5b1d25fe7 --- /dev/null +++ b/mods/_standard/course_tools/module.php @@ -0,0 +1,16 @@ +getPrivilege()); + +$this->_pages['mods/_standard/course_tools/modules.php']['title_var'] = 'course_tools'; +$this->_pages['mods/_standard/course_tools/modules.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_standard/course_tools/modules.php']['children'] = array('mods/_standard/course_tools/side_menu.php'); +$this->_pages['mods/_standard/course_tools/modules.php']['guide'] = 'instructor/?p=student_tools.php'; + + $this->_pages['mods/_standard/course_tools/side_menu.php']['title_var'] = 'side_menu'; + $this->_pages['mods/_standard/course_tools/side_menu.php']['parent'] = 'mods/_standard/course_tools/modules.php'; + $this->_pages['mods/_standard/course_tools/side_menu.php']['guide'] = 'instructor/?p=side_menu.php'; + +?> \ No newline at end of file diff --git a/mods/_standard/course_tools/module.xml b/mods/_standard/course_tools/module.xml new file mode 100644 index 000000000..f589884e9 --- /dev/null +++ b/mods/_standard/course_tools/module.xml @@ -0,0 +1,23 @@ + + + Student Tools + Allows instructors to set which course tools will be available to students on the home page and/or coruse navigation. Instructors can also select which available side menu items will be shown for the course. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + existing + + + 2005-08-25 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_standard/course_tools/modules.php b/mods/_standard/course_tools/modules.php new file mode 100644 index 000000000..d4840625d --- /dev/null +++ b/mods/_standard/course_tools/modules.php @@ -0,0 +1,195 @@ +addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: modules.php'); + exit; +} + + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +//being displayed +$_current_modules = array_slice($_pages[AT_NAV_COURSE], 1, -1); // removes index.php and tools/index.php +$num_main = count($_current_modules); +//main and home merged +$_current_modules = array_merge( (array) $_current_modules, array_diff($_pages[AT_NAV_HOME],$_pages[AT_NAV_COURSE]) ); +$num_modules = count($_current_modules); +//all other mods +$_current_modules = array_merge( (array) $_current_modules, array_diff($_modules, $_current_modules)); + +$count = 0; + +?> +
    + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +   + + 1)): ?> + + + + + + + + + + +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/course_tools/side_menu.php b/mods/_standard/course_tools/side_menu.php new file mode 100644 index 000000000..7f09260ab --- /dev/null +++ b/mods/_standard/course_tools/side_menu.php @@ -0,0 +1,88 @@ +addFeedback('CANCELLED'); + header('Location: modules.php'); + exit; + +} + +if (isset($_POST['submit'])) { + + $side_menu = ''; + $_stack_names = array(); + + $_stack_names = array_keys($_stacks); + + $_POST['stack'] = array_unique($_POST['stack']); + $_POST['stack'] = array_intersect($_POST['stack'], $_stack_names); + + $side_menu = implode('|', $_POST['stack']); + + $sql = "UPDATE ".TABLE_PREFIX."courses SET side_menu='$side_menu' WHERE course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + $msg->addFeedback('COURSE_PREFS_SAVED'); + header('Location: side_menu.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    +
    +
    +
    +

    +
    + +
    + '; + echo ''; + foreach ($_stacks as $name=>$info) { + if (isset($info['title'])) { + $title = $info['title']; + } else { + $title = _AT($info['title_var']); + } + echo ''; + } + echo ''; + echo '
    '; + } ?> +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/directory/directory.php b/mods/_standard/directory/directory.php new file mode 100644 index 000000000..dd7aad219 --- /dev/null +++ b/mods/_standard/directory/directory.php @@ -0,0 +1,109 @@ +addInfo('NOT_ENROLLED'); + $msg->printAll(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +if ($_GET['reset_filter']) { + unset($_GET); +} + +if (isset($_GET['online_status']) && ($_GET['online_status'] != '')) { + if ($_GET['online_status'] == 1) { + $on = 'checked="checked"'; + } else if ($_GET['online_status'] == 2) { + $all = 'checked="checked"'; + } else if ($_GET['online_status'] == 0) { + $off = 'checked="checked"'; + } +} else { + $all = 'checked="checked"'; +} + +$group = abs($_GET['group']); + +$sql_groups = implode(',', $_SESSION['groups']); +$sql = "SELECT G.title, G.group_id, T.title AS type_title FROM ".TABLE_PREFIX."groups G INNER JOIN ".TABLE_PREFIX."groups_types T USING (type_id) WHERE T.course_id=$_SESSION[course_id] AND G.group_id IN ($sql_groups) ORDER BY T.title"; +$result_groups = mysql_query($sql, $db); + +if ($_GET['order'] == 'asc') { + $order = 'desc'; +} else { + $order = 'asc'; +} + +$group_members = ''; +if ($group) { + $group_members = array(); + $sql = "SELECT member_id FROM ".TABLE_PREFIX."groups_members WHERE group_id=$group"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $group_members[] = $row['member_id']; + } + $group_members = ' AND C.member_id IN (' . implode(',', $group_members) . ')'; +} + +/* look through enrolled students list */ +$sql_members = "SELECT C.member_id, C.approved, C.privileges, M.login, M.first_name, M.second_name, M.last_name FROM ".TABLE_PREFIX."course_enrollment C, ".TABLE_PREFIX."members M WHERE C.course_id=$_SESSION[course_id] AND C.member_id=M.member_id AND (C.approved='y' OR C.approved='a') $group_members ORDER BY M.login $order"; + +$result_members = mysql_query($sql_members, $db); + +while ($row_members = mysql_fetch_assoc($result_members)) { + $all_[$row_members['member_id']] = $row_members; + $all_[$row_members['member_id']]['online'] = FALSE; +} + +$sql_online = "SELECT member_id FROM ".TABLE_PREFIX."users_online WHERE course_id = $_SESSION[course_id] AND expiry>".time(); +$result_online = mysql_query($sql_online, $db); + +while ($row_online = mysql_fetch_assoc($result_online)) { + if ($all_[$row_online['member_id']] != '') { + $all_[$row_online['member_id']]['online'] = TRUE; + $online[$row_online['member_id']] = $all_[$row_online['member_id']]; + } +} + +if ($all) { + $final = $all_; +} else if ($on) { + $final = $online; +} else { + foreach ($all_ as $id=>$attrs) { + if ($attrs['online'] == FALSE) { + $final[$id] = $attrs; + } + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$savant->assign('result_groups', $result_groups); +$savant->assign('final', $final); +$savant->assign('base_href', $_base_href); +$savant->assign('on', $on); +$savant->assign('off', $off); +$savant->assign('all', $all); + +$savant->display('directory.tmpl.php'); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> diff --git a/mods/_standard/directory/module.php b/mods/_standard/directory/module.php new file mode 100644 index 000000000..953c20e07 --- /dev/null +++ b/mods/_standard/directory/module.php @@ -0,0 +1,15 @@ +_list['directory'] = array('title_var'=>'directory','file'=>'mods/_standard/directory/sublinks.php'); + +$this->_pages['mods/_standard/directory/directory.php']['title_var'] = 'directory'; +$this->_pages['mods/_standard/directory/directory.php']['img'] = 'images/home-directory.png'; +$this->_pages['mods/_standard/directory/directory.php']['icon'] = 'images/home-directory_sm.png'; + +?> \ No newline at end of file diff --git a/mods/_standard/directory/module.xml b/mods/_standard/directory/module.xml new file mode 100644 index 000000000..b75b6834d --- /dev/null +++ b/mods/_standard/directory/module.xml @@ -0,0 +1,23 @@ + + + Directory + Lists members of a course. Status, online status, and a link to his/her individual profile page is given for each member. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + + + + 2005-08-25 + stable + This is a core module. + + \ No newline at end of file diff --git a/mods/_standard/directory/sublinks.php b/mods/_standard/directory/sublinks.php new file mode 100644 index 000000000..fa7b43b4f --- /dev/null +++ b/mods/_standard/directory/sublinks.php @@ -0,0 +1,35 @@ + detail view + +$sql = "SELECT C.member_id, M.login FROM ".TABLE_PREFIX."course_enrollment C, ".TABLE_PREFIX."members M WHERE C.course_id=$_SESSION[course_id] AND C.member_id=M.member_id AND (C.approved='y' OR C.approved='a') limit ".$record_limit; +$result = mysql_query($sql, $db); + +if (mysql_num_rows($result) > 0) { + while ($row = mysql_fetch_assoc($result)) { + $list[] = ' SUBLINK_TEXT_LEN ? ' title="'.$row['login'].'"' : '') .'>'. + validate_length($row['login'], SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''; + } + return $list; + +} else { + return 0; +} + + +?> \ No newline at end of file diff --git a/mods/_standard/faq/add_question.php b/mods/_standard/faq/add_question.php new file mode 100644 index 000000000..a947a7910 --- /dev/null +++ b/mods/_standard/faq/add_question.php @@ -0,0 +1,111 @@ +addFeedback('CANCELLED'); + header('Location: index_instructor.php'); + exit; +} else if (isset($_POST['submit'])) { + $_POST['question'] = trim($_POST['question']); + $_POST['answer'] = trim($_POST['answer']); + + $missing_fields = array(); + + if (!$_POST['question']) { + $missing_fields[] = _AT('question'); + } + + if (!$_POST['answer']) { + $missing_fields[] = _AT('answer'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + + if (!$msg->containsErrors()) { + $_POST['question'] = $addslashes($_POST['question']); + $_POST['answer'] = $addslashes($_POST['answer']); + $_POST['topic_id'] = intval($_POST['topic_id']); + //These will truncate the content of the length to 240 as defined in the db. + $_POST['question'] = validate_length($_POST['question'], 250); + $_POST['answer'] = validate_length($_POST['answer'], 250); + + // check that this topic_id belongs to this course: + $sql = "SELECT topic_id FROM ".TABLE_PREFIX."faq_topics WHERE topic_id=$_POST[topic_id] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $sql = "INSERT INTO ".TABLE_PREFIX."faq_entries VALUES (NULL, $_POST[topic_id], NOW(), 1, '$_POST[question]', '$_POST[answer]')"; + $result = mysql_query($sql,$db); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index_instructor.php'); + exit; + } +} + +$onload = 'document.form.topic.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $sql = "SELECT name, topic_id FROM ".TABLE_PREFIX."faq_topics WHERE course_id=$_SESSION[course_id] ORDER BY name"; + $result = mysql_query($sql, $db); + $num_topics = mysql_num_rows($result); + if (!$num_topics) { + $msg->printErrors('NO_FAQ_TOPICS'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } +?> + +
    + +
    +
    +
    + + *
    + +
    +
    + *
    + + +
    +
    + *
    + +
    + + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/faq/add_topic.php b/mods/_standard/faq/add_topic.php new file mode 100644 index 000000000..cf7ed3cff --- /dev/null +++ b/mods/_standard/faq/add_topic.php @@ -0,0 +1,65 @@ +addFeedback('CANCELLED'); + header('Location: index_instructor.php'); + exit; +} else if (isset($_POST['submit'])) { + if (trim($_POST['name']) == '') { + $msg->addError('NAME_EMPTY'); + } + + if (!$msg->containsErrors()) { + $_POST['name'] = $addslashes($_POST['name']); + //This will truncate the content of the length to 240 as defined in the db. + $_POST['name'] = validate_length($_POST['name'], 250); + + $sql = "INSERT INTO ".TABLE_PREFIX."faq_topics VALUES (NULL, $_SESSION[course_id], '$_POST[name]')"; + $result = mysql_query($sql,$db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index_instructor.php'); + exit; + } +} + +$onload = 'document.form.name.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + +
    + +
    +
    +
    + *
    + +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/faq/delete_question.php b/mods/_standard/faq/delete_question.php new file mode 100644 index 000000000..7d4b89b22 --- /dev/null +++ b/mods/_standard/faq/delete_question.php @@ -0,0 +1,61 @@ +addFeedback('CANCELLED'); + Header('Location: index_instructor.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + $_POST['id'] = intval($_POST['id']); + $_POST['topic_id'] = intval($_POST['topic_id']); + + // check that this topic_id belongs to this course: + $sql = "SELECT topic_id FROM ".TABLE_PREFIX."faq_topics WHERE topic_id=$_POST[topic_id] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $sql = "DELETE FROM ".TABLE_PREFIX."faq_entries WHERE entry_id=$_POST[id] AND topic_id=$_POST[topic_id]"; + $result = mysql_query($sql, $db); + } + + $msg->addFeedback('QUESTION_DELETED'); + header('Location: index_instructor.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +$_GET['id'] = intval($_GET['id']); + +$sql = "SELECT question, topic_id FROM ".TABLE_PREFIX."faq_entries WHERE entry_id=$_GET[id]"; + +$result = mysql_query($sql,$db); +if ($row = mysql_fetch_assoc($result)) { + $hidden_vars['topic_id'] = $row['topic_id']; + $hidden_vars['id'] = $_GET['id']; + + $confirm = array('DELETE_FAQ_QUESTION', htmlentities_utf8($row['question'])); + $msg->addConfirm($confirm, $hidden_vars); + $msg->printConfirm(); +} else { + $msg->addError('ITEM_NOT_FOUND'); +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/faq/delete_topic.php b/mods/_standard/faq/delete_topic.php new file mode 100644 index 000000000..1c32fdd12 --- /dev/null +++ b/mods/_standard/faq/delete_topic.php @@ -0,0 +1,58 @@ +addFeedback('CANCELLED'); + Header('Location: index_instructor.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + $_POST['id'] = intval($_POST['id']); + + // check that this topic_id belongs to this course: + $sql = "DELETE FROM ".TABLE_PREFIX."faq_topics WHERE topic_id=$_POST[id] AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + if (mysql_affected_rows($db) == 1) { + $sql = "DELETE FROM ".TABLE_PREFIX."faq_entries WHERE topic_id=$_POST[topic_id]"; + $result = mysql_query($sql, $db); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index_instructor.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +$_GET['id'] = intval($_GET['id']); + +$sql = "SELECT name, topic_id FROM ".TABLE_PREFIX."faq_topics WHERE topic_id=$_GET[id]"; + +$result = mysql_query($sql,$db); +if ($row = mysql_fetch_assoc($result)) { + $hidden_vars['id'] = $_GET['id']; + + $confirm = array('DELETE_FAQ_TOPIC', htmlentities_utf8($row['name'])); + $msg->addConfirm($confirm, $hidden_vars); + $msg->printConfirm(); +} else { + $msg->addError('ITEM_NOT_FOUND'); +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/faq/edit_question.php b/mods/_standard/faq/edit_question.php new file mode 100644 index 000000000..c463f27ed --- /dev/null +++ b/mods/_standard/faq/edit_question.php @@ -0,0 +1,133 @@ +addFeedback('CANCELLED'); + header('Location: index_instructor.php'); + exit; +} + +if (isset($_GET['id'])) { + $id = intval($_GET['id']); +} else { + $id = intval($_POST['id']); +} + +if (isset($_POST['submit'])) { + $_POST['question'] = trim($_POST['question']); + $_POST['answer'] = trim($_POST['answer']); + + $missing_fields = array(); + + if (!$_POST['question']) { + $missing_fields[] = _AT('question'); + } + + if (!$_POST['answer']) { + $missing_fields[] = _AT('answer'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) { + $_POST['question'] = $addslashes($_POST['question']); + $_POST['answer'] = $addslashes($_POST['answer']); + $_POST['topic_id'] = intval($_POST['topic_id']); + //These will truncate the content of the length to 240 as defined in the db. + $_POST['question'] = validate_length($_POST['question'], 250); + $_POST['answer'] = validate_length($_POST['answer'], 250); + + $sql = "UPDATE ".TABLE_PREFIX."faq_entries SET question='$_POST[question]', answer='$_POST[answer]', topic_id=$_POST[topic_id] WHERE entry_id=$id"; + $result = mysql_query($sql,$db); + + $msg->addFeedback('QUESTION_UPDATED'); + header('Location: index_instructor.php'); + exit; + } +} +$onload = 'document.form.topic.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($id == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$sql = "SELECT * FROM ".TABLE_PREFIX."faq_entries WHERE entry_id=$id"; +$result = mysql_query($sql,$db); +if (!($row = mysql_fetch_assoc($result))) { + $msg->printErrors('ITEM_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + + +$sql = "SELECT name, topic_id FROM ".TABLE_PREFIX."faq_topics WHERE course_id=$_SESSION[course_id] ORDER BY name"; +$result = mysql_query($sql, $db); +$num_topics = mysql_num_rows($result); +if (!$num_topics) { + $msg->printErrorS('NO_FAQ_TOPICS'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +?> + +
    + + +
    +
    +
    + + + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    +
    + \ No newline at end of file diff --git a/mods/_standard/faq/edit_topic.php b/mods/_standard/faq/edit_topic.php new file mode 100644 index 000000000..f3216bc72 --- /dev/null +++ b/mods/_standard/faq/edit_topic.php @@ -0,0 +1,89 @@ +addFeedback('CANCELLED'); + header('Location: index_instructor.php'); + exit; +} + +if (isset($_GET['id'])) { + $id = intval($_GET['id']); +} else { + $id = intval($_POST['id']); +} + +if (isset($_POST['submit'])) { + if (trim($_POST['name']) == '') { + $msg->addError('NAME_EMPTY'); + } + + if (!$msg->containsErrors()) { + $_POST['name'] = $addslashes($_POST['name']); + //This will truncate the content of the length to 240 as defined in the db. + $_POST['name'] = validate_length($_POST['name'], 250); + + $sql = "UPDATE ".TABLE_PREFIX."faq_topics SET name='$_POST[name]' WHERE topic_id=$id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql,$db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index_instructor.php'); + exit; + } +} +$onload = 'document.form.name.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($id == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$sql = "SELECT name FROM ".TABLE_PREFIX."faq_topics WHERE course_id=$_SESSION[course_id] AND topic_id=$id ORDER BY name"; +$result = mysql_query($sql, $db); +if (!$row = mysql_fetch_assoc($result)) { + $msg->printErrorS('ITEM_NOT_FOUND'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} else if (!isset($_POST['name'])) { + $_POST['name'] = $row['name']; +} + +?> + +
    + + +
    +
    +
    + *
    + +
    + +
    + + +
    +
    +
    +
    + \ No newline at end of file diff --git a/mods/_standard/faq/icon.gif b/mods/_standard/faq/icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..56ae823123136bb18f482f65f9af0c7e03953650 GIT binary patch literal 502 zcmV_wErP@2Y3o5x$A$!4a@XsFI{tj=_= z(tozre6rDpxYUxq*O$ZHqRQc%!`ZFS<)_Kqx!35j&)?AG@Z9V4^Z5I{)#Aw8<a9I-gDjNOqg&GVD8-y8- z02nC%nGA#-h_ZGXnE@z`nF$&ikrhr3s+j|eBTU9m1_n(Hk#(M#4=2l85rwP93MLlU zSrvtc*WTby6NSRy=I4){?j{)S1H^U_?g%H!;MsQ0=c<{I;LgmMGxR`_zyOGz!)_=9 zoYO!^&9wr^N<08Z(T>Gs6j;Dn*0IjVOd(ry5Hwbj&dDtWEWnVMsilXPSrlYk@h#JV z1VDiz(gHxh3Z$u{o?3uT)qc+l6c;lqd%D_+dFF_I7fJNg~!@c;k- literal 0 HcmV?d00001 diff --git a/mods/_standard/faq/index.php b/mods/_standard/faq/index.php new file mode 100644 index 000000000..6d6b039b8 --- /dev/null +++ b/mods/_standard/faq/index.php @@ -0,0 +1,52 @@ + + + +
      + +
    • + + +
        + + +
      1. +

        +

        +
      2. + + +
      + +

      + +
    • + +
    + + + + + \ No newline at end of file diff --git a/mods/_standard/faq/index_instructor.php b/mods/_standard/faq/index_instructor.php new file mode 100644 index 000000000..190de571a --- /dev/null +++ b/mods/_standard/faq/index_instructor.php @@ -0,0 +1,94 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$counter = 1; +$sql = "SELECT name, topic_id FROM ".TABLE_PREFIX."faq_topics WHERE course_id=$_SESSION[course_id] ORDER BY name"; +$result = mysql_query($sql, $db); +?> + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    +
     
    +
    + + \ No newline at end of file diff --git a/mods/_standard/faq/module.php b/mods/_standard/faq/module.php new file mode 100644 index 000000000..0b98aacc9 --- /dev/null +++ b/mods/_standard/faq/module.php @@ -0,0 +1,43 @@ +getPrivilege()); + +// if this module is to be made available to students on the Home or Main Navigation +$_student_tool = 'mods/_standard/faq/index.php'; + +//modules sub-content +$this->_list['faq'] = array('title_var'=>'faq','file'=>'mods/_standard/faq/sublinks.php'); + +// instructor Manage section: +$this->_pages['mods/_standard/faq/index_instructor.php']['title_var'] = 'faq'; +$this->_pages['mods/_standard/faq/index_instructor.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_standard/faq/index_instructor.php']['children'] = array('mods/_standard/faq/add_topic.php', 'mods/_standard/faq/add_question.php'); +$this->_pages['mods/_standard/faq/index_instructor.php']['guide'] = 'instructor/?p=faq.php'; + + + $this->_pages['mods/_standard/faq/add_topic.php']['title_var'] = 'add_topic'; + $this->_pages['mods/_standard/faq/add_topic.php']['parent'] = 'mods/_standard/faq/index_instructor.php'; + + $this->_pages['mods/_standard/faq/delete_topic.php']['title_var'] = 'delete'; + $this->_pages['mods/_standard/faq/delete_topic.php']['parent'] = 'mods/_standard/faq/index_instructor.php'; + + $this->_pages['mods/_standard/faq/edit_topic.php']['title_var'] = 'edit'; + $this->_pages['mods/_standard/faq/edit_topic.php']['parent'] = 'mods/_standard/faq/index_instructor.php'; + + $this->_pages['mods/_standard/faq/add_question.php']['title_var'] = 'add_question'; + $this->_pages['mods/_standard/faq/add_question.php']['parent'] = 'mods/_standard/faq/index_instructor.php'; + + $this->_pages['mods/_standard/faq/delete_question.php']['title_var'] = 'delete'; + $this->_pages['mods/_standard/faq/delete_question.php']['parent'] = 'mods/_standard/faq/index_instructor.php'; + + $this->_pages['mods/_standard/faq/edit_question.php']['title_var'] = 'edit'; + $this->_pages['mods/_standard/faq/edit_question.php']['parent'] = 'mods/_standard/faq/index_instructor.php'; + +// student page: +$this->_pages['mods/_standard/faq/index.php']['title_var'] = 'faq'; +$this->_pages['mods/_standard/faq/index.php']['img'] = 'images/home-faq.png'; +$this->_pages['mods/_standard/faq/index.php']['icon'] = 'images/home-faq_sm.png'; + +?> \ No newline at end of file diff --git a/mods/_standard/faq/module.xml b/mods/_standard/faq/module.xml new file mode 100644 index 000000000..2f75b7430 --- /dev/null +++ b/mods/_standard/faq/module.xml @@ -0,0 +1,19 @@ + + + Frequently Asked Questions (FAQ) + Adds course-specific FAQs which instructors can manage. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + 2005-10-12 + stable + + + \ No newline at end of file diff --git a/mods/_standard/faq/module_backup.php b/mods/_standard/faq/module_backup.php new file mode 100644 index 000000000..b36b54130 --- /dev/null +++ b/mods/_standard/faq/module_backup.php @@ -0,0 +1,29 @@ + \ No newline at end of file diff --git a/mods/_standard/faq/module_delete.php b/mods/_standard/faq/module_delete.php new file mode 100644 index 000000000..69bd5d049 --- /dev/null +++ b/mods/_standard/faq/module_delete.php @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/mods/_standard/faq/module_news.php b/mods/_standard/faq/module_news.php new file mode 100644 index 000000000..44cfa228f --- /dev/null +++ b/mods/_standard/faq/module_news.php @@ -0,0 +1,42 @@ + + */ +function faq_news() { + global $db, $enrolled_courses, $system_courses; + $news = array(); + + if ($enrolled_courses == ''){ + return $news; + } + + $sql = "SELECT * FROM ".TABLE_PREFIX."faq_topics T INNER JOIN ".TABLE_PREFIX."faq_entries E ON T.topic_id = E.topic_id WHERE T.course_id IN $enrolled_courses ORDER BY E.revised_date DESC"; + $result = mysql_query($sql, $db); + if($result){ + while($row = mysql_fetch_assoc($result)){ + $news[] = array('time'=>$row['revised_date'], + 'alt'=>_AT('faq'),'object'=>$row, + 'course'=>$system_courses[$row['course_id']]['title'], + 'thumb'=>'images/home-faq_sm.png', + 'link'=>' SUBLINK_TEXT_LEN ? ' title="'.$row['question'].'"' : '') .'>'. + validate_length($row['question'], SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''); + } + } + return $news; +} + +?> \ No newline at end of file diff --git a/mods/_standard/faq/sublinks.php b/mods/_standard/faq/sublinks.php new file mode 100644 index 000000000..8887dd7ac --- /dev/null +++ b/mods/_standard/faq/sublinks.php @@ -0,0 +1,24 @@ + 0) { + while ($row = mysql_fetch_assoc($result)) { + $list[] = ' SUBLINK_TEXT_LEN ? ' title="'.$row['question'].'"' : '') .'>'. + validate_length($row['question'], SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''; + } + return $list; +} else { + return 0; +} + + +?> \ No newline at end of file diff --git a/mods/_standard/farchive/forum_post.php b/mods/_standard/farchive/forum_post.php new file mode 100755 index 000000000..a9445e377 --- /dev/null +++ b/mods/_standard/farchive/forum_post.php @@ -0,0 +1,94 @@ +'._AT('reply').''; + +?> + +
  • + + + +
    + +
    +

    +
    +

    + +
    +

    +
    +
    +
  • + + "; + +$head .= "Exported Forum"; + +$tmpfile = AT_CONTENT_DIR."/exported_forum.html"; +$main = fopen($tmpfile, "w"); + +//---- Header---- +fwrite($main, $head); +fwrite($main, "

    ".$forum_title."


    "); +fwrite($main, "
    "); + +$filearr = array(); // will hold all the files that were created + +$sql = "SELECT *, DATE_FORMAT(date, '%Y-%m-%d %H-%i:%s') AS date, UNIX_TIMESTAMP(date) AS udate FROM ".TABLE_PREFIX."forums_threads WHERE parent_id=0 AND forum_id=".$forum_id." ORDER BY date ASC LIMIT 0, 70"; +$result = mysql_query($sql) or die(mysql_error()); + +// Print out each post for each thread +while ($row = mysql_fetch_array($result)) { + + + $handle = fopen(AT_CONTENT_DIR."/t-".$row['post_id'].".html", "w"); + array_push($filearr, "t-".$row['post_id'].".html"); + + fwrite($main, "".$row['subject']."
    "); + fwrite($handle, $head); + fwrite($handle, "

    "); + fwrite($handle, "


    ".$row['subject']."


    "); + fwrite($handle, "
      "); + + $sql = "SELECT *, DATE_FORMAT(date, '%Y-%m-%d %H-%i:%s') AS date, UNIX_TIMESTAMP(date) AS udate FROM ".TABLE_PREFIX."forums_threads WHERE parent_id=".$row['post_id']." AND forum_id=".$forum_id." ORDER BY date ASC LIMIT 0, 70"; + + $result_post = mysql_query($sql, $db); + ob_start(); + + print_entry2($row); + + while ($post_row = mysql_fetch_assoc($result_post)) { + print_entry2($post_row); + } + fwrite($handle, ob_get_contents()); + ob_end_clean(); + fwrite($handle, "
    "); + fwrite($handle, ""); + fclose($handle); +} +fwrite($main, "
    "); + +?> \ No newline at end of file diff --git a/mods/_standard/farchive/index.php b/mods/_standard/farchive/index.php new file mode 100755 index 000000000..909c1f118 --- /dev/null +++ b/mods/_standard/farchive/index.php @@ -0,0 +1,11 @@ + + +
    + There's nothing here! :) +
    + + \ No newline at end of file diff --git a/mods/_standard/farchive/index_instructor.php b/mods/_standard/farchive/index_instructor.php new file mode 100755 index 000000000..8203c5f83 --- /dev/null +++ b/mods/_standard/farchive/index_instructor.php @@ -0,0 +1,124 @@ +addInfo('NO_FORUMS'); + require (AT_INCLUDE_PATH.'header.inc.php'); + exit; +} + +require (AT_INCLUDE_PATH.'header.inc.php'); +?> + +
    +
    +
    +

    +
    +
    + +
    +
    + + ' onclick='submForm()' /> +
    +
    +
    + + + + + + diff --git a/mods/_standard/farchive/module.php b/mods/_standard/farchive/module.php new file mode 100755 index 000000000..d7fc7da28 --- /dev/null +++ b/mods/_standard/farchive/module.php @@ -0,0 +1,28 @@ +getPrivilege()); +//define('AT_ADMIN_PRIV_FARCHIVE', $this->getAdminPrivilege()); + +/******* + * instructor Manage section: + */ + +$this->_pages['mods/_standard/farchive/index_instructor.php']['title_var'] = 'farchive_export'; +$this->_pages['mods/_standard/farchive/index_instructor.php']['parent'] = 'mods/_standard/forums/index.php'; +$this->_pages['mods/_standard/farchive/index_instructor.php']['guide'] = 'instructor/?p=forum_export.php'; +$this->_pages['mods/_standard/forums/index.php']['children'] = array('mods/_standard/farchive/index_instructor.php'); + + +?> \ No newline at end of file diff --git a/mods/_standard/farchive/module.xml b/mods/_standard/farchive/module.xml new file mode 100755 index 000000000..d12ef9fac --- /dev/null +++ b/mods/_standard/farchive/module.xml @@ -0,0 +1,19 @@ + + + Forum Archiver + This module adds the 'Export Forum' link for forums, under 'Manage'. This link will allow you to download a zip file that contains the entire forum, seperated by each thread, outputted to individual html files. + + + Martin + info@atutor.ca + + + http://atutor.ca + BSD + + 0.1 + 2008-02-22 + beta + + + \ No newline at end of file diff --git a/mods/_standard/farchive/send_zip_archive.php b/mods/_standard/farchive/send_zip_archive.php new file mode 100755 index 000000000..0491eb56f --- /dev/null +++ b/mods/_standard/farchive/send_zip_archive.php @@ -0,0 +1,35 @@ +add_file(file_get_contents(AT_CONTENT_DIR.'/exported_forum.html'), 'exported_forum.html'); + +foreach($filearr as $val) { + $zipfile->add_file(file_get_contents(AT_CONTENT_DIR.'/'.$val), $val); + unlink(AT_CONTENT_DIR.'/'.$val); +} + +$zipfile->add_file(file_get_contents(AT_CONTENT_DIR.'/styles.css'), 'styles.css'); +unlink(AT_CONTENT_DIR.'/styles.css'); + +// replaces spaces with underscores +if (strpos($forum_title, chr(32)) != false) { + $forum_title = str_replace(chr(32), chr(95), $forum_title); +} +$zipfile->send_file($forum_title); +exit; + +?> + + \ No newline at end of file diff --git a/mods/_standard/farchive/styles.css b/mods/_standard/farchive/styles.css new file mode 100755 index 000000000..8854f3145 --- /dev/null +++ b/mods/_standard/farchive/styles.css @@ -0,0 +1,88 @@ +/* main body attributes */ +body { + font-family: Helevetica, Arial, sans-serif; + margin-top: 0px; + margin-left: 0px; + margin-right: 0px; + /* max-width: 760px; */ + margin-bottom: 0px; + font-size: small; + background-color: white; + /*border-right: 1px solid #788CB3;*/ +} + +a.midtext { + font-family: Helevetica, Arial, sans-serif; + margin-top: 0px; + margin-left: 0px; + margin-right: 0px; + /* max-width: 760px; */ + margin-bottom: 0px; + font-size: medium; + background-color: white; +} + +html,body { + height: 100%; +} + +/* heading attributes */ +h1 { + font-family: trebuchet ms, Arial, sans-serif; + margin-bottom: 10px; + margin-top: 10px; + margin-left: 5px; + margin-right: 0px; + font-size: large; + color: #384F89; /* #152065; #D15600; */ +} + +h2, h3, h4, h5, h6 { + font-family: trebuchet ms, Arial, sans-serif; + margin-bottom: 0px; + margin-top: 0px; + margin-left: 5px; + margin-right: 0px; + font-size: medium; + color: #384F89; /* #152065; #D15600; */ +} + +/* paragraph attributes */ +p { + font-family: Helevetica, Arial, sans-serif; + margin-bottom: 10px; + margin-top: 0px; + margin-left: 5px; + margin-right: 0px; +} + + +/** forum stuff **/ +ul.forum-thread { margin-bottom: 50px; } +ul.forum-thread { margin-bottom: 50px; } +ul.forum-thread li { clear:left; border:2px solid #ccc; border-bottom: 2px solid #ccc; float:left; width: 95%; list-style: none;} /* #eee, #f0f0f0*/ +ul.forum-thread li.even { background: #eeeeee; } /* #fdfdfd border-top: none; */ +ul.forum-thread li.odd { background: #f5f5f5; } /* #fff */ +div.forum-post-author { float:left; width:150px; padding:8px 10px; } +div.forum-post-author label.title { font-size: 1.1em; line-height: 1.2em; font-weight: bold; text-decoration:none; color: #384F89; } +div.forum-post-author img.profile-picture { border: 2px solid #f0f0f0; } +div.forum-post-content { margin-left: 150px; padding: 5px 0px 12px 8px; } +div.forum-post-content h3 { font-weight: 500; } +div.forum-post-ctrl { float: right; padding-right: 5px; color: #a1a1a1; } +div.forum-post-ctrl a { text-decoration: none; } +div.forum-post-ctrl span { color: black; background-color: #fefdc2; padding: 3px; } +div.forum-post-content div.postheader { border-bottom: 1px solid #ccc; font-weight: 500; } /* #f0f0f0*/ +div.forum-post-content div.date { float: right; color: #aaa; } /* #a1a1a1*/ +div.forum-post-content div.body p { margin-bottom:0px; } + +div.threadlist { margin-left: 50px; margin-top: 10px; margin-right: 100px; padding: 30px 30px 30px 30px; + border:2px solid #ccc; background: #f5f5f5; font-size: medium; } + + + + + + +/** inbox stuff - reuses some of the forum layout **/ +#inbox-msg li { clear:left; border:1px solid #eee; border-bottom: 1px solid #f0f0f0; width: 98%; list-style: none; } + diff --git a/mods/_standard/file_storage/assignment.php b/mods/_standard/file_storage/assignment.php new file mode 100644 index 000000000..4e07230c7 --- /dev/null +++ b/mods/_standard/file_storage/assignment.php @@ -0,0 +1,151 @@ +addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.abs($_POST['folder']), AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit'])) { + $_POST['assignment'] = abs($_POST['assignment']); + $assignment_row = fs_get_assignment($_POST['assignment']); + + if (!$assignment_row) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; + } + + if (!$assignment_row['assign_to']) { + if (!$_SESSION['enroll']) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; + } + + } else { + $sql = "SELECT group_id FROM ".TABLE_PREFIX."groups WHERE group_id=$owner_id AND type_id=$assignment_row[assign_to]"; + $result = mysql_query($sql, $db); + if (!$row = mysql_fetch_assoc($result)) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; + } + } + + if ($assignment_row['u_date_cutoff'] && ($assignment_row['u_date_cutoff'] < time())) { + $msg->addError('ASSIGNMENT_CUTOFF'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$_POST['folder'], AT_PRETTY_URL_IS_HEADER)); + exit; + } + + foreach ($_POST['files'] as $file) { + $file = abs($file); + fs_copy_file($file, $owner_type, $owner_id, WORKSPACE_ASSIGNMENT, $_POST['assignment'], $owner_id); + } + + $msg->addFeedback('ASSIGNMENT_HANDED_IN'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$_POST['folder'], AT_PRETTY_URL_IS_HEADER)); + exit; +} + +// get all the assignments assigned to $owner_id (which is either a student ID or a group type ID) +if ($owner_type == WORKSPACE_GROUP) { + // get all the assignments assigned to this group type + + $sql = "SELECT type_id FROM ".TABLE_PREFIX."groups WHERE group_id=$owner_id LIMIT 1"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $sql = "SELECT assignment_id, title, date_due, date_cutoff FROM ".TABLE_PREFIX."assignments WHERE assign_to=$row[type_id] AND course_id=$_SESSION[course_id] AND (date_cutoff=0 OR UNIX_TIMESTAMP(date_cutoff) > ".time().") ORDER BY title"; + +} else if ($owner_type == WORKSPACE_PERSONAL) { + // get all the assignments assigned to students + + $sql = "SELECT assignment_id, title, date_due FROM ".TABLE_PREFIX."assignments WHERE assign_to=0 AND course_id=$_SESSION[course_id] AND (date_cutoff=0 OR UNIX_TIMESTAMP(date_cutoff) > ".time().") ORDER BY title"; +} else { + exit('wrong workspace'); +} + +$assignments = array(); +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $assignments[] = $row; +} + +if (!$assignments) { + $msg->addError('NO_ASSIGNMENTS_FOUND'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$_GET['folder'], AT_PRETTY_URL_IS_HEADER)); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    + + $file): ?> + + + + +
    + +
    + *
    + +
    + +
    + +
      + + +
    • + +
    +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/file_storage/comments.php b/mods/_standard/file_storage/comments.php new file mode 100644 index 000000000..75afcc011 --- /dev/null +++ b/mods/_standard/file_storage/comments.php @@ -0,0 +1,198 @@ +addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +if (isset($_GET['done'])) { + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.abs($_GET['folder']), AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_GET['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.abs($_GET['folder']), AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['edit_cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/comments.php'.$owner_arg_prefix.'id='.$_GET['id'], AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['edit_submit'])) { + $_POST['comment'] = trim($_POST['comment']); + $_POST['comment_id'] = abs($_POST['comment_id']); + + if (!$_POST['edit_comment']) { + $msg->addError(array('EMPTY_FIELDS', _AT('comments'))); + } + + if (!$msg->containsErrors()) { + $_POST['edit_comment'] = $addslashes($_POST['edit_comment']); + + $sql = "UPDATE ".TABLE_PREFIX."files_comments SET comment='$_POST[edit_comment]', date=date WHERE member_id=$_SESSION[member_id] AND comment_id=$_POST[comment_id]"; + mysql_query($sql, $db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.url_rewrite('mods/_standard/file_storage/comments.php'.$owner_arg_prefix.'id='.$_GET['id'], AT_PRETTY_URL_IS_HEADER)); + exit; + } +} else if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$_POST['folder'], AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit'])) { + $_POST['comment'] = trim($_POST['comment']); + $_POST['id'] = abs($_POST['id']); + + if (!$_POST['comment']) { + $msg->addError(array('EMPTY_FIELDS', _AT('comments'))); + } + + if (!$msg->containsErrors()) { + $_POST['comment'] = $addslashes($_POST['comment']); + + $sql = "INSERT INTO ".TABLE_PREFIX."files_comments VALUES (NULL, $_POST[id], $_SESSION[member_id], NOW(), '$_POST[comment]')"; + if (mysql_query($sql, $db)) { + $sql = "UPDATE ".TABLE_PREFIX."files SET num_comments=num_comments+1, date=date WHERE file_id=$_POST[id]"; + mysql_query($sql, $db); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.url_rewrite('mods/_standard/file_storage/comments.php'.$owner_arg_prefix.'id='.$_POST['id'], AT_PRETTY_URL_IS_HEADER)); + exit; + } + $_GET['id'] = $_POST['id']; +} + +if (isset($_GET['comment_id'])) { + $onload = 'document.form.edit_comment.focus();'; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$id = abs($_GET['id']); + +$files = fs_get_revisions($id, $owner_type, $owner_id); +if (!$files) { + $msg->printErrors('FILE_NOT_FOUND'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +?> + + +
    + + +
    +
    + +
    +
    + + +
    +
    + +
    + + + + +
    +
    +

    -

    + - +

    +
    +
    + + + +
    + +
    + +
    +

    -

    + +
    +
    + + +
    +
    + + +
    +

    -

    +

    + +
    + | +
    + +
    + +
    + + +
    +
    +

    +
    +
    + + + +
    + + +
    +
    + *
    + +
    + +
    + + +
    +
    +
    + + + \ No newline at end of file diff --git a/mods/_standard/file_storage/delete_comment.php b/mods/_standard/file_storage/delete_comment.php new file mode 100644 index 000000000..00c393b28 --- /dev/null +++ b/mods/_standard/file_storage/delete_comment.php @@ -0,0 +1,62 @@ +addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +$_pages['mods/_standard/file_storage/delete_comment.php']['parent'] = 'mods/_standard/file_storage/comments.php' . $owner_arg_prefix.'id='.$_GET['file_id']; +$_pages['mods/_standard/file_storage/comments.php' . $owner_arg_prefix.'id='.$_GET['file_id']]['title_var'] = 'comments'; +$_pages['mods/_standard/file_storage/comments.php' . $owner_arg_prefix.'id='.$_GET['file_id']]['parent'] = 'mods/_standard/file_storage/index.php'; + +$id = abs($_REQUEST['id']); + +if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/comments.php'.$owner_arg_prefix.'id='.$_POST['file_id'], AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit_yes'])) { + $_POST['file_id'] = abs($_POST['file_id']); + $_POST['id'] = abs($_POST['id']); + + $sql = "DELETE FROM ".TABLE_PREFIX."files_comments WHERE file_id=$_POST[file_id] AND comment_id=$_POST[id] AND member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + if (mysql_affected_rows($db) == 1) { + $sql = "UPDATE ".TABLE_PREFIX."files SET num_comments=num_comments-1, date=date WHERE owner_type=$owner_type AND owner_id=$owner_id AND file_id=$_POST[file_id]"; + $result = mysql_query($sql, $db); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.url_rewrite('mods/_standard/file_storage/comments.php'.$owner_arg_prefix.'id='.$_POST['file_id'], AT_PRETTY_URL_IS_HEADER)); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$hidden_vars = array('id' => $id, 'ot' => $owner_type, 'oid' => $owner_id, 'file_id' => $_GET['file_id']); +$msg->addConfirm(array('DELETE'), $hidden_vars); +$msg->printConfirm(); + + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/file_storage/delete_revision.php b/mods/_standard/file_storage/delete_revision.php new file mode 100644 index 000000000..662641b09 --- /dev/null +++ b/mods/_standard/file_storage/delete_revision.php @@ -0,0 +1,91 @@ +addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/revisions.php'.$owner_arg_prefix.'id='.$first['file_id'], AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit_yes'])) { + $path = fs_get_revisions($id, $owner_type, $owner_id); + + // set the new parent // + $sql = "SELECT parent_file_id, owner_type, owner_id, folder_id FROM ".TABLE_PREFIX."files WHERE file_id=$id AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $sql = "UPDATE ".TABLE_PREFIX."files SET parent_file_id=$row[parent_file_id], date=date WHERE parent_file_id=$id AND owner_type=$owner_type AND owner_id=$owner_id"; + mysql_query($sql, $db); + + $sql = "UPDATE ".TABLE_PREFIX."files SET num_revisions=num_revisions-1, date=date WHERE file_id>$id AND owner_type=$row[owner_type] AND owner_id=$row[owner_id] AND folder_id=$row[folder_id]"; + mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."files WHERE file_id=$id AND owner_type=$owner_type AND owner_id=$owner_id"; + mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."files_comments WHERE file_id=$id"; + mysql_query($sql, $db); + + $file = fs_get_file_path($id); + if (file_exists($file . $id)) { + @unlink($file . $id); + } + + $back_id = FALSE; + foreach($path as $file) { + if ($file['file_id'] != $id) { + $back_id = $file['file_id']; + break; + } + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + if ($back_id) { + header('Location: '.url_rewrite('mods/_standard/file_storage/revisions.php'.$owner_arg_prefix.'id='.$back_id, AT_PRETTY_URL_IS_HEADER)); + } else { + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix, AT_PRETTY_URL_IS_HEADER)); + } + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$sql = "SELECT file_id, file_name, owner_type, owner_id, date, member_id FROM ".TABLE_PREFIX."files WHERE file_id=$id AND owner_type=$owner_type AND owner_id=$owner_id"; +$result = mysql_query($sql, $db); +if (!$row = mysql_fetch_assoc($result)) { + $msg->printErrors('FILE_NOT_EXIST'); +} else { + $hidden_vars = array('id' => $id, 'ot' => $owner_type, 'oid' => $owner_id); + $msg->addConfirm(array('FILE_DELETE', '
  • '.$row['date'].' - '. $row['file_name'].' - '.get_display_name($row['member_id']).'
  • '), $hidden_vars); + $msg->printConfirm(); +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/file_storage/edit.php b/mods/_standard/file_storage/edit.php new file mode 100644 index 000000000..7a964dfbf --- /dev/null +++ b/mods/_standard/file_storage/edit.php @@ -0,0 +1,222 @@ +addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.abs($_POST['folder']), AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit'])) { + $_POST['id'] = abs($_POST['id']); + + + if (!$_POST['name']) { + $msg->addError('MISSING_FILENAME'); + } + + if (!$msg->containsErrors()) { + $_POST['name'] = $addslashes($_POST['name']); + $_POST['comment'] = $addslashes(trim($_POST['comment'])); + $_POST['description'] = $addslashes(trim($_POST['description'])); + $_POST['body'] = $stripslashes($_POST['body']); // saved to disk not db so no need to escape. + $original_file = fs_get_file_path($_POST['id']); + $folder = abs($_POST['folder']); + + if (!$_POST['edit'] || (file_get_contents($original_file . $_POST['id']) == $_POST['body'])) { + // file is not editable ,or it is editable but no changes made. + // only add the comment (if any) and the file name + + $num_comments = 0; + + if ($_POST['comment']){ + $sql = "INSERT INTO ".TABLE_PREFIX."files_comments VALUES (NULL, $_POST[id], $_SESSION[member_id], NOW(), '{$_POST['comment']}')"; + mysql_query($sql, $db); + + $num_comments = 1; + } + + $sql = "UPDATE ".TABLE_PREFIX."files SET file_name='$_POST[name]', description='$_POST[description]', num_comments=num_comments+$num_comments, date=date WHERE file_id=$_POST[id] AND owner_type=$owner_type AND owner_id=$owner_id"; + mysql_query($sql, $db); + } else { + // this file is editable, and has changed + + $size = strlen($_POST['body']); + + if ($_POST['comment']) { + $num_comments = 1; + } else { + $num_comments = 0; + } + $sql = "SELECT * FROM ".TABLE_PREFIX."files WHERE file_id=$_POST[id] AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + if ($_config['fs_versioning']) { + $sql = "INSERT INTO ".TABLE_PREFIX."files VALUES (NULL, {$row['owner_type']}, {$row['owner_id']}, $_SESSION[member_id], {$row['folder_id']}, 0, NOW(), $num_comments, {$row['num_revisions']}+1, '{$_POST['name']}', $size, '$_POST[description]')"; + $result = mysql_query($sql, $db); + + $file_id = mysql_insert_id($db); + + $file_path = fs_get_file_path($file_id); + if ($fp = fopen($file_path . $file_id, 'wb')) { + ftruncate($fp, 0); + fwrite($fp, $_POST['body'], $size); + fclose($fp); + + $sql = "UPDATE ".TABLE_PREFIX."files SET parent_file_id=$file_id, date=date WHERE file_id=$_POST[id] AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + + if ($_POST['comment']){ + $sql = "INSERT INTO ".TABLE_PREFIX."files_comments VALUES (NULL, $file_id, $_SESSION[member_id], NOW(), '{$_POST['comment']}')"; + mysql_query($sql, $db); + } + } + } else { + $file_path = fs_get_file_path($_POST['id']); + if ($fp = fopen($file_path . $_POST['id'], 'wb')) { + ftruncate($fp, 0); + fwrite($fp, $_POST['body'], $size); + fclose($fp); + } + } + } + $msg->addFeedback('FILE_EDITED_SUCCESSFULLY'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$folder, AT_PRETTY_URL_IS_HEADER)); + exit; + } + + $_GET['id'] = $_POST['id']; +} + +$onload = 'document.form.name.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (($_POST['setvisual'] && !$_POST['settext']) || $_GET['setvisual']) { + require(AT_INCLUDE_PATH.'lib/tinymce.inc.php'); + + load_editor(false, 'body'); +} + +$id = abs($_REQUEST['id']); + +$sql = "SELECT file_name, folder_id, description FROM ".TABLE_PREFIX."files WHERE file_id=$id AND owner_type=$owner_type AND owner_id=$owner_id"; +$result = mysql_query($sql, $db); +if (!$row = mysql_fetch_assoc($result)) { + $msg->printErrors('FILE_NOT_EXIST'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +if (isset($_POST['description'])) { + $row['description'] = $stripslashes($_POST['description']); + $row['file_name'] = $stripslashes($_POST['name']); + $row['comment'] = $stripslashes($_POST['comment']); + $_POST['body'] = $stripslashes($_POST['body']); +} +$ext = fs_get_file_extension($row['file_name']); +$file_path = fs_get_file_path($id); +?> + +
    + + + +
    +
    + *
    + +
    + +
    +
    + +
    + + + +
    +
    + +
    + +
    + '; + echo ''; + } else { + echo ''; + } + ?> +
    + +
    +
    + +
    + +
    +
    + +
    +
    +
    + +
    + + + + + + + + + + + + + + <bgsound src="mods/_standard/file_storage/index.php<?php echo $owner_arg_prefix; ?>download=1<?php echo SEP; ?>files<?php echo urlencode('[]').'='.$id; ?>"> + + +
    + + + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/file_storage/edit_folder.php b/mods/_standard/file_storage/edit_folder.php new file mode 100644 index 000000000..68a996324 --- /dev/null +++ b/mods/_standard/file_storage/edit_folder.php @@ -0,0 +1,86 @@ +addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.abs($_POST['parent_folder']), AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit'])) { + $_POST['id'] = abs($_POST['id']); + + if (!$_POST['name']) { + $msg->addError(array('EMPTY_FIELDS', _AT('name'))); + } + + if (!$msg->containsErrors()) { + $_POST['name'] = $addslashes($_POST['name']); + $folder = abs($_POST['folder']); + $parent_folder = abs($_POST['parent_folder']); + + $sql = "UPDATE ".TABLE_PREFIX."folders SET title='$_POST[name]' WHERE owner_type=$owner_type AND owner_id=$owner_id AND folder_id=$_POST[id] AND parent_folder_id=$parent_folder"; + mysql_query($sql, $db); + + $msg->addFeedback('FOLDER_EDITED_SUCCESSFULLY'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$parent_folder, AT_PRETTY_URL_IS_HEADER)); + exit; + } + + $_GET['id'] = $_POST['id']; +} + +$onload = 'document.form.name.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$id = abs($_GET['id']); + +$sql = "SELECT title, parent_folder_id FROM ".TABLE_PREFIX."folders WHERE folder_id=$id"; +$result = mysql_query($sql, $db); +if (!$row = mysql_fetch_assoc($result)) { + $msg->printErrors('FOLDER_NOT_EXIST'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +?> + +
    + + +
    +
    + *
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/file_storage/file_storage.inc.php b/mods/_standard/file_storage/file_storage.inc.php new file mode 100644 index 000000000..3616f9bfa --- /dev/null +++ b/mods/_standard/file_storage/file_storage.inc.php @@ -0,0 +1,577 @@ + 0) RETURN WORKSPACE_AUTH_READ; + } + + } else if ($owner_type == WORKSPACE_GROUP) { + if (isset($_SESSION['groups'][$owner_id])) { + global $db; + $sql = "SELECT * FROM ".TABLE_PREFIX."file_storage_groups WHERE group_id=$owner_id"; + $result = mysql_query($sql, $db); + if (mysql_fetch_assoc($result)) { + return WORKSPACE_AUTH_RW; + } + } + + } else if ($owner_type == WORKSPACE_COURSE) { + if (($owner_id == $_SESSION['course_id']) && authenticate(AT_PRIV_FILE_STORAGE, AT_PRIV_RETURN)) { + return WORKSPACE_AUTH_RW; + } else if ($owner_id == $_SESSION['course_id']) { + return WORKSPACE_AUTH_READ; + } + } + /* else if ($owner_type == WORKSPACE_SYSTEM) { + if (admin_authenticate(AT_ADMIN_PRIV_FILE_STORAGE, TRUE)) { + return WORKSPACE_AUTH_RW; + } // else + return WORKSPACE_AUTH_READ; // everyone can read the System File Space + } */ + + return WORKSPACE_AUTH_NONE; +} + +/** + * returns the localised name of the specified workspace + */ +function fs_get_workspace($owner_type, $owner_id) { + if ($owner_type == WORKSPACE_PERSONAL) { + return _AT('my_files'); + + } else if ($owner_type == WORKSPACE_COURSE) { + return _AT('course_files'); + + } else if ($owner_type == WORKSPACE_GROUP) { + global $db; + $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=$owner_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + return $row['title']; + + } else if ($owner_type == WORKSPACE_ASSIGNMENT) { + $row = fs_get_assignment($owner_id); + return ($row ? $row['title'] : false); + } /* + else if ($owner_type == WORKSPACE_SYSTEM) { + return _AT('system_files'); + } + */ +} + +/** + * returns the assignment row specified by $assignment_id + * false if not found. + */ +function fs_get_assignment($assignment_id) { + global $db; + $sql = "SELECT assignment_id, title, assign_to, date_due, date_cutoff, UNIX_TIMESTAMP(date_cutoff) AS u_date_cutoff, multi_submit FROM ".TABLE_PREFIX."assignments WHERE assignment_id=$assignment_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + return $row; +} + +/** + * retrieve folder(s) specified by $folder_id + * $folder_id the ID of the single folder, or array of IDs + * if $folder_id is an array then returns an array of folder rows + * if $folder_id is an int then returns the single row array + * + * This function does not authenticate the $folder_id for the assignment. + * + * Note: This function checks if the $owner_type is an Assignment. + * + */ +function fs_get_folder_by_id($folder_id, $owner_type, $owner_id) { + global $db; + + $rows = array(); + + if ($owner_type == WORKSPACE_ASSIGNMENT) { + // get the folder row from the assignments table + + $sql = "SELECT assign_to FROM ".TABLE_PREFIX."assignments WHERE assignment_id=$owner_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + if ($row['assign_to']) { + $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=$folder_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $rows = array('title' => $row['title'], 'folder_id' => $folder_id); + } else { + $rows = array('title' => get_display_name($folder_id), 'folder_id' => $folder_id); + } + } else { + if (is_array($folder_id)) { + $folder_id_list = implode(',', $folder_id); + + $sql = "SELECT folder_id, title, parent_folder_id FROM ".TABLE_PREFIX."folders WHERE folder_id IN ($folder_id_list) AND owner_type=$owner_type AND owner_id=$owner_id ORDER BY title"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $rows[] = $row; + } + + } else { + $sql = "SELECT folder_id, title, parent_folder_id FROM ".TABLE_PREFIX."folders WHERE folder_id=$folder_id AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + $rows = mysql_fetch_assoc($result); + } + } + return $rows; +} + +/** + * retrieve folder(s) specified by $parent_folder_id + * + * Note: This function checks if the $owner_type is an Assignment. + * + */ +function fs_get_folder_by_pid($parent_folder_id, $owner_type, $owner_id) { + global $db; + + $rows = array(); + if ($owner_type == WORKSPACE_ASSIGNMENT) { + // get the folder row from the assignments table + // does not currently support sub-folders for assignments + if ($parent_folder_id == 0 && authenticate(AT_PRIV_ASSIGNMENTS, AT_PRIV_RETURN)) { + $sql = "SELECT assign_to FROM ".TABLE_PREFIX."assignments WHERE assignment_id=$owner_id AND course_id=$_SESSION[course_id]"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + if ($row['assign_to']) { + $sql = "SELECT G.group_id AS folder_id, G.title FROM ".TABLE_PREFIX."groups G INNER JOIN ".TABLE_PREFIX."file_storage_groups FS USING (group_id) WHERE G.type_id=$row[assign_to] ORDER BY G.title"; + } else { + global $system_courses; + + $sql = "SELECT E.member_id AS folder_id, M.login AS title FROM ".TABLE_PREFIX."course_enrollment E INNER JOIN ".TABLE_PREFIX."members M USING (member_id) WHERE E.course_id=$_SESSION[course_id] AND E.approved='y' AND E.privileges & ".AT_PRIV_GROUPS." = 0 AND E.member_id<>{$system_courses[$_SESSION[course_id]][member_id]} ORDER BY M.login"; + } + $result = mysql_query($sql, $db); + + while ($row = mysql_fetch_assoc($result)) { + $rows[] = $row; + } + } + } else { + $sql = "SELECT folder_id, title FROM ".TABLE_PREFIX."folders WHERE parent_folder_id=$parent_folder_id AND owner_type=$owner_type AND owner_id=$owner_id ORDER BY title"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $rows[] = $row; + } + } + return $rows; +} + +/** + * outputs the folders as a list. + * + * $current_folder_id the current folder id, used for pre-selecting the radio button + * $parent_folder_id the folder id to display children of + * $folders the array of folders returned from get_folders() + * $disable whether or not the radio button is available + */ +function fs_print_folders($current_folder_id, $parent_folder_id, &$folders, $disable = FALSE) { + if (!isset($folders[$parent_folder_id])) { + return; + } + + echo '
      '; + foreach ($folders[$parent_folder_id] as $folder_id => $folder_info) { + echo '
    • '; + + echo ''; + + fs_print_folders($current_folder_id, $folder_id, $folders, $disable); + if ($_GET['folders'] && in_array($folder_id, $_GET['folders'])) { + $disable = FALSE; + } + echo '
    • '; + } + echo '
    '; +} + +/** + * returns an array of all the revisions for the given file_id + * + * $file_id ID of a file in a revision sequence. can be any revision, does not have to be the latest. + * This function is recursive and uses fs_get_revisions_down_recursive() and fs_get_revisions_recurisve() below. + */ +function fs_get_revisions($file_id, $owner_type, $owner_id) { + global $db; + + $sql = "SELECT * FROM ".TABLE_PREFIX."files WHERE file_id=$file_id AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + return array_merge(array_reverse(fs_get_revisions_down_recursive($row['parent_file_id'])), array($row), fs_get_revisions_recursive($file_id)); + } + return array(); +} + +/** + * recursively retrieves all the revisions of the file. + * recurses DOWN the revisions path. + * PRIVATE! use fs_get_revisions() above. + */ +function fs_get_revisions_down_recursive($file_id) { + global $db; + + if ($file_id == 0) { + return array(); + } + + $sql = "SELECT * FROM ".TABLE_PREFIX."files WHERE file_id=$file_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + if (!$row) { + return array(); + } else if (!$row['parent_file_id']) { + return array($row); + } + + return array_merge(array($row), fs_get_revisions_down_recursive($row['parent_file_id'])); +} + +/** + * recursively retrieves all the revisions of the file. + * recurses UP the revisions path. + * PRIVATE! use fs_get_revisions() above. + */ +function fs_get_revisions_recursive($file_id) { + global $db; + + if ($file_id == 0) { + return array(); + } + + $sql = "SELECT * FROM ".TABLE_PREFIX."files WHERE parent_file_id=$file_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + if (!$row) { + return array(); + } + + return array_merge(array($row), fs_get_revisions_recursive($row['file_id'])); +} + +/** + * returns the full path based on $file_id with trailing slash. + * + * Ex. if file_id is 2345 and WORKSPACE_PATH_DEPTH is set to 3 then + * the path returned will be WORKSPACE_FILE_PATH.'5/4/3/' + * + * If the path does not exist within the WORKSPACE_FILE_PATH then attempts + * to create it. + */ +function fs_get_file_path($file_id) { + $end_part = substr($file_id, -WORKSPACE_PATH_DEPTH); + $path = WORKSPACE_FILE_PATH; + $dirs = max(-WORKSPACE_PATH_DEPTH, -strlen($file_id)); + $id_threshold = pow(10,WORKSPACE_PATH_DEPTH); // only check for the dir before reaching this value. + for ($i = -1; $i >= $dirs; $i--) { + $path .= substr($file_id, $i, 1) . DIRECTORY_SEPARATOR; + if ($file_id <= $id_threshold) { + if (!is_dir($path)) { + @mkdir($path); + } + } + } + + return $path; +} + +/** + * delete a given file, its revisions, and comments. + * + * $file_id the ID of the file to delete. can be any ID within a revision sequence. + */ +function fs_delete_file($file_id, $owner_type, $owner_id) { + global $db; + $revisions = fs_get_revisions($file_id, $owner_type, $owner_id); + foreach ($revisions as $file) { + $sql = "DELETE FROM ".TABLE_PREFIX."files WHERE file_id=$file[file_id] AND owner_type=$owner_type AND owner_id=$owner_id"; + mysql_query($sql, $db); + + if (mysql_affected_rows($db) == 1) { + $sql = "DELETE FROM ".TABLE_PREFIX."files_comments WHERE file_id=$file[file_id]"; + mysql_query($sql, $db); + + $path = fs_get_file_path($file['file_id']); + if (file_exists($path . $file['file_id'])) { + @unlink($path . $file['file_id']); + } + } + } +} + +/** + * returns only the extension part of the specified file name + * + * $file_name the full name of the file. + */ +function fs_get_file_extension($file_name) { + $ext = pathinfo($file_name); + return $ext['extension']; +} + +/** + * returns the image name (w/o the ".gif" ending) of the icon to use + * for the given file name. + * if no icon is specified (by mime.inc.php) then returns "generic" + */ +function fs_get_file_type_icon($file_name) { + global $mime; + if (!isset($mime)) { + require(AT_INCLUDE_PATH.'lib/mime.inc.php'); + } + $ext = fs_get_file_extension($file_name); + + if (isset($mime[$ext]) && $mime[$ext][1]) { + return $mime[$ext][1]; + } + return 'generic'; +} + +/** + * deletes the folder, its sub-folders and associated files. + * + * $folder_id the ID of the folder to delete, recursively, with content. + */ +function fs_delete_folder($folder_id, $owner_type, $owner_id) { + if (!$folder_id) { return; } + + global $db; + + $rows = fs_get_folder_by_pid($folder_id, $owner_type, $owner_id); + foreach ($rows as $row) { + fs_delete_folder($row['folder_id'], $owner_type, $owner_id); + } + + $sql = "DELETE FROM ".TABLE_PREFIX."folders WHERE folder_id=$folder_id AND owner_type=$owner_type AND owner_id=$owner_id"; + mysql_query($sql, $db); + + // delete this file's folders (we only select the latest versions because + // the delete_file() function takes care of the revisions for us + $sql = "SELECT file_id FROM ".TABLE_PREFIX."files WHERE folder_id=$folder_id AND parent_file_id=0 AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + fs_delete_file($row['file_id'], $owner_type, $owner_id); + } +} + +/** + * archives a folder into a specified zip handler. + * + * $folder_id the ID of the folder to archive recursively, with content. + * $zipfile reference to the zipFile object. + * $path the absolute path to the current folder. + */ +function fs_download_folder($folder_id, &$zipfile, $owner_type, $owner_id, $path = '') { + global $db; + + $parent_row = fs_get_folder_by_id($folder_id, $owner_type, $owner_id); + //$sql = "SELECT title FROM ".TABLE_PREFIX."folders WHERE folder_id=$folder_id AND owner_type=$owner_type AND owner_id=$owner_id"; + //$result = mysql_query($sql, $db); + if ($parent_row) { + $zipfile->create_dir($path . $parent_row['title']); + } + + $sql = "SELECT file_id, file_name, UNIX_TIMESTAMP(date) AS date FROM ".TABLE_PREFIX."files WHERE folder_id=$folder_id AND parent_file_id=0 AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $file_path = fs_get_file_path($row['file_id']) . $row['file_id']; + + $zipfile->add_file(file_get_contents($file_path), $path . $parent_row['title'] .'/' . $row['file_name'], $row['date']); + } + + $rows = fs_get_folder_by_pid($folder_id, $owner_type, $owner_id); + foreach ($rows as $row) { + fs_download_folder($row['folder_id'], $zipfile, $owner_type, $owner_id, $path . $parent_row['title'] . '/'); + } +} + +/** + * returns the full path to the current folder + * + * $folder_id the current folder + * $workspace the owner_type of this folder + * $owner_id the ID of the owner. + */ +function fs_get_folder_path($folder_id, $owner_type, $owner_id) { + $folder_path = fs_get_folder_path_recursive($folder_id, $owner_type, $owner_id); + + return array_reverse($folder_path); +} + +/** + * recursively return the path to the current folder + * PRIVATE! do not call directly, use get_folder_path() above. + */ +function fs_get_folder_path_recursive($folder_id, $owner_type, $owner_id) { + global $db; + + if ($folder_id == 0) { + return array(); + } + + $row = fs_get_folder_by_id($folder_id, $owner_type, $owner_id); + + return array_merge(array($row), fs_get_folder_path_recursive($row['parent_folder_id'], $owner_type, $owner_id)); +} + +/** + * deletes all the files, folders, comments, revisions, etc.. in the specified workspace. + */ +function fs_delete_workspace($owner_type, $owner_id) { + global $db; + + $sql = "SELECT folder_id, owner_type, owner_id FROM ".TABLE_PREFIX."folders WHERE owner_type=$owner_type AND owner_id=$owner_id AND parent_folder_id=0"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + fs_delete_folder($row['folder_id'], $row['owner_type'], $row['owner_id']); + } + + $sql = "SELECT file_id, owner_type, owner_id FROM ".TABLE_PREFIX."files WHERE owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + fs_delete_file($row['file_id'], $row['owner_type'], $row['owner_id']); + } +} + +/** + * copies a file to another workspace. + * currently only used for submitting assignments. + **/ +function fs_copy_file($file_id, $src_owner_type, $src_owner_id, $dest_owner_type, $dest_owner_id, $dest_folder_id) { + global $db; + + $sql = "SELECT file_name, file_size, description FROM ".TABLE_PREFIX."files WHERE file_id=$file_id AND owner_type=$src_owner_type AND owner_id=$src_owner_id"; + $result = mysql_query($sql, $db); + if (!$row = mysql_fetch_assoc($result)) { + return false; + } + $sql = "INSERT INTO ".TABLE_PREFIX."files VALUES (NULL, $dest_owner_type, $dest_owner_id, $_SESSION[member_id], $dest_folder_id, 0, NOW(), 0, 0, '$row[file_name]', '$row[file_size]', '$row[description]')"; + $result = mysql_query($sql, $db); + + $id = mysql_insert_id($db); + + $src_file = fs_get_file_path($file_id) . $file_id; + $dest_file = fs_get_file_path($id) . $id; + copy($src_file, $dest_file); +} + +/** + * used with usort() to sort the revisions array returned from fs_get_revisions() + * $col is a valid array key to sort by + * $order is either 'asc' or 'desc' + */ +function fs_revisions_sort_compare($a, $b) { + global $col, $order; + + if ($order == 'asc') { + return strcasecmp($a[$col], $b[$col]); + } + return strcasecmp($b[$col], $a[$col]); +} + +/** + * copies a directory to another workspace. + * not currently used anywhere. + */ +/*** +function fs_copy_folder($folder_id, $src_owner_type, $src_owner_id, $dest_owner_type, $dest_owner_id, $dest_parent_folder_id) { + global $db; + + $folder = fs_get_folder_by_id($folder_id, $src_owner_type, $src_owner_id); + if (!$folder) { + return false; + } + + $sql = "INSERT INTO ".TABLE_PREFIX."folders VALUES (0, $dest_parent_folder_id, $dest_owner_type, $dest_owner_id, '$folder[title]')"; + $result = mysql_query($sql, $db); + $id = mysql_insert_id($db); + + $sql = "SELECT file_id FROM ".TABLE_PREFIX."files WHERE folder_id=$folder_id AND owner_type=$src_owner_type AND owner_id=$src_owner_id"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + fs_copy_file($row['file_id'], $src_owner_type, $src_owner_id, $dest_owner_type, $dest_owner_id, $id); + } + + $folders = fs_get_folder_by_pid($folder_id, $src_owner_type, $src_owner_id); + foreach ($folders as $folder) { + fs_copy_folder($folder['folder_id'], $src_owner_type, $src_owner_id, $dest_owner_type, $dest_owner_id, $id); + } +} +*/ +?> \ No newline at end of file diff --git a/mods/_standard/file_storage/index.php b/mods/_standard/file_storage/index.php new file mode 100644 index 000000000..0d136004d --- /dev/null +++ b/mods/_standard/file_storage/index.php @@ -0,0 +1,742 @@ +$v){ + $_GET['folders'][$k] = abs($_GET['folders'][$k]); + } + } else { + $_GET['folders']= abs($_GET['folders']); + } +} +if (isset($_GET['files'])){ + if (is_array($_GET['files'])){ + foreach($_GET['files'] as $k=>$v){ + $_GET['files'][$k] = abs($_GET['files'][$k]); + } + } else { + $_GET['files']= abs($_GET['files']); + } +} + +if (isset($_GET['submit_workspace'])) { + unset($_GET['folder']); + unset($assignment_for); + + $owner_type = abs($_GET['ot']); + + if ($owner_type == WORKSPACE_GROUP) { + + $parts = explode('_', $_GET['ot'], 2); + if (isset($parts[1]) && $parts[1] && isset($_SESSION['groups'][$parts[1]])) { + $owner_id = $parts[1]; + } else { + $owner_type = WORKSPACE_COURSE; + unset($owner_id); + } + } else if ($owner_type == WORKSPACE_ASSIGNMENT) { + $parts = explode('_', $_GET['ot'], 3); + + if (isset($parts[1]) && $parts[1]) { + if ($parts[2] == 'my') { + $assignment_for = 'my'; + } + $owner_id = $parts[1]; + } else { + $owner_type = WORKSPACE_ASSIGNMENT; + unset($owner_id); + } + } else { + unset($owner_id); + } + $_REQUEST['folder'] = 0; +} else if (isset($_REQUEST['ot'], $_REQUEST['oid'])) { + $owner_type = abs($_REQUEST['ot']); + $owner_id = abs($_REQUEST['oid']); +} else if (isset($_SESSION['fs_owner_type'], $_SESSION['fs_owner_id'], $_SESSION['fs_folder_id'])) { + $owner_type = abs($_SESSION['fs_owner_type']); + $owner_id = abs($_SESSION['fs_owner_id']); +} else { + $owner_type = WORKSPACE_COURSE; +} + +if (isset($_REQUEST['folder'])) { + $folder_id = abs($_REQUEST['folder']); +} else if (isset($_SESSION['fs_folder_id'])) { + $folder_id = abs($_SESSION['fs_folder_id']); +} else { + $folder_id = 0; +} + +// init the owner_id if not currently set +if (!isset($owner_id)) { + if ($owner_type == WORKSPACE_COURSE) { + $owner_id = $_SESSION['course_id']; + } else if ($owner_type == WORKSPACE_PERSONAL) { + $owner_id = $_SESSION['member_id']; + } else if ($owner_type == WORKSPACE_GROUP) { + $owner_id = $group_id; + } +} + +$owner_arg_prefix = '?ot='.$owner_type.SEP.'oid='.$owner_id. SEP; + +if ($assignment_for == 'my') { + $owner_arg_prefix .= 'folder='.$_SESSION['member_id']; +} +if (!($owner_status = fs_authenticate($owner_type, $owner_id))) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} +$_SESSION['fs_owner_type'] = $owner_type; +$_SESSION['fs_owner_id'] = $owner_id; +$_SESSION['fs_folder_id'] = $folder_id; + +if (isset($_GET['submit_workspace'])) { + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix, AT_PRETTY_URL_IS_HEADER)); + exit; +} + +// action - Submit Assignment +if (isset($_GET['assignment']) && (isset($_GET['files']) || isset($_GET['folders']))) { + if (isset($_GET['folders'])) { + $msg->addError('HAND_IN_FOLDER'); + } else if (!isset($_GET['files'])) { + $msg->addError('NO_ITEM_SELECTED'); + } else { + header('Location: '.AT_BASE_HREF.'mods/_standard/file_storage/assignment.php?'.$_SERVER['QUERY_STRING']); + exit; + } +} +// action - View Revisions +else if (isset($_GET['revisions'], $_GET['files'])) { + if (is_array($_GET['files']) && (count($_GET['files']) == 1) && empty($_GET['folders'])) { + $file_id = current($_GET['files']); + header('Location: '.url_rewrite('mods/_standard/file_storage/revisions.php'.$owner_arg_prefix.'id='.$file_id, AT_PRETTY_URL_IS_HEADER)); + exit; + } +} +// action - View Comments +else if (isset($_GET['comments'], $_GET['files'])) { + if (is_array($_GET['files']) && (count($_GET['files']) == 1) && empty($_GET['folders'])) { + $file_id = current($_GET['files']); + header('Location: '.url_rewrite('comments.php'.$owner_arg_prefix.'id='.$file_id, AT_PRETTY_URL_IS_HEADER)); + exit; + } +} +// action - Edit File/Folder +else if (query_bit($owner_status, WORKSPACE_AUTH_WRITE) && isset($_GET['edit']) && (isset($_GET['folders']) || isset($_GET['files']))) { + if (is_array($_GET['files']) && (count($_GET['files']) == 1) && empty($_GET['folders'])) { + $file_id = current($_GET['files']); + header('Location: '.AT_BASE_HREF.'mods/_standard/file_storage/edit.php'.$owner_arg_prefix.'id='.$file_id); + exit; + } else if (is_array($_GET['folders']) && (count($_GET['folders']) == 1) && empty($_GET['files'])) { + $folder_id = current($_GET['folders']); + header('Location: '.AT_BASE_HREF.'mods/_standard/file_storage/edit_folder.php'.$owner_arg_prefix.'id='.$folder_id); + exit; + } +} +// action - Move Files/Folders +else if (query_bit($owner_status, WORKSPACE_AUTH_WRITE) && isset($_GET['move']) && (isset($_GET['folders']) || isset($_GET['files']))) { + header('Location: '.AT_BASE_HREF.'mods/_standard/file_storage/move.php'.$owner_arg_prefix.$_SERVER['QUERY_STRING']); + exit; +} +// action - Download Files/Folders +else if (isset($_GET['download']) && (isset($_GET['folders']) || isset($_GET['files']))) { + if (is_array($_GET['files']) && (count($_GET['files']) == 1) && empty($_GET['folders'])) { + $file_id = current($_GET['files']); + $sql = "SELECT file_name, file_size FROM ".TABLE_PREFIX."files WHERE file_id=$file_id AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $ext = fs_get_file_extension($row['file_name']); + + if (isset($mime[$ext]) && $mime[$ext][0]) { + $file_mime = $mime[$ext][0]; + } else { + $file_mime = 'application/octet-stream'; + } + $file_path = fs_get_file_path($file_id) . $file_id; + + ob_end_clean(); + header("Content-Encoding: none"); + header('Content-Type: ' . $file_mime); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="'.htmlspecialchars($row['file_name']).'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: '.$row['file_size']); + + // see the note in get.php about the use of x-Sendfile + header('x-Sendfile: '.$file_path); + header('x-Sendfile: ', TRUE); // if we get here then it didn't work + + @readfile($file_path); + exit; + } + } else { + // zip multiple files and folders + require(AT_INCLUDE_PATH . 'classes/zipfile.class.php'); + $zipfile = new zipfile(); + + $zip_file_name = fs_get_workspace($owner_type, $owner_id); // want the name of the workspace + $zip_file_name = str_replace(" ","_",$zip_file_name ); + + if (is_array($_GET['files'])) { + foreach ($_GET['files'] as $file_id) { + $file_path = fs_get_file_path($file_id) . $file_id; + + + $sql = "SELECT file_name, UNIX_TIMESTAMP(date) AS date FROM ".TABLE_PREFIX."files WHERE file_id=$file_id AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + if (($row = mysql_fetch_assoc($result)) && file_exists($file_path)) { + $zipfile->add_file(file_get_contents($file_path), $row['file_name'], $row['date']); + } + } + } + if (is_array($_GET['folders'])) { + foreach($_GET['folders'] as $folder_id) { + fs_download_folder($folder_id, $zipfile, $owner_type, $owner_id); + $row['title'] = str_replace(" ","_",$row['title'] ); + $zipfile->create_dir($row['title']); + } + + if (count($_GET['folders']) == 1) { + // zip just one folder, use that folder's title as the zip file name + $row = fs_get_folder_by_id($_GET['folders'][0], $owner_type, $owner_id); + if ($row) { + $zip_file_name = $row['title']; + $zip_file_name = str_replace(" ","_",$zip_file_name ); + } + } + } + $zipfile->close(); + $zipfile->send_file($zip_file_name); + } + exit; +} +// action - Delete Files/Folders (pre-confirmation) +else if (query_bit($owner_status, WORKSPACE_AUTH_WRITE) && isset($_GET['delete']) && (isset($_GET['folders']) || isset($_GET['files']))) { + $hidden_vars = array(); + $hidden_vars['folder'] = $folder_id; + $hidden_vars['ot'] = $owner_type; + $hidden_vars['oid'] = $owner_id; + if (isset($_GET['files'])) { + $file_list_to_print = ''; + $files = implode(',', $_GET['files']); + $hidden_vars['files'] = $files; + $sql = "SELECT file_name FROM ".TABLE_PREFIX."files WHERE file_id IN ($files) AND owner_type=$owner_type AND owner_id=$owner_id ORDER BY file_name"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $file_list_to_print .= '
  • '.htmlspecialchars($row['file_name']).'
  • '; + } + $msg->addConfirm(array('FILE_DELETE', $file_list_to_print), $hidden_vars); + } + + if (isset($_GET['folders'])) { + $dir_list_to_print = ''; + $folders = implode(',', $_GET['folders']); + $hidden_vars['folders'] = $folders; + $rows = fs_get_folder_by_id($_GET['folders'], $owner_type, $owner_id); + foreach ($rows as $row) { + $dir_list_to_print .= '
  • '.htmlentities_utf8($row['title']).'
  • '; + } + $msg->addConfirm(array('DIR_DELETE', $dir_list_to_print), $hidden_vars); + } + + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printConfirm(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + +} +// action - Confirm Delete Files/Folders +else if (query_bit($owner_status, WORKSPACE_AUTH_WRITE) && isset($_POST['submit_yes'])) { + + // handle the delete + if (isset($_POST['files'])) { + $files = explode(',', $_POST['files']); + } + if (isset($_POST['folders'])) { + $folders = explode(',', $_POST['folders']); + } + if (isset($files)) { + foreach ($files as $file) { + fs_delete_file($file, $owner_type, $owner_id); + } + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } + + if (isset($folders)) { + foreach ($folders as $folder) { + fs_delete_folder($folder, $owner_type, $owner_id); + } + $msg->addFeedback('DIR_DELETED'); + } + + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.abs($_POST['folder']), AT_PRETTY_URL_IS_HEADER)); + exit; +} +// action - Cancel Delete +else if (query_bit($owner_status, WORKSPACE_AUTH_WRITE) && isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.abs($_POST['folder']), AT_PRETTY_URL_IS_HEADER)); + exit; + +// action - Create Folder +} else if (query_bit($owner_status, WORKSPACE_AUTH_WRITE) && isset($_POST['create_folder'])) { + // create a new folder + $_POST['new_folder_name'] = trim($_POST['new_folder_name']); + + if (!$_POST['new_folder_name']) { + $msg->addError(array('EMPTY_FIELDS', _AT('name'))); + } + + if (!$msg->containsErrors()) { + $_POST['new_folder_name'] = $addslashes($_POST['new_folder_name']); + + $parent_folder_id = abs($_POST['folder']); + + $sql = "INSERT INTO ".TABLE_PREFIX."folders VALUES (NULL, $parent_folder_id, $owner_type, $owner_id, '$_POST[new_folder_name]')"; + $result = mysql_query($sql, $db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$parent_folder_id, AT_PRETTY_URL_IS_HEADER)); + exit; + } +} +// action - Upload +else if (query_bit($owner_status, WORKSPACE_AUTH_WRITE) && isset($_POST['upload'])) { + // handle the file upload + $_POST['comments'] = trim($_POST['comments']); + + $parent_folder_id = abs($_POST['folder']); + + if ($_FILES['file']['error'] == UPLOAD_ERR_INI_SIZE) { + $msg->addError(array('FILE_TOO_BIG', get_human_size(megabytes_to_bytes(substr(ini_get('upload_max_filesize'), 0, -1))))); + + } else if (!isset($_FILES['file']['name']) || ($_FILES['file']['error'] == UPLOAD_ERR_NO_FILE) || ($_FILES['file']['size'] == 0)) { + $msg->addError('FILE_NOT_SELECTED'); + + } else if ($_FILES['file']['error'] || !is_uploaded_file($_FILES['file']['tmp_name'])) { + $msg->addError('FILE_NOT_SAVED'); + } + + // check that we own this folder + if ($parent_folder_id) { + $sql = "SELECT folder_id FROM ".TABLE_PREFIX."folders WHERE folder_id=$parent_folder_id AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + if (!$row = mysql_fetch_assoc($result)) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/file_storage/index.php'); + exit; + } + } + + if (!$msg->containsErrors()) { + $_POST['description'] = $addslashes(trim($_POST['description'])); + $_FILES['file']['name'] = addslashes($_FILES['file']['name']); + + if ($_POST['comments']) { + $num_comments = 1; + } else { + $num_comments = 0; + } + + $sql = "INSERT INTO ".TABLE_PREFIX."files VALUES (NULL, $owner_type, $owner_id, $_SESSION[member_id], $parent_folder_id, 0, NOW(), $num_comments, 0, '{$_FILES['file']['name']}', {$_FILES['file']['size']}, '$_POST[description]')"; + $result = mysql_query($sql, $db); + + if ($result && ($file_id = mysql_insert_id($db))) { + $path = fs_get_file_path($file_id); + move_uploaded_file($_FILES['file']['tmp_name'], $path . $file_id); + + // check if this file name already exists + $sql = "SELECT file_id, num_revisions FROM ".TABLE_PREFIX."files WHERE owner_type=$owner_type AND owner_id=$owner_id AND folder_id=$parent_folder_id AND file_id<>$file_id AND file_name='{$_FILES['file']['name']}' AND parent_file_id=0 ORDER BY file_id DESC LIMIT 1"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + if ($_config['fs_versioning']) { + $sql = "UPDATE ".TABLE_PREFIX."files SET parent_file_id=$file_id, date=date WHERE file_id=$row[file_id]"; + $result = mysql_query($sql, $db); + + $sql = "UPDATE ".TABLE_PREFIX."files SET num_revisions=$row[num_revisions]+1, date=date WHERE file_id=$file_id"; + $result = mysql_query($sql, $db); + } else { + fs_delete_file($row['file_id'], $owner_type, $owner_id); + } + } + + $msg->addFeedback('FILE_UPLOADED'); + } else { + $msg->addError('FILE_NOT_SAVED'); + } + } + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$parent_folder_id, AT_PRETTY_URL_IS_HEADER)); + exit; +} else if ((isset($_GET['delete']) || isset($_GET['download']) || isset($_GET['move']) || isset($_GET['edit']) || isset($_GET['assignment'])) && !isset($_GET['files']) && !isset($_GET['folders'])) { + $msg->addError('NO_ITEM_SELECTED'); +} + +if (query_bit($owner_status, WORKSPACE_AUTH_WRITE)) { + $onload = 'hideform(\'upload\'); hideform(\'c_folder\');'; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$orders = array('asc' => 'desc', 'desc' => 'asc'); +$cols = array('file_name' => 1, 'file_size' => 1, 'date' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'file_name'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'file_name'; +} else { + // no order set + $order = 'asc'; + $col = 'file_name'; +} + +$folder_path = fs_get_folder_path($folder_id, $owner_type, $owner_id); + +$folders = fs_get_folder_by_pid($folder_id, $owner_type, $owner_id); + +$files = array(); +$sql = "SELECT * FROM ".TABLE_PREFIX."files WHERE folder_id=$folder_id AND owner_type=$owner_type AND owner_id=$owner_id AND parent_file_id=0 ORDER BY $col $order"; +$result = mysql_query($sql, $db); + +while ($row = mysql_fetch_assoc($result)) { + $files[] = $row; +} + +?> + + +
    + +
    +
    +
    +
    +

    +
    +
    +
    + *
    + +
    +
    + +
    +
    +
    + + +
    +
    +

    +
    +
    +
    + *
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    + + + + +
    +
    + + + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + » + + + » + + +
    + + + + + + + + + +
         
    + + +

    + +
    + + + + + + - + + + + +
    +
    +
    + + + \ No newline at end of file diff --git a/mods/_standard/file_storage/module.php b/mods/_standard/file_storage/module.php new file mode 100644 index 000000000..2a82bec69 --- /dev/null +++ b/mods/_standard/file_storage/module.php @@ -0,0 +1,58 @@ +getPrivilege() ); +// define('AT_ADMIN_PRIV_FILE_STORAGE', $this->getAdminPrivilege() ); + + +// if this module is to be made available to students on the Home or Main Navigation +$_group_tool = $_student_tool = 'mods/_standard/file_storage/index.php'; + +//modules sub-content +$this->_list['file_storage'] = array('title_var'=>'file_storage','file'=>'mods/_standard/file_storage/sublinks.php'); + +//student pages +$this->_pages['mods/_standard/file_storage/index.php']['title_var'] = 'file_storage'; +$this->_pages['mods/_standard/file_storage/index.php']['img'] = 'images/home-file_storage.png'; +$this->_pages['mods/_standard/file_storage/index.php']['icon'] = 'images/application_get.png'; +$this->_pages['mods/_standard/file_storage/index.php']['guide'] = 'general/?p=file_storage.php'; + +$this->_pages['mods/_standard/file_storage/revisions.php']['title_var'] = 'revisions'; +$this->_pages['mods/_standard/file_storage/revisions.php']['parent'] = 'mods/_standard/file_storage/index.php'; +$this->_pages['mods/_standard/file_storage/revisions.php']['children'] = array(); // empty array creates a "back to" link to index.php + +$this->_pages['mods/_standard/file_storage/comments.php']['title_var'] = 'comments'; +$this->_pages['mods/_standard/file_storage/comments.php']['parent'] = 'mods/_standard/file_storage/index.php'; +$this->_pages['mods/_standard/file_storage/comments.php']['children'] = array(); // empty array creates a "back to" link to index.php + +$this->_pages['mods/_standard/file_storage/edit.php']['title_var'] = 'edit'; +$this->_pages['mods/_standard/file_storage/edit.php']['parent'] = 'mods/_standard/file_storage/index.php'; +$this->_pages['mods/_standard/file_storage/edit.php']['children'] = array(); // empty array creates a "back to" link to index.php + +$this->_pages['mods/_standard/file_storage/edit_folder.php']['title_var'] = 'edit'; +$this->_pages['mods/_standard/file_storage/edit_folder.php']['parent'] = 'mods/_standard/file_storage/index.php'; +$this->_pages['mods/_standard/file_storage/edit_folder.php']['children'] = array(); // empty array creates a "back to" link to index.php + +$this->_pages['mods/_standard/file_storage/move.php']['title_var'] = 'move'; +$this->_pages['mods/_standard/file_storage/move.php']['parent'] = 'mods/_standard/file_storage/index.php'; +$this->_pages['mods/_standard/file_storage/move.php']['children'] = array(); // empty array creates a "back to" link to index.php + +$this->_pages['mods/_standard/file_storage/assignment.php']['title_var'] = 'hand_in'; +$this->_pages['mods/_standard/file_storage/assignment.php']['parent'] = 'mods/_standard/file_storage/index.php'; +$this->_pages['mods/_standard/file_storage/assignment.php']['children'] = array(); // empty array creates a "back to" link to index.php + +$this->_pages['mods/_standard/file_storage/new.php']['title_var'] = 'new_file'; +$this->_pages['mods/_standard/file_storage/new.php']['parent'] = 'mods/_standard/file_storage/index.php'; +$this->_pages['mods/_standard/file_storage/new.php']['children'] = array(); // empty array creates a "back to" link to index.php + +$this->_pages['mods/_standard/file_storage/delete_revision.php']['title_var'] = 'delete'; +$this->_pages['mods/_standard/file_storage/delete_revision.php']['parent'] = 'mods/_standard/file_storage/index.php'; + +$this->_pages['mods/_standard/file_storage/delete_comment.php']['title_var'] = 'delete'; +//$this->_pages['file_storage/delete_comment.php']['parent'] = 'file_storage/comments.php'; + +function file_storage_get_group_url($group_id) { + return 'mods/_standard/file_storage/index.php?ot='.WORKSPACE_GROUP.SEP.'oid='.$group_id; +} +?> \ No newline at end of file diff --git a/mods/_standard/file_storage/module.xml b/mods/_standard/file_storage/module.xml new file mode 100644 index 000000000..08f0277d0 --- /dev/null +++ b/mods/_standard/file_storage/module.xml @@ -0,0 +1,23 @@ + + + File Storage + Personal, group, and course drafting room. Is required when using the Assignment Manager. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + create + + 2005-08-26 + stable + This is a standard module. + + \ No newline at end of file diff --git a/mods/_standard/file_storage/module_delete.php b/mods/_standard/file_storage/module_delete.php new file mode 100644 index 000000000..79ebd959a --- /dev/null +++ b/mods/_standard/file_storage/module_delete.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/mods/_standard/file_storage/module_groups.php b/mods/_standard/file_storage/module_groups.php new file mode 100644 index 000000000..f4e9b7f69 --- /dev/null +++ b/mods/_standard/file_storage/module_groups.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/mods/_standard/file_storage/module_news.php b/mods/_standard/file_storage/module_news.php new file mode 100644 index 000000000..57cf97c72 --- /dev/null +++ b/mods/_standard/file_storage/module_news.php @@ -0,0 +1,49 @@ + + */ +function file_storage_news() { + global $db, $enrolled_courses, $system_courses; + $news = array(); + + if ($enrolled_courses == ''){ + return $news; + } + + $sql = "SELECT date, file_id, file_name, owner_id, description FROM ".TABLE_PREFIX."files WHERE owner_id IN $enrolled_courses ORDER BY date DESC"; + $result = mysql_query($sql, $db); + if($result){ + while($row = mysql_fetch_assoc($result)){ + $row['course_id'] = $row['owner_id']; + if($row['description'] !=""){ + $filetext = $row['description']; + } else { + $filetext = $row['file_name']; + } + $news[] = array('time'=>$row['date'], + 'object'=>$row, + 'course'=>$system_courses[$row['owner_id']]['title'], + 'alt'=>_AT('download'), + 'thumb'=>'images/application_get.png', + 'link'=>' SUBLINK_TEXT_LEN ? ' title="'.$filetext.'"' : '') .'>'. + validate_length($filetext, SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''); + } + } + return $news; +} + +?> \ No newline at end of file diff --git a/mods/_standard/file_storage/move.php b/mods/_standard/file_storage/move.php new file mode 100644 index 000000000..1ef93930b --- /dev/null +++ b/mods/_standard/file_storage/move.php @@ -0,0 +1,129 @@ +addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.abs($_POST['folder']), AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit'])) { + $_POST['new_folder'] = abs($_POST['new_folder']); + + if ($_POST['folder'] == $_POST['new_folder']) { + // src = dest + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$_POST['new_folder'], AT_PRETTY_URL_IS_HEADER)); + exit; + } + + if (isset($_POST['files'])) { + foreach ($_POST['files'] as $file) { + $file = abs($file); + // check if this file name already exists + $sql = "SELECT file_name FROM ".TABLE_PREFIX."files WHERE file_id=$file"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + $sql = "SELECT file_id FROM ".TABLE_PREFIX."files WHERE folder_id={$_POST['new_folder']} AND file_id<>$file AND file_name='{$row['file_name']}' AND parent_file_id=0 AND owner_type=$owner_type AND owner_id=$owner_id ORDER BY file_id DESC LIMIT 1"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + fs_delete_file($row['file_id'], $owner_type, $owner_id); + } + + $sql = "UPDATE ".TABLE_PREFIX."files SET folder_id={$_POST['new_folder']}, date=date WHERE file_id=$file AND owner_type=$owner_type AND owner_id=$owner_id"; + mysql_query($sql, $db); + } + $msg->addFeedback('FILES_MOVED'); + } + + if (isset($_POST['folders'])) { + foreach ($_POST['folders'] as $folder) { + $file = abs($file); + $sql = "UPDATE ".TABLE_PREFIX."folders SET parent_folder_id={$_POST['new_folder']} WHERE folder_id=$folder AND owner_type=$owner_type AND owner_id=$owner_id"; + mysql_query($sql, $db); + } + $msg->addFeedback('DIRS_MOVED'); + } + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$_POST['new_folder'], AT_PRETTY_URL_IS_HEADER)); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$folder_id = abs($_GET['folder']); + +// can't use fs_get_folders() because we want all folders, not just at one level +$folders = array(); +$sql = "SELECT folder_id, parent_folder_id, title FROM ".TABLE_PREFIX."folders WHERE owner_type=$owner_type AND owner_id=$owner_id ORDER BY parent_folder_id, title"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)) { + $folders[$row['parent_folder_id']][$row['folder_id']] = $row; +} + +?> + +
    + + + + + + + + + + + +
    +
    +

    +
    + +
    +
      +
    • /> + + +
    • +
    +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/file_storage/new.php b/mods/_standard/file_storage/new.php new file mode 100644 index 000000000..f0320b90a --- /dev/null +++ b/mods/_standard/file_storage/new.php @@ -0,0 +1,160 @@ +addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php'.$owner_arg_prefix.'folder='.abs($_POST['folder'])); + exit; +} else if (isset($_POST['submit'])) { + $_POST['comments'] = trim($_POST['comments']); + $_POST['name'] = trim($_POST['name']); + + $parent_folder_id = abs($_POST['folder']); + + // check that we own this folder + if ($parent_folder_id) { + $sql = "SELECT folder_id FROM ".TABLE_PREFIX."folders WHERE folder_id=$parent_folder_id AND owner_type=$owner_type AND owner_id=$owner_id"; + $result = mysql_query($sql, $db); + if (!$row = mysql_fetch_assoc($result)) { + $msg->addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; + } + } + + if (!$_POST['name']) { + $msg->addError(array('EMPTY_FIELDS', _AT('file_name'))); + } + + if (!$msg->containsErrors()) { + $_POST['description'] = $addslashes(trim($_POST['description'])); + $_POST['comment'] = $addslashes(trim($_POST['comment'])); + $_POST['name'] = $addslashes($_POST['name']); + $_POST['body'] = $stripslashes($_POST['body']); // file gets saved to disk not db, so no need to escape. + + if ($_POST['comment']) { + $num_comments = 1; + } else { + $num_comments = 0; + } + + $size = strlen($_POST['body']); + $sql = "INSERT INTO ".TABLE_PREFIX."files VALUES (NULL, $owner_type, $owner_id, $_SESSION[member_id], $parent_folder_id, 0, NOW(), $num_comments, 0, '$_POST[name]',$size, '$_POST[description]')"; + $result = mysql_query($sql, $db); + + if ($result && ($file_id = mysql_insert_id($db))) { + $file_path = fs_get_file_path($file_id) . $file_id; + $fp = fopen($file_path, 'wb'); + fwrite($fp, $_POST['body'], $size); + fclose($fp); + + // check if this file name already exists + $sql = "SELECT file_id, num_revisions FROM ".TABLE_PREFIX."files WHERE owner_type=$owner_type AND owner_id=$owner_id AND folder_id=$parent_folder_id AND file_id<>$file_id AND file_name='$_POST[name]' AND parent_file_id=0 ORDER BY file_id DESC LIMIT 1"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + if ($_config['fs_versioning']) { + $sql = "UPDATE ".TABLE_PREFIX."files SET parent_file_id=$file_id, date=date WHERE file_id=$row[file_id]"; + $result = mysql_query($sql, $db); + + $sql = "UPDATE ".TABLE_PREFIX."files SET num_revisions=$row[num_revisions]+1, date=date WHERE file_id=$file_id"; + $result = mysql_query($sql, $db); + } else { + fs_delete_file($row['file_id'], $owner_type, $owner_id); + } + } + + if ($_POST['comment']){ + $sql = "INSERT INTO ".TABLE_PREFIX."files_comments VALUES (NULL, $file_id, $_SESSION[member_id], NOW(), '{$_POST['comment']}')"; + mysql_query($sql, $db); + } + + $msg->addFeedback(array('FILE_SAVED', $_POST['name'])); + header('Location: index.php'.$owner_arg_prefix.'folder='.$parent_folder_id); + exit; + } + } +} + +$onload = 'document.form.name.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (($_POST['setvisual'] && !$_POST['settext']) || $_GET['setvisual']) { + require(AT_INCLUDE_PATH.'lib/tinymce.inc.php'); + + load_editor(false, 'body'); +} +if (isset($_POST['description'])) { + $_POST['description'] = $stripslashes($_POST['description']); + $_POST['name'] = $stripslashes($_POST['name']); + $_POST['comment'] = $stripslashes($_POST['comment']); + $_POST['body'] = $stripslashes($_POST['body']); +} +?> +
    + + +
    +
    + *
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + '; + echo ''; + } else { + echo ''; + } + ?> +
    + +
    +
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/file_storage/revisions.php b/mods/_standard/file_storage/revisions.php new file mode 100644 index 000000000..417f30483 --- /dev/null +++ b/mods/_standard/file_storage/revisions.php @@ -0,0 +1,142 @@ +addError('ACCESS_DENIED'); + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +if (isset($_GET['download'], $_GET['revision'])) { + header('Location: '.AT_BASE_HREF.'mods/_standard/file_storage/index.php'.$owner_arg_prefix.'download=1'.SEP.'files'.urlencode('[]').'='.$_GET['revision']); + exit; +} else if (query_bit($owner_status, WORKSPACE_AUTH_WRITE) && isset($_GET['delete'], $_GET['revision'])) { + header('Location: '.AT_BASE_HREF.'mods/_standard/file_storage/delete_revision.php'.$owner_arg_prefix.'id='.$_GET['revision']); + exit; +} else if (isset($_GET['cancel'])) { + header('Location: '.url_rewrite('mods/_standard/file_storage/index.php'.$owner_arg_prefix.'folder='.$_GET['folder'], AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_GET['comments'])) { + header('Location: '.url_rewrite('mods/_standard/file_storage/comments.php'.$owner_arg_prefix.'id='.$_GET['revision'], AT_PRETTY_URL_IS_HEADER)); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$id = abs($_GET['id']); + +$orders = array('asc' => 'desc', 'desc' => 'asc'); +$cols = array('num_revisions' => 1, 'file_name' => 1, 'date' => 1, 'num_comments' => 1, 'file_size' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'num_revisions'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'num_revisions'; +} else { + // no order set + $order = 'desc'; + $col = 'num_revisions'; +} + +$files = fs_get_revisions($id, $owner_type, $owner_id, $col, $order); +$current_file = current($files); + + +usort($files, 'fs_revisions_sort_compare'); + +?> + +
    + + + + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    + + + + + +
    + + +

    + +
    +
    + + + \ No newline at end of file diff --git a/mods/_standard/file_storage/sublinks.php b/mods/_standard/file_storage/sublinks.php new file mode 100644 index 000000000..064d3350f --- /dev/null +++ b/mods/_standard/file_storage/sublinks.php @@ -0,0 +1,29 @@ + 0) { + while ($row = mysql_fetch_assoc($result)) { + if($row['description'] !=""){ + $filetext = $row['description']; + }else{ + $filetext = $row['file_name']; + } + + $list[] = ' SUBLINK_TEXT_LEN ? ' title="'.$filetext.'"' : '') .'>'. + validate_length($filetext, SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''; + } + return $list; +} else { + return 0; +} + +?> \ No newline at end of file diff --git a/mods/_standard/flowplayer/LICENSE.txt b/mods/_standard/flowplayer/LICENSE.txt new file mode 100644 index 000000000..2a00962f2 --- /dev/null +++ b/mods/_standard/flowplayer/LICENSE.txt @@ -0,0 +1,721 @@ +The Flowplayer Free version is released under the +GNU GENERAL PUBLIC LICENSE Version 3 (GPL). + +The GPL requires that you not remove the Flowplayer copyright notices +from the user interface. See section 5.d below. + +Commercial licenses are available. The commercial player version +does not require any Flowplayer notices or texts and also provides +some additional features. + +======================================================================== + +ADDITIONAL TERM per GPL Section 7 +If you convey this program (or any modifications of it) and assume +contractual liability for the program to recipients of it, you agree +to indemnify Flowplayer, Ltd. for any liability that those contractual +assumptions impose on Flowplayer, Ltd. + +Except as expressly provided herein, no trademark rights are granted in +any trademarks of Flowplayer, Ltd. Licensees are granted a limited, +non-exclusive right to use the mark Flowplayer and the Flowplayer logos +in connection with unmodified copies of the Program and the copyright +notices required by section 5.d of the GPL license. For the purposes +of this limited trademark license grant, customizing the Flowplayer +by skinning, scripting, or including PlugIns provided by Flowplayer, Ltd. +is not considered modifying the Program. + +Licensees that do modify the Program, taking advantage of the open-source +license, may not use the Flowplayer mark or Flowplayer logos and must +change the fullscreen notice (and the non-fullscreen notice, if that +option is enabled), the copyright notice in the dialog box, and the +notice on the Canvas as follows: + +the full screen (and non-fullscreen equivalent, if activated) notice +should read: "Based on Flowplayer source code"; in the context menu +(right-click menu), the link to "About Flowplayer free version #.#.#" +can remain. The copyright notice can remain, but must be supplemented with +an additional notice, stating that the licensee modified the Flowplayer. +A suitable notice might read "Flowplayer Source code modified by ModOrg 2009"; +for the canvas, the notice should read "Based on Flowplayer source code". +In addition, licensees that modify the Program must give the modified +Program a new name that is not confusingly similar to Flowplayer and +may not distribute it under the name Flowplayer. + +======================================================================== + + + 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/mods/_standard/flowplayer/README.txt b/mods/_standard/flowplayer/README.txt new file mode 100644 index 000000000..5e2913b05 --- /dev/null +++ b/mods/_standard/flowplayer/README.txt @@ -0,0 +1,222 @@ +Version history: + +3.1.2 +----- +- The domain of the logo url must the same domain from where the player SWF is loaded from. +- Fullscreen can be toggled by doublclick on the video area. +Fixes: +- Player was not initialized correctly when instream playlists were used and the provider used in the instream clips was defined in the common clip. +- A separator in the Context Menu made the callbacks in the following menu items out of order. Related forum post: http://flowplayer.org/forum/8/22541 +- the width and height settings of a logo were ignored if the logo was a sWF file +- volume control and mute/unmute were not working after an instream clip had been played +- now possible to use RTMP for mp3 files +- Issue 12: cuepointMultiplier was undefined in the clip object set to JS event listeners +- Issue 14: onBeforeStop was unnecessarily fired when calling setPlaylist() and the player was not playing, + additionally onStop was never fired even if onBeforeStop was +- fixed screen vertical placement problems that reappeared with 3.1.1 +- The rotating animation now has the same size and position as it has after initialized + +3.1.1 +----- +- External configuration files +- Instream playback +- Added toggleFullscreen() the API +- Possibility to specify controls configuration in clips +- Seek target position is now sent in the onBeforeSeek event +Fixes: +- The screen size was initially too small on Firefox (Mac) +- Did not persist a zero volume value: http://www.flowplayer.org/forum/8/18413 + +3.1.0 +----- +New features: +- clip's can have urlResolvers and connectionProviders +- Added new configuration options 'connectionCallbacks' and 'streamCallbacks'. Both accept an Array of event names as a value. + When these events get fired on the connection or stream object, corresponding Clip events will be fired by the player. + This can be used for example when firing custom events from RTMP server apps +- Added new clip event types: 'onConnectionEvent' and 'onStreamEvent' these get fired when the predefined events happen on the connection and stream objects. +- Added Security.allowDomain() to allow loaded plugins to script the player +- Added addClip(clip, index) to the API, index is optional +- Possibility to view videos without metadata, using clip.metaData: false +- Now the player's preloader uses the rotating animation instead of a percent text to indicate the progress + of loading the player SWF. You can disable the aninamtion by setting buffering: false +- calling close() now does not send the onStop event +- Clip's custom properties are now present in the root of the clip argument in all clip events that are sent to JS. + +Bug fixes: +- The preloader sometimes failed to initialize the player +- Allow seeking while in buffering state: http://flowplayer.org/forum/8/16505 +- Replay of a RTMP stream was failing after the connection had expired +- Security error when clicking on the screen if there is an image in the playlist loaded from a foreign domain +- loadPlugin() was not working +- now fullscreen works with Flash versions older than 9.0.115, in versions that do not support hardware scaling +- replaying a RTMP stream with an image in front of the stream in the playlist was not working (video stayed hidden). Happened + because the server does not send metadata if replaying the same stream. +- the scrubber is disabled if the clip is not seekable in the first frame: http://flowplayer.org/forum/8/16526 + By default if the clip has one of following extensions (the typical flash video extensions) it is seekable + in the first frame: 'f4b', 'f4p', 'f4v', 'flv'. Added new clip property seekableOnBegin that can be used to override the default. + +3.0.6 +----- +- added possibility to associate a linkUrl and linkWindow to the canvas +Fixes: +- fix for entering fullscreen for Flash versions that don't support the hardware scaled fullscreen-mode +- when showing images the duration tracking starts only after the image has been completely loaded: http://flowplayer.org/forum/2/15301 +- fix for verifying license keys for domains that have more than 4 labels in them +- if plugin loading failis because of a IO error, the plugin will be discarded and the player initialization continues: + +3.0.4 +----- +- The "play" pseudo-plugin now supports fadeIn(), fadeOut(), showPlugin(), hidePlugin() and + additionally you can configure it like this: + // make only the play button invisible (buffering animation is still used) + play: { display: 'none' } + // disable the play button and the buffering animation + play: null + // disable the buffering animation + buffering: null +- Added possibility to seek when in the buffering state: http://flowplayer.org/forum/3/13896 +- Added copyright notices and other GPL required entries to the user interface + +Fixes: +- clip urls were not resolved correctly if the HTML page URL had a query string starting with a question mark (http://flowplayer.org/forum/8/14016#post-14016) +- Fixed context menu for with IE (commercial version) +- a cuepoint at time zero was fired several times +- screen is now arranged correctly even when only bottom or top is defined for it in the configuration +- Fixed context menu for with IE (commercial version) +- a cuepoint at time zero was fired several times +- screen is now arranged correctly even when only bottom or top is defined for it in the configuration +- Now possible to call play() in an onError handler: http://flowplayer.org/forum/8/12939 +- Does not throw an error if the player cannot persist the volume on the client computer: http://flowplayer.org/forum/8/13286#post-13495 +- Triggering fullscreen does not pause the player in IE +- The play button overlay no longer has a gap between it's pieces when a label is used: http://flowplayer.org/forum/8/14250 +- clip.update() JS call now resets the duration +- a label configured for the play button overlay did not work in the commercial version + +3.0.3 +----- +- fixed cuepoint firing: Does not skip cuepoints any more +- Plugins can now be loaded from a different domain to the flowplayer.swf +- Specifying a clip to play by just using the 'clip' node in the configuration was not working, a playlist definition was required. This is now fixed. +- Fixed: A playlist with different providers caused the onMetadata event to fire events with metadata from the previous clip in the playlist. Occurred when moving in the playlist with next() and prev() +- the opacity setting now works with the logo +- fadeOut() call to the "screen" plugin was sending the listenerId and pluginName arguments in wrong order +- stop(), pause(), resume(), close() no longer return the flowplayer object to JS +- changing the size of the screen in a onFullscreen listener now always works, there was a bug that caused this to fail occasionally +- fixed using arbitrary SWFs as plugins +- the API method setPlaylist() no longer starts playing if autoPlay: true, neither it starts buffering if autoBuffering: true +- the API method play() now accepts an array of clip objects as an argument, the playlist is replaced with the specified clips and playback starts from the 1st clip + +3.0.2 +----- +- setting play: null now works again +- pressing the play again button overlay does not open a linkUrl associated with a clip +- now displays a live feed even when the RTMP server does not send any metadata and the onStart method is not therefore dispatched +- added onMetaData clip event +- fixed 'orig' scaling: the player went to 'fit' scaling after coming back from fullscreen. This is now fixed and the original dimensions are preserved in non-fullscreen mode. +- cuepoint times are now given in milliseconds, the firing precision is 100 ms. All cuepoint times are rounded to the nearest 100 ms value (for example 1120 rounds to 1100) +- backgroundGradient was drawn over the background image in the canvas and in the content and controlbar plugins. Now it's drawn below the image. +- added cuepointMultiplier property to clips. This can be used to multiply the time values read from cuepoint metadata embedded into video files. +- the player's framerate was increased to 24 FPS, makes all animations smoother + +3.0.1 +----- +- Fixed negative cuepoints from common clip. Now these are properly propagated to the clips in playlist. +- buffering animation is now the same size as the play button overlay +- commercial version now supports license keys that allows the use of subdomains +- error messages are now automatically hidden after a 4 second delay. They are also hidden when a new clips + starts playing (when onBeforeBegin is fired) +- added possibility to disable the buffering animation like so: buffering: false +- pressing the play button overlay does not open a linkUrl associated with a clip +- license key verification failed if a port number was used in the URL (like in this url: http://mydomain.com:8080/video.html) +- added audio support, clip has a new "image" property +- workaround for missing "NetStream.Play.Start" notfication that was happending with Red5. Because of this issue the video was not shown. +- commercial version has the possibility to change the zIndex of the logo + +3.0.0 +----- +- Removed security errors that happened when loading images from foreign domains (domains other than the domain of the core SWF). + Using a backgroundImage on canvas, in the content plugin, and for the controls is also possible to be loaded + from a foreign domain - BUT backgroundRepeat cannot be used for foreign images. +- Now allows the embedding HTML to script the player even if the player is loaded from another domain. +- Added a 'live' property to Clips, used for live streams. +- A player embedded to a foreign domain now loads images, css files and other resources from the domain where the palyer SWF was loaded from. This is to generate shorter embed-codes. +- Added linkUrl and linkWindow properties to the logo, in commercial version you can set these to point to a linked page. The linked page gets opened + when the logo is clicked. Possible values for linkWindow: + * "_self" specifies the current frame in the current window. + * "_blank" specifies a new window. + * "_parent" specifies the parent of the current frame. + * "_top" specifies the top-level frame in the current window. +- Added linkUrl and linkWindow properties to clips. The linked page is opened when the video are is clicked and the corresponding clip has a linkUrl specified. +- Made the play button overlay and the "Play again" button slightly bigger. + +RC4 +--- +- Now shows a "Play again" button at the end of the video/playlist +- Commercial version shows a Flowplayer logo if invalidKey was supplied, but the otherwise the player works +- setting play: null in configuration will disable the play button overlay +- setting opacity for "play" also sets it for the buffering animation +- Fixed firing of cuepoints too early. Cuepoint firing is now based on stream time and does not rely on timers +- added onXMPData event listener +- Should not stop playback too early before the clip is really completed +- The START event is now delayed so that the metadata is available when the event is fired, METADATA event was removed, + new event BEGIN that is dispatched when the playback has been successfully started. Metadata is not normally + available when BEGIN is fired. + +RC3 +--- +- stopBuffering() now dispatches the onStop event first if the player is playing/paused/buffering at the time of calling it +- fixed detection of images based on file extensions +- fixed some issues with having images in the playlist +- made it possible to autoBuffer next video while showing an image (image without a duration) + +RC2 +--- +- fixed: setting the screen height in configuration did not have any effect + +RC1 +----- +- better error message if plugin loading fails, shows the URL used +- validates our redesigned multidomain license key correctly +- fix to prevent the play button going visible when the onBufferEmpty event occurs +- the commercial swf now correctly loads the controls using version information +- fixed: the play button overlay became invisible with long fadeOutSpeeds + +beta6 +----- +- removed the onFirstFramePause event +- playing a clip for the second time caused a doubled sound +- pausing on first frame did not work on some FLV files + +beta5 +----- +- logo only uses percentage scaling if it's a SWF file (there is ".swf" in it's url) +- context menu now correctly builds up from string entries in configuration +-always closes the previous connection before starting a new clip + +beta4 +----- +- now it's possible to load a plugin into the panel without specifying any position/dimensions + information, the plugin is placed to left: "50%", top: "50%" and using the plugin DisplayObject's width & height +- The Flowplayer API was not fully initialized when onLoad was invoked on Flash plugins + +beta3 +----- +- tweaking logo placement +- "play" did not show up after repeated pause/resume +- player now loads the latest controls SWF version, right now the latest SWF is called 'flowplayer.controls-3.0.0-beta2.swf' + +beta2 +----- +- fixed support for RTMP stream groups +- changed to loop through available fonts in order to find a suitable font also in IE +- Preloader was broken on IE: When the player SWf was in browser's cache it did not initialize properly +- Context menu now correctly handles menu items that are configured by their string labels only (not using json objects) +- fixed custom logo positioning (was moved to the left edge of screen in fullscreen) +- "play" now always follows the position and size of the screen +- video was stretched below the controls in fullscreen when autoHide: 'never' +- logo now takes 6.5% of the screen height, width is scaled so that the aspect ratio is preserved + +beta1 +----- +- First public beta release diff --git a/mods/_standard/flowplayer/flowplayer-3.1.2.min.js b/mods/_standard/flowplayer/flowplayer-3.1.2.min.js new file mode 100644 index 000000000..0d202cb5f --- /dev/null +++ b/mods/_standard/flowplayer/flowplayer-3.1.2.min.js @@ -0,0 +1,24 @@ +/* + * flowplayer.js 3.1.2. The Flowplayer API + * + * Copyright 2009 Flowplayer Oy + * + * This file is part of Flowplayer. + * + * Flowplayer 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. + * + * Flowplayer 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 Flowplayer. If not, see . + * + * Date: 2009-02-25 16:24:29 -0500 (Wed, 25 Feb 2009) + * Revision: 166 + */ +(function(){function g(o){console.log("$f.fireEvent",[].slice.call(o))}function k(q){if(!q||typeof q!="object"){return q}var o=new q.constructor();for(var p in q){if(q.hasOwnProperty(p)){o[p]=k(q[p])}}return o}function m(t,q){if(!t){return}var o,p=0,r=t.length;if(r===undefined){for(o in t){if(q.call(t[o],o,t[o])===false){break}}}else{for(var s=t[0];p1){var r=arguments[1];var q=(arguments.length==3)?arguments[2]:{};if(typeof o=="string"){if(o.indexOf(".")!=-1){var t=[];m(n(o),function(){t.push(new b(this,k(r),k(q)))});return new d(t)}else{var s=c(o);return new b(s!==null?s:o,r,q)}}else{if(o){return new b(o,r,q)}}}return null};i(window.$f,{fireEvent:function(){var o=[].slice.call(arguments);var q=$f(o[0]);return q?q._fireEvent(o.slice(1)):null},addPlugin:function(o,p){b.prototype[o]=p;return $f},each:m,extend:i});if(typeof jQuery=="function"){jQuery.prototype.flowplayer=function(q,p){if(!arguments.length||typeof arguments[0]=="number"){var o=[];this.each(function(){var r=$f(this);if(r){o.push(r)}});return arguments.length?o[arguments[0]]:new d(o)}return this.each(function(){$f(this,k(q),p?k(p):{})})}}})();(function(){var e=typeof jQuery=="function";function i(){if(c.done){return false}var k=document;if(k&&k.getElementsByTagName&&k.getElementById&&k.body){clearInterval(c.timer);c.timer=null;for(var j=0;j'}o.width=o.height=o.id=o.w3c=o.src=null;for(var j in o){if(o[j]!==null){m+=''}}var n="";if(s){for(var l in s){if(s[l]!==null){n+=l+"="+(typeof s[l]=="object"?g(s[l]):s[l])+"&"}}n=n.substring(0,n.length-1);m+='"}m+="
    ";return m}function d(l,o,k){var j=flashembed.getVersion();f(this,{getContainer:function(){return l},getConf:function(){return o},getVersion:function(){return j},getFlashvars:function(){return k},getApi:function(){return l.firstChild},getHTML:function(){return a(o,k)}});var p=o.version;var q=o.expressInstall;var n=!p||flashembed.isSupported(p);if(n){o.onFail=o.version=o.expressInstall=null;l.innerHTML=a(o,k)}else{if(p&&q&&flashembed.isSupported([6,65])){f(o,{src:q});k={MMredirectURL:location.href,MMplayerType:"PlugIn",MMdoctitle:document.title};l.innerHTML=a(o,k)}else{if(l.innerHTML.replace(/\s/g,"")!==""){}else{l.innerHTML="

    Flash version "+p+" or greater is required

    "+(j[0]>0?"Your version is "+j:"You have no flash plugin installed")+"

    "+(l.tagName=="A"?"

    Click here to download latest version

    ":"

    Download latest version from here

    ");if(l.tagName=="A"){l.onclick=function(){location.href="http://www.adobe.com/go/getflashplayer"}}}}}if(!n&&o.onFail){var m=o.onFail.call(this);if(typeof m=="string"){l.innerHTML=m}}if(document.all){window[o.id]=document.getElementById(o.id)}}window.flashembed=function(k,l,j){if(typeof k=="string"){var m=document.getElementById(k);if(m){k=m}else{c(function(){flashembed(k,l,j)});return}}if(!k){return}var n={width:"100%",height:"100%",allowfullscreen:true,allowscriptaccess:"always",quality:"high",version:null,onFail:null,expressInstall:null,w3c:false};if(typeof l=="string"){l={src:l}}f(n,l);return new d(k,n,j)};f(window.flashembed,{getVersion:function(){var l=[0,0];if(navigator.plugins&&typeof navigator.plugins["Shockwave Flash"]=="object"){var k=navigator.plugins["Shockwave Flash"].description;if(typeof k!="undefined"){k=k.replace(/^.*\s+(\S+\s+\S+$)/,"$1");var m=parseInt(k.replace(/^(.*)\..*$/,"$1"),10);var q=/r/.test(k)?parseInt(k.replace(/^.*r(.*)$/,"$1"),10):0;l=[m,q]}}else{if(window.ActiveXObject){try{var o=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7")}catch(p){try{o=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");l=[6,0];o.AllowScriptAccess="always"}catch(j){if(l[0]==6){return l}}try{o=new ActiveXObject("ShockwaveFlash.ShockwaveFlash")}catch(n){}}if(typeof o=="object"){k=o.GetVariable("$version");if(typeof k!="undefined"){k=k.replace(/^\S+\s+(.*)$/,"$1").split(",");l=[parseInt(k[0],10),parseInt(k[2],10)]}}}}return l},isSupported:function(j){var l=flashembed.getVersion();var k=(l[0]>j[0])||(l[0]==j[0]&&l[1]>=j[1]);return k},domReady:c,asString:g,getHTML:a});if(e){jQuery.tools=jQuery.tools||{version:{}};jQuery.tools.version.flashembed="1.0.3";jQuery.fn.flashembed=function(k,j){var l=null;this.each(function(){l=flashembed(this,k,j)});return k.api===false?this:l}}})(); \ No newline at end of file diff --git a/mods/_standard/flowplayer/flowplayer-3.1.2.swf b/mods/_standard/flowplayer/flowplayer-3.1.2.swf new file mode 100644 index 0000000000000000000000000000000000000000..40b9647b1fd84a428e0a46ec8317f6da17bffd01 GIT binary patch literal 107779 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%0A9EUcxVUAi-nh0e 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⁢KzX`F+{)TKP<%yfYPJ+4YMUHTf) zpK>5W&#KP9_~6s)R#cIBTd3<0Yx=>7I$GW%`u9dy!BE{k$k3w;0vO0QYPeq(+51fb zBf{VrjBZm49LTKVKGV{A8&P`X!a1XPyPAcicT;^HWFF{6^HPGNU}zr&9rc#8rA#54 zLAA3skPRiXc`Va8Q?aehPi8UJk*!2Ng^`wyZ!b>o z6`vwlMF#583qzC}Urw?qcs!}{wIudUES;aHf+IbWcS{#a~BHNuW2wrDGOjAzdcnFHVMt#I*ZIC5mqKK=~B%i*G#F5mfw;ESh zoc!<R zyso41>eb?(*;1;!9>s)x6b^am3yiN@8FD`MDPu5MrVYwm^7Sj#2tcl*(@dh%Zha#e z0AoO$zuFTVuZ{JXk-|UZ!Ma*T;)Aw&6ceH_IoI9jgdEf-Y-0?t4ZtcGr!m0nY-A>b zLT?CCAe+DTs{sYYH{=BRxnf-wJ>pZUdcdjZl;YBi?Q)!#bd_5p-`J|=yBv6g0q0Ct zh2)^G`pSGMkOhz8V(KSGoc`MCD4Zm03M2J#6K&eq437u(aDI1I2vf|A`q5cMALK!0 z?i3=r5Gv77XRr#@WYVeV)a_NOrVM@R23it(8E%sZYW^D3(xXXrm}@eV7n{rhtXSN*J~8{R7v$rKc;^PiA&6WtxA=p;YZK-=N4UA zgN<)U%+;c}j8mFgKo@ko@(HFiZvtkm1K77IKD(~%*c*enwVoCAg$ zRs{G5s{O8t_DSaW$vbqVN`rB}$wUSwB!%Xl=+d=4I(;}%_+V-TTq8JDi)6g=JSd0~ z0(Xjfh2L-M(V}FMb-#Ez=Wx&hrLHp7GoG)2E`-`ue5U4Bj!J0_6%J_oq!@$ZmlW!`j4GT zM*>PBn2os>)fL!K?lU+pw*4SJaw)!6%05%8EAChbb~4w~nZASR(I(>seeRQY4)!27 zRrj;HB5v6{@;j$Gm_LM!#MFebIItD^27plOi80_6?dFu`Rx03b)3@Rps&*5FewJsz z1nzo_p&@W%WT^Vs6sln&AM}hB0^4xS*Tg4^D?%whW~_-3Wn3fhqqt$ZK>%`zkPi82 zQ-oss)4GTYnFW>Fb6Otr0q(_h@y!(S3+7&3!73kfRE<`$5fJRnnXhFaC(Jm_!2X)+8&>=RnP?S$1P$3=b}Pl;-+lU8vK5!-Im%2nB&tE| z{1AdqpH=z%Qu*SjhaKA0c`5+jE>vP}1tyAOoXy0i%#WM*zkT6WG-wBitbn3sVI%y|Y0`?!nAhkr zq}rubo4U2fL=x3^Z~~@=GbSx)mT~zVrwT&-HT9K64Vx;9v`NhK38q&B2Qyli;A#d{ zpj?>cOh;QBC_|%GWs#}+)pbRzK_!BwQ~&{0YpA-2#KSx~_V{j6E%9?Ey0}nDnu@Aa z-K|tQ)2Zk~&~56SkZIRth?is>nHQb0pz>8&L}przCsBTFH06z5$yAoIXcO@8)%;q3 zNqDBJ**i21M$h89pM7UhE01=JiJeXIE5Pj!s-%RwyB?ntw0HI9FzzgyTXoXL{|qd?}NfcIa^- zGg`O?WZnCskHf8~%kost(gbRc2%%`y8=t~C)>AnwG@RGJW6zUm4sk9=+c5t;SaYMx zIj)nXEEIan=2hb$`s#0m8TS(T*R<^ld#$V2%sQvJLON?#iSd{{;rGkOife-l#ZWGh z`i(2VDCW-HC}R{~1LGA!F%R|#AG;5YCxZ&*63E1PoV@2jkdO;px}v>1>B+`Ha>*3g z{R-M2yw*Hj`ewQKuuEIIU>qKoE~smBBv zrxA^7xSxvsquBt_NFeMLQ1p0}$2yAQYNNkf{!22(HB8H$HDfv>?+F!SRkWJS>a)_lHgMZU(K*^uUgmEf* zopl9@e!RwS2*j{ z^)KjE!FV{o%&!6l%pNpRfjbf`LC_q-%u={b_Apk=cgjY@FPT@sC~F?Y#9IfH8ZO-Q zRdO3!X5308L>6p^4d@0re|9WN7Yu;qUVZcvSNy;dCjjPlakdg!M?3%ewpUk$07sRa z4pyDSWqTX^EEL|ukrkdK#LmoDEgzw|r&L5yT`9fFo7lbnXNPKZYfP3t2RbVn?m4cyW5=(XLjvBZPUnVx}Ry z`ed>VKMcLRHoCnk+K~_%1Fz0Yux-jfJU7jB2_V_vVV5qB%wn+J(^r8=IzFe0Ko9Wgo6<2cx|L;H$R(@;+z6*d;K$v7(C( z_1Q`@gbS~}#9+C~PWvm^=T)I&z8~ z#Ez2C1F`KyzF)bO9fxo-rlFp;hjZF`?2>uzt)TAJ2AqC2wWhwdxJJ+E+UtpWLYGdV`j!DgFQVm0KzN%{*T3}qiDW$9KGa8SAj4(;EA(ffZS9BDM zfM}W4m{)lzaIWkmD#I%tM}^U+V}t2E^s)^rE?g(sQh(f51G(5f0yBl<5qBJ!$ICs! zz=f9|vlTw9n{ivgNWsMnkCHIpG`Gp^H0}?1xcLlT{;K+wG=rcvzSiP~kKI787~nu- z0Ae;@SwBY;gcWC`jq|UOwf0C*cFq^oE>)ap!V6k>_|=}wwj8CPd6KyFUCE*bsN*^g#3N<}bQ_f`F3ZtQF_&s$(|tLA0?(Hw>003PW)mOC78% z4_AWl+}*;;2Ro#IomXJymtY*?ja~@!8iCBBQa-n*cL2j1aA~iU#?XAUE}aX_Pd^Rd z0tj-~*4@b1`fFe|e2|uMg(KTRM~T~zrIP_&ajKsd-TA5nUakiwn4p6Kcd-}FLj`Rrt-s=8|FZ^&jab$Ca&U{%uluJVN2_nq-xg^y=F&hq;c;HBXwdUhs=PrC6T|)D62sH#xfq=TU-VGQ$8Lguka`F}j|%GwJFbS)0$vNB9u_y)`Thbsy@#o|Q`}kCVf@B< zH0}=$4h`u7DZlBmuD)bB2aY#{k&=_!7r`yOszY}uN;ZkBV%10S8hDDTuz55i6}hCb zj_ATpts7pIohxb3=E5!D3WPb5Pb@QqC)ab_JCBf84HVL4zDhRhk1iZm07GV}kh2By z1gjGhtBokeO{_hdG-nbgQDBBCnTMm?Ih-$WkDtkwct3~xQMP6iG50zRgobow{`)ubQ z6gC#+0_p;^Qe#gZc&?%Ts>sdrXHpN`Y0PxX=h@2;=fP0wQ;YjmcxQ1-z|-B6K>}lu ztL8~-0%__h7qXcIGeF1!*s8p4?#f*kZsk!c$BbEKG2cxEO>UdX;zelr0!NsMwOXOiXnYhWR*T5^xYMf1JtN8ogd{`$@?evC(Tjf zm?9Re`;*m28^no-uIg0mG@ibU`uQOcV_|mi;|;@T#Du%kXbPE4`}~+$Mq@!k1t44tEK+WS>K@ObRt9NToni&B+=VS}qe{4!@d0bk zZldhzWhVj`*6OrHco0R=s#)Neql>Loup5rfH=4+Ap<=!YR1r#>*d1PRb;>yO8W6W= zD;FX7RbO$V%q=und}}+4Xqv|K6>Dl)c6aosbXjAA*1=BuL6{HmH(+$uG4G4(3anEB zTH#x#y2Ga60aGjf;9+gu2$*vWk21wF*{sv!+3SS0h;oELX086mOShK4%^hizM<+q(PY&i=l>Xy>rIr(+0;kAk<$qnIp1 z*>?_fbqq(XxHE5$b@z1-ZMO|{>>P@^O+W#W3)UmLE7~{g-nuguiw-K7DlpK~aduBP zWIq@MXJ^zF9USZ*baeJ~4=5d7UG~A~(9YhdM;{IOcWh@*k98;-J<|dpj?SIYf&T8kVSitAcxZSq+R-a&A`HtRhv?!bMcEBgyxxi` z2#0Pa)pTPPO&~b-snY{U#308A>#kPZc6C+2923HPrwcbA_^}{7T8=werX$qGy(pJq zZVaS)HmiFEdUir5@R~Mv_lb%l!j!)LVSIP@ZEFMs_3s?)j4D0-9bHPSqq`^C6%t8J zL2l@sVc~8R(cOK!`p=AtK)C?J2yGIgRy@b9{g=`{ao?f?Nm(mdVQh?7E~yOmEMnI+ zbRTX@zJwE-om<7SkF!Hzc8$HPhz4QXOSml=7sP~ImvDlFjDqNaj#ewl zx_djeMQt5BySn>b9X&nDu8y9a(V<1L?jAt6GTg6p>;l|(Y=vPxGyuf!=pXEcj_K$L zZSUxbDY3!+UPTXD9_t<+hExO4;nBfeP`c7FGyvmD8SH?urCWpI2YFmdhTmsZi+r1I zP!&5l0&PlH6dWOOE^%k&1dKUIu*!`hXuy2t$A>&qg@;eQu<-$wD#^Cj@ozi_rL_?uYdxPbPwBi_F=43 zK<^y3a|4lfm5I5w&a*pvqF%#(JX6H!KE_V^be}NErF;2d9Zv-|UoaB#>%E=*y#qbb z;iv`D;h8Ywy8_JWjMzDplbHz^!c%zPh@Y(~4&cEd~vj85hx&LF_^%V zSpUwxu8;^YO;ruB#NmJ&WKb9p?LzB`uoydX`5caO4QBEoHX6k8Q90enPu4PFjJs{@ zV3wC1yHb;kh&6G~ih0~Hz2b^7*wGvH>S8t*k@}|4o3`aUhB^kqZJUqZ zd{TIQVnet$4{5{O$~jd_q$A3KRJPh1b8cFsT8 zVjVq0Q7_Ci+lQ5{gB_h`Mu)9^xP%T5?u@!C;Zb>K-O&MY10PnL7!<5Z-^fO~98g89t_imUi0CE3W#P|eDFT8TJC(4Qi(GrXG zB7SgH*(L%U&H zQXMOg%PO+d+3bor@fj^#s#xmvbK|?=alt}*$IS-$y;bJKwq(-1rUfmmU+4d+wg1sXp}VQNIUln>+ak+WbX z%cL@RhCi-p$V9GZ@fsQn4r>K9!D7R4b5}8qaM)TdX4gBl@))1-PO84u6vpq3#P#j1 zi2el<YVOQha@C)63`=vfQAHd8#Ci5C@Kyrip-rEb({cU8G$e)2;79UmoK(<>x@h)PRIHRmb? zTZIIPCJzl0wbahj7&-|`mO{tnktR3jo}}%G*lqIdu5xDp;v!!)6K(L)c6Mw37TA0$ zRIMqxN;=DRIQQF88Gt5At1rna>sdy1zO(l%A{S~ZVKyYgMIDx=e`yky8a1gZSv6Ri z)ku|AyuOq^MSVlHGVPsRR@}KwtI{4e^%;unh}M#HsTw$XR&GeldC<|iJ2Me*IgRK# zlry2KBD3{$XEoQ-TY_gdYTon)@0x>r8{cH>vzb-mw8`^cUh$+c*N#5w1~=Zxq{8VB zIkb+LY}7VCiUR$nkSxlzysH@!!{JNL8+9D&gO6M%p4v=lkv{U@dTIVrrz!F0q3cj)I~5_UdzO^nB%_LCdFfbZ4*5m;k=oJ)a6)TL;j)|t~AeUf4S@*U< zs_^g7qLGb$yQ-C2w=U^YPE~{Smo}l>Ub4!Z%u2#u#c9#lk34`r-96HeE?1B%9Uhbd zq&$X1?riKty&|gP%fR2eem9L}qpI;uW;})4B zJ2T&TQeT%v^)$U(ULayR!$m>J-zBAvjvN*1D<*7`Ov>v}PC-ivXZ3X-&ZgpOC|>ocoevx49HanIkn33vYJInRRPZlyYL?z;~WOE-d;PI{#% zeSPQlA=?|e%D3CdwWwsOg)duP&r) zmP&Gv%63SWx{Nm7T`Nv2|Im{yX*5R4{MOS?HfttF0!+oVe8!X`y9oDVy7fS9{ZfhD ziXues1f>GtoNX^_sJiuZEz?;_o^WKl50`W;DX8^eW!1l2B(B=W;+_Rnc9B$Gv;?rl@ry)&t_Q^a)c>kP@EudGV1 z2FNQ;l$V_RMJ=8ul&C{mY$-uV(a8?Bh0vG+Nw#Z;9=v%|efFI*%-*=PN7Ug-wn}N% z^ob^oo(^{DR@Oc>VeCHCqJ43f66*!0cNKP)wKpn{c^fE5%uP{ZF0(@jVTbatn)0F9 znW!2WYTltoNyjo)E7%@oH-!$|?$#;d>&UTsDQY{F)w#63;?xx@kUn^W9x1Aq!3u8h zNp9Y-zpV%N?YPgnEi!;>!RiM|AG)L9_JW3zHb|8v!m0SLw=F_nC+c3jkx=;Z{HFT2 zRkzJP zky1R_#pj`RY`LrRZ*a1jvoby3R+nT*an!z}!i5+lHFQIDgYO{ex~n|b-!5;DR;yd9 z*3_50&AoKuzjs@+vimX;gOH(*beHwAYFuxw^s5dU6T21(s>)t+jU}3Cfrpa9%}M%qn6E{LCa&i$7Ggvt>SbEow~(x zZZoiBZA#R_ZneLmfvj`el$NN7lr_KZ_`02YwX(Iqrb$R!FL-TZhrO2ZcO9sT@7kt2 zwT-^B#S+?IH(8XBC^HJZT;Kjc#h{AgA;feUu9&Uw*pR&{TV5h#%g-P=$nIgM`fk&x zTd#^wXVc=+AU$9@zNYCwAj5V-b8V-zmsz%|+mTY|#L1Kq^!_JGyJuFot)gzdG~W#p zlrbWz7cWCBO4Sy^Xo>Ii$D7?@3*QG8L)b5&GE}afyiMQfQ6#km&Ou`&=Cm#DScZ&E zU#BFg8orx0DAPK}Br2bpB($G4&HpQlQW9g!=ZJ)euVUeHm$0foGAfR%(M~a2?t8WCDR<;W)47O17dg~%F@GlRX~!UxP{&aVyjAk;L;;$zWl7z7*=<#4 zn7XY}@GGN3aO@0Mj!|KPM`_-um<)C`RUJAx`bl^o16|D-s8H`KaBg&|>fOXZnMuXK zt;u&n0eSX@9^-?H}k%Dq-F|-sjb?b$l+6pWQ;%cXNws${5zO7CqrwBIb>nPNw z^FgWpllJXM?R+;eD!;U@z;|h#zCl2b(wNTi&)JFN49*CG=^PLcQ{6jfuTH2Vs*;W= zZRPrh8orA)-2CANKhK{Jwf8!a>SVOg!|<+fStnk{k!XrL+s^70YAm9Dku+?pfW6qd zEW;g1bHZw;QA(mEZlgQhW(kCLYM_j^U0(QO^6;PgK6iM6B7jO%*KVC@im5&l8EkV$77l<(|Rd2;)tG>S)+>XtGLTujVFqq2z4j}jkYc; za|emy?MihC(TDXr7N%NeQMX=>T8Sa+zhYh4sPa`y+awB7iEhH)^=Co!Ii1@VNN;y@ zzgS6bwtEbx8Vjr@^SJ6pclTLpQNK~wc44O=H*Ir6!i7?(+GIWAisjLadr)u<0^S3iqTPgc7&Vur6T~VoHE=VMyiA39U)&p2> zI}p*spi~qKT?vO;G}$EjeDR4fY6jE3$$#HMk$Od97wuNR3u;!>tYDiqh1v`6bH3VRFx!XMt)lRyBgFE z-rk@=?XbM}L0`QpB_4IN!?Ts%dV$=D=A?_-rqR`P9Z{o@YP$zrL!s7#6zhHGykUb} z=TKW)_WAwUGI)2vUA3b+0py~Yba|clLq3y~C!T!z@kY(6IQ=Gy6a?FrgXVGy>;^f0 zL# zP>^0;DwoNZG(#sA%~F}VV4-fkR5eMfPY+FR)J3gQ??6{D({!FQ=B0G$#mV+DXShpi z%M!Hdx?|YPo#io`KKX)L+t~c+Cm*4uMmM>gsl=%iRW@ikCB5PW5B@71Qak!Tq55^N zPe8SH?|)&L?-Nv{AWW}!+ItegrHrH`1iLPzgHjo!BGvZMas(R3=-yW5b@#cj>(Hjf znN5mYw^y@5Bmn9)z%~9$AEK>&mqS>M{`mOqkE6Du1K?C+-?FTvO@^LCl1|kqaBXi^ z%Wm;+4?A<11`3d9aEFIoCE<%ap_H;(vpg*MeZDcj&Ed*-~IRJQPxd-e?2YF+4tEs5+=fvW%ASn zgIu+qOLx&am3A(a12bfIgWd*3xy2KYUGY8PxDVUR*Zqg|{Dh&QR^@ioy>0=K5>|a8 zYNj7e7O_dRkdl)e>QNPSz7CcOt#T^I$V}Dq0-}9+)ERz#&F+8 zt|n^M=+8}_wpuy*u#`AyQE*vU&MeBof*al0eJa@K%a?T2Jrz3prpOMGlG+F*wo%6p z>b45A(L;Z1^u&Wr9!f3t8}rgKp)TerOKsMzb%!>^_9I>C2#8FrYf+3SY}vU3iG8ug z9m;jIKte4RAZ>~}7l#_rlKIdJWyN&s5G|l@`&5Eo`F16q<=ctA<=fi3d|UdLZ%YqJ zAs>U(as*qIc5D-p%be2n2&We*L5igqtqqnVW-`u9iU^Vl3o76BcfI>3qRz;kv%P{+ z)pWMo*HeS{)R`#hfvkW^L*KS5Gl!DwX={^xi?=E*$tlfGIu324M<>-oqKYG;9X>(F zWobTI-}i$^K+>yU9=5|uCsW$-Hl=m@;;xO#Bju$nNvxM`o3gVgzjvpFSO$a*14Y6l z6WQ2$9R{bL=AbAa|^9JAgh{r2~RCEG%ucS-k6RO;z137-1v`vh-upD0aSu;t7_wJER~_bk^tl4YLP zo17`f>lpxdKNoN#dFso6e@z9mjGtr|W(TqRn`*q^HrKOApqds$9{P z)JdvEYuRLbsYP4g!W8$K%Fc9anBefQCQ^yq)!8RyS66d^o)eYWYAI1xrAv=)RZ?d6_R{B3gpltJJWr)9$1Crh^+L9al7>5+o<={&by{88*8S@oYY6M9T4?^R2@HX`3??K0fJmN{!0>m@s-ZieToJ7q}qbWLXt$eFM5O#pQ3 zEVmz6`qGIaJ2$Cna(%@2BrJ?6vYRGG4z)vGl>R@(l+cIASnh0z&_}(bsEr;Wc~;EL zx^C0srNwRPw=ZpNw@{Ney31H5q`t^D^6ffC!-loPa+XD|+KM_;DPrP+Om1f!Q zqPJN+-X z?g-9^EoU{Fn&o!dti;K0nYc6)m^3)y9quNpW;s^{^Xl@gP9~D2&vPkct=9CRTS~mf zxPr&-4HCAUFr`Vo=+d$T7j!6M7u{k+NSvX)V5n!8WPHOvq*u;YX$NW)vOA}BtBR5OsVQCkSj(q6E_pcRVSGh9F}(Rc}d4> z6g(j>fXGcUZQ5CGD5u%J8WlY8-v@I;!XbvUb(drZiT+Z`$jj9sd-@~ zJjrFday&h0p9BT)R&`hiMSFz;DWly>=djvUEAMoEq6r;N(CC?#EpCTYbG^|@cd)X+ zyX&^d&Z5BSs#I?Bwg0`OYiTtlhWqE}Wx2`7x05c1u^+D3nyQZa|65a<@86x3=n0Kx z>dOOc6qfrTB>GlGD2GLMH42#XAsxEP9U1tXoAkyZbr_D&@Aspky(P0^>9p_j-!$TDXNsM+R}OU9?X*Vqmdhx4 zDDPC)3pFe4q~)sa7%kfG+Ew*zi*{a`|4D2S^ynqb+XaY z)8afm?B{H(aNX&M_8xS63ej9s&ss;zSrMGa% zr_6U#)z(30h$wM!L(h`(gvsJE`MTVvz2Tjt)qY0XJ6i!=fR~K69bfX#nHBb^ z-?nvGjYN|&h@mfbN0io=WyF%>Qia9Etu@!_&SHn_a;MAP5q7wecIvbfcy&j+E?pvy zhMtpOR@kmX+pyyP4%e2$I$Rfs&a(KT@BWe+$SWkDG~2%6-FIqx1R$z7{Vqp?2VK6? zoj$(HoyHyQ8UkGme35}L0WTToNE&4KQkT0!^aXbs2!w$k1u29E0&J9ZZwn`P)TvZz zcP`iQ1owzzR0P4jZHMw;`{IsmgEXaEw^AS63wS&)QlE7VQ6dc#MN3YE(Y$5w#gd*4 z*-Nan%CpW%KF_Q1QeIRPDx*;j_I#@C-QQAFlhus+^`>6n(v_Fo&Py6rXzzxiV=Y4ca%wt@>}R8L zeYM>4T4dkc+CKv29$MipGLmc2YPpUe9bd;y%><7e(LcdSs#B^PoD{dv!1A@LgKXQ;cDAg#JbWB**6qhz$9&qQ7Lgl5;ly))8OV#;~ zF+FHU!zU$e%Tvlr=}1UXUMi1!td{7%>q!t|MOiv^MBP_X!q(KD`OY-)o>A3=fd``Q zWd{#+=)`VI^*O)arYpkaLK(0$&qiC88kJYkvasATL4tbCq+uUDZ0xkuBtG8$45hXs zczJ?@Q(~OCc(1Q!RXXin$%w%1WJdcRDSs((Sk^I4$!aGnev*xfJ4?CxrgZNP#e9_) zYr8QcbH0`q)Gp4FGG_>(hI=(wk}QPRLH70S0oSV|sGQa@P~HA6VSR;@Qyq6@Mc$51 zDRNR1Zg*&lcb8C1baAfm2)mbgpzE6;sMJuie8slw)^?BRwCm*7$@hBI)bIBeiew~S z`+>@e#)6#vQnGG#re~TenNyvi&^h_!0B;RgH)Q2vxqGx@Pq`qo1I;Dq%pt!wKl!Ek zws$6hjyu^5(g;ArSL(tZpOrAt?Q8)HyO)I6cRJ%!dcMV*E=tUQ3Ey#pS4JvvWY@Pl zP*W^VPqaxcZl@|uno{fOOIo!svZm*m8laLgtPs$oQq^TJ5rs`AaU-i_pBAHVk{lvBYaQQag2%5uz4Q=g6hDo?cZuFO_t(y$vpE zYpd)jSJUUS`W}$3@3{WKgx9?eBiFeNLw%YvkJb^9eUrDL(RtW*UCTTXcJ*cHNnJ!a z66#eUY09B1R9UV^ro&~(Z9i(?eh0M*r_dP-m#@(FPP3iCCbfE;jtw!Y|82ZlJn<}?$)TK*{_T9>97pYyj3q8OBhdJILL$UK}C`69iyW+@wJb>iDGk_M6>CL#L=j)V84; zXpFa5t$n7w32J1>{hE+GStG}_aC*}n^jL5FS^v!wzE{T{lG6^hjOVs?Z8v(=hRb|u z;p`8k39QZ?dlrOH-;{RERVV4W^HB0Rne_`&)ZU-$q&-7g)UV<1KXg#L_|-v4Bq2L% zf0T&q?%=HZL}^7aS2c$2$H;a!O>Zc6gq^TE-c+Z$v*Shgvb+s8NT<9eJ-Sdyh8d;2 zoi7(?D9?O$|B-B$?b#Jo{)jr5$!AxsCB)_~aun4rfgTYrZ)}bD%*_sE4^yI+1ZljP zKUb4K_Z)w&6@RWRf37ost}B18oHuA-G-%L(Ki7>t$4nMwVV24)_AL7kR%X+EXr7#; zrA(DP_MaYaR$$5LU0Sv#TC2OAf^=e=1&O6&X<4_fB|+MxAEKj2x}-YNfsExGXG%b} z=;+bPMuZX}>@6SHso>7KsC7+xlf2s(PLnl0)oP!wQ24yktoPA3CRCS54?q$auohrd7 z>F$y4$g+PeQvJBjrQO=KM>4f7)t#5t?r55`wx9qFf;x2LU;5Auk|wDu@h`Q`qTgC(ynEx?^XjGWJ6QHT2fXkc-z>Xq~@#$D_6%<*K(?wBF~EQtzOY3gBOw&V0w$>75J&D z;n2$m!uf7(Zo0b~@}0$Z=@kwxucK#(^6kV$<=PLovaI!l(^%+i>@|(9n<~zHf|_yc z*prp}>a{vlGu83cYpiW`pRJr3_SM8z+rgVHz_lj97)|GY3Ye`Rcf~-)k&el?6?{uacZEAL2U95bo)kvw17}BAW zY+vZiZ+%#n-GDVIY|*-dIc0>K~TLHMgDJy)#0u+l@IbsjPBusv(c5 ztuNP%>O&?~#Y|f*+uZE`cDrtg7fXe8sJ*FkYaP2DyO%X$4~JX3M=2U8@*(+HJztZp zS~MCll5%CK?%{VTrb19s#hH`Wi&$x+Z)uk<2?ox(f)1)vQs2qI9cgY$ZQOO|Llg5o zweq%ibhPZwuCu9rTe2>Pw;+_oLMCamjKYcl0PW(V)!^_@mG z%MNCn+2QQe>?+wcvTw<*SIx}MuNKVyUA1ubQ`s%DTUV=*-8Q>JcK2%avj0zZf0jLv znNyfKgT-gFP$0gS$V?szm@N$S@`2ByRZr^JV(fW1Uyyr3tE%o6cAq%J11R>aqlx=qA)kh+h^J46nEm>-Jx zNz!~xWQG}^X+m9M#+RD$WyG#A<7=UsubT1Aq~AhhJDKb>*I@$FW8rxkzGGIv|q zd#(7}R(ziof5(a+AXo2M@%KsnkjM!lKM?5?1{pwP5QsT89G^s*8KjvVj?W`?emK5} z)K%g5tKs+-vfn}E5Rto8_KO z&HW)Y_eyH+)zsV{Q*(bx%}vRk6*D)-V7-@RpNN^KiKSefdyt#E)1)6$jO_kZB=@`%cY!`OF1rgpZwa~ z56x=F6nBD5`ynG$g}TThvuf+C+zsGzcM>@u&2rx(p0ZFbWuaWkLb;S-aw((aQZC7* z{86nx%l(8jzl3usyXI1oR~yK3Damsw$#W^mb1A8FDXDWQsdFivb17|eDQ$BpZSmM% z@}O6=1v40DL6*X*Kq3#bSXvE(RS(bS3;065h%e?#_)@-%FXt&|7s`_!Vx<=XEwh7tSBgS(M*ae-oZw!K>WK;MRDJ*S}+Pl0T8H zoU?5_I-lROo$uhU)5G@#W32v8Jo&pfpp8CYu=~UH--Lb_-_6IvD77&&!u99#J-pFg z*DySRv$r_;)Z}fxkAf(|kA@Al9}m`lkDt#E^6=A#OWDE^m35zo&fu zQ+F3 ze8vU5dH!>HpcM-n$KY%6ap+?0*a>bRLfU?j5Ea%G`I=LBP5UoN-wC?YJQ(YEhIB7w zvaet@pLaV;+8*6wEci9F=jeS?Ir~#jIpaclU_KK{YVBQ5L;|G`w1pL(>s6R>{t1~NOnz{ zus9pTqP-0^laFEJSudjxqPwp#Lw;TAL^gr3eg=Jbe`BU=9-hV7K!d!Q$p#rR>^YIa z2Ahx24l(Ajp~f)nlFeh*Y=pqDvgh-KY=i-$CCpk$-!&3GtYD*HWQ{i1%Z3d1DmDfN z;dPAFA8Sx@t!3lrp^Web#>997CiT~|3FKxYV-uynudqpmd|9@&gR#j*FgBlWXH&@I zU5rhY`Zw7$((gqv+QX(BFquL6{fy0&_WRf@dd+*xn#0*_BR&Vw@By1kW?7NrcxoO! zQ~wy7Z!DmfpMp`W{zAAq$rh1MXIKutX|b`yIL(%l!CA(Z897Vf;w!eC49+pOLVnWM zY$bVdkuhtPf$(2ot7Z5jmr=Ygu{C72)=*~F_b^*WCf~93 zKsL&-{mNb?gWkLs53)_t=ruCv%VR;d8R6)|w-{t%Z8g|7V+q?1v!Q%0CFn>zvV$HO z!C#l}{i>>9TrflMkYFi-31L_w7!fQgLNQ^aiIj8^$`C1;g2QE&;Msyz6Fe?ba)gm9 zc%II&;t5xz}C>I+^Vc%d*F2!6ZZ4F$hL zMDG;*F2U~>#yx`nM(}$@xJVek6{+`$;O_;$Ul&u zFq;bgm|)EWe_R+(2;)g%G#6=4iz?3u-cne96xP29{wI<0XOZ$3k@BnvJtz3{g8x-sKYdW%hjX6xPxlrc;^TE#o7Jy#}ECLn-OPHv>6zVdl%b~6S zR)Svzbv4|tfw~s@bx_v>8-Q1UjWBx^>L%bdxZ4bM3$PW~25g7f4ydog-A<@)Kz$SH zE|?>XHFhJsdzfW0apPOS+rU0xKkyE40M779Hy#8I0q+8bf%ky-fe(NWfg`|C;3ME; z;1l3e;4|QJ;20AqhvLQ)z!$(t;1uvBa2hxRd1Au|RAYd>THy~;mlJLeM&<_QM z0mFe2z(`;eFdBFn7z2z2#sTAj3BW{P5-=H<0!#&_0n>pQD2=nYxM?=jIZ)?9od;iTJdtkm7cnkd7z&`N%xe)JgQ5Wy6dx(qhyD&ctyazq< zM&0*e_5t)CLOlW;1&;>0F0xzQkD-17{ijfoH|nBU4u1~)G3bv&Jpug};7@`-1^!Fm zG#B;00MB4C(E*0IZ6NFh0fSL(4uKus z%!Wb5n;G7WHeIk$OccORwC4hCxG2CgXu|~`4i%wf2tWH8KnNQk|FV&WXfO(Ph?fSV z!M_Y1--%{Yu+gv|2X(w58cu*Z5$YshvLV7#4RPl*U^*}Zm73AZpWIt`qmIERWlq3A65ufd-Kz5%|abOSDczX)6c5I;qThoZ~i zzoYaAz6bvU)GJW00zU#j0Y8(!z^}kHpjS{By@P`F3Bn(!{h;=TIshu_uzL}YMW|~Po zP$`cuf!R_3bs1Y06c4Nb(5&5$y5ND8Fkb~A+z+6gV5`Be0oDTRfc3zJpt%1Ps2hVK z{3_H<0KV;k*Pw2O9qQr-wtz?Y*j8X0upRb0fY)J<^kDPpIm&l1L)@@8puP!p7wmU~ z-vfRxfVgCD0dE6{BeoCrh>r*M>ZdL01g6&fOmnz0MdaWULHVvF~kc)ys-D6 zN1cBE``|x-KOaIxTs1xlp!sZ!ro8b-;6DaF0X_vj13m}PWH&xW=7wl|9O?<+3*aRD zI0f~~pm^vE_^+T+9r1Ngh;u>l=y?En;ZelxqZhzmgd6z(=q0F^0le?g?*QbYEch$cF`>W9Z2>cWjkNpDuHK@HH2lfW~ghbQ6Q2PNBkXGZFc&tD80l>hJcx({V z!N54!4S_ll>QJb|fZ@OhU<}o3P{%?Yg=Z-2qoIEp{1{*?Jr9hhcL0+@qS<7qQ=m?T zIt`c(eg-fT{4A)mfjPk3kOFX$J%u_W1#LtN5&31rCdbM~R>Q3Md;7wo`up8I| z>;>L}d$jQ_-VO=A5Bxj8fslBH%Gp8isOQ-ssP6)(-`QcP?*Z=v9{?W$M}VWiN5IFx zC%~t`XTawG>T-rUn;nC{RBn&M>;&*dNYpr>``fs3q3-vs3 zf#MUm1hdP)ci_JVegLjOe--MFQ1LAL3HTZQ{sR54z%}T5p~KM|=mYcx`lX0J;a#kM zipU(0BE%q=4F!g!i06g_BfyUYMggONmw_?BBqp94n=A@J`4hrz!G73Ju;_o03O z{fEF2#NSbMILuA}UjQd5Uf~bYnwJ{KB@FVaO%zlRY3-Bv&4d^A1 zXMjFJ)bA_AU;BaY4|M?4flvnlgN4W#0{u{681%z|ZB!>g9SNX*Wuss>8vM&p$3Ptm zj1%Go)B!J`4tN2w$=@bGk9yz*l3~#PwwfeFtH}V$a4Xa=t)_sV3QPkaGqjoxbq0WZ z)Cy(4)lBec^NiU-l*|R@0rLSoU$Ow|LcDvC5bc%#Fk?%hM}5_98Pw%4TLEUv-UfIbv^MToy|1dz}Fj&|$suY%tMpq~9Z(wDskcbkDN0_~X)!R^9|7@{M( zE}c-Vb$VThPIzsnolxHZP%U+O6Y4Gs27-ewMyK7-@1fv;-wXaNU?(-1P$_GnOHzv0 zm7<%%_Y12r7ym#={z1~~K|FRyh_ZK~9)|iJfLgokeW(as*#|3Kgw=`Dai+2hi%4qtz{kBvF10dbIH6$Dy7;41FQQOZcRhPT`$j zf=8?J(rKt?fUkhF!s^Wo(SxKuysrmplpg2c7Vqov4e+fH-OmFTV1}^txClVn?tzx0 z`z4rPhWU3eLkf5Q9_kOk72qoHBka)==H+j&uaMnFB9}?Kg$TszzHE~T@){9@xi5*+{TcE(+iHq|Y#ThF zB|H4ODTc5&h`fnV?=rah zkDz~Gijm}K6cxu&?5HWKd}LZ5n?W&}eQJVi2wESR)S{Z=Wd^bOWrn(93_EUG{a8qh zWnaL{lfWt9O8`EMBe8cJ$&2HtvLDCJkQp-e81|KEJp<3ahQo8F7|*^T@-1}diMc@J zB6OFCxlAdG6#b4gXavTy?}__?$Q9UKCFVyW$cG@r{Y>N+BEQ1L8Dg%<$1RZF7AjL> zAnlH4=cp+r9r~;jS$|7RkSR9+js}tjub99FS+E;S1ii0`YzT2f$rhcs@GwhEW+-2i z*$CoB5<%yRjUwh8nWNxOqLiM@Mq9#o*%H&(I3nZm5)_na6rpSaH0Y~LqeOuT*-s)e z8Rk=nnM!1uC1S`L)7cC#h~McH+UX1p)^s)#Mza9)C}QXZ#fFphY#59tW{w4d{56B2 z2Rnn!g*m!IGuSH9&4X?}fT9_jNv;==>xD#+<6`Jr#E_0L$oDby1^8l1%wkI|5ng7A zIrQyp1wy$JSOu)MM9vyZ%wy{;wu|SRz zLHw*^AHn^{r27QSr^I|lHQXGr%Ik+Vd;hS51>W{!rz|R&e){yNl#QkcCSJ*Woy~3#L!(tQbO9U=9v3@Y>9~PTQx0wwL zi>)+tvz4J;-%4g%*V_NvLo(aJCKEw^Cd(ZRqT>!Wg}A9irs091O>|`^cn-vysusKBL zhOJp)L+p|Ta&8z5`2wrm*!vgLm#> zONpCF+%h7|NsH!$yxPN7kP*c1y$r2RaAjD$#a0tpLu4(Hbwt(^*+AqKB53Vm8^hL0 zgp_T-%svQ~kW=pgRtnAk7zGP7-q} zERL|#(47IkBF$MaUjyez^9}j+ZCHH7E{2718TcL%aRvAh_&F?&Q!np0b@Pr@ zYsaWQJ5F7<lK3uGKM4X_9zMxUaFIe9QNWTbVcp?)bB4;8olMsZ-#7u$7)QC9CrW2U~r8F+eI9jqS)?;HH0A(!r&@7zsmL!hic?U z_I3nhUqn>dAF=vEp!kW}-S^mmi1>xQOXM(-_lUe75!dMPYwSbfju1g3dW{_=?jxk` z#}VuIhUmp1pwuMrNN! zz#S)UF+FyPp6E+X&_ed+$H>#mf zEY+8z57LKT*oXY>LxJc+9`zx|u`eT{KSw_7N9O(LL;CZ%2DnS4y+YbE5pWksdxALl z)Q>{lj{@9}Jn2Vo9LOO#4&;y{2lKBZAm@mDL*!c`=ZRcEU@sDL3Cv|;z9aHIky%83 zAaaGsRb=KLBWR6(Mks%ch~X5YBYE#Ah~$taBRM3mk-QI?^(E3T3OSt2`;%?}k%2^x zk{Rls(TX5i*x;zh84?v^_%J*(oR|?{MgpUvVk{pG=4D_EFcugGjE{=&gs2$DCqgp` zm`s`}V5S1oqGCKplm5W;sAx=Ls3z^n$=0BeDDzXXx&_z@Yy-9f zJECF|Jv52G4&BbEn9ScG0;A_-?G4jiz;0jcpKOU><8Y7iYfd6n1jF};9cM_ z@E-7fRFr-I^+VtYa1{6m_!#&E_!Rhz5-TdE%FjDS3`NlKsF+6KU?-yJmV5z|lfWs` zda0d7aI7^1~malxCIVxuIU%>ndTq8}dRMh@JAE0k4q*#=RSU+g`0|QbazypI)#Vq*=gP|D$ z3n58r(j2L~5G@D_%g_x~Gwn4X@m>opWm7l|3CvGRq-vHhu&2A!lpxX<) zm5SDGI^`*7_5u5WcYp)HLEsSZE^rul4|pH=0Qe9%3VZ~70(_P#=JLsG$zuA z0mH$M07k|{=w+y5fUz+V83%Pj%zBcE&?K;vfoZ^L#MjK2SjlHYGY6Op%md~F3xLHj z5nc-{ppFaluL7H5Vil)R{w>gKi{(MK8VG-O#SpebFggsp4}1W87!&3Zs7Ha1fRBMs zfKOv0^E0TQ1IJ=wE&qZ-aT2;yF|m%1VtB>45XfjE1M#x6^ww)Z5Sn~APcOSjMq|n8 z7-n6KL7e&p9{h^n_DU1s-f4&;U=T1E7y=9hh5-|RiNGXaGTcr9rUENdM0gs^W~7O# zGoj9c**svkVTHIzoe!f0z``^US_E}*8bYxI%u*OEgSrA(nP#ovk-R9Z)})EdwJ=%- z(A4{8a=$grN@IDc;#Iz#f<7uF!Zb{GAkE5UdGuMIrdd}ww}u$p8g6ir{yCmLmSz>u z(zO+-dC;9ikS+j-q44)me*}I?gJ<6xRwEiBts=JY0aft*z+hlV6%ic{i~#xz>n@&G z#j1(`jfdUDD%MGZ<)vFU8rFj>FGGaisv^Ssfc>z22RHzJG85rZ%sOmlSciZH(4K_V zY2Xa-Rh7I<5k3!x7l4bvcfj{mtT9HW^#HSK@w}=cW0ff~u2vBlN0`V!gOq_5Dq~uT z$XL#-bHOZY0?W3>00U#$*5FunUbYBdgD;TH!UNJpn8p|eLLHPY4)D{d);%;im5#&& zCIORyDZo@Nkt2(!)hpHiFSEhsDOIPyM z=^$(1Y%Q>kH0!}^NYAT=Y6sj#;8pl=jEnGeq`-2-#cR-S2DXqdJHZ?=L})i0lI5Os z>x5CwdV*Q|kc*;|Szg?_pXcRRaV|n%!qI8q4DeODFwa6g2lX4M-vZ}>3&2mn&%iIh zudu%cwO2-7u3)`0tN|$z5$T&@1@Z*zmtkFuWI1wBn{O08P>hr zI&ajleib!DWMu|A=D_NVyc&2>4XeMXVIAW&tTn*ej2hMrSynbj;k*G&e1=sdteS|m zeRyI&@D6Yw1NnBmEbl}nu)G_^NxoDN34yF4Zn>~-6M42c!cSyaYeWRW{W`;XjOX2i z$jJ~f#BywU$Xd*yzno!h7lyT8aO*cbuckF2R1<|h6VIZba)b{j4kFzVKAE`Bqt+LK zWH=#WsOVxe7CSQ&6(KMWn4g(f3k5yXn#ZB;fXSP{u1xU_KbR?ELsP6i%&?}L5$kqt zHRO3WTX%5l&f0mmSSK@Yu~wV4^XiE3>5#SE5P5a2zw*3#Rx57Jf$ml$&#fpQw_1T) zky^J}Q>)$@TbprfY^!-I0yZ=QWLi~_=|pA_nOW7^Wk&LDvx<3MzV(4w8}Trwsx^Ss zw~m_yqL+bE8d+G?x@s1povn&+SB2r%kiU&{M6MD!PvjJl87UxN5_gHnQ8TX)9-hSa zEvagKWftT$u$rTct*t5s80+EN24G`VYnUahhq=|n%)8xsgj>s?U1K%Oy91^2Oscis zx>HcsiF!%w13d8|a1{8cYTjMeCspqf*`HSx*~joH7S(7ct6FbacU#YMYrj=H?;dMh zO6|PgSck2M)t=|wi^6%Y^=Znz*5~40YltYy`z z9vTorqW{(^Da!ku^}ThU)jLenb(D|LU1USDtbZ7JzZbCyS=Pib(k^V|-EWOFk?>Qq zkjxKQ3t_r3Z00qx=4LgrHiu1XdstWp!!!w#WxWgjXt;6SgGk{At&hX?k+ZX`e}Bm8 z@sRaxxUqEw?tjG7O{|~6^^TZ%4_hOcX^jH+i1l)$A@Y3|(*03<{1_weQDlQht-zzm z0*@jKJc=yvD6+t#Vu^t)u*5(XSYpg1g6zA*IFSW1gS2Zitf`TP)+<(1>tt3_e121F zWV9(fZi=GNG&Y$v&1-61fXzkVr%+St64c9CO=B}snp$%sMR|`QLTEtkhb#!0S=Q>v zeR+Sdw#5Em{p-HGW~f}Ut8Jb%|BBqF)=jdpnG@&kypD_J& z%CJ_ZJcUnr%37EGR34c_xY=&3&j#6$ZT%d%+xivJ_q6pHy#Zau*!FB|I3jgOwBf-j z0sI)_fq=n`U?^bnXfzTD_xqKr92xg{p-e^_Wu$T6TWT4174(nU2V-B{`!(y@Bd`4pBaRj#~9Ro}&8N)whE zW$3&sK`*~44ROT6Vee@bGR{~Pa$ZG;Azi+P{D4h{j$-d~GUeO+QI2>ZpCb{*s*;PU zKIdi@B~KP2*ksI*ap1;7w(<_7nhHoDuC0RHbLYmL)(2TME5}W3Cqh-MGJim0uJ;lB zy^cE`LbTxHa{Vbn?-!0i8pT&mo{R^bW2&cLto4(h%$d6!tpsW zB|?e0Foo4H=?6=0gh-Q?<}(F%LwcID=M3@9*bVgcHPU2m^=I=Cr$BN277B=yC3O}J z@&tY0MgXr0*h*z-PJEl%(8&WDoJ$usX@r!i`f%GNbJ9r7v2(^O<71_uw+ zRV6&c>IH*T+JjO3)1@TGPYjk*U1h~^*o||Kxz115S>B@|Ry`*tq%uqxKPO6%1w)kN zwQdbL-*}s5LSOg(FqR zKtlGZo>&3$5USZQRp3-UIOienfdn`eXR^5)Yt>~qbnBP=ZogAq%}eWZ)LTe<4|f_xLcxb_7xlUJE!z!lg`k(P)kDw*`2))9EU!{ZOwP2NMo#OT z(}+`j)7YP=C1bP9^8OQ@MxWq6rLN&%)D*q{!qY;%LkCmzI?1)ahM zH%{}U!WQH{VVVbnl*eIa(gP`|OG40uTpeP$QXcOxT@BBoI?M>-wOXi5OXoUxvLMUk z&(XGC^T2h%4*A3j=jTWhjkJa5I?1K{>`*m)1Ls=U7RZG;g7iGGajS`}7R-tU({r4U z&5HVT%nVYK8s(-m3ek5y6g8y?NSa6_1g&46WQ{ETkSUSoTDR=IPxs`5RaOELG=;J{(&fWL{k)sd?`Ua0$aB;!kmm`R?s9f zUeoN8@RY%${+^MKgt-6trq^l0lBFmAdt~+D1w&?)biYYO!NIBVLy%)b*B8u;K+VVQ z^0!a^CQWyh{@~f`zF4~I+DQH}UuV*Y zxkR<5X^!k=u=~>`-;5+qSpz+;N|9@msy-I}Jpo$#e%Zd(4(e_r3VzgeOc8@xoa?ff zo>=OFtdaj~ID0ba6P!Y~Nuf7K*ZZ1*sGQ@}MgKpqc-b{jT>{Ni)_yB-E2la24GSF! zRkt5HOMvCc{SU7LAsZUEdsOK9;I{u-OiD`_Ur5u*Rw$cT!yq#fm6QP_BF~D1CZ|@> z;u1?1n>_K|bcCp6p-Our2XdHMsEddcIGg)2-|s^_U416C`;4b0)iUfU6d!{<^>k3T zyb0}qQ5e*H>|mkGDlxfG2_9M;&hU+-gxol4p$Sh*H)ZQ9nE-FmBDvB^s7Vt|n^#iT zGS=Kz{Z&#+yFCiG_VlE%s8jVzkDh_<8Nc`G{u8_&xCyxZ$-?xC@~ewN$D=}jj62=3mSoV<*XIss zS%Xsz2(c%q$A!G`7m|5e8YjQfbPqX02j_lr^hYg3766YqqT9desQ!}_ZsqVHmG9b7 z9`r@`&B?N(+cB-jlg9##>$EYIG|i#FeHk^Y*7xJJC~NB*80Pw{ez zCY25`hMk`CbHMZN=QbnqSDEo&&>p4uMpYBiz;wcCCPcG4yjyYn#uM&Bdi=-9VwH}9 zxu0CIL{}`xZbZ2ui8+;hw9u8zydjyG(;~)9PiS=^efyg~Z)T`U+us|bLK`TqWNb?c zB-H7l*Yl99n}~8%;?Q@Jy_lqNKI)y{>FIiuQH|pkKH~^i+#4rT<7%=Ab>pCs7S4WquWx9;pDCMj-8oQPbEn2* ze#ygi{mgZ3WWJ+Z=wcf*No>=ja@-prFY%W>)0?ZY0;ZXhR)QRD2ErCgVJWH+SD71iePscY{XCQPWG4DZo9P4Yg)8_RCXN^#!w4ggg zEk|=3!R8LP=TdR{pwbbm%IcG3X^RE!s$}Tgv6ORt$x}044r;3>_uwmkIN&s859^90 z^spo6RF+}nAd`xShc)rZLT;S7W?5N5IV6xJN7)$93R1k%BqNwC?BiprNiWmLyHJMn z; zlSI@UN_IjO@S;B>NQt~-p=|tHC8czmw5mio%~!~tnRcjUBQ;5#@R;kOGXJ!^%_}i= zhf9Rs-^u*CKpNPbhY!cd; z>p1gkI?HOKv(sdUN)w1Jm)oQ`stlnWGNrfyV_E(}>6#?`+$nZj=XEf&JcY3?KC$0) z7fgi`Ye!X<(mesj*Zk0gQNRx6KtK(0BNCA#4gbrZ)0kJ%Y$pmW+F6ob_Qlk5cRFH8 ztn#M1;*7pdqS*CA_M65Kkb`ACaVvWuN zqR>AJku>J0mW~KL48+}Kj!EqTWLOB&CYlgh!{{z8#4j?E1x7M9ij8J3Yi1rA$)FWHwVaa0c&zXz$4a?BA(t!~e+w;$JL~^5E@Ob@m^TdjE%n-~YTR z5bcF9+|V1u?gNt57aw+8KSm+$4}IDIkh%jYoP$7a9t@H@gc*VSp^W;c!=S%uI7r?I zkXuHwj6n5Kj8^=PhCUUa5fFhfU^B*oWRH^%jR#w80!WpKAT=j}?+L61`!-2J(l^AZ1%Xp4KV^1!;R2r1N_q zkGv1k;RBGaAA&r81f=;b~4>7PKl{S0#VFCh2-3exf#$RCZM zWe0ku^g=uQ@4Y##0_+3!pM5zcWMzI21iT->!~irk|2Yt(9~%VPmks7dpbr}Y zzBd~R=l$6*@V(e@9<*wqkOjg)>t6c3DsW;%NrE+?}w;ODckpflMx zPOF8-!+bfL0J@S*1YOD|!FDR6oIHt520xcg0iDLCg3e~sK*zG_p!3)a8QPiP=dkH0 zNaNWo_^^!4hRI?!2XrBu3%ZES1D(R=!*&8(^1wK@0Q?HJ5Oe`s1Uiu|hMOg93HTXo zDZc2Iz$+*jL8}fjXWlaPEz3DIjw|417hB0GZ?3{qJJ@PYrD6@IMQLkczLTv3-NV*{ z?q(Z6_pw(%UuPRZx3O0_m4r=_z6KvQv(1v<0^7IQR?xj{8_Zv0+okyqm~3IMgTBRf zf^KDRfWE=r1bvh30^QG^LaH5R#%^fdWqUvmvb~^(SnWW_c#G3v`6pO(Ka7sDci{JD zphnSr^19nd!Bu~mm-ad-R zx-D>prGF&D`Z4UkXP>AD`xK9V%|3(C5A1VkeGL3J>^SJR>;z8_Twq^-zr;?0o@b|E z@)i3M{AG3;^gDJ2KAdA;!TcgS3zM_#Ykrhl1%UxPat``m**Boq*te7|`FYBabRa&m z_C?B~{1UGj=*us|nDpa6fd83Y;a_vBkbTGp^Yp9u>{0wGZyXp2yNkS4 zU^vttfzkXY(3kno@Np>rg$CRMzrx*ENCDSmCj1%C4&%KH78t^N8)Vegl=w?10bQ7Nex}DDiUCL*HuI95r7xOuw z>-k*JxqKezRzBaL+HV2)SNTHFwS19571Uybs=_5OS;CisuHnn@X$$yr@Z0zb@Qe6L z-jF$LgN*H0B;6?Kt8nxt-vs(0 ze+~2_zFC@YF=__h;#*kZx213UV6vZ6O}mfpH>j$82PQ}P0X%hx z9|V1uAA-p{Ja(IP4+=%-U4-r=KMZ<`zX$q-^!tPi%W?kx|7Gkw!0S4Wv*F!y_Vjuo zQj)D=N`fenf=pVjvK3pDZHPw7wPQtA3M`P6Zji)soH&w`e4i5#Bnb9Su#;f#73{rt zkoR5?015Wqz}_qVclKNWq>}G>{=}TKXLfgXc6N5k?zs>hjn^w`Eqb0)_5of{_5+?* z>eO#oZ}S7{Wu?tQm0al|QbP5xWcvup*OjAy-PL0%xv=9Z`Mwjt^iWR%-d9ek6t+%#0sE*|)K{SBS5bbbTm$T> zUI#2vZ%7_)qTEZp1^7U@tx}wE2j#BnU6nT1Jz(yu>;Ya%)rWw06xzwRlt=218TPT# zfCfKdMS`=wYFEG;O0jr}H7MOszNvHvELKZ|W&VH-RN1q_dIzfhdJjSBUW%}BzSuQ$`jP#g1)rwk2dqv5x`7WM*_}NM*%KYM+1&k#|R2W#tPbRWSu2vTU zu2Cxh*Qu?)XZ;9a;5PMVi|~G2T`VZpC7*XoT_VWmEd~4&whXnW)#ZSDRf-4e)fIvQ z!j-^0Qda>UR#yYwR@VR?QrALQr_^>TQfjOvdp+hvb z6__e@n;@gI9p!Ff2Vhd&33x=^1-MVG1iY&52J9g=gU$hU56V47QjpfBP~N4c0e7lZ zfcI6>)ZOY{l)DRx$1bYXC|^-Yi4)p}echr-B*VI$!djb1RVdp_jVf)q7 zfL+BIz?14(z+!O@@T__s?Q7HvfF+_HaF2QsuvWbUm{uqT!(Sh);q{id+qXFElUJ{s+szM9;?{WQ|L z{urr73;;YR1_CCo;1ifQO&s_Jw!C;83*@}iijkwv61;f9!nt~bSO;BSdJXa#9& zbMbat%tOt2F&?>rh6cnZ$6HDc4ECc3*l>arc95hag6&ig!y^@$!)Kx%T6{|t2tH#YWXnar1z#A!F z-uu>{vL0H?wP@2zqtJecwibi+Mj7T;TSp`ebv;mhHHz2=X&dC28-eMsZ3676Z6+;o zoGr3qJt+0jwxVJnsud{DHi;hxdV_&i*Z^%iFfp#~AmX4z8nF`^^XCjJx(kKBV52ow zseQwWvvFE%w?-~;k47tzBvsW?8i_tF)u@WpR@)0WU8~k8&8$IrvR13nH*ovF$1H6> zFf+9}jlM!VpuNP(wS&Nn*AB^rJ4~BEd-@IQ7i_7fAJJZh;En>W(vAVH){Xj%Xtyxde(g4CyLKnq zT6amZ?4BfjpEi>A0C0=;5OBNpNRn?sg1Tc`55S{Zss1u(_e8l~>jij1>kW8H>jQXF>kD{V>j!vF>koKM8vuAk z8whw-8w7Yp8w_|+8v=M-8wz+!8wPkmD+9ct4F|ldjR3rjRHKcjRw4~QC@II z8-wyCZ7eu>pph#)q>TgSszxsIpf(=m%UU_$ZS7m#b?dJ%LvsRNyXg~kg>}~_0rt=@ zC@-;6eKN|u^eH;U-BYDF&H~?4&-#}2E7n(kX_`)2N*26apAH%W^cjF<`b@y7`Yga1 z`fR}A`W(Ri`dq+%I{CjL`aDT>J}{H@1sG?%z7TM_UIEM$eS-cH8>lY=W`e$0j=Tir z5&BZVG5RvJ8LTe{9Hg%R9H!F_pQf)wd8EDyZ6@mEMJDO1ff=Q*(N#81UyJfkl#wgz z>*VO`ff=iB(1rEaY_85WqVP9tk*ait{vNxHR+~X#xUof-!Lm+YiERVs zh`t?lV~iar7aKcKex&b0xu>xU{YnjJJ>E{~yU}Bh&XPK5;5cMf{ZN$xBSVcet%qKv z(+A3AR|Xn;Ni+0n(mS1!p;1PS>{pBSJM?`zZHoOMe@5q43gm|w6|h)W(Y_9E`}6}+ zTnF*KUO$9(4^TRcedaS!c}>G$zA1#f5Y z*4ua>7m8Hjtp1Rc$=C$mXo(J?{=WVQt!wbsAeXVofYZ^t8Z?t)gD~BInPzkcyrY*G zG-eNj#w?X>dSaX&#wk#$)^~tIq;1XyBh!%!r2a*!AGz92c; zphcf(450*EiV{9iuS2^7v^VH582I;CmCo&f@@)`Wt<*^z4f+tkRr*kg9|mgOjC%Ax zq?Z}+ruuNyZqY{=v;ddE%nXQ=lw>4YPBKQxUiUE4QH(TNPIZiIdjm9v7-P}+i6}qRCt=iW`ecJthQz;Gp9;)4W11A&bUDHd z;O84NL20er7b+BZ7I1s@Ddb>*n=Sb|iJHE~8TCC@j@mNNzp2jwEHdVz?>)4egy&YW zD8P<2=AqR_eZC?6>H;~9g=j#VZy1>9ZfpXPs1`?355pcb+32>{i8Tc*67Qjklt8Bjw<;})+z}?0Uz#Ya; zaJ1Ceh4N0L5^$NZTaLR28x)yb0P2Xb z8bnSQ^#-Lq7fG3nO9mzJmr*`#Trnsnq2%YNaaB(1nnBL~I%Q$ZMDnH~ ztWQA0xP{7V#%($OJ5scFqjB${;<|Ak@T&0u@RISeWhK}x!+l8B#CQ}nYrxBGqsTxNVXwUH^QV)a)>!tVupauV3Um0SaT@K1I%FaqJDunivC^(cp{07F{Kd3qGGN&P7X@;f2KL!Bs*9R{A_aq;Cyo;V1+pe z)0u8gMtL46BH1!0L5UWdQ&FB~PLr%s1U18)Zc@-T1Nb@SO!Qi4&XNqz24<>B;o2he zZDIYEEi=WOrpYZg=gN`k^nQgo&wStd9iMNmHa(Kg1}Vq&CLLRDHR%X*tw}-XCUb#g zeHCQ4(WHaSEhgu5gt^V6a5&U$ON%8$|bD2rrV!4F0G`q|dlFlOxnl@L; z@)Gn-n!lkS*KD~8A~=IdVM~~6P;u5=3wX+02h2fpJz$-=0hkl!M!-wvCJcYh+zk8y z6EkCnfx*Tyw*r6K+=lXTb35?$<_^F^=1#z)<}Tn*nw2PDFn6PT%-jQb(M&=iK4;fV zGZigGl!@&2nj*#YAM*){u#{kD!#{tW%6M+4#lYon?Q)s!?+6MtX zFi)HGYk)Jr54U!JemCo^nZ-t1=g?+`b>1Z7aDkN2s;Av*T?AZaU6LbSCPlTbm@ziq zx+*c(qKRHd#bWD*MBPMrg>?%=Ct0^i`z^|j?wfZ=}~Zyd5+Z=aEwJqmjkVSXw%y>p$2=i@heS|76=O zXR;h+ibdKo72{S~(~x5*R2g0^_H~}<(t;eh1DI7!%{cNxsoR+vWfey_6Nf1fk@F-H`9XHTdYQ# z*TWtHK6`T3+g7%MN*{Zh9Co{fu?E`84&eIRI{^pSyD+yt$ZBk@lJv&j4LIE1L#t#b zE!xN_l*{a23acOImC6`9NMpEiy9zR%Z11HNwyQ09!&`%wDR!;wwa))HGihWADGali`u7zi%5e|JK&26KMGT4n1efnRRZDe6l5Bx+aKr)UV6 zjnfu=1VPcqYWs}!J8LNCXRQskaZa*x9%F5^$%k*UFGx+Rm+&HJthXN-0YES#`* z+1e#E-ezA0+-_e1+-hG1+-Y9}++kn0&RA9%=QpgQ_8T|Rf4_YT{nPesxtH!(GED)# z%DxMHoqbQ@?@J|o0L(%Ap&a`W$ep$uNFnVao7y};uLE`+G-9va6~aAl7fU>yG9R}o zm8-Vt#O#pW4YlX&?l#7^XYEp(2JR^d^^&l+EpNYV zcs08Z2%WO~+VXynPA%+yvQ2-Rek3sfwP);sfc5qui64w1kJxl5d%_+9`~~|b*DY%} z^xpP|$`-?Hxp&IY@RmIs@U}g|eu-VTN22*vdz5T6+NOl_5iJjYKqNTje%+lnFL{I51JdNwVi;(7kU@0eoOjwdqJ}8peBQPnT_G z*p@jO4!~hQ5Y`yZx;alDHj#)sq>wU>pvxm?mLx?ks*gju-2?Tr!9a<#PQ1tZ<86*j zzltbET~CJ;7uMU^jJke!n}@*%II|(qfzAQo`T{p!QeR+`L0%}!6%sB&&+g7*Io=Y9 z`M$8mVRt%TTM9CxoMnL1oaKOHoE4EUSK?*3vkGvwv)X=&l{srbWrVX9m~v+w#+~S_ z2WFnL0dTUj5%`(TCcvrAX27}57Rmiqlt((-0OvT{B?miDo&gF9o8at3dAhR;aJ*A# z(*?k8lxI16Y_c^;lt()$z;R9*aI8~>UQ;ll$|gDQJa3i5MLW_OFLp?%OPsy*N$(1W zSEKoIr^Xi6MC3h=Rx4L>AFykk{ebJ7I>0r~0l@XnLA0BU48%O@fw>gDdyg*A_}zOG3JU;W%G zcH?o@K$i~6`nz=GHpsn7dgWdN9PD0~^SJ@y3!n@x`$$*|u@zdxp`p4tr2pON z?*y_Bg|!4hp8M4jG+5%&7F+6;%Br4@9Iuyr>5YojZXdwKZeNL^J+{uJUAWL4An6W7 zd4)R&aHBgIaE&_zaHTucp@e9dY*PlzDt9>GGIs>v26rUjT6Yv+g*)1j=?%)uUCPtf zyJJya?VwY5uO0<07?ne1H*o@@3l8(ImNTKO{Hw_XSi9|O~BND|Pktp)2nmTv&_R4P6 z^moI0dP)uIOTAja9$tS-m|HmC=M3}=veo^({gIR?F7EHup-~@?jDBD5fFyVjm;qiF zP#W$DSO8Y$9R?ic9l=OLyrY0ay<^~MJLkupDPHVEMDHXTjrL9fj`2=A^xKXzXgbb2 zD_J8p$9w0X1m)g&iJ_@X@Gbx|$*TvP>|K-!d^z!}~>+LGRVu(}Irud~*3AJQi99+Ap>ba}Ac zYoIM9rLfGS%ZW8!k&6xFbp>4P6$38yx&f~Cx&yBCN&uI5JzQZ{63q=>Q0hhpCLpm* zlHTm~bV*l8kd?B-7D;r69AKl@%cb2)q_=v#0e4B#+r2)3o4md*EdmX>)9dF7YY%pn zm(|}z0OSpDDTbkFJ>?CIh8={8gWg~?ukwZf?)8SsM#Ee>k}i|Ph67*ijX=x&-bj@9 zd7}Vpz0rVaZwz3KN51QTHx}hOZ=CxLD}_Dk9`&?xV2*hc0FQg}LltkLOS^EAq&yid ztElB^&!XCTZ;HfIuk+qimtv7=D4+4By9mp?8NkeR8GT4{P7=M~&2m4sYT#Ntm|(Y&uucPu^qc`ki3H6Qq5e*y5NKHa(Z@fQOBJD52H z8~%J(W!?NmuJoXEA=1nLG-2(B8}S{A7>D{4#}4xsyE1NEf=&bcrGSI{Wv=v0ATZEh z4osQ90&uXu(j~=R0sIhu75J`$ruwkjmU$4`>cbve<{<$RR^Q-Ka*$&x%gQUL6-;3#Q_x~WQQ&4X|tai!r)}V3H zuZ?taA71wO`|(oe!hFleL6?5ddI&Ws|FBDL;Rxxhe^hRUW3u)*FuVN|E`1nt(xp89 z6lth`8XTX2lKL8PTqij`AbHy7?*iQKp8@x^{#hu{S(}vq&c8tl?B68S^>4W{h#+nC?*N|l@1k`* z5?$Z8hxhCLeZb59gJ|{-@p9FF1bD@70KDcGdB~IfzlTY9;A>s+a@Q{gyzO@byyJHV zyyuqy-uHWW!n(|Psn_7^Jw2Jd0N*v}4cIN{1K2(23s@YG(=7`6dGOm;u>k_RKhXVy z0UoLDK#xvr2LaPJAOgK5fu52;FG-+JFc`33FvO$XT-eXEuG6rC0(&U%BZ6UGl+bwe zk-%`Eh6E!3hXo@6hs)6i2cu9f3q}Ku48{Ns4aS21n?z_#;Ee;OJQyzrDEH{^{!WlH zni!FuBrAxoiIT^$lDkR4WKW)wV1RMK6kx_nekTOKr?1ijZK|iTslha$W(Lzyo*qo~ z6gCZIh0O?NVEj9r&-CU6Ci(US!7Q|#AIy%%Bv-yLm;=<}fZY9}fE;>7Fc;PT#(qnI zd!Woi^|D|-;PPOBhx~YJeTCEDdh}(=EIS*em=O zVf8@d9qP9{ibd#1YG8QVBacdFR{cY|Q5X-HFfl5;N4gx| zmsWv^M zgx3>RALw`pM<%Sk3Y2_ysFRY<4SV}d>OC(cjh_>edM^k`z2}D;$x8HB*rG7j$EVPw zZ`8h@-=vF6LQzkd@|$1eF_1-5Y|A2k1*GVHeGFO2;bb+!x90dYfL%ROh z5z=MK=8&#VHio19kFCK9s|>{$$=O(py*nHyr8OSqJt57Y9}~U#6hEf+;)A#{6@|fkny>SKE#$^@6d+qaOIyrm`dP+O zKFimULe567Mf{-1QY-izKTBW2S75Kg7h&hY6L6){cuGCT=lb;1$9pib*oX?7ps-B} zJBsb6uuBLlR5n&f1cHKUL&j$y6#UjC0h_5X0b(wQ5<9^()MXchu~}52=T@;pu)~7yM)eU)9fcI1gY(zLbBGgkmwxY|}ageJC|ggVz%C z47luRo{a`N2g=>d^O#%@^8zN?%dE$U-OY=bTyL|VyeGVbHWSRrCR=Limwl#dhzfM& zZA1tA+EA6+Myg7+b>C+i0xxuF!wy*Ns3jhN z&T;EuMsP|@J7L{dV(KyLw8gGj?5^cJ!t@?m4RWIt1u=2o>Kf?E&k=zz4Q4BdsrRhz zK}>sKl?1x@IXkY$j7L_FKv#c`sDvim%{F=lG;l98_yDm8A^V`g2Z&B|~$^ynsgfL8)in_&SyKQp>hS+0|qzbN%!X%US zXf*x`8-rHpG8Xeo+2cS4UB+W{JTBSnuFV>3R_w6Bj#-ZO8(f4bjDH^i4HKu4BZ0v` zu*ozLX9{}laS^#NWxb2IHYSET3mku1pzH4-hS8OG5WO(>6+|_<`Hoz#h0ZL@-;vw4!kGurzJoYNx87mP9Jb0~8y#fSI2>ce%J{aQhbtE(dd`n1J!#oZfqbp?|hka2MM16}eobn(1h~q8>m7~rQ z2%D~mAdlnDvLNPObe0D({e-h3h}jpMl^F1tvnq%gr<~P6%sk_)31Z$QXKfI3>Ya5# z%sS^FnZMz%JC3y;%^o@%(B+=95!CNGn?R(<-3)>coGs}7$k~dHUEOWy*x+o(p!c2b zuDSzE#1U8NN*rO8uErTs@IEdZ>ME6>R_5*owPEf^mrZo}9#W4hDxdGN%`S2p%0&*# z4B)1#CeiSgn?k4CZaR>ApuuG&9->k<*kdC-HqB!TJ+|Isdpvf;W2ZcZxZ}AYa-mNK%!U@83Yhl_A~&WXp*;i1-1E-T>=5mp z3z+!|!Zv1E2=HR=eGjokJu1->9vzMr-gCUqCi-lO52wc9hSvEo==>t4{RdX z3UoJSsh6RLFG4@1s|eY;knIAOTyR}5X7T?NjK9f$EST~KJ{4H?kMsxcMJeZoVA?=V zzjtK%0?uv0jOCnuEXd70oVO6ns^;{EUhR{d{$!(bhV$qhQQ_YaEOtcUZwQukM&Z6-PhC`a4MuIzU*&apwj8AL5+V50OH}@>V9#t( z`S%2SwzuG|1^Y^c;NKSPtBBX07wowW^p}wN^V7kwjX@Mi?eIbiTmus07Hyj!R<_NIB(;Ozzb`UQhOCs?~1 z2ES2a#PhEYIQ$Tve{cXn*H5%hhM-m|F*;HQT^*~F2985$30y73)ls{ zU49v_orb{XVYNDsaQO|qcbVYwj|BTrXei{6oPKX_t?41pE8_E}xBO;USmL zcLe{3OD-=E>>uGjKN0Mo9=UugYJbz+<7qrULz-TV=Whpjygz39&m%nEMX=vZ_4qNM z|7C&4ui^Qx>#$St{QYi^_izRKH|)`Vcz%wZI1bM*E_!@2o`1N4or&kaBO98H=YRC` zc|x%N9O3g%1^YjveZC6V|2xj-t6jnWIKk&@@cggIK3|LHf6wyyI@A{-_~z?T=*rjn zd;&_4Q{4@|H{G`v%qR@la`}`UTr3kP1T@-rqhd%#%!FutM zfcNmQrx1AXex6``_^5yv3f7lT4)|Eq^y9Mw{(Hgt^CbbF;nB^~%7Fh?uz`GSz!v~9 zh;In^A`}Mm?E(K>upvAZ@EsUtD8C$Vr1fkVzZ>v6V9I!(ke>l&I3FDHe-Ug1FAq6V zOE!{E3OVvWHj2*-InqBinlB7FvOhM4uMPP|bQ;TdhWrb`#_?pxN26vuucE)|p{o3S z`Xg_tRF_mD-sDBa#C4Ha`XFz zRLt!c^52IFeFm24(nz`77omyX=o6pllhCqAXwQS@u6IKOB1oN4GpGdiZsnKDVZ3nq^YuzA;z92E%+g{AXDAq z#faT>d?Y5DX&3L8nRpnBYE$X>U`2sS$IGNHJ;T*TX*??+m>)#ad*~(08&{G3j2CuC z{-7|KJ1FFtO+vIfjU(CiYnJU-XaSz41wyUlBi@S?KdSv|^VWH;c@dr`tP%CWv|gY! z{$BK&7^P%A7s;u?%OE1Q;zgJe?b=p_WAN6O28s6Pi!_TrYwBN8o!KQhMSiqQEofh4 zi&SEXJ|uT=h!)AjU>>uuZXf3q`*|O8l@Ji?cpmSmkdVHng61n^335vnR^XOmP-3c^ zpG=J58IGdiTM5YXG^te4OV#*SLoFl+$@mB!>C;3dv#euyyg%pR;>MQ=JlZys6m*uQ z$taN?<#hKWt)y~^pN=#%F$DXO{`?{B0($E-Gz?$E^b*5R;lI#Wfwxa4VcSlm*TfVp zTbVQgP18^Gr?*aJvVZ!C{^?BricJ5CsDD|A>AplJ@b%1&d_G#L-aavxzrrA7e1*ZW zNPT$gj;~>LU#9kdj`niN@~u3Jp)P-E)OIP_CUbjoRyZlbO^R@+qjIO=ne7hVGX=@S zE3`b>u>2#!{E=bCfaZ|6BmKMh<8 zoWDw{?L{+95m$t4t(vy80ILn#ptUM;yXXXrf>9x8q#)@@xuap;Y6wvQL`4Qs3pu58 z2KwdE<|oS%c7jzQGGQDAXxdoLi`=$d^hkL+>$wvmgwa_L<%tkNXQQ(uIuUXp$q({7 z8;PM|IZ;NbEHXM9w9Z=9(=!_yHi*t5{q%^22FT$xvK}9i4}s}z)iG5tCalPM3VUEo zi2tQ>$AqvcVICf+%ddz$WPU0)AF>UJw~qPaLzsjVnS|W&5Z}9{Meo+6-=pmT$-GXE zW-upt4dZaMHn}hq6PNYmtSkouk$wz|^n)bbUnzsi$+<>qF)UJnVNwNXc4dC1VR?L~ z$|bRkgX$ld#WC<1p+f#5s{7xT5>s!}WN`YB!D&mT$j%rHX<6TBYS5itb7&*neuZ*+2wTI1c<16Em?W3uOqB|KGqQW`zW2hw=Yb3T7*b89x0a69++v-_FDl@6O~m}N4D&1T3N`y`Vm{RDl2o%~{Fs#fcND068`AiC?2k@YR4Tk$ z#yd^Xs6_t~3&Ktd!({xn?C@QI4lhSsuJSX|Xf>-W6Z$m1!UlYg=JN)2`j<6Yl1e6S z@<`lZ7J=kVG7fJO@&AapiEKEr6-eJz&m#iK!~*Fy>6xeqVag+X?jkq^ySz9=ev)k$ zPmruvXn3xdPuP1ZX1kKw`erN_Crsy`ka7Jc4Y488zFj0Ly_fH@BT>? zQ1YcAU9bH}RpoS2i8X4$tEEM+%1Cb$qPv{Uenc(4Q7Y=Y)bh12x7>hsjBc2#$7QNcGi-;jL zN@9THe$Gz+vFc4)dCo4s{4vZmc=vN$=`0deN23_?vf#u^q=5Y)=R}plj5~+Xh=yjZD1$zXhX8% za+ri43sqVhY#z)5c}7mk&s`q!&udcYY+jzqsfrQ~QBGL~I{ZXa-B@0vSOq(^f)!yx zb|sBT928R1wi$B&#?r86zdixxx?s~|sEn=M9I7%PoD<~)@oK5+?~_Sr4a1D6sQhbDR8+K%SEmY| zN^Aj<4bpI>;*WG{-I4x#NDk&w5KRTBpOMS{v&TI4|0;pk$*7&=@>B*-s(AIR@ovZX zNKFl&6c4sHxx%MhH;+tSUM2$bBj<+1`-Y;(F5k=h9*bU+sYG`r=Q#FTqF5z> z1$F6y?^a}WMzUD&-BL6oY4r46HUp{HY18j7n}PPPsmMSEM?*Z8ZCu{Q8N4@W6?IOnh-rFWq&g9@fO7C zRiEfY1~%uBXcT{IDgM(u)jGZyy56`Dsd$mu{1z`ZN&9|I3LcLXoYvrw!AaF}hVk5V z$mJnt#Cda^)G?hi^vT-AkJo0?e|Gg3D%SeH$ss~~v5Ysko)K)Ss#8_iczAikZ;bsi z!$a6DGTSVJ$r&Pt`{E07Y%fIJZj#f!tgILDUta0mu6QjQ}EY1 zrWz+ne~c4vLmzviqmy4!JozqlUcjRsQr-3_(F+Z9$IqY=)nU#(e-?&Sp2UF82M*|b zmv;WQA*RSv%#z+BnIy)V^&sl}8akuqR}`>4^J|1@g}Y=by!DBo>?0FF*%~F|JR{G! z&yxG{Q=VTF0+m}6@)U^@RSnp<+%$=16cBr5**nHA3d@ z@UbYNpdUdLi6ZftLfEplv2SF06}P3+>n{-~E^el)B@?~$od||J(93euuDta zRg$?&k-#7gp5qgo>F4h9HB1?ze{9C#FBkyC#ZhwKLbm8|}Q3fMVCy9XBXxnMUkwXrQf>Pnpr*&=&Yk^R!RObezV``;I6)5M|-Dh-_O{ zV{s1fRIAtGm${I^;B)^=&Q3ilc)UoEzW$no`&V?mLsT{*sZL4q8Banl4|)8uk{J_O z(p7~M$=oz&WlfCKscKQ3TAh?3Xf!^Ee@uC9>x>y$sgfCKGzd+IoZ-@ZV6p{dQos3_ z-M3L8)?l;=x=jG+$ zr6{jDzcTWyq#AjZnp#w;)g;qcjAFBq+s*>E7Xen9ym}ANj%Y+jL0?d;BiA|=->e|) zmrjrf6|eJ2yj?U%pj^hVYdI`U9hB^vv^2jy5VkHn5Ylm& zlQ;{@D9ryzi0k(LGqaPh;tcIQ2*Hnd*P%yG~t?Re3|0rtnRoFq=Y>!ZXdf zS(HV_rbg}tRR04-cW`;B+#M>*l3OR0SfNs|JdlEA6cH~+<(+DI&Y++imH;_23_*F$ z;Gi5~Gtv1+8s>Q#W;V~}Ei9SDAL8-zoF}tc;cRO2Pt>MmWRM0bO%vHAh|k}U_4$lq z;&wI3d{++P$~~XEmn&I6kLEw5Y5s{bs@!HZX}Zd76IBmw7Eirk(Pc$Cb5oJdTvNO+ ztLx;TpHUV+NGkbEE@w|vh6OZwc}`h?NgvHbTXeZWYYHdyECnl%v6xwCu=xeWu1)l% zESQY3Q-y~(oe|9D>*N{1Yy9k*zx9<3YDUdBc zKa`K)C&HDXI2khGvQr^bO?Da{(Pd}g5ncArluLdPsld3*Vr3*BZPQfZY?zz&dF~~J zM=9asSlYl;E;tt^&WA0`$Arz;@8~$5!lrUsq-V&8_6zYs*WdCce3mTEqdYOchlv`w z>3f)D?*4^>qn44zJT&{S{^#K+O6`=)>8Uh}@BT|#t(3|Jj9~92*uP57o%Kl&XKR?F*K2l$3 zyaSp_Q~Gz#RS{hR{RjOAbX!K>rKAeGD#>(VHw8&=39amZaxOG^57m`~^?#aPOX&6g zHobPE*FS<4HH73Z%DX7>k$9ny(%k=Q{640XH-0=b!ff@CR)~0z_2kX0^x}r43OMS{ zf>Qhs)uNz_k$)-7yA-mbOJUnCX1WT=Wa4s|txSqv3Ds1=)i805E+KXL{}0t{_G%G- zwSj-Ab~fWvd<{f7d@N#FbS!2m23h&<2OQG=DZP# z{55*?<%X$>zXzie{SG)(Rhyk3mM3)MN*MX!1D#DKZ zI~gs}UnuNHx;BXW(1f>=?SDPZ&snA9tWk0nD>(~-oV7~MMkQyVlCxUL*{tN$`Z?>B zoFz(5rShRyIL$}b!CX;P;ls8~^HWhiG#?5%1bQd5Hz<;6yxAL|M0!46N#=ho?`y1Z zm?Gz#Ovgt^hd-1zDfM_#>S5qlXo0*%RtT3nL!p?qjH`;!JZU(_E2;QY`I*LWIieS} zh8!d82(SkmkiljMjQkRT!LsQnE)1dllD-F`^Po)gB0*B1E=yGzHbys3Z5T~$3>T)*cF2Rapa%cw3<~37mFX)2 z?1m)PA$PbU)2nQ)ZQE?^LxB)~90wkw(uwoJAdkz==x?B zMi|FcNbyIIBGgj~M}EE_rLvf$QlQAs7?O`;N^@t?&`lW78P7Fv`UBd27yTB|ClEPH zgBBj0^0$K}YbV-iA8J4@3n*S}r?)fO86T=0+bQkLb|wmpl}qLP8u7BGiI>-+an{f{ zIm-h&aG4-^{t2~P7)c~cidZ&WNDWEW5Y2)>QlTpb-jNy*1_Fv?@IYpB6D6^?iP+l5 z#Jjdpyb+B^1dZL5CK1L!VUnakUo&B2Hw~zDY~*O8(mdK^E+D2t+7MXJ)zN@-ra=u? zH)>(imsMf(WmV1!xrFlCqq!y4p?UdZlD}yEssD6E>DUSbPLVPVX_uhi`o?~YO_dd` zi0dU0*T`8A1VsZZX>K?TW=v@BXD%Awg6^lPBE4u!QfUKKtBN!&-FWx|31-@qc5J?K z+}zMW98H%Z_#q7(@h-pcdnudcy_8YsNQLEvsNzah%6};py-omCYRA|NN#>pZe`NR* zWQH^mnJI-JdD$GTMUYvG%~Ic1Lf@K1{8aM}%UPA#Q*-1=;w(wV%LutLvn6KB&HH@D zPR1X~i~44(TR4sk^c=V?p**jzcq7bsMt|ARcfB3(DHE#>5Rof?R;shpNnb{DEsp0HELL}JCt|v3wUFwQxuDwnDIiP4dxco zZysPG+y8pHpRMMs3(B*R2GT<*FP!fGdX`UlOwRhCm=fNc4MB0ibiX`DejSsuG05S~ z7cPaf$g@>&4Fq#AQY>4|i*Y=7^ic4S#74Riu}wj74u`>DY>|wq;BpIQ`FLF{Uq!M# zNq$LVUC!nJuS=U=x6tddC!O{3#(753rTEBvp>Q_2k`<3dZ7b!f{p4{kJ4eYJ#;t5D ztY|E(ZY-=vfh>FB+>A?JNQ754`>2)VqgFS+ucr4k(!qWZ)u$3&V@0BE%#lm9`b+QY zGw(&BCYhL{P&jlEK3YIZv7b z=7YLT&C$kO?4s-P+<&u_+pFL?9q$(7k@R+isZL#E5e1ZqalE7TmgbEJ!EL9*QTe?a zi5Nj>)M%@0^tDW*9RbnEv|Haq9PPHrcJDXlPmM$WW7k-l#-XF~>ZWpvF7~p>uq;&a zznjYauDqwpM5WuMdHo35J;|aDIXi>+O<&1x*+`kB(Ngv(a%-z(L|gU5q%#vu)0+J+ zv){ofCCZi`+s=ncYzZRtU1HL#%1fF&9j`KZvMRn<#)~_pw7;H_!bv6bab5*|5VBf{ zuTjW`t!yl;mj!g&C6(fh=#CRgks^)o8#MwNn~pD4IBCI!NDI)X5);?#Ql;^xxK(sD z5M3B6IlF@B6A&;!1=8|o=yX26r9218NCaKN7otP^f>))G(c)Fv;#HogQZm;KO2KCp zS+9|JK)I4AN1r|CeFqdZDldDw4c0m91_{ zWp^p9KC%mc0bC7gg}k9U*;Km^wNBxyz@6gs->0V9GflPgkp{bk-v#dMV+Qq{%w}vj zk-2`V(_C@`=RsIHr8FHyC(}^qvx*uT6rWbbkIC@<0@rk*{Q~<;o+_vE+or4X+A1=` zJfn#4TeO;ol$=zM`2;1|Swv^whn1pN=m%%X#5t56?{{3G%(Py5Yhw80oOD3f_l#Y9 z3|E!GUE%^9yeDNZFYVLOXM2Yga!^E_Ztz<)-{99Ks|q@l!X#lbbj5xux?;zWmpPu~ zR6weWCi&hQG<~Y9W4jpi^$aEE`YW_83&xNl_gZXpX*Ql$>_>NTWVwJ_bOpjXGqjg6*>1;*k zn5kr^HEKFbE=%k9Df+A;nK+=tPb!K$K&IH?nk3vh`jYDLUb;N$JOw*}PDf=Y@Nr!x zgcUMLYuiPqag#awg7U~Dr&Ea{B~cnfru88u=%OmtCSop`v!9Muh=Qbwe`Wl}A!c<{Tuxt0XIjo@Ave(!>Kzns^kYqF35>(Nc1H^c8xn zDZ>PpTB@_&sWg^MrE`xcO6)nh<)K)Oj$7KM<2_??cXZa%83%Y%?jZ73|LaiD!Gj9z z{aai$(F{HAnr54o;tI>Pa&CzP?EO)2$HrSn43*!L0Q15I~jt5hShp$T~c$lMbO zMLk6dA;~UZ0HwqcKNY_qTX%i3;$Bll@sky6nku?M5}Iyel5G+zC`j)97-B0SN+beN zGsRy$*8v_9%DbZ|MRydsi@K|Hx~t$>tad6<>EgQ&sOv^_DodvTeH8`$ zO^R1Ql}`4W8#IvI`ajk{jyRyX!9!{=@UaFDn@46ZYf-pF-cEkdtosO+9iS$vAu1W5X2_!)3A^DCZmrX!i2v3=M~qoGA{? z@6#mbdin38fX=e-$kR1BnMl|p6;(CNEYiciu|&UEVQ&={;%D+?4b2Qw&bcQ^Z8VrL z%!rAe#Kf5927Rc(*vA_5p$6lc8}z0I;~#6#n;Mi8yLv0I+y4o6@5p~2h4TD88x1EY z&A$tsi15*N{#vRQ-6~`N`cRf`ubUv!<$W?}amotx1Sb<*zkc6}6)Sxf&S^!L$vG0a2BhXvm+AR>dd7 zp$z)GCc3*gD6Q6Hg?^>ef#y6I3%=Eor^Ly`6s^+~4bKX#Q-zkM&)f2s8tKO0$(+cz z%Vc6Wl%dl=imIn5^dpu?S~G~Psfyr|H1#+c&VrfufNtIt@pQX^R21iIwjWr|HaOD@?XTVa<*`Pd=JEt-$pNdwV25I^tQF8c2R_14==cFE| z{b#ZfNnq)Tf-@+oll}^yF6wAWKdz8Gl z1coO@F9ub>mFHXv%B$!n4>FAD7*Swsj-o*s$VI&MrOz`!fsU$_=HH*rlX~%;XeV5j z-%7~O?Aw*f`}>JTbf21T9UrOE{ak-3yDLFPGV`H#j$;-Kj?q2@VgVuz$E$C;9jA+N z_3@iW5&`8^QVO3*YS$>yQstKnRFFpadO&4G#%sg0=#=S3P~4beDp>`P&~{5+mv6VC z9~$hCPnEv7q$d{sill!tq91MUsq*4H=a!l+J0*7M@xhwFiW9n7L+FQ~l9^Rn^b?il zelmegoYCK%ub`{;{`*WF9Am1>h9C>n*idAI?bt$vewO*INUpb~xRR|Py=4Bk=xdGK zZ*iE=_;g_Nv*gzbSrRc!O0_~RN`BP&P7rIr6NyMrJhfJ8m+Y=!*Z*p+~b(J|d0sV(5Ue*A?E7(&O)@b-jStr|<=JX{WMg@*8Xw(Q9;(LsHkJpd@m`JPX=;2FmET>g z6n!PSt4<~ck@S^#PyNaF!8#0xG7NJ63M+%$zr~hHxwp@JWEp)e()5E|+I;^XXKw;0 zWp(X~R@FCFH&$1D-I$n&X_FQr`4Tdi@#ID{rfB<|SQ$<{=ABfhsxfuyoD<)<@8;?| z@BH3-r)g~(WR!UjXhZ}78D)}DCP6{#tCDU;85BfhoQHYz|^Tj>-Af-I}jhSLMjBf%&fdc@UX~Yl^~^#i%@ri>*R2IZR;@cL{KLLZMmb zVM?Ex%)`5iC()iTGLy>~?YL1T@x`Xv;wk$x5W$2W%pK7)zsj}JcK;J;h+ZXa*u%qo5H$>b-vMJC-PveCE~iG@;{lj>95u{Z;OJrqFiU_{6~;Su4&NU()ucv+%<^{*6|84 zTU64vC~;d)iTfF6oDd;9O`5#3VABaVVJ{Tzo975lcO6mr6_t0E%`oozqu}RLBe|#6 zGa>dED!VP(8Jvrk zq2d~bEKsEMbOX~v|0vcbZ40gv4I!|d`vOHM%S$3@d;)4XptFGA9G4_f*@- zpxEAEMvg&mE4ihh`nM9XDOk#H00`WMZ-5Qj7Ku{C-4rn~VSnGA6t=f3_HM;~Q?Yj_ zc1f{2l;p&ONtkbPuHsSyS-!>L8dgG7*tUfJ))&;Zh}=%giy2dD`gRrr`>Eds!rNSJ zAoR>{WbtKJK*3M35hXT?XUIj$LsX*Oq@Pm zbs?)nS7s(9a+4DYsVhezdr(}ctqZkVihe%PQjqzxJlRn|RxSiw`OSxg+D` zl!S>|#%sH3OJ%6CL*ft)w?jPD!&I3@2)Elq<2AhZ^sEo}UlLvpQL!>L0k3e=670RO zJpnEBB6skea*W$FDET?{>FrUcVS6;3d?yNWu0(s2rqh%zCOF!$N5ru?jH?S_(b^;9 zPJ2Cm>mq#BNJ-xHdz}YzXO~g$@Waay?)1Ye5Z>j7S0cRI4{t$uPhgn0V3_xI$#7qn z4EJ}*@I61g0pSBI)u@3p0baJZ(Z9zmTA<&c%nt@c8iyhprK@DWM5QM6VF+vw)3RVz zKOM_7M>20I4KXZ=_i4M)4NEMF8Z3(4SOqBV17`ZM6A}IcxivA!&@!|q-q%LEcfEkF zi`BrsWwnBPJqxqPMW7;bFC?1PdO-THigvnBh81sfa|}SKj3Y#a>&D-@Wo(T>r6c`Y zN}K#`I9H6;6L3+CewN{91Af-yXL0ONjgm;OIx5$T7UX*5d6et@b2ra*QhCEG;j4sA z^u*pM-!jr?vkH#OD|JGYDqT=tM_wPYfP+}Z^bEJlzd`>VtH! zV)+~*NS;=GMe~Ie9-vA;4rlm)XE#*&le7`1KIRIYjlj>1P=Vj4BwX5N6s-hSe12B& zvEBKczpRWC`u+IKOsiNj&ULFZIxgfL(<+dS(=e?fk~%;n%{bTUiIYYHN%;yS{c=Ua z%M~F;Wy=(#zpNyuRlpSBJtg&GMWm2wVa?ZbOpi*7p)si6SnOLjB|GA1&@{cQh|7x0 zio=?x*Z40a@DbOg5F%3-S;lgidh!$tA1*{Rie_7g%Zj|=qgYE}G21!UEcO8L+)XsuEn$QUnd*Lwb4#EPs_hkKk4?~1pXj(Yxyt=k4Zw9GViSVQ6>-g!0#f74J1c@uV*CFn$U>+DcX{;m%Aj z@q3ozu6u$VIqTfvcPH2lM);Z-v5Si6ej3rY;(X7D(k?5~eBve(V7Sl$@>}f_C`L@9%Svu>Ji92409Qb$zvf9s zTFS95E9Ih>NeqLB9(!`Uog&)p=dmgdDZIYuZ*Ga^S({4|saesfr~g&deTtlCr=^1+ zTE0+_W8L*QcjN1n=`_mxj9h7Qn&*#zOxvgAK2$}m8MTnRqV!5m)YSY$UrH;bJ;HKh9oNiM6E1qH!xRz4&<6o5D9+%~b1&f;i&Lzz-9GzI4htA#@fiyOIzq?P{*GATtp&vkuTiAX~|5&j!$4W zcBJOOGYa(LD<{Yq z`i$yL(^dsL_Oo*8y-27hsmE98LdM27l#)I4VxDphh-ZqXioNDl6MIcRAIoLuPEwqn z+bWq)Tx3cNse{iuEN9TlIfOz36|CJaiz>aubF#hb*tK!5J|u8v#od!dNJvp>X&^xv zFEP27$`&|!00Ch+GKe#KRAe?=4j{#@RKfgS6B|5DhHPaXJ&5D@>frK$bA& z@#;F5+tbT)jfk z)=zo94O3jn_YX1duH^fNkbDnUKpenh%u9cT3+wK3VS8}&jG!8rQBfOWDT%i5?!2Bu zc4p7dwKRx6cDu}}74+aJ#aCz;O|-MB^%9hc?_^1Z6DyTkHbo^;<#`p%+zg~0qZmR3 zFY}W^jxY+jDJo4R!pO}~Q!iHGPJgTdy&Gs_CMT#NVS$EypjT?P8b&H7HJeY8af+up zH41{)@d^|d*k|To)t(b)iBje#x#?A@i6UhoIa3X#7Q#8wcr{h12mzQ|t5f0$+>&)+ zahFmnWC#>FLzsd-*%2Qnu2yD=s}*0|0nVqC+oQuKAEx#7T_ah*H~JOpJ5V&M3%64i zEFB;|Cm`LyoNrQDykp|Vxo6hVy}Skx21_nVzI2Dn@&$<5n>X1UeIj>X6tgQem33Ao zDN?JATkW9Lqp1^L*UNfGbO@Bm6{s;Z%5IzsqKbIHqD%JS_`J6WQ-3UZr{+%4EhU#za@aMUhHc%}hC)C!dheqB%Q_C1}kxm_R8#45!!rXDJ ze2S{glS^FW$R(^4B@~l1#=}5EddkopXTWEv}8!Hzd%acoPnu zc6)KWoxZL~(JBlhIerOG1B!8UoZd}(FIqWnr@#h_lMB*E0uv0prdw`ZXtd)$mifIZ z^3!O#ox{O^9^nv8J}(y}F#Q~xFcoH$%@QX#OI|FC+8&IW$*c2+SO$T|o|Mz2ynL#t=C64254X+U{r4Q+y=aE_Q&<<$11C{PG%IZZ9= zDA_CG94ja*7ggr5t~~QsyomMX5p%tW*UKXoco7@QBj$M#8x>b|=%+%LXj1^dx^7@L_t(yn*y~Rp7!}&Y9Y0ar`DScjWhH#O>Fv~ zJqpXtw-l%g^$=sU3IxJyRdUJiqMc<9krNPaGZt=-l5gNpOBj5RpQ3M&GH}^;FqZl# z9Pa1T-N??>z*Bsv(7Be{q(($eFEjnJLxEWCc2V4{xLZ(aGST&rk_V$_`_a#e=y!tA ztNrMgM06n-y~dAzK}0(o-5{dZ%4p^g&c$kQUzK`O4fU*-u=5gjLgkAw=|4fhimLRr zQ{*h!LPlnUy|2X39v<|3Y_4nW;4&nTJO^vL&adqdQQMsy{RI)d-iz+Zlrsd=!Q`)d z@x78Ss^BX+Il8-u-rz+iXQ?18cX8}DW#%fc$y1Tc?a7_75F4%YUVmDJTLb*ft-;x_ zTY(N2Z@s(J+#rDV@Vu%NASXKr%YJV;Y&c;1xWF1w;6|@Hx#7rUKkZ+vT35&wh>qF- zQ%ZOT$#hShBjzOCy(snKQ?K$QdXMpA%)g%@dMve@xAp@JPB3^sfs+KzXt*FcsOTCC zm{CAc6wn`QsVItY9#3~~Qe`0}$Q{xJGu^MDU#PaBU`9&Gk||vJHK{F`?rjfXLp-FY zx^8R?3&{Y?RT6i0NmoQVb`91EHP@o}bV?x1O1bfgCs}S2elmMTad4j}WdqXhnaP*d z9cp$*9G$j_;o|Hs%G#bt`YNRKw9u!aiaVw_D5kFi&_-JtJ+KjDh zOFXc3jS=hQLq#R5Zwst9fVC9@G31)D@4L$WwX5vC4R&pIkQ16 z6YO{y+GwDC?1MOG0-f;J#8Rr;|HXFɀu&83*d|Bhqo6gTP)I(7P{r^W4Uarn$m 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 literal 0 HcmV?d00001 diff --git a/mods/_standard/flowplayer/flowplayer.controls-3.1.2.swf b/mods/_standard/flowplayer/flowplayer.controls-3.1.2.swf new file mode 100644 index 0000000000000000000000000000000000000000..82c03fd29e63d7973977cb30f899b9ddc17c3d0a GIT binary patch literal 26349 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 AegFUf literal 0 HcmV?d00001 diff --git a/mods/_standard/flowplayer/module.php b/mods/_standard/flowplayer/module.php new file mode 100644 index 000000000..980e49a46 --- /dev/null +++ b/mods/_standard/flowplayer/module.php @@ -0,0 +1,20 @@ +getAdminPrivilege()); + +global $_custom_head; +$_custom_head .=''; + +?> \ No newline at end of file diff --git a/mods/_standard/flowplayer/module.xml b/mods/_standard/flowplayer/module.xml new file mode 100644 index 000000000..58e221fc7 --- /dev/null +++ b/mods/_standard/flowplayer/module.xml @@ -0,0 +1,19 @@ + + + FlowPlayer + This module extends ATutor [media] tag to display .flv file with the built in flowplayer Flash player. No special permissions, tools tabs or icons are defined. Once installed just enter the path to a local flv file or URL to a remote flv file in between media tags. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + BSD + + 0.1 + 2009-08-22 + stable + The browser on linux OS may have problem accessing remote flv file. This module is only for ATutor 1.6.3+. For 1.6.3, please install patch 04 before enabling this module. + + diff --git a/mods/_standard/flowplayer/module_format_content.php b/mods/_standard/flowplayer/module_format_content.php new file mode 100644 index 000000000..dfbd0e9cb --- /dev/null +++ b/mods/_standard/flowplayer/module_format_content.php @@ -0,0 +1,67 @@ + +"; + +// .flv - uses Flowplayer 3.0 from flowplayer.org (playing file from AT_content_dir) +preg_match_all("#\[media[0-9a-z\|]*\]([\w\./-]+)\.flv\[/media\]#i",$_input,$media_matches[1],PREG_SET_ORDER); +$media_replace[1] =" +"; + +$has_flv = false; +// Executing the replace +for ($i=0;$i + '; +} +?> \ No newline at end of file diff --git a/mods/_standard/forums/add_forum.php b/mods/_standard/forums/add_forum.php new file mode 100644 index 000000000..9ffbc3241 --- /dev/null +++ b/mods/_standard/forums/add_forum.php @@ -0,0 +1,73 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/index.php'); + exit; +} + +if ($_POST['add_forum'] && (authenticate(AT_PRIV_FORUMS, AT_PRIV_RETURN))) { + if ($_POST['title'] == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('title'))); + } else { + $_POST['title'] = validate_length($_POST['title'], 60); + } + + if (!$msg->containsErrors()) { + require(AT_INCLUDE_PATH.'../mods/_standard/forums/lib/forums.inc.php'); + add_forum($_POST); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/index.php'); + exit; + } +} + +$onload = 'document.form.title.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + +
    + + +
    +
    +
    + *
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/forums/admin/forum_add.php b/mods/_standard/forums/admin/forum_add.php new file mode 100644 index 000000000..4763bfcef --- /dev/null +++ b/mods/_standard/forums/admin/forum_add.php @@ -0,0 +1,111 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/admin/forums.php'); + exit; +} else if (isset($_POST['add_forum'])) { + $missing_fields = array(); + + if (empty($_POST['title'])) { + $missing_fields[] = _AT('title'); + } + + if (empty($_POST['courses'])) { + $missing_fields[] = _AT('courses'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + $_POST['edit'] = intval($_POST['edit']); + + if (!($msg->containsErrors())) { + //add forum + $sql = "INSERT INTO ".TABLE_PREFIX."forums (title, description, mins_to_edit) VALUES ('" . $_POST['title'] . "','" . $_POST['description'] ."', $_POST[edit])"; + $result = mysql_query($sql, $db); + $forum_id = mysql_insert_id($db); + write_to_log(AT_ADMIN_LOG_INSERT, 'forums', mysql_affected_rows($db), $sql); + + //for each course, add an entry to the forums_courses table + foreach ($_POST['courses'] as $course) { + $sql = "INSERT INTO ".TABLE_PREFIX."forums_courses VALUES (" . $forum_id . "," . $course . ")"; + $result = mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_INSERT, 'forums_courses', mysql_affected_rows($db), $sql); + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + if($course =="0"){ + $msg->addFeedback('FORUM_POSTING'); + } + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/admin/forums.php'); + exit; + } +} + +$onload = 'document.form.title.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    + + +
    +
    + *
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + *
    + + + + + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/forums/admin/forum_delete.php b/mods/_standard/forums/admin/forum_delete.php new file mode 100644 index 000000000..69adb2a9c --- /dev/null +++ b/mods/_standard/forums/admin/forum_delete.php @@ -0,0 +1,77 @@ +addFeedback('CANCELLED'); + header('Location: forums.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + $forum_id = intval($_POST['forum']); + + $sql = "SELECT post_id FROM ".TABLE_PREFIX."forums_threads WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_array($result)) { + $sql = "DELETE FROM ".TABLE_PREFIX."forums_accessed WHERE post_id=$row[post_id]"; + $result2 = mysql_query($sql, $db); + } + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_subscriptions WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_threads WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_courses WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'forums_courses', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'forums', mysql_affected_rows($db), $sql); + + $sql = "OPTIMIZE TABLE ".TABLE_PREFIX."forums_threads"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: forums.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $_GET['forum'] = intval($_GET['forum']); + + $row = get_forum($_GET['forum']); + + if (!is_array($row)) { + $msg->addError('FORUM_NOT_FOUND'); + $msg->printErrors(); + } else { + + $hidden_vars['delete_forum'] = TRUE; + $hidden_vars['forum'] = $_GET['forum']; + $msg->addConfirm(array('DELETE_FORUM', AT_print($row['title'], 'forums.title')), $hidden_vars); + $msg->printConfirm(); + } + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/forums/admin/forum_edit.php b/mods/_standard/forums/admin/forum_edit.php new file mode 100644 index 000000000..725dbcf37 --- /dev/null +++ b/mods/_standard/forums/admin/forum_edit.php @@ -0,0 +1,166 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/admin/forums.php'); + exit; +} else if (isset($_POST['edit_forum'])) { + $missing_fields = array(); + if (empty($_POST['title'])) { + $missing_fields[] = _AT('title'); + } + + if (empty($_POST['courses'])) { + $missing_fields[] = _AT('courses'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!($msg->containsErrors())) { + + //update forum + $forum_id = intval($_POST['forum']); + $_POST['title'] = $addslashes($_POST['title']); + $_POST['edit'] = intval($_POST['edit']); + $_POST['description'] = $addslashes($_POST['description']); + + $sql = "UPDATE ".TABLE_PREFIX."forums SET title='" . $_POST['title'] . "', description='" . $_POST['description'] . "', last_post=last_post, mins_to_edit=$_POST[edit] WHERE forum_id=".$forum_id; + $result = mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_UPDATE, 'forums', mysql_affected_rows($db), $sql); + + // unsubscribe all the members who are NOT in $_POST['courses'] + $courses_list = implode(',', $_POST['courses']); + + // list of all the students who are in other courses as well + $sql = "SELECT member_id FROM ".TABLE_PREFIX."course_enrollment WHERE course_id IN ($courses_list)"; + $result2 = mysql_query($sql, $db); + while ($row2 = mysql_fetch_assoc($result2)) { + $students[] = $row2['member_id']; + } + + // list of students who must REMAIN subscribed! + $students_list = implode(',', $students); + + if ($students_list) { + // remove the subscriptions + $sql = "SELECT post_id FROM ".TABLE_PREFIX."forums_threads WHERE forum_id=$forum_id"; + $result2 = mysql_query($sql, $db); + while ($row2 = mysql_fetch_assoc($result2)) { + $sql = "DELETE FROM ".TABLE_PREFIX."forums_accessed WHERE post_id=$row2[post_id] AND member_id NOT IN ($students_list)"; + $result3 = mysql_query($sql, $db); + } + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_subscriptions WHERE forum_id=$forum_id AND member_id NOT IN ($students_list)"; + $result3 = mysql_query($sql, $db); + } + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_courses WHERE forum_id=$forum_id AND course_id NOT IN ($courses_list)"; + $result = mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_DELETE, 'forums_courses', mysql_affected_rows($db), $sql); + + //update forums_courses + if (in_array('0', $_POST['courses'])) { + //general course - used by all. put one entry in forums_courses w/ course_id=0 + $sql = "REPLACE INTO ".TABLE_PREFIX."forums_courses VALUES (" . $_POST['forum'] . ", 0)"; + $result = mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_REPLACE, 'forums_courses', mysql_affected_rows($db), $sql); + } else { + foreach ($_POST['courses'] as $course) { + $sql = "REPLACE INTO ".TABLE_PREFIX."forums_courses VALUES (" . $_POST['forum'] . "," . $course . ")"; + $result = mysql_query($sql, $db); + write_to_log(AT_ADMIN_LOG_REPLACE, 'forums_courses', mysql_affected_rows($db), $sql); + } + } + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/admin/forums.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if (!($forum = @get_forum($_GET['forum']))) { + //no such forum + $msg->addError('FORUM_NOT_FOUND'); + $msg->printAll(); +} else { + $msg->printAll(); + + $sql = "SELECT * FROM ".TABLE_PREFIX."forums_courses WHERE forum_id=$forum[forum_id]"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $courses[] = $row['course_id']; + } +?> +
    + + + +
    +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    +
    + +
    +
    +
    + \ No newline at end of file diff --git a/mods/_standard/forums/admin/forums.php b/mods/_standard/forums/admin/forums.php new file mode 100644 index 000000000..5038ee577 --- /dev/null +++ b/mods/_standard/forums/admin/forums.php @@ -0,0 +1,109 @@ +addError('NO_ITEM_SELECTED'); +} + +include(AT_INCLUDE_PATH.'../mods/_standard/forums/lib/forums.inc.php'); + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    + + + + + + + + + + + + + + + + + + +'; + echo ''; + echo ' '; + echo ' '; + echo ' '; + echo ''; + } + } else { + echo ''; + echo ' '; + echo ''; + } +?> + + + + + + + + + + + + + + + + + + + + +
     
    ' . htmlentities_utf8($forum['description']) . ''; + + $courses = array(); + $sql = "SELECT F.course_id FROM ".TABLE_PREFIX."forums_courses F WHERE F.forum_id=$forum[forum_id]"; + $c_result = mysql_query($sql, $db); + while ($course = mysql_fetch_assoc($c_result)) { + $courses[] = $system_courses[$course['course_id']]['title']; + } + natcasesort($courses); + echo implode(', ', $courses); + echo '
    ' . _AT('no_forums') . '
    +
    + + \ No newline at end of file diff --git a/mods/_standard/forums/delete_forum.php b/mods/_standard/forums/delete_forum.php new file mode 100644 index 000000000..16503e5e4 --- /dev/null +++ b/mods/_standard/forums/delete_forum.php @@ -0,0 +1,73 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/index.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + $_POST['fid'] = intval($_POST['fid']); + + // check if this forum is shared: + // (if this forum is shared, then we do not want to delete it.) + if (!is_shared_forum($_POST['fid']) && valid_forum_user($_POST['fid'])) { + delete_forum($_POST['fid']); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } else { + $msg->addError('FORUM_NO_DEL_SHARE'); + } + + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/index.php'); + exit; +} + +$_section[0][0] = _AT('discussions'); +$_section[0][1] = 'discussions/'; +$_section[1][0] = _AT('forums'); +$_section[1][1] = 'forum/list.php'; +$_section[2][0] = _AT('delete_forum'); + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +$_GET['fid'] = intval($_GET['fid']); + +$row = get_forum($_GET['fid'], $_SESSION['course_id']); + +if (!is_array($row)) { + $msg->addError('FORUM_NOT_ADDED'); +} else { ?> +
    + + + + addConfirm($confirm, $hidden_vars); + $msg->printConfirm(); +} + + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/forums/dropdown/posts.inc.php b/mods/_standard/forums/dropdown/posts.inc.php new file mode 100644 index 000000000..cd9663882 --- /dev/null +++ b/mods/_standard/forums/dropdown/posts.inc.php @@ -0,0 +1,50 @@ + 0) { + while ($row = mysql_fetch_assoc($result)) { + echo '° ' . AT_print(validate_length($row['subject'], 20, VALIDATE_LENGTH_FOR_DISPLAY), 'forums_threads.subject') . '
    '; + } + } else { + echo ''._AT('none_found').''; + } +} else { + echo ''._AT('none_found').''; +} + +$savant->assign('dropdown_contents', ob_get_contents()); +ob_end_clean(); + +$savant->assign('title', _AT('forum_posts')); +$savant->display('include/box.tmpl.php'); +?> \ No newline at end of file diff --git a/mods/_standard/forums/edit_forum.php b/mods/_standard/forums/edit_forum.php new file mode 100644 index 000000000..c3496ecaa --- /dev/null +++ b/mods/_standard/forums/edit_forum.php @@ -0,0 +1,98 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/index.php'); + exit; +} else if (isset($_POST['edit_forum'])) { + $_POST['fid'] = intval($_POST['fid']); + + if ($_POST['title'] == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('title'))); + } else { + $_POST['title'] = validate_length($_POST['title'], 60); + } + + if (!$msg->containsErrors()) { + if (!is_shared_forum($_POST['fid'])) { + edit_forum($_POST); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } else { + $msg->addError('FORUM_NO_EDIT_SHARE'); + } + + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/index.php'); + exit; + } +} + +$onload = 'document.form.title.focus();'; +require(AT_INCLUDE_PATH.'header.inc.php'); + +$fid = intval($_REQUEST['fid']); + +if (!isset($_POST['submit'])) { + $row = get_forum($fid, $_SESSION['course_id']); + if (!is_array($row)) { + $msg->addError('FORUM_NOT_FOUND'); + $msg->printALL(); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } +} else { + $row['description'] = $_POST['body']; + $row['mins_to_edit'] = $_POST['edit']; +} + +$msg->printErrors(); + +?> + + + + +
    +
    +
    + *
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/forums/edit_post.php b/mods/_standard/forums/edit_post.php new file mode 100644 index 000000000..2159d9727 --- /dev/null +++ b/mods/_standard/forums/edit_post.php @@ -0,0 +1,149 @@ +addError('ITEM_NOT_FOUND'); + header('Location: ../../../forum/list.php'); + exit; +} + +$sql = "SELECT *, UNIX_TIMESTAMP(date) AS udate FROM ".TABLE_PREFIX."forums_threads WHERE post_id=$pid"; +$result = mysql_query($sql,$db); +if (!($post_row = mysql_fetch_assoc($result))) { + $msg->addError('ITEM_NOT_FOUND'); + header('Location: '.url_rewrite('/mods/_standard/forums/forum/list.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +$forum_info = get_forum($fid, $_SESSION['course_id']); + +$expiry = $post_row['udate'] + $forum_info['mins_to_edit'] * 60; + +// check if we're either a) an assistant or, b) own this post and within the time allowed: +if (!( authenticate(AT_PRIV_FORUMS, AT_PRIV_RETURN) + || ($post_row['member_id'] == $_SESSION['member_id'] && ($expiry > time() || isset($_POST['edit_post']) ) ) + ) + ) { + $msg->addError('POST_EDIT_EXPIRE'); + header('Location: '.url_rewrite('mods/_standard/forums/forum/list.php', AT_PRETTY_URL_IS_HEADER)); + exit; +} + +if ($_POST['cancel']) { + $msg->addFeedback('CANCELLED'); + Header('Location: '.url_rewrite('mods/_standard/forums/forum/view.php?fid='.$_POST['fid'].SEP.'pid='.$_POST['pid'], AT_PRETTY_URL_IS_HEADER)); + exit; +} + +if ($_POST['edit_post']) { + $missing_fields = array(); + +// $_POST['subject'] = str_replace('<', '<', trim($_POST['subject'])); +// $_POST['body'] = str_replace('<', '<', trim($_POST['body'])); + $_POST['pid'] = intval($_POST['pid']); + + $_POST['subject'] = $addslashes($_POST['subject']); + //If subject > 60,then chop subject + $_POST['subject'] = validate_length($_POST['subject'], 60); + + $_POST['body'] = $addslashes($_POST['body']); + + if ($_POST['subject'] == '') { + $missing_fields[] = _AT('subject'); + } + + if ($_POST['body'] == '') { + $missing_fields[] = _AT('body'); + } + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + if (!$msg->containsErrors()) { + $sql = "UPDATE ".TABLE_PREFIX."forums_threads SET subject='$_POST[subject]', body='$_POST[body]', last_comment=last_comment, date=date WHERE post_id=$_POST[pid]"; + $result = mysql_query($sql,$db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + if ($_POST['ppid'] == 0) { + $_POST['ppid'] = $_POST['pid']; + } + header('Location: '.url_rewrite('mods/_standard/forums/forum/view.php?fid='.$_POST['fid'].SEP.'pid='.$_POST['ppid'], AT_PRETTY_URL_IS_HEADER)); + exit; + } +} + +$_pages['mods/_standard/forums/forum/index.php?fid='.$fid]['title'] = $forum_info['title']; +$_pages['mods/_standard/forums/forum/index.php?fid='.$fid]['parent'] = 'mods/_standard/forums/forum/list.php'; +$_pages['mods/_standard/forums/forum/index.php?fid='.$fid]['children'] = array('mods/_standard/forums/forum/new_thread.php?fid='.$fid); + +$_pages['mods/_standard/forums/forum/new_thread.php?fid='.$fid]['title_var'] = 'new_thread'; +$_pages['mods/_standard/forums/forum/new_thread.php?fid='.$fid]['parent'] = 'mods/_standard/forums/forum/index.php?fid='.$fid; + +$_pages['mods/_standard/forums/forum/view.php']['title'] = $post_row['subject']; +$_pages['mods/_standard/forums/forum/view.php']['parent'] = 'mods/_standard/forums/forum/index.php?fid='.$fid; + +$_pages['mods/_standard/forums/edit_post.php']['title_var'] = 'edit_post'; +$_pages['mods/_standard/forums/edit_post.php']['parent'] = 'mods/_standard/forums/forum/index.php?fid='.$fid; +$_pages['mods/_standard/forums/edit_post.php']['children'] = array(); + + +$onload = 'document.form.subject.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + +
    + + + + + +
    +
    + *
    + +
    + +
    + * + +
    + +
    +
    ·
    + ·
    + ·
    +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/forums/forum/delete_thread.php b/mods/_standard/forums/forum/delete_thread.php new file mode 100644 index 000000000..3be41f46f --- /dev/null +++ b/mods/_standard/forums/forum/delete_thread.php @@ -0,0 +1,138 @@ +addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; +} + +if (isset($_POST['submit_no'])) { + + $msg->addFeedback('CANCELLED'); + if ($_POST['nest']) { + header('Location: view.php?fid='.$_POST['fid'].SEP.'pid='. ($_POST['ppid'] ? $_POST['ppid'] : $_POST['pid'])); + exit; + } else { + header('Location: index.php?fid='.$_POST['fid']); + exit; + } + + exit; + +} else if (isset($_POST['submit_yes'])) { + // check if they have access + if (!valid_forum_user($fid)) { + $msg->addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; + } + + if ($ppid == 0) { /* If deleting an entire post */ + /* First get number of comments from specific post */ + $sql = "SELECT * FROM ".TABLE_PREFIX."forums_threads WHERE post_id=$pid AND forum_id=$fid"; + $result = mysql_query($sql, $db); + if (!($row = mysql_fetch_assoc($result))) { + $msg->addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; + + } // else: + + /* Decrement count for number of posts and topics*/ + $sql = "UPDATE ".TABLE_PREFIX."forums SET num_posts=num_posts-1-".$row['num_comments'].", num_topics=num_topics-1, last_post=last_post WHERE forum_id=$fid"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_threads WHERE (parent_id=$pid OR post_id=$pid) AND forum_id=$fid"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_accessed WHERE post_id=$pid"; + $result = mysql_query($sql, $db); + + } else { /* Just deleting a single thread */ + $sql = "DELETE FROM ".TABLE_PREFIX."forums_threads WHERE post_id=$pid AND forum_id=$fid"; + $result = mysql_query($sql, $db); + if (mysql_affected_rows($db) == 0) { + $msg->addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; + } + + /* Decrement count of comments in forums_threads table*/ + $sql = "UPDATE ".TABLE_PREFIX."forums_threads SET num_comments=num_comments-1, last_comment=last_comment, date=date WHERE post_id=$ppid"; + $result = mysql_query($sql, $db); + + /* Decrement count of posts in forums table */ + $sql = "UPDATE ".TABLE_PREFIX."forums SET num_posts=num_posts-1, last_post=last_post WHERE forum_id=$fid"; + $result = mysql_query($sql, $db); + + } + + if ($ppid) { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: view.php?fid='.$fid.SEP.'pid='.$ppid); + exit; + } else { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php?fid='.$fid); + exit; + } +} + +$_pages['mods/_standard/forums/forum/index.php?fid='.$fid]['title'] = get_forum_name($fid); +$_pages['mods/_standard/forums/forum/index.php?fid='.$fid]['parent'] = 'forum/list.php'; +$_pages['mods/_standard/forums/forum/index.php?fid='.$fid]['children'] = array('mods/_standard/forums/forum/new_thread.php?fid='.$fid); + +$_pages['mods/_standard/forums/forum/new_thread.php?fid='.$fid]['title_var'] = 'new_thread'; +$_pages['mods/_standard/forums/forum/new_thread.php?fid='.$fid]['parent'] = 'mods/_standard/forums/forum/index.php?fid='.$fid; + +$_pages['mods/_standard/forums/forum/view.php']['title'] = $post_row['subject']; +$_pages['mods/_standard/forums/forum/view.php']['parent'] = 'forum/index.php?fid='.$fid; + +$_pages['mods/_standard/forums/forum/delete_thread.php']['title_var'] = 'delete_post'; +$_pages['mods/_standard/forums/forum/delete_thread.php']['parent'] = 'mods/_standard/forums/forum/index.php?fid='.$fid; +$_pages['mods/_standard/forums/forum/delete_thread.php']['children'] = array(); + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +$sql = "SELECT * from ".TABLE_PREFIX."forums_threads WHERE post_id = '$pid'"; +$result = mysql_query($sql, $db); +while ($row = mysql_fetch_assoc($result)){ + $title = htmlentities_utf8($row['subject']); +} + +$hidden_vars['fid'] = $_GET['fid']; +$hidden_vars['pid'] = $_GET['pid']; +$hidden_vars['ppid'] = $_GET['ppid']; +$hidden_vars['nest'] = $_GET['nest']; + +$msg->addConfirm(array('DELETE', $title),$hidden_vars); +if (($ppid=='') || ($ppid =='0')) { + $ppid = '0'; +} + +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/forums/forum/index.php b/mods/_standard/forums/forum/index.php new file mode 100644 index 000000000..714052212 --- /dev/null +++ b/mods/_standard/forums/forum/index.php @@ -0,0 +1,60 @@ +addError('FORUM_DENIED'); + $msg->printErrors(); + require(AT_INCLUDE_PATH.'footer.inc.php'); +} + +$_pages['mods/_standard/forums/forum/index.php']['title'] = get_forum_name($fid); +$_pages['mods/_standard/forums/forum/index.php']['parent'] = 'mods/_standard/forums/forum/list.php'; +$_pages['mods/_standard/forums/forum/index.php']['children'] = array('mods/_standard/forums/forum/new_thread.php?fid='.$fid, 'search.php?search_within[]=forums'); + +$_pages['mods/_standard/forums/forum/new_thread.php?fid='.$fid]['title_var'] = 'new_thread'; +$_pages['mods/_standard/forums/forum/new_thread.php?fid='.$fid]['parent'] = 'mods/_standard/forums/forum/index.php'; + +$_pages['search.php?search_within[]=forums']['title_var'] = 'search'; +$_pages['search.php?search_within[]=forums']['parent'] = 'mods/_standard/forums/forum/index.php?fid='.$fid; + +/* the last accessed field */ +$last_accessed = array(); +if ($_SESSION['valid_user'] && $_SESSION['enroll']) { + $sql = "SELECT post_id, last_accessed + 0 AS last_accessed, subscribe FROM ".TABLE_PREFIX."forums_accessed WHERE member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $post_id = $row['post_id']; + unset($row['post_id']); + $last_accessed[$post_id] = $row; + + } +} + +require(AT_INCLUDE_PATH . 'header.inc.php'); + +require(AT_INCLUDE_PATH . '../mods/_standard/forums/html/forum.inc.php'); + +require(AT_INCLUDE_PATH . 'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/forums/forum/list.php b/mods/_standard/forums/forum/list.php new file mode 100644 index 000000000..5d86ab0ba --- /dev/null +++ b/mods/_standard/forums/forum/list.php @@ -0,0 +1,95 @@ + + + + + + + + + + + $forums) { + if (($num_shared && $num_nonshared) || ($num_nonshared && $num_groups)) { + if ($num_nonshared && ($shared == 'nonshared')) { + echo ''; + echo ''; + echo ''; + } else if ($num_shared && ($shared == 'shared')) { + echo ''; + echo ''; + echo ''; + } else if ($num_groups && ($shared == 'group')) { + echo ''; + echo ''; + echo ''; + } + } + + foreach ($forums as $row) : ?> + + + + + + '; +} else { + echo ''; +} +echo '
    ' . _AT('course_forums') . '
    ' . _AT('shared_forums') . '
    ' . _AT('group_forums') . '
    +
    '._AT('unsubscribe1').''; + } else { + echo ' +
    '._AT('subscribe1').'
    '; + } + } ?> +

    +
    '._AT('na').''; + } else { + echo AT_DATE(_AT('server_date_format'), $row['last_post'], AT_DATE_MYSQL_DATETIME); + } ?> +
    '._AT('no_forums').'
    '; + +require (AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/forums/forum/lock_thread.php b/mods/_standard/forums/forum/lock_thread.php new file mode 100644 index 000000000..84bf08b54 --- /dev/null +++ b/mods/_standard/forums/forum/lock_thread.php @@ -0,0 +1,98 @@ +addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/forum/index.php?fid='.$fid); + exit; +} else if (isset($_POST['submit'])) { + $_POST['lock'] = intval($_POST['lock']); + $_POST['pid'] = intval($_POST['pid']); + $_POST['fid'] = intval($_POST['fid']); + + $sql = "UPDATE ".TABLE_PREFIX."forums_threads SET locked=$_POST[lock], last_comment=last_comment, date=date WHERE post_id=$_POST[pid]"; + $result = mysql_query($sql, $db); + + if($_POST['lock'] == '1' || $_POST['lock'] == '2'){ + $msg->addFeedback('THREAD_LOCKED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/forum/index.php?fid='.$fid); + exit; + } else { + $msg->addFeedback('THREAD_UNLOCKED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/forum/index.php?fid='.$fid); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$pid = intval($_GET['pid']); +$fid = intval($_GET['fid']); +?> +
    + + + +
    + + +
    + +
    + + +
    + >
    + > +
    + +
    + + +
    +
    + +
    + \ No newline at end of file diff --git a/mods/_standard/forums/forum/move_thread.php b/mods/_standard/forums/forum/move_thread.php new file mode 100644 index 000000000..17ef1d77a --- /dev/null +++ b/mods/_standard/forums/forum/move_thread.php @@ -0,0 +1,108 @@ +addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: index.php?fid='.$_REQUEST['fid']); + exit; + +} else if (isset($_POST['submit'])) { + // check if they have access + if (!valid_forum_user($_REQUEST['fid']) || !valid_forum_user($_REQUEST['new_fid'])) { + $msg->addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; + } + + if ($_REQUEST['fid'] == $_REQUEST['new_fid']) { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php?fid='.$_REQUEST['fid']); + exit; + } + + $sql = "SELECT * FROM ".TABLE_PREFIX."forums_threads WHERE post_id=$_REQUEST[pid] AND forum_id=$_REQUEST[fid]"; + $result = mysql_query($sql, $db); + if (!($row = mysql_fetch_assoc($result))) { + $msg->addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; + } // else: + + /* Decrement count for number of posts and topics*/ + $sql = "UPDATE ".TABLE_PREFIX."forums SET num_posts=num_posts-1-".$row['num_comments'].", num_topics=num_topics-1, last_post=last_post WHERE forum_id=$_REQUEST[fid]"; + $result = mysql_query($sql, $db); + + $sql = "UPDATE ".TABLE_PREFIX."forums SET num_posts=num_posts+1+".$row['num_comments'].", num_topics=num_topics+1, last_post=last_post WHERE forum_id=$_REQUEST[new_fid]"; + $result = mysql_query($sql, $db); + + $sql = "UPDATE ".TABLE_PREFIX."forums_threads SET forum_id=$_REQUEST[new_fid], last_comment=last_comment, date=date WHERE (parent_id=$_REQUEST[pid] OR post_id=$_REQUEST[pid]) AND forum_id=$_REQUEST[fid]"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: index.php?fid='.$_REQUEST['fid']); + exit; +} + +$_pages['mods/_standard/forums/forum/index.php?fid='.$_REQUEST['fid']]['title'] = get_forum_name($_REQUEST['fid']); +$_pages['mods/_standard/forums/forum/index.php?fid='.$_REQUEST['fid']]['parent'] = 'mods/_standard/forums/forum/list.php'; +$_pages['mods/_standard/forums/forum/index.php?fid='.$_REQUEST['fid']]['children'] = array('mods/_standard/forums/forum/move_thread.php'); + +$_pages['mods/_standard/forums/forum/move_thread.php']['title_var'] = 'move_thread'; +$_pages['mods/_standard/forums/forum/move_thread.php']['parent'] = 'mods/_standard/forums/forum/index.php?fid='.$_REQUEST['fid']; +$_pages['mods/_standard/forums/forum/move_thread.php']['children'] = array(); + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> +
    + + + + +
    +
    + +
      + +
    • + /> +
    • + +
    +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/forums/forum/new_thread.php b/mods/_standard/forums/forum/new_thread.php new file mode 100644 index 000000000..9f5c98dd5 --- /dev/null +++ b/mods/_standard/forums/forum/new_thread.php @@ -0,0 +1,196 @@ +addError('FORUM_DENIED'); + require(AT_INCLUDE_PATH.'header.inc.php'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.url_rewrite('mods/_standard/forums/forum/index.php?fid='.$fid, AT_PRETTY_URL_IS_HEADER)); + exit; +} else if (isset($_POST['submit'])) { + $missing_fields = array(); + + if ($_POST['subject'] == '') { + $missing_fields[] = _AT('subject'); + } else { + //60 was set by db + $_POST['subject'] = validate_length($_POST['subject'], 60); + } + + if ($_POST['body'] == '') { + $missing_fields[] = _AT('body'); + } + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + if (!$msg->containsErrors()) { + if ($_POST['replytext'] != '') { + $_POST['body'] .= "\n\n".'[reply][b]'._AT('in_reply_to').': [/b]'."\n"; + if ($strlen($_POST['replytext']) > 200) { + $_POST['body'] .= $substr($_POST['replytext'], 0, 200).'...'; + } else { + $_POST['body'] .= $_POST['replytext']; + } + $num_open_replies = substr_count($_POST['body'], '[reply]'); + $num_close_replies = substr_count($_POST['body'], '[/reply]'); + $num_replies_add = $num_open_replies - $num_close_replies - 1; + for ($i=0; $i < $num_replies_add; $i++) { + $_POST['body'] .= '[/reply]'; + } + + $_POST['body'] .= "\n".'[op]mods/_standard/forums/forum/view.php?fid='.$_POST['fid'].SEP.'pid='.$_POST['parent_id'].SEP.'page='.$_POST['page'].'#'.$_POST['reply']; + $_POST['body'] .= '[/op][/reply]'; + } + + /* use this value instead of NOW(), because we want the parent post to have the exact */ + /* same date. and not a second off if that may happen */ + $now = date('Y-m-d H:i:s'); + + $sql_subject = $addslashes($_POST['subject']); + $sql_body = $addslashes($_POST['body']); + + $sql = "INSERT INTO ".TABLE_PREFIX."forums_threads VALUES (NULL, $_POST[parent_id], $_SESSION[member_id], $_POST[fid], '$now', 0, '$sql_subject', '$sql_body', '$now', 0, 0)"; + $result = mysql_query($sql, $db); + $this_id = mysql_insert_id($db); + + /* Increment count for posts in forums table in database */ + $sql = "UPDATE ".TABLE_PREFIX."forums SET num_posts=num_posts+1, last_post='$now' WHERE forum_id=$_POST[fid]"; + $result = mysql_query($sql, $db); + + // If there are subscribers to this forum, send them an email notification + $subscriber_email_list = array(); // list of subscribers array('email', 'full_name') + $subscriber_list = ''; + $enrolled = array(); + //get list of student enrolled in this course + // This needs to be replaced with a tool to clean forum subscriptions when unenrolling + $sql = "SELECT member_id from ".TABLE_PREFIX."course_enrollment WHERE course_id = '$_SESSION[course_id]' AND approved = 'y'"; + $result1 = mysql_query($sql, $db); + while($row = mysql_fetch_assoc($result1)){ + $enrolled[] = $row['member_id']; + } + //get a list of users subscribed to this forum + $sql = "SELECT member_id FROM ".TABLE_PREFIX."forums_subscriptions WHERE forum_id=$fid"; + $result = mysql_query($sql, $db); + while($row = mysql_fetch_assoc($result)){ + $subscriber_list .= $row['member_id'] . ','; + } + if ($_POST['parent_id']) { + $sql = "SELECT member_id FROM ".TABLE_PREFIX."forums_accessed WHERE post_id=$_POST[parent_id] AND subscribe=1"; + $result = mysql_query($sql, $db); + while($row = mysql_fetch_assoc($result)){ + if(in_array($row['member_id'], $enrolled)){ + $subscriber_list .= $row['member_id'] . ','; + } + } + } + $subscriber_list = $substr($subscriber_list, 0, -1); + + if ($subscriber_list != '') { + $sql = "SELECT first_name, second_name, last_name, email, member_id FROM ".TABLE_PREFIX."members WHERE member_id IN ($subscriber_list)"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_assoc($result)) { + $subscriber_email_list[] = array('email'=> $row['email'], 'full_name' => $row['first_name'] . ' '. $row['second_name'] . ' ' . $row['last_name'], 'member_id'=>$row['member_id']); + } + } + $sql = "UPDATE ".TABLE_PREFIX."forums_threads SET num_comments=num_comments+1, last_comment='$now', date=date WHERE post_id=$_POST[parent_id]"; + $result = mysql_query($sql, $db); + + if ($subscriber_email_list) { + require(AT_INCLUDE_PATH . 'classes/phpmailer/atutormailer.class.php'); + + if ($_POST['parent_name'] == ''){ + $_POST['parent_name'] = $_POST['subject']; + } + $_POST['parent_name'] = urldecode($_POST['parent_name']); + foreach ($subscriber_email_list as $subscriber){ + $mail = new ATutorMailer; + $mail->AddAddress($subscriber['email'], get_display_name($subscriber['member_id'])); + $body = _AT('forum_new_submsg', $_SESSION['course_title'], get_forum_name($_POST['fid']), $_POST['parent_name'], AT_BASE_HREF.'mods/_standard/forums/forum/view.php?fid='.$_POST['fid'].SEP.'pid='.$_POST['parent_id']); + $body .= "\n----------------------------------------------\n"; + $body .= _AT('posted_by').": ".get_display_name($_SESSION['member_id'])."\n"; + $body .= $_POST['body']."\n"; + $mail->FromName = $_config['site_name']; + $mail->From = $_config['contact_email']; + $mail->Subject = _AT('thread_notify1'); + $mail->Body = $body; + + if(!$mail->Send()) { + $msg->addError('SENDING_ERROR'); + } + + unset($mail); + } + } + if ($_REQUEST['subscribe']) { + if($_POST['parent_id'] != 0){ + $this_id = $_POST['parent_id']; + $subject = $_POST['parent_name']; + } else { + $subject = $_POST['subject']; + } + $sql = "REPLACE INTO ".TABLE_PREFIX."forums_accessed VALUES ($this_id, $_SESSION[member_id], NOW(), 1)"; + $result = mysql_query($sql, $db); + + $msg->addFeedback(array('THREAD_SUBSCRIBED', $subject)); + } else if ($_POST['parent_id'] == 0) { + // not subscribe and it's a new thread, mark read: + + $sql = "REPLACE INTO ".TABLE_PREFIX."forums_accessed VALUES ($this_id, $_SESSION[member_id], NOW(), 0)"; + $result = mysql_query($sql, $db); + } + + if ($_POST['parent_id'] == 0) { + $sql = "UPDATE ".TABLE_PREFIX."forums SET num_topics=num_topics+1, last_post='$now' WHERE forum_id=$_POST[fid]"; + $result = mysql_query($sql, $db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + $_POST['parent_id'] = $this_id; + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.url_rewrite('mods/_standard/forums/forum/view.php?fid='.$fid.SEP.'pid='.$_POST['parent_id'].SEP.'page='.$_POST['page'], AT_PRETTY_URL_IS_HEADER)); + exit; + } +} + +$onload = 'document.form.subject.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$parent_id = 0; +$new_thread = TRUE; +require(AT_INCLUDE_PATH.'../mods/_standard/forums/html/new_thread.inc.php'); +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/forums/forum/stick.php b/mods/_standard/forums/forum/stick.php new file mode 100644 index 000000000..7c13a60f1 --- /dev/null +++ b/mods/_standard/forums/forum/stick.php @@ -0,0 +1,30 @@ +addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + +header('Location: '.AT_BASE_HREF.'mods/_standard/forums/forum/index.php?fid='.intval($_GET['fid'])); +exit; + +?> \ No newline at end of file diff --git a/mods/_standard/forums/forum/subscribe.php b/mods/_standard/forums/forum/subscribe.php new file mode 100644 index 000000000..5242a8c22 --- /dev/null +++ b/mods/_standard/forums/forum/subscribe.php @@ -0,0 +1,82 @@ +addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; +} + +$sql = "SELECT subject FROM ".TABLE_PREFIX."forums_threads WHERE post_id=$pid AND forum_id=$fid"; +$result = mysql_query($sql, $db); +if (!($row = mysql_fetch_assoc($result))) { + $msg->addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; +} // else: +$thread_name = $row['subject']; + +/** + * Protect against url injection + * Maintain consistency in data by not allowing any subscription to a reply thread, only top level id's (0). + */ + $sql = "SELECT parent_id FROM " . TABLE_PREFIX."forums_threads WHERE post_id=$pid AND forum_id=$fid"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + if ($row['parent_id'] > 0) { // not allowed, only top level + $msg->addError('FORUM_NO_SUBSCRIBE'); + header('Location: view.php?fid='.$fid.SEP.'pid='.$row['parent_id']); // take us back to where we were + exit; + } + } + +if ($_GET['us']) { + // unsubscribe: + $sql = "UPDATE ".TABLE_PREFIX."forums_accessed SET subscribe=0 WHERE post_id=$pid AND member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); +} else { + // subscribe: + $sql = "REPLACE INTO ".TABLE_PREFIX."forums_accessed VALUES ($pid, $_SESSION[member_id], NOW(), 1)"; + $result = mysql_query($sql, $db); +} + + +if($_REQUEST['t']){ + $this_pid = 'index.php?fid='.$fid; +} else{ + $this_pid = 'view.php?fid='.$fid.SEP.'pid='.$pid; +} + +if ($_GET['us'] == '1') { + $msg->addFeedback(array('THREAD_UNSUBSCRIBED', $thread_name)); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/forum/'.$this_pid); + exit; +} + +/* else: */ + $msg->addFeedback(array('THREAD_SUBSCRIBED', $thread_name )); + header('Location: '.AT_BASE_HREF.'mods/_standard/forums/forum/'.$this_pid); + exit; + +?> \ No newline at end of file diff --git a/mods/_standard/forums/forum/subscribe_forum.php b/mods/_standard/forums/forum/subscribe_forum.php new file mode 100644 index 000000000..60c3b0ac2 --- /dev/null +++ b/mods/_standard/forums/forum/subscribe_forum.php @@ -0,0 +1,52 @@ +addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; +} + +$sql = "SELECT title FROM ".TABLE_PREFIX."forums WHERE forum_id=$fid"; +$result = mysql_query($sql, $db); +if ($row = mysql_fetch_assoc($result)) { + $forum_title = $row['title']; +} else { + $msg->addError('FORUM_NOT_FOUND'); + header('Location: list.php'); + exit; +} + +if (isset($_GET['us'])) { + $sql = "DELETE from ".TABLE_PREFIX."forums_subscriptions WHERE forum_id = $fid AND member_id = $_SESSION[member_id]"; + $result = mysql_query($sql, $db); + $msg->addFeedback(array(FORUM_UNSUBSCRIBED, $forum_title)); + +} else { + $sql = "INSERT into ".TABLE_PREFIX."forums_subscriptions VALUES($fid, '$_SESSION[member_id]')"; + mysql_query($sql, $db); + + $msg->addFeedback(array(FORUM_SUBSCRIBED,$forum_title)); +} + +header('Location: list.php'); +exit; +?> \ No newline at end of file diff --git a/mods/_standard/forums/forum/view.php b/mods/_standard/forums/forum/view.php new file mode 100644 index 000000000..e18f7741f --- /dev/null +++ b/mods/_standard/forums/forum/view.php @@ -0,0 +1,220 @@ +printErrors('FORUM_DENIED'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +// set default thread display order to ascending +if (!isset($_SESSION['thread_order'])) +{ + $_SESSION['thread_order'] = 'a'; +} +else if (isset($_GET['order'])) +{ + $_SESSION['thread_order'] = $_GET['order']; +} + +$forum_info = get_forum($fid); + +$_pages[url_rewrite('mods/_standard/forums/forum/index.php?fid='.$fid)]['title'] = get_forum_name($fid); +$_pages[url_rewrite('mods/_standard/forums/forum/index.php?fid='.$fid)]['parent'] = 'mods/_standard/forums/forum/list.php'; +$_pages[url_rewrite('mods/_standard/forums/forum/index.php?fid='.$fid)]['children'] = array(url_rewrite('mods/_standard/forums/forum/new_thread.php?fid='.$fid), 'search.php?search_within[]=forums'); + +$_pages[url_rewrite('mods/_standard/forums/forum/new_thread.php?fid='.$fid)]['title_var'] = 'new_thread'; +$_pages[url_rewrite('mods/_standard/forums/forum/new_thread.php?fid='.$fid)]['parent'] = url_rewrite('mods/_standard/forums/forum/index.php?fid='.$fid); + +$_pages['mods/_standard/forums/forum/view.php']['parent'] = url_rewrite('mods/_standard/forums/forum/index.php?fid='.$fid); +$_pages['search.php?search_within[]=forums']['title_var'] = 'search'; +$_pages['search.php?search_within[]=forums']['parent'] = url_rewrite('mods/_standard/forums/forum/index.php'); + +if ($_REQUEST['reply']) { + $onload = 'document.form.subject.focus();'; +} + +$pid = intval($_GET['pid']); + +$num_per_page = 10; +if (!$_GET['page']) { + $page = 1; +} else { + $page = (intval($_GET['page'])>0)?(intval($_GET['page'])):1; +} +$start = ($page-1)*$num_per_page; + +/* get the first thread first */ +$sql = "SELECT *, DATE_FORMAT(date, '%Y-%m-%d %H:%i:%s') AS date, UNIX_TIMESTAMP(date) AS udate FROM ".TABLE_PREFIX."forums_threads WHERE post_id=$pid AND forum_id=$fid"; +$result = mysql_query($sql, $db); + +if (!($post_row = mysql_fetch_array($result))) { + require(AT_INCLUDE_PATH.'header.inc.php'); + $_pages['mods/_standard/forums/forum/view.php']['title'] = _AT('no_post'); + + echo _AT('no_post'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +$_pages['mods/_standard/forums/forum/view.php']['title'] = $post_row['subject']; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + <?php echo _AT('reply'); ?> +'._AT('lock_no_read1').'

    '; + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + + $parent_name = $post_row['subject']; + + echo '
      '; + print_entry($post_row); + $subject = $post_row['subject']; + if ($_GET['reply'] == $post_row['post_id']) { + $saved_post = $post_row; + } + echo ' +
    +
      +

    '; + + $sql = "SELECT *, DATE_FORMAT(date, '%Y-%m-%d %H-%i:%s') AS date, UNIX_TIMESTAMP(date) AS udate FROM ".TABLE_PREFIX."forums_threads WHERE parent_id=$pid AND forum_id=$fid ORDER BY date "; + if ($_SESSION['thread_order'] == 'a') + $sql .= "ASC LIMIT $start, $num_per_page"; + else + $sql .= "DESC LIMIT $start, $num_per_page"; + + $result = mysql_query($sql, $db); + + if (mysql_num_rows($result) > 0) + { + echo '
    '; + echo '
    '; + if ($_SESSION['thread_order'] == 'a') + echo ' +  '._AT('recent_first').' + '; + else + echo ' +  '._AT('recent_last').' + '; + + echo '
    '; + + echo _AT('page').': '; + for ($i=1; $i<=$num_pages; $i++) { + if ($i == $page) { + echo ''.$i.''; + } else { + echo ''.$i.''; + } + + if ($i<$num_pages){ + echo ' | '; + } + } + echo '
    '; + echo '
      '; + + while ($row = mysql_fetch_assoc($result)) { + print_entry($row); + $subject = $row['subject']; + if ($_GET['reply'] == $row['post_id']) { + $saved_post = $row; + } + } + echo '
    '; + + echo '
    '; + echo _AT('page').': '; + for ($i=1; $i<=$num_pages; $i++) { + if ($i == $page) { + echo ''.$i.''; + } else { + echo ''.$i.''; + } + + if ($i<$num_pages){ + echo ' | '; + } + } + echo '
    '; + } + + $parent_id = $pid; + $body = ''; + if (substr($subject,0,3) != 'Re:') { + $subject = 'Re: '.$subject; + } + + if ($_SESSION['valid_user'] && $_SESSION['enroll'] && !$locked) { + $sql = "SELECT subscribe FROM ".TABLE_PREFIX."forums_accessed WHERE post_id=$_GET[pid] AND member_id=$_SESSION[member_id]"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + if ($row['subscribe']) { + echo '

    '._AT('unsubscribe').'

    '; + $subscribed = true; + } else { + echo '

    '._AT('subscribe').'

    '; + $subscribed = false; + } + } + if ($_SESSION['valid_user'] && !$_SESSION['enroll']) { + echo '

    '._AT('enroll_to_post').'

    '; + } else if ($locked == 0) { + require(AT_INCLUDE_PATH.'../mods/_standard/forums/html/new_thread.inc.php'); + } else { + echo '

    '._AT('lock_no_post1').'

    '; + } + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/forums/html/forum.inc.php b/mods/_standard/forums/html/forum.inc.php new file mode 100644 index 000000000..62ba2dfcc --- /dev/null +++ b/mods/_standard/forums/html/forum.inc.php @@ -0,0 +1,195 @@ + 'desc', 'desc' => 'asc'); +$cols = array('subject' => 1, 'num_comments' => 1, 'last_comment' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'last_comment'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'last_comment'; +} else { + // no order set + $order = 'desc'; + $col = 'last_comment'; +} + +$sql = "SELECT *, last_comment + 0 AS stamp, DATE_FORMAT(last_comment, '%Y-%m-%d %H:%i:%s') AS last_comment FROM ".TABLE_PREFIX."forums_threads WHERE parent_id=0 AND forum_id=$fid AND member_id>0 ORDER BY sticky DESC, $col $order LIMIT $start,$num_per_page"; +$result = mysql_query($sql, $db); + +if (!($row = mysql_fetch_assoc($result))) { + echo '
    '; + $msg->printInfos('NO_POSTS_FOUND'); + echo '
    '; + return; +} +?> + ++ + + + + + + + + + + + + + + + + + + + '; + $colspan++; + } + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + do { + /* crop the subject, if needed */ + $full_subject = $row['subject']; //save a copy before croping + if ($strlen($row['subject']) > 28) { + $row['subject'] = $substr($row['subject'], 0, 25).'...'; + } + $row['subject'] = AT_print($row['subject'], 'forums_threads.subject'); + echo ''; + echo ''; + + echo ''; + + echo ''; + + echo ''; + + if (authenticate(AT_PRIV_FORUMS, AT_PRIV_RETURN)) { + echo ''; + } + echo ''; + + } while ($row = mysql_fetch_assoc($result)); + echo ''; + echo '
    ">">">
    '._AT('page').': '; + + for ($i=1; $i<=$num_pages; $i++) { + if ($i == $page) { + echo $i; + } else { + echo ''.$i.''; + } + + if ($i<$num_pages){ + echo ' | '; + } + } + + echo '
    '; + + if ($_SESSION['valid_user']) { + if ($row['stamp'] > $last_accessed[$row['post_id']]['last_accessed']) { + echo ''._AT('new').' '; + } + } + + if ($row['num_comments'] > 10) { + echo ''._AT('hot').' '; + } + + if ($row['locked'] != 0) { + echo ''._AT('thread_locked').' '; + } + + if ($row['sticky'] != 0) { + echo ''._AT('sticky_thread').' '; + } + + if ($row['locked'] != 1) { + echo ''.$row['subject'].''; + + if ($row['locked'] == 2) { + echo ' ('._AT('post_lock').')'; + } + } else { + echo $row['subject'].' ('._AT('read_lock').')'; + } + + /* print page numbers */ + $num_pages_2 = ceil(($row['num_comments']+1)/$num_per_page); + + if ($num_pages_2 > 1) { + echo ' ( Page: '; + for ($i=2; $i<=$num_pages_2; $i++) { + echo ''.$i.''; + + if ($i<$num_pages_2){ + echo ' | '; + } + } + echo ' ) '; + } + if ($_SESSION['enroll'] && !$row['locked']) { + if (isset($last_accessed[$row['post_id']]) && $last_accessed[$row['post_id']]['subscribe']){ + echo '
    ('._AT('unsubscribe1').')'; + } else { + echo '
    ('._AT('subscribe1').')'; + } + } + echo '
    '.$row['num_comments'].''.get_display_name($row['member_id']).''; + echo AT_date(_AT('forum_date_format'),$row['last_comment'], AT_DATE_MYSQL_DATETIME); + echo ''; + echo ' '._AT('sticky_thread').' '; + + if ($row['locked'] != 0) { + echo ''._AT('unlock_thread').''; + } else { + echo ''._AT('lock_thread').''; + } + echo ' '._AT('move_thread').''; + + echo ' '._AT('delete_thread').''; + + echo '
    '; + +?> \ No newline at end of file diff --git a/mods/_standard/forums/html/new_thread.inc.php b/mods/_standard/forums/html/new_thread.inc.php new file mode 100644 index 000000000..bef3ba76f --- /dev/null +++ b/mods/_standard/forums/html/new_thread.inc.php @@ -0,0 +1,102 @@ +printInfos('LOGIN_TO_POST'); + return; +} + +$msg->printErrors(); + +if (isset($_POST['submit'])) { + $subject = htmlentities_utf8($_POST['subject']); + $body = htmlentities_utf8($_POST['body']); + $parent_id = $_POST['parent_id']; + $parent_name = $_POST['parent_name']; + //post reply is set when there is an error occuring. + if ($_POST['reply']!=''){ + $saved_post['body'] = $_POST['replytext']; + $reply_hidden = ''; + } +} else if (isset($_GET['reply']) && $_GET['reply'] != '') { + $subject = $saved_post['subject']; + $reply_hidden = ''; + + if (substr($subject, 0, 3) != 'Re:') { + $subject = 'Re: '.$subject; + } +} + +?> + +
    + + + + + + + + +
    +
    +
    + *
    + +
    + +
    + *
    + + +
    ·
    + ·
    + ·
    +
    + + +
    +
    + +
    + + + +
    + <?php echo _AT('jump_codes'); ?> + + +
    + + +
    + + +
    + +
    + +
    + +
    +
    + + + + +
    +
    +
    \ No newline at end of file diff --git a/mods/_standard/forums/index.php b/mods/_standard/forums/index.php new file mode 100644 index 000000000..e7b18c8cc --- /dev/null +++ b/mods/_standard/forums/index.php @@ -0,0 +1,78 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'../mods/_standard/forums/lib/forums.inc.php'); + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +$all_forums = get_forums($_SESSION['course_id']); +?> +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    + + + + + +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/forums/lib/forums.inc.php b/mods/_standard/forums/lib/forums.inc.php new file mode 100644 index 000000000..473e26649 --- /dev/null +++ b/mods/_standard/forums/lib/forums.inc.php @@ -0,0 +1,287 @@ + 1) { + return TRUE; + } // else: + + return FALSE; +} + + +/** +* Returns forum information for given forum_id +* @access public +* @param integer $forum_id id of the forum +* @param integer $course id of the course (for non-admins) +* @return string array each row is a forum +* @see $db in include/vitals.inc.php +* @author Heidi Hazelton +*/ +function get_forum($forum_id, $course = '') { + global $db; + + if (!empty($course)) { + $sql = "SELECT * FROM ".TABLE_PREFIX."forums_courses fc, ".TABLE_PREFIX."forums f WHERE (fc.course_id=$course OR fc.course_id=0) AND fc.forum_id=f.forum_id and fc.forum_id=$forum_id ORDER BY title"; + $result = mysql_query($sql, $db); + $forum = mysql_fetch_assoc($result); + } else if (empty($course)) { //only admins should be retrieving forums w/o a course! add this check + $sql = "SELECT * FROM ".TABLE_PREFIX."forums WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + $forum = mysql_fetch_assoc($result); + } else { + + return; + } + + return $forum; +} + +/** +* Checks to see if signed in member is allowed to view the forum page +* @access public +* @param integer $forum_id id of the forum +* @return boolean view (true) or not view (false) +* @see $db in include/vitals.inc.php +* @author Heidi Hazelton +*/ +function valid_forum_user($forum_id) { + global $db; + + $sql = "SELECT forum_id FROM ".TABLE_PREFIX."forums_courses WHERE (course_id=$_SESSION[course_id] OR course_id=0) AND forum_id=$forum_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + if (empty($row)) { + // not a course forum, let's check group: + if (!empty($_SESSION['groups'])){ + $groups = implode(',', $_SESSION['groups']); + $sql = "SELECT forum_id FROM ".TABLE_PREFIX."forums_groups WHERE group_id IN ($groups) AND forum_id=$forum_id"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + return TRUE; + } + } + + return FALSE; + } + + return TRUE; +} + +/** +* Adds a forum +* @access public +* @param array $_POST add-forum form variables +* @see $db in include/vitals.inc.php +* @see $addslashes in include/vitals.inc.php +* @author Heidi Hazelton +*/ +function add_forum($_POST) { + global $db; + global $addslashes; + + $_POST['title'] = $addslashes($_POST['title']); + $_POST['body'] = $addslashes($_POST['body']); + $_POST['edit'] = intval($_POST['edit']); + + $sql = "INSERT INTO ".TABLE_PREFIX."forums VALUES (NULL,'$_POST[title]', '$_POST[body]', 0, 0, NOW(), $_POST[edit])"; + $result = mysql_query($sql,$db); + + $sql = "INSERT INTO ".TABLE_PREFIX."forums_courses VALUES (LAST_INSERT_ID(), $_SESSION[course_id])"; + $result = mysql_query($sql,$db); + + return; +} + +/** +* Edits a forum +* @access public +* @param array $_POST add-forum form variables +* @see $db in include/vitals.inc.php +* @see $addslashes in include/vitals.inc.php +* @author Heidi Hazelton +*/ +function edit_forum($_POST) { + global $db; + global $addslashes; + + $_POST['title'] = $addslashes($_POST['title']); + $_POST['body'] = $addslashes($_POST['body']); + + $_POST['fid'] = intval($_POST['fid']); + $_POST['edit'] = intval($_POST['edit']); + + $sql = "UPDATE ".TABLE_PREFIX."forums SET title='$_POST[title]', description='$_POST[body]', last_post=last_post, mins_to_edit=$_POST[edit] WHERE forum_id=$_POST[fid]"; + $result = mysql_query($sql,$db); + + return; +} + +/** +* Deletes a forum (checks if its shared). +* Assumes the forum is not shared. +* Assumes the user has the priv to delete this forum. +* @access public +* @param array $_POST add-forum form variables +* @see $db in include/vitals.inc.php +* @see $addslashes in include/vitals.inc.php +* @author Heidi Hazelton +*/ +function delete_forum($forum_id) { + global $db; + + $sql = "SELECT post_id FROM ".TABLE_PREFIX."forums_threads WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + while ($row = mysql_fetch_array($result)) { + $sql = "DELETE FROM ".TABLE_PREFIX."forums_accessed WHERE post_id=$row[post_id]"; + $result2 = mysql_query($sql, $db); + } + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_subscriptions WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_threads WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_courses WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."forums WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + + $sql = "DELETE FROM ".TABLE_PREFIX."content_forums_assoc WHERE forum_id=$forum_id"; + $result = mysql_query($sql, $db); + + $sql = "OPTIMIZE TABLE ".TABLE_PREFIX."forums_threads"; + $result = mysql_query($sql, $db); + +} + +function print_entry($row) { + global $page,$system_courses, $forum_info; + static $counter; + $counter++; + + $reply_link = ''._AT('reply').''; + +?> + +
  • + + + +
    +

    +
    +
    + + | | + time())): ?> + | () + + + +
    +

      

    + +
    + +
    +

    +
    +
    +
  • + \ No newline at end of file diff --git a/mods/_standard/forums/module.php b/mods/_standard/forums/module.php new file mode 100644 index 000000000..3b27e5315 --- /dev/null +++ b/mods/_standard/forums/module.php @@ -0,0 +1,77 @@ +getPrivilege() ); +define('AT_ADMIN_PRIV_FORUMS', $this->getAdminPrivilege() ); + +// if this module is to be made available to students on the Home or Main Navigation +$_group_tool = $_student_tool = 'mods/_standard/forums/forum/list.php'; + +//side dropdown +$this->_stacks['posts'] = array('title_var'=>'posts','file'=>AT_INCLUDE_PATH.'../mods/_standard/forums/dropdown/posts.inc.php'); + +//modules sub-content +$this->_list['forums'] = array('title_var'=>'forums','file'=>'mods/_standard/forums/sublinks.php'); + +//tool manager +$this->_tool['forums'] = array('title_var'=>'forums','file'=>'mods/_core/tool_manager/forums_tool.php','table'=>'content_forums_assoc'); + +//instructor pages +$this->_pages['mods/_standard/forums/index.php']['title_var'] = 'forums'; +$this->_pages['mods/_standard/forums/index.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_standard/forums/index.php']['guide'] = 'instructor/?p=forums.php'; +$this->_pages['mods/_standard/forums/index.php']['children'] = array('mods/_standard/forums/add_forum.php'); + + $this->_pages['mods/_standard/forums/add_forum.php']['title_var'] = 'create_forum'; + $this->_pages['mods/_standard/forums/add_forum.php']['parent'] = 'mods/_standard/forums/index.php'; + + $this->_pages['mods/_standard/forums/delete_forum.php']['title_var'] = 'delete_forum'; + $this->_pages['mods/_standard/forums/delete_forum.php']['parent'] = 'mods/_standard/forums/forums/index.php'; + + $this->_pages['mods/_standard/forums/edit_forum.php']['title_var'] = 'edit_forum'; + $this->_pages['mods/_standard/forums/edit_forum.php']['parent'] = 'mods/_standard/forums/index.php'; + +//student pages +$this->_pages['mods/_standard/forums/forum/list.php']['title_var'] = 'forums'; +$this->_pages['mods/_standard/forums/forum/list.php']['img'] = 'images/home-forums.png'; +$this->_pages['mods/_standard/forums/forum/list.php']['icon'] = 'images/pin.png'; //added favicon +//$this->_pages['forum/list.php']['text'] = 'Sezione Forum'; //added text +$this->_pages['mods/_standard/forums/forum/list.php']['children'] = array('search.php?search_within[]=forums'); + //list.php's children + $this->_pages['search.php?search_within[]=forums']['title_var'] = 'search'; + $this->_pages['search.php?search_within[]=forums']['parent'] = 'mods/_standard/forums/index.php'; + +// for admin +if (admin_authenticate(AT_ADMIN_PRIV_FORUMS, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + if (admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + $this->_pages['mods/_core/courses/admin/courses.php']['children'] = array('mods/_standard/forums/admin/forums.php'); + $this->_pages['mods/_standard/forums/admin/forums.php']['parent'] = 'mods/_core/courses/admin/courses.php'; + } else { + $this->_pages[AT_NAV_ADMIN] = array('mods/_standard/forums/admin/forums.php'); + $this->_pages['mods/_standard/forums/admin/forums.php']['parent'] = AT_NAV_ADMIN; + } + + $this->_pages['mods/_standard/forums/admin/forums.php']['title_var'] = 'forums'; + $this->_pages['mods/_standard/forums/admin/forums.php']['guide'] = 'mods/_standard/forums/admin/?p=forums.php'; + $this->_pages['mods/_standard/forums/admin/forums.php']['children'] = array('mods/_standard/forums/admin/forum_add.php'); + + $this->_pages['mods/_standard/forums/admin/forum_add.php']['title_var'] = 'create_forum'; + $this->_pages['mods/_standard/forums/admin/forum_add.php']['parent'] = 'mods/_standard/forums/admin/forums.php'; + + $this->_pages['mods/_standard/forums/admin/forum_edit.php']['title_var'] = 'edit_forum'; + $this->_pages['mods/_standard/forums/admin/forum_edit.php']['parent'] = 'mods/_standard/forums/admin/forums.php'; + + $this->_pages['mods/_standard/forums/admin/forum_delete.php']['title_var'] = 'delete_forum'; + $this->_pages['mods/_standard/forums/admin/forum_delete.php']['parent'] = 'mods/_standard/forums/admin/forums.php'; +} + +function forums_get_group_url($group_id) { + global $db; + $sql = "SELECT forum_id FROM ".TABLE_PREFIX."forums_groups WHERE group_id=$group_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + return 'mods/_standard/forums/forum/index.php?fid='.$row['forum_id']; +} +?> \ No newline at end of file diff --git a/mods/_standard/forums/module.xml b/mods/_standard/forums/module.xml new file mode 100644 index 000000000..e3bbf856f --- /dev/null +++ b/mods/_standard/forums/module.xml @@ -0,0 +1,23 @@ + + + Forums + Instructors may create and manage forums for course discussion. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + create + + 2005-08-26 + stable + This is a standard module. + + \ No newline at end of file diff --git a/mods/_standard/forums/module_delete.php b/mods/_standard/forums/module_delete.php new file mode 100644 index 000000000..3650a291a --- /dev/null +++ b/mods/_standard/forums/module_delete.php @@ -0,0 +1,74 @@ + 1) { + // this is a shared forum: + // debug('unsubscribe all the students who will not be able to access this forum anymore.'); + $sql = "SELECT course_id FROM ".TABLE_PREFIX."forums_courses WHERE forum_id=$forum[forum_id] AND course_id <> $course"; + $result2 = mysql_query($sql, $db); + while ($row2 = mysql_fetch_assoc($result2)) { + $courses[] = $row2['course_id']; + } + $courses_list = implode(',', $courses); + + // list of all the students who are in other courses as well + $sql = "SELECT member_id FROM ".TABLE_PREFIX."course_enrollment WHERE course_id IN ($courses_list)"; + $result2 = mysql_query($sql, $db); + while ($row2 = mysql_fetch_assoc($result2)) { + $students[] = $row2['member_id']; + } + + $students_list = implode(',', $students); + + if ($students_list) { + // remove the subscriptions + $sql = "SELECT post_id FROM ".TABLE_PREFIX."forums_threads WHERE forum_id=$forum[forum_id]"; + $result2 = mysql_query($sql, $db); + while ($row2 = mysql_fetch_array($result2)) { + $sql = "DELETE FROM ".TABLE_PREFIX."forums_accessed WHERE post_id=$row2[post_id] AND member_id NOT IN ($students_list)"; + $result3 = mysql_query($sql, $db); + } + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_subscriptions WHERE forum_id=$forum[forum_id] AND member_id NOT IN ($students_list)"; + $result3 = mysql_query($sql, $db); + } + + $sql = "DELETE FROM ".TABLE_PREFIX."forums_courses WHERE forum_id=$forum[forum_id] AND course_id=$course"; + $result = mysql_query($sql, $db); + } + } + + $sql = "OPTIMIZE TABLE ".TABLE_PREFIX."forums_threads"; + $result = mysql_query($sql, $db); +} + +?> \ No newline at end of file diff --git a/mods/_standard/forums/module_format_content.php b/mods/_standard/forums/module_format_content.php new file mode 100644 index 000000000..4e8f654bc --- /dev/null +++ b/mods/_standard/forums/module_format_content.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/mods/_standard/forums/module_groups.php b/mods/_standard/forums/module_groups.php new file mode 100644 index 000000000..3193ba688 --- /dev/null +++ b/mods/_standard/forums/module_groups.php @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/mods/_standard/forums/module_news.php b/mods/_standard/forums/module_news.php new file mode 100644 index 000000000..a49170f9a --- /dev/null +++ b/mods/_standard/forums/module_news.php @@ -0,0 +1,56 @@ + + */ +function forums_news() { + require_once(AT_INCLUDE_PATH.'../mods/_standard/forums/lib/forums.inc.php'); + global $db, $enrolled_courses, $system_courses; + $news = array(); + + if ($enrolled_courses == ''){ + return $news; + } + + $sql = 'SELECT E.approved, E.last_cid, C.* FROM '.TABLE_PREFIX.'course_enrollment E, '.TABLE_PREFIX.'courses C WHERE E.member_id=1 AND E.course_id=C.course_id ORDER BY C.title'; + $result = mysql_query($sql, $db); + if ($result) { + while($row = mysql_fetch_assoc($result)){ + $all_forums = get_forums($row['course_id']); + if (is_array($all_forums)){ + foreach($all_forums as $forums){ + if (is_array($forums)){ + + foreach ($forums as $forum_obj){ + $forum_obj['course_id'] = $row['course_id']; + $link_title = $forum_obj['title']; + $news[] = array('time'=>$forum_obj['last_post'], + 'object'=>$forum_obj, + 'alt'=>_AT('forum'), + 'thumb'=>'images/pin.png', + 'course'=>$system_courses[$row['course_id']]['title'], + 'link'=>' SUBLINK_TEXT_LEN ? ' title="'.$link_title.'"' : '') .'>'. + validate_length($link_title, SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''); + } + } + } + } + } + } + return $news; +} + +?> \ No newline at end of file diff --git a/mods/_standard/forums/sublinks.php b/mods/_standard/forums/sublinks.php new file mode 100644 index 000000000..70863358b --- /dev/null +++ b/mods/_standard/forums/sublinks.php @@ -0,0 +1,35 @@ + $forums) { + if (is_array($forums)) { + + foreach($forums as $row) { + if ($cnt >= $record_limit) break 2; + $cnt++; + + $link_title = $row['title'].' ('.AT_DATE('%F %j, %g:%i',$row['last_post'],AT_DATE_MYSQL_DATETIME).')'; + $list[] = ' SUBLINK_TEXT_LEN ? ' title="'.$link_title.'"' : '') .'>'. + validate_length($link_title, SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''; + } + } +} + +if (count($list) > 0) { + return $list; +} else { + return 0; +} +?> \ No newline at end of file diff --git a/mods/_standard/forums/view_item.php b/mods/_standard/forums/view_item.php new file mode 100644 index 000000000..1e89f3509 --- /dev/null +++ b/mods/_standard/forums/view_item.php @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/mods/_standard/google_search/SOAP_Google.php b/mods/_standard/google_search/SOAP_Google.php new file mode 100644 index 000000000..4ad561233 --- /dev/null +++ b/mods/_standard/google_search/SOAP_Google.php @@ -0,0 +1,114 @@ +_licenseKey = $licenseKey; + + $this->_soapClient = new nusoapclient("http://api.google.com/search/beta2"); + } + +/** + * Retrieves a page by URL from the Google Cache. + * + * @param string + * @return mixed + * @access public + */ + function getCachedPage($url) { + $result = $this->_performAPICall( + "doGetCachedPage", + + array( + "key" => $this->_licenseKey, + "url" => $url + ) + ); + + if ($result) { + $result = base64_decode($result); + } + + return $result; + } + + /** + * Retrieves a spelling suggestion for a phrase. + * + * @param string + * @return mixed + * @access public + */ + function getSpellingSuggestion($phrase) { + return $this->_performAPICall( + "doSpellingSuggestion", + + array( + "key" => $this->_licenseKey, + "phrase" => $phrase + ) + ); + } + + /** + * Performs a web search. + * + * @param array + * @return mixed + * @access public + */ + function search($parameters = array()) { + if (!isset($parameters["query"])) { + return false; + } + + return $this->_performAPICall( + "doGoogleSearch", + + array( + "key" => $this->_licenseKey, + "q" => $parameters["query"], + "start" => isset($parameters["start"]) ? $parameters["start"] : 0, + "maxResults" => isset($parameters["maxResults"]) ? $parameters["maxResults"] : 10, + "filter" => isset($parameters["filter"]) ? $parameters["filter"] : false, + "restrict" => isset($parameters["restrict"]) ? $parameters["restrict"] : "", + "safeSearch" => isset($parameters["safeSearch"]) ? $parameters["safeSearch"] : false, + "lr" => isset($parameters["lr"]) ? $parameters["lr"] : "", + "ie" => isset($parameters["ie"]) ? $parameters["ie"] : "", + "oe" => isset($parameters["oe"]) ? $parameters["oe"] : "" + ) + ); + } + + /** + * @param string + * @param array + * @return mixed + * @access private + */ + function _performAPICall($apiCall, $parameters) { + $result = $this->_soapClient->call( + $apiCall, + $parameters, + "urn:GoogleSearch" + ); + + // if (!PEAR::isError($result)) { + if (is_array($result)) { + return $result; + } else { + return false; + } + } +} +?> \ No newline at end of file diff --git a/mods/_standard/google_search/admin/module_prefs.php b/mods/_standard/google_search/admin/module_prefs.php new file mode 100644 index 000000000..6c2c3b75f --- /dev/null +++ b/mods/_standard/google_search/admin/module_prefs.php @@ -0,0 +1,127 @@ +addFeedback('GOOGLE_KEY_SAVED'); + } elseif ($_GET['keyIsValidated']=='false'){ + //If invalid, remove whatever key that's in the system + $msg->addError('GOOGLE_KEY_INVALID'); + $key = htmlspecialchars($stripslashes($_GET['key'])); + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='gsearch'"; + $result = mysql_query($sql, $db); + $key = ''; + } + //Manually print it out + $msg->printAll(); +} + +if (isset($_POST['submit'])) { + require('../SOAP_Google.php'); + $_POST['key'] = trim($_POST['key']); + $_POST['gtype'] = trim($_POST['gtype']); + + if ($_POST['key']) { + //Default google search type to soap + if (!isset($_POST['gtype'])){ + $_POST['gtype'] = GOOGLE_TYPE_SOAP; + } + if ($_POST['gtype']==GOOGLE_TYPE_SOAP){ + //test key + $google = new SOAP_Google($_POST['key']); + $search_array = array(); + $search_array['filter'] = true; + $search_array['query'] = 'testing'; + $search_array['maxResults'] = 1; + $search_array['lr'] = "lang_en"; + + $result = $google->search($search_array); + + if (isset($result['faultstring'])) { + //If it is invalid, remove whatever keys that are in the system. + $msg->addError('GOOGLE_KEY_INVALID'); + $key = htmlspecialchars($stripslashes($_POST['key'])); + $sql = "DELETE FROM ".TABLE_PREFIX."config WHERE name='gsearch'"; + $result = mysql_query($sql, $db); + $key = ''; + } else { + $key = $addslashes($_POST['key']); + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('gsearch','$key')"; + $result = mysql_query($sql, $db); + $msg->addFeedback('GOOGLE_KEY_SAVED'); + } + } elseif ($_POST['gtype']==GOOGLE_TYPE_AJAX){ + $key = $addslashes($_POST['key']); + $gtype = $addslashes($_POST['gtype']); + //Test the key by the script site. + ?> + + + addFeedback('GOOGLE_KEY_SAVED'); + $key = ''; + } + + //Set Google interface's type. + $googleType = $addslashes($_POST['gtype']); + $sql = "REPLACE INTO ".TABLE_PREFIX."config VALUES('gtype','$googleType')"; + $result = mysql_query($sql, $db); + + //Manually print it out + $msg->printAll(); +} + +?> +
    +
    +
    +
    + + /> +
    + + /> +
    +
    + +
    +

    + +
    +
    + +
    + +
    + +
    +
    + +
    + + + \ No newline at end of file diff --git a/mods/_standard/google_search/google.gif b/mods/_standard/google_search/google.gif new file mode 100644 index 0000000000000000000000000000000000000000..845b9f9f455b1d735e2a182bec7088dc723cba76 GIT binary patch literal 1446 zcmW-feNYrc7{*slM?8cyAS<1+j~Pv%MwU6x!^6x;#8L0mLKN$)#jcQYne5gph(u!K9#45Y^;DaxOWOoJvmAT_z;sk}=7sWJHZpAt{%X zNlGOpP?AJQ!X;snP)Sfg8`99iFm4mIM;$H%6M_ms)LK-W3(f?mf)ho7CgXxJ!Kh$F zLE$dtf-*s=pahH<6@&}I1fhapE2tp`Xix$RJuHScq@jgjq711ThjC6hC(0kzqGF6Q z${A61L4hV^oKj8++ye@CIboboPGApt029-Q1O5R4hoFWSph1bN;J{*NLmFB{C8ye` zaVTR%1qpvxi;7Z4337#9P@u^OWdu1QX#@Z+Tu0Dwiztx?Ffoldzy!l^2x^D{8k4{R z2Npvc(nLLxx{Wq!9D)}^kno4Ks3=jNL*?pwZ`JfYp^Dk}D(bE;XqCDzJ2mQyLp_4e zp;Gcet2gm+C}E#8z5`wf;lYz{)pM`cJ3KT}Sy44Q>h0?4b`N>S8L!SV7h0?}rPp0y z>*|(SsTW-Gs;BbG@SR)dSH^jsJ!@-ibvm8+`frSk9Rs5z91H-7B*Y)4Crn#> zsi1nlfAFkC$E9=jLzCWGa(1!pd~Np2bYqLdR`{oN`og2r;(jXn>%`1e-)H$3Y`C_) zE%|uZ&Y(4QdHNmQHJ>yjRfI-w^=`_@Jv(>rbI%dY_|i2ui=)f0u6po7w>iDz$HDRm zq*=FPaLbd5QxP+CyB>f0&4I?!wRN^ndn`Y!e*AjH_SX2c%{(VO^RwZ;ddF8~;h7oz zFV4vPyD?9{t@F*Mf&b$3kG&H5#InYAK0j%DQTLS!dgtXWm?R#M9%}#i_R5$IiMGsnAsdJC_5WsRh@mK+QCHmjkl zXl4j;>8aKbv2CGsU-RJ3;Fz25nCXLn{(ihj$;dQ;G*j`Ak+P*%IpKip*x z)EU3H((r0zWnn{1VtVe0Z?o7MCPq41?--jMR1*-M8eCv-dD!5=Y^`rD1>r0V@mrsA`1 zYC3wqWdF(BmZEm+{D0Eyg)72N+drD$lirl0^Ss?@HH@h`vr5;Sb|NQ5pOSYm<>9e) zwMN~=)}`Cbzg}CRb43LQt$z5WU&s=Yt~pF;_2odL^S6Najm-&@Z|J-?54Ob3aa&AA eXK7L2?-S?r_Sq-JhWFQmtWNE(ojPtTiT)pzGv?j^ literal 0 HcmV?d00001 diff --git a/mods/_standard/google_search/gsearch.php b/mods/_standard/google_search/gsearch.php new file mode 100644 index 000000000..194b21b4a --- /dev/null +++ b/mods/_standard/google_search/gsearch.php @@ -0,0 +1,317 @@ + + + + +
    + + + + + + + +
    +
    +
    +
    Loading
    +
    +
    +
    diff --git a/mods/_standard/google_search/index.php b/mods/_standard/google_search/index.php new file mode 100644 index 000000000..11106ea55 --- /dev/null +++ b/mods/_standard/google_search/index.php @@ -0,0 +1,109 @@ + + + + search($search_array); + + if (isset($result['faultstring'])) { + $msg->printErrorS('GOOGLE_QUERY_FAILED'); + } else if ($result) { + echo '

    Search Results

    '; + if (is_array($result['resultElements'])) { + echo '
      '; + + foreach ($result['resultElements'] as $r) { + echo '
    1. ' . ($r['title'] ? $r['title'] : ''._AT('no_title').'' ) . ''; + echo '
      '; + echo ''.($r['snippet'] ? $r['snippet'] : ''._AT('no_content_avail').'' ) .'
      '.$r['URL'].'
      '; + echo '
    2. '; + } + + if (count($result['resultElements']) == 10) { + $search_array['start'] = 10; + $result2 = $google->search($search_array); + + if (false !== $result2) { + foreach ($result2['resultElements'] as $r) { + echo '
    3. ' . ($r['title'] ? $r['title'] : ''._AT('no_title').'' ) . ''; + echo '
      '; + echo ''.($r['snippet'] ? $r['snippet'] : ''._AT('no_content_avail').'' ) .'
      '.$r['URL'].'
      '; + echo '
    4. '; + } + } + } + echo '
    '; + + if (count($result2['resultElements']) == 10) { + echo '

    '._AT('top_20').'

    '; + } + } else { + echo '

    '._AT('none_found').'

    '; + } + } + } + ?> + +
    + + + + + +
    +
    + + +

    +

    + +
    + +
    + *
    + +
    + +
    + + +
    + +
    + +
    +
    +
    + + + + + \ No newline at end of file diff --git a/mods/_standard/google_search/module.css b/mods/_standard/google_search/module.css new file mode 100644 index 000000000..8a5667090 --- /dev/null +++ b/mods/_standard/google_search/module.css @@ -0,0 +1,55 @@ +/* Load the defaulted google search css */ +@import url('http://www.google.com/uds/css/gsearch.css'); + +/* Overwrite google defaulted form styles */ +td { +vertical-align : top; +} +td.search-form { +width : 400px; +} +td.gsc-clear-button { +padding-left: 0px; +} +input.gsc-search-button { + border: 1px solid #999; + background-color: white; + padding: 2px; + padding-left: 10px; + padding-right: 10px; +} +input.gsc-search-button:hover { + background-color: #f5f5ff; + border: 1px solid blue; + cursor: pointer; +} + +#results .header { +font-size : 16px; +font-weight : bold; +margin-bottom : .25em; +margin-top : 1em; +} +#results .gs-result { + margin-bottom : .5em; + margin-left: 1em; + width: 600px; +} +#results div.gs-text-box{ + float: left; +} +#results div.gs-watermark { +display : none; +} + +/* Styles for the tabbed menu */ +#searcher table{ + margin-left: 5px; + width: 500px; +} +#searcher div.selector{ + border-bottom: solid 1px #e9e9e9; + margin-top: 0px; +} + +/* Styles for buttons */ diff --git a/mods/_standard/google_search/module.php b/mods/_standard/google_search/module.php new file mode 100644 index 000000000..eef34cecf --- /dev/null +++ b/mods/_standard/google_search/module.php @@ -0,0 +1,25 @@ +_pages['admin/config_edit.php']['children'] = array('mods/_standard/google_search/admin/module_prefs.php'); + + $this->_pages['mods/_standard/google_search/admin/module_prefs.php']['title_var'] = 'google_key'; + $this->_pages['mods/_standard/google_search/admin/module_prefs.php']['parent'] = 'admin/config_edit.php'; + $this->_pages['mods/_standard/google_search/admin/module_prefs.php']['guide'] = 'admin/?p=google_key.php'; + +} + +$this->_pages['mods/_standard/google_search/index.php']['text'] = _AT('google_search_text'); + +//side menu +$this->_stacks['google_search'] = array('title_var'=>'google_search', 'file'=>dirname(__FILE__).'/side_menu.inc.php'); + + +$this->_pages['mods/_standard/google_search/index.php']['title_var'] = 'google_search'; +$this->_pages['mods/_standard/google_search/index.php']['img'] = 'mods/_standard/google_search/google.gif'; + +?> \ No newline at end of file diff --git a/mods/_standard/google_search/module.xml b/mods/_standard/google_search/module.xml new file mode 100644 index 000000000..ecfd1cc0a --- /dev/null +++ b/mods/_standard/google_search/module.xml @@ -0,0 +1,23 @@ + + + Google Search + Allows one to search Google and have the results displayed within ATutor. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + + + + 2005-10-11 + stable + + + \ No newline at end of file diff --git a/mods/_standard/google_search/side_menu.inc.php b/mods/_standard/google_search/side_menu.inc.php new file mode 100644 index 000000000..36a5eb776 --- /dev/null +++ b/mods/_standard/google_search/side_menu.inc.php @@ -0,0 +1,31 @@ + + + +
    + + + + + + + + + + + +

    + + +
    + +assign('dropdown_contents', ob_get_contents()); +ob_end_clean(); + +$savant->assign('title', _AT('google_search')); +$savant->display('include/box.tmpl.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/gradebook/README b/mods/_standard/gradebook/README new file mode 100644 index 000000000..aa8aa0b5b --- /dev/null +++ b/mods/_standard/gradebook/README @@ -0,0 +1,51 @@ +Gradebook ReadMe + +This module can be installed with ATutor for: +1) instructors to manage students' marks from ATutor generated tests, from ATutor assignments, or from external tests and assignments. Multiple Tests can be combined into a single Gradebook entry. Custom scales can be defined and used across courses. Gradebook data can be exported for reporting and data analysis. +2) students to view their marks for completed tests or assignments, list tests or assignments yet to be completed or yet to be marked, and print out a report of their marks. + +For additional documentation see: http://wiki.atutor.ca/display/atutorwiki/Gradebook + +* How to install Patcher +1. Copy the downloaded file into the ATutor mods/ directory and unzip it there. + +On Windows systems use and application like WinZip the extract the file into the mods/ directory + +On Unix/Linux systems use the command: + +tar xzvf gradebook-0.1.tar.gz + +2. Login to ATutor as the administrator and run Install Module under the Modules tab, Select the Gradebook module, which should be listed as available to be installed when the module has been unzipped into the modes directory + +3. Once the module has been installed, and enabled, login as instructor, click on tab "Manage", "Gradebook" line is there. Instructors can grant access on gradebook to students through "Student Tools". + +* How to use gradebook (for instructors) +1. Gradebook Scales + ATutor includes a number of predefined, commonly used grade scales, used to convert number grades into some other form, such as a letter grade, or a Pass/Fail grade. In the Grade Scales submenu choose Add Grade Scale to create your own custom scale(s). You may choose to populate the Grade Scale form with an existing scale, then edit the scale to create a new one. Or, you may create a brand new scale. Once created, new scales become available to you in any of the course you own. + +2. Add ATutor Test/Assignment to Gradebook + +2.1 Add ATutor Assignment + Assignments that have been created using the ATutor Assignment Manager can be added to the gradebook. Select the "Title" of the assignment from those available, then select the scale to be used. Once an assignment has been added to the gradebook, marks are entered as "External Marks". Marks may be entered either as a percentage mark or a scale mark. If percentage is used, the gradebook will attempt to convert those percentage marks to a scale mark. + +2.2 Add ATutor Test + Tests that are created using the ATutor Test & Survey Manager can be added to the gradebook if the test's "Attempts Allowed" property has been set to 1 attempt. Select the test "Title" from those available, then select the Grade Scale to be used for the test. Additional scales can be created by using the Grade Scales tool. Note that Surveys will not be available to add to the Gradebook. Surveys are technically tests in the Test & Survey Manager that do not have any "weight" or mark assigned to questions. + +3. Update ATutor Marks + +3.1 Update ATutor Marks + Marks are imported from ATutor tests, rather than displaying them live from the Test & Surveys Manager. Therefore, when marks are updated in the Test & Surveys Manager, the Gradebook needs to be updated to reimport the modified marks. You may choose to update all ATutor tests at once, or choose only to update a single test at a time. Or, you may choose to update only marks for a single student, on all test or a single test. + +3.2 Combine ATutor Tests + Different ATutor generated tests can be combined into a single gradebook entry, if for instance you needed to combine marks from a term test, and marks from a make up test for students who happened to miss the term test. As many tests as required can be combined into a single parent test listed in the "Combine Into" menu. Select the test to be combined from the "Combined From" select menu, then press the "Combine" button to import the marks from that test. Be sure to run "Update ATutor Marks" on the "Combined Into" test at least once before combining marks from other tests. When combining marks from multiple tests, should you encounter a conflict such as a mark that already exists for a particular student, you will be given the option to overwrite the old mark with the new one, use the old mark, use the higher mark, or use the lower mark. + +4. External Marks + +4.1 Export + The Export tool is used to export a course list in a CSV form into which marks can be entered manually, then reimported back into the Gradebook. It can also be used to export marks from the Gradebook to import those marks back into another application such as a spreadsheet, or another database. + +4.2 Import + Marks from an external assignment or test can be imported in a Comma Separated Values (CSV) file in the form ""firstname", "lastname", "email", "grade"" with one student per line. The mark can either be a scale mark such ""A"" or ""Pass"", or a percentage mark such as "78%". Select the test or assignment previously defined through "Add Tests/Assignment" The first line of the imported file should contain the field names ""First Name, Last Name, Email, Grade" " If it is not included the first line will be removed when the marks are imported. + +5. Edit Marks + Use the Search feature to narrow the data displayed in the table below. The table below can be printed as a report, or the data displayed in it can be exported as a CSV file to be reimported into a spreadsheet or database for archiving or for additional analysis. External marks displayed in the table can be edited directly from within the table by choosing the appropriate "edit" link, editing either marks for a single student, or editing marks for a single test. To edit marks from ATutor based tests, use the "Test & Surveys Manager". When editing ATutor based marks, be sure to run "Update ATutor Marks" to have the changes take affect in the Gradebook.. diff --git a/mods/_standard/gradebook/edit_marks.php b/mods/_standard/gradebook/edit_marks.php new file mode 100644 index 000000000..a9c99f42c --- /dev/null +++ b/mods/_standard/gradebook/edit_marks.php @@ -0,0 +1,423 @@ +addFeedback('CANCELLED'); + header('Location: gradebook_tests.php'); + exit; +} +else if (isset($_GET['save'])) +{ + foreach($_GET as $key => $value) + { + $value = $addslashes($value); + if (preg_match('/^grade_(.*)_(.*)$/', $key, $matches) > 0) + { + $sql = "SELECT grade_scale_id FROM ".TABLE_PREFIX."gradebook_tests WHERE gradebook_test_id = ". $matches[1]; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + $sql = "REPLACE ".TABLE_PREFIX."gradebook_detail SET gradebook_test_id = ". $matches[1].", member_id=". $matches[2].", grade='".get_mark_by_grade($row["grade_scale_id"], $value)."'"; + $result = mysql_query($sql, $db) or die(mysql_error()); + } + } +} + +$orders = array('asc' => 'desc', 'desc' => 'asc'); + +if (isset($_GET['asc'])) +{ + $order = 'asc'; + $order_col = $_GET['asc']; +} +else if (isset($_GET['desc'])) { + $order = 'desc'; + $order_col = $_GET['desc']; +} else { + // no order set + $order = 'asc'; + $order_col = 'name'; +} + +if ($_GET['reset_filter']) { + unset($_GET); +} + +// Initialize all applicable tests array and all enrolled students array +$all_tests = array(); +$all_students = array(); + +// generate test array +$sql = "(SELECT g.gradebook_test_id, g.id, g.type, t.title". + " FROM ".TABLE_PREFIX."gradebook_tests g, ".TABLE_PREFIX."tests t". + " WHERE g.type='ATutor Test'". + " AND g.id = t.test_id". + " AND t.course_id=".$_SESSION["course_id"]." ORDER BY title)". + " UNION (SELECT g.gradebook_test_id, g.id, g.type, a.title". + " FROM ".TABLE_PREFIX."gradebook_tests g, ".TABLE_PREFIX."assignments a". + " WHERE g.type='ATutor Assignment'". + " AND g.id = a.assignment_id". + " AND a.course_id=".$_SESSION["course_id"]." ORDER BY title)". + " UNION (SELECT gradebook_test_id, id, type, title". + " FROM ".TABLE_PREFIX."gradebook_tests". + " WHERE course_id=".$_SESSION["course_id"]." ORDER BY title)"; +$result = mysql_query($sql, $db) or die(mysql_error()); + +while ($row = mysql_fetch_assoc($result)) +{ + $no_error = true; + + if($row["type"]=="ATutor Test") + { + $studs_take_num = get_studs_take_more_than_once($_SESSION["course_id"], $row["id"]); + + foreach ($studs_take_num as $member_id => $num) + { + if ($no_error) $no_error = false; + $error_msg .= get_display_name($member_id) . ": " . $num . " times
    "; + } + + if (!$no_error) + { + $f = array('ADD_TEST_INTO_GRADEBOOK', + $row['title'], + $error_msg); + $msg->addFeedback($f); + } + } + + if ($no_error) array_push($all_tests, $row); +} + +// generate students array +$sql_students = "SELECT m.first_name, m.last_name, e.member_id FROM ".TABLE_PREFIX."members m, ".TABLE_PREFIX."course_enrollment e WHERE m.member_id = e.member_id AND e.course_id=".$_SESSION["course_id"]." AND e.approved='y' AND e.role!='Instructor'"; +if ($order_col == "name") +{ + $sql_students .= " ORDER BY m.first_name ".$order.",m.last_name ".$order; +} +$result = mysql_query($sql_students, $db) or die(mysql_error()); + +while ($row = mysql_fetch_assoc($result)) + array_push($all_students, $row); +// end of initialization + +// Creates arrays for filtered test/student +$selected_tests = array(); +//$csv_content .= $selected_students[$i]["first_name"]." " . $selected_students[$i]["last_name"]; + +$selected_students = array(); +$grades = array(); + +// generate test array +if (($_GET["filter"] || $_GET["download"]) && $_GET["gradebook_test_id"]<>0) +{ + foreach ($all_tests as $test) + { + if ($test["gradebook_test_id"] == $_GET["gradebook_test_id"]) + { + $selected_tests[0]["gradebook_test_id"] = $test["gradebook_test_id"]; + $selected_tests[0]["title"] = $test["title"]; + $selected_tests[0]["type"] = $test["type"]; + } + } +} +else + $selected_tests = $all_tests; + +// generate students array +if (($_GET["filter"] || $_GET["download"]) && $_GET["member_id"]<>0) +{ + foreach ($all_students as $student) + { + if ($student["member_id"] == $_GET["member_id"]) + { + $selected_students[0]["member_id"] = $student["member_id"]; + $selected_students[0]["first_name"] = $student["first_name"]; + $selected_students[0]["last_name"] = $student["last_name"]; + } + } + + $sql_students = "SELECT first_name, last_name, member_id FROM ".TABLE_PREFIX."members WHERE member_id=" . $_GET["member_id"]; +} +else + $selected_students = $all_students; + +// generate grade 2-dimentional array +foreach ($selected_tests as $selected_test) + foreach($selected_students as $selected_student) + { + $sql = "SELECT grade FROM ".TABLE_PREFIX."gradebook_detail WHERE gradebook_test_id=".$selected_test["gradebook_test_id"]." AND member_id=".$selected_student["member_id"]; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + $grades[$selected_test["gradebook_test_id"]][$selected_student["member_id"]] = $row["grade"]; + } + +// sort grade +if ((isset($_GET["asc"]) || isset($_GET["desc"])) && $order_col <> "name") +{ + $sort = '$grades['.$order_col.'], SORT_'.strtoupper($order).', $selected_students, SORT_'.strtoupper($order); + + foreach($selected_tests as $test) + { + if ($test["gradebook_test_id"] <> $order_col) + $sort .= ', $grades['.$test["gradebook_test_id"].'], SORT_'.strtoupper($order); + } + $sort='array_multisort('.$sort.');'; + eval($sort); +} +// end of initialization + +$num_students = count($selected_students); +$results_per_page = 50; +$num_pages = max(ceil($num_students / $results_per_page), 1); + +$page = intval($_GET['p']); +if (!$page) { + $page = 1; +} +$count = (($page-1) * $results_per_page) + 1; +$offset = ($page-1)*$results_per_page; + +// generate table & csv head +$table_head = "\n\r"; +$table_head .= "\n\r"; + +if ($_GET[filter] <> "") + $query_str = '&filter='.$_GET[filter]; + +if ($_GET[member_id] <> "") + $query_str .= '&member_id='.$_GET[member_id]; + +if ($_GET[gradebook_test_id] <> "") + $query_str .= '&gradebook_test_id='.$_GET[gradebook_test_id]; + +$table_head .= " ". _AT('name')."\n\r"; + +$csv_content = _AT('name'); + +foreach ($selected_tests as $selected_test) +{ + $table_head .= " ". $selected_test[title]."\n\r"; + $csv_content .= ",".$selected_test[title]; +} +$table_head .= " \n\r"; +$table_head .= "\n\r"; + +$csv_content .= "\n"; + +$table_head .= "\n\r"; +$table_head .= " \n\r"; + +$has_edit_button = false; +foreach ($selected_tests as $selected_test) +{ + if ($selected_test["type"] == "External" || $selected_test["type"] == "ATutor Assignment") + { + $has_edit_button = true; + $table_head .= " ". _AT("edit")."\n\r"; + } + else + { + $table_head .= " \n\r"; + } +} +if ($has_edit_button) $table_head .= " "; +$table_head .= "\n\r"; +$table_head .= "\n\r"; + +// generate table & csv content +if ($num_students > 0) +{ + $table_content = " \n\r"; + if ($offset + $results_per_page > $num_students) $end_pos = $num_students; + else $end_pos = $offset + $results_per_page; + + $tabindex_input = 1; + $tabindex_edit = 2; + + for ($i=$offset; $i < $end_pos; $i++) + { + $table_content .= " \n\r"; + $table_content .= " ".$selected_students[$i]["first_name"]." " . $selected_students[$i]["last_name"]."\n\r"; + + $csv_content .= $selected_students[$i]["first_name"]." " . $selected_students[$i]["last_name"]; + + foreach ($selected_tests as $selected_test) + { + $sql = "SELECT grade FROM ".TABLE_PREFIX."gradebook_detail WHERE gradebook_test_id=".$selected_test["gradebook_test_id"]." AND member_id=".$selected_students[$i]["member_id"]; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + $row["grade"] = htmlspecialchars($row["grade"]); // handle html special chars + + if ($_GET["edit"]=="c_".$selected_test["gradebook_test_id"] || $_GET["edit"]=="r_".$selected_students[$i]["member_id"] && ($selected_test["type"]=="External" || $selected_test["type"]=="ATutor Assignment")) + { + $table_content .= " \n\r"; + $csv_content .= ",".$row["grade"]; + } + else + { + if ($row["grade"]=="") + { + $table_content .= " "._AT("na")."\n\r"; + $csv_content .= ",". _AT("na"); + } + else + { + $table_content .= " ".$row["grade"]."\n\r"; + $csv_content .= ",".$row["grade"]; + } + } + } + + if ($has_edit_button) + $table_content .= " ". _AT("edit") ."\n\r"; + + $table_content .= " \n\r"; + $csv_content .= "\n"; + } + + $table_content .= " \n\r"; +} + +// download csv file +if ($_GET['download']) +{ + if ($num_students == 0) + { + require (AT_INCLUDE_PATH.'header.inc.php'); + $msg->printErrors('ITEM_NOT_FOUND'); + require (AT_INCLUDE_PATH.'footer.inc.php'); + exit; + } + + header('Content-Type: application/x-excel'); + header('Content-Disposition: inline; filename="grades.csv"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + + echo $csv_content; + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$msg->printFeedbacks(); + +if (count($selected_tests)==0) +{ + echo '
    '._AT('empty_gradebook').'
    '; + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} +?> +
    +
    + +
    +
    +
    + +
    + +
    +
    + +
    + +
    + + + +
    +
    +
    + +
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    + +
    + + \ No newline at end of file diff --git a/mods/_standard/gradebook/grade_scale.php b/mods/_standard/gradebook/grade_scale.php new file mode 100644 index 000000000..c58a6c972 --- /dev/null +++ b/mods/_standard/gradebook/grade_scale.php @@ -0,0 +1,185 @@ + + + + /> + + + + + + + +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> +
    +
    + +

    + + + + + + + + + + + + + + + + + + + + + + + + + $prev_row['grade_scale_id']) + { + // print row + if ($prev_row['grade_scale_id'] <> 0) print_row($prev_row['grade_scale_id'], $prev_row['scale_name'], $whole_scale_value, $prev_row['created_date'], $_POST['grade_scale_id']); + + // initialize next $whole_scale_value + $whole_scale_value = $row['scale_value'] . ' = ' . $row['percentage_from'] . ' to ' . $row['percentage_to'] . '%'; + $prev_row = $row; + } + else + { + $whole_scale_value .= '
    '.$row['scale_value'] . ' = ' . $row['percentage_from'] . ' to ' . $row['percentage_to'] . '%'; + } + } + // print last row + if ($prev_row['grade_scale_id'] <> 0) print_row($prev_row['grade_scale_id'], $prev_row['scale_name'], $whole_scale_value, $prev_row['created_date'], $_POST['grade_scale_id']); +} +?> + + +
     
    +
    + + +
    +
    +
    + +

    + + + + + + + + + + + + + + + $prev_row['grade_scale_id']) + { + // print row + if ($prev_row['grade_scale_id'] <> 0) print_row($prev_row['grade_scale_id'], $prev_row['scale_name'], $whole_scale_value, $prev_row['created_date'], $_POST['grade_scale_id'], false); + + // initialize next $whole_scale_value + $whole_scale_value = $row['scale_value'] . ' = ' . $row['percentage_from'] . ' to ' . $row['percentage_to'] . '%'; + $prev_row = $row; + } + else + { + $whole_scale_value .= '
    '.$row['scale_value'] . ' = ' . $row['percentage_from'] . ' to ' . $row['percentage_to'] . '%'; + } + } + // print last row + if ($prev_row['grade_scale_id'] <> 0) print_row($prev_row['grade_scale_id'], $prev_row['scale_name'], $whole_scale_value, $prev_row['created_date'], $_POST['grade_scale_id'], false); +} +?> + + +
    +
    + diff --git a/mods/_standard/gradebook/grade_scale_add.php b/mods/_standard/gradebook/grade_scale_add.php new file mode 100644 index 000000000..c1f34ef67 --- /dev/null +++ b/mods/_standard/gradebook/grade_scale_add.php @@ -0,0 +1,17 @@ +addFeedback('CANCELLED'); + header('Location: grade_scale.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + /* delete has been confirmed, delete this category */ + $grade_scale_id = intval($_POST['grade_scale_id']); + + $sql = "DELETE FROM ".TABLE_PREFIX."grade_scales WHERE grade_scale_id=$grade_scale_id"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $sql = "DELETE FROM ".TABLE_PREFIX."grade_scales_detail WHERE grade_scale_id=$grade_scale_id"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $sql = "UPDATE ".TABLE_PREFIX."gradebook_tests SET grade_scale_id=0 WHERE grade_scale_id=$grade_scale_id"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: grade_scale.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$_GET['grade_scale_id'] = intval($_GET['grade_scale_id']); + +$sql = "SELECT grade_scale_id, scale_name FROM ".TABLE_PREFIX."grade_scales g WHERE g.grade_scale_id=$_GET[grade_scale_id]"; +$result = mysql_query($sql,$db) or die(mysql_error()); + +if (mysql_num_rows($result) == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); +} else { + $row = mysql_fetch_assoc($result); + + $hidden_vars['scale_name']= $row['scale_name']; + $hidden_vars['grade_scale_id'] = $row['grade_scale_id']; + + $confirm = array('DELETE_GRADE_SCALE', $row['scale_name']); + $msg->addConfirm($confirm, $hidden_vars); + + $msg->printConfirm(); +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/gradebook/grade_scale_edit.php b/mods/_standard/gradebook/grade_scale_edit.php new file mode 100644 index 000000000..edd08743b --- /dev/null +++ b/mods/_standard/gradebook/grade_scale_edit.php @@ -0,0 +1,17 @@ +Px#24YJ`L;$`3zyQH3cxh|^000SaNLh0L01G+*01G++WPeu;00007bV*G`2iXS# z5g#ACdO7d_00yr~L_t(&-tC&*ZyePbho5t1HeSbxv)F|MlVFDgTB0UvCrVT-jn};> zG=K<-0Ev&J5|y~DZ>ruP8?lHRE|V}Fj%He_@H3;un2lkOD;=-S+au4(9+MAI}hO`=H+sYx^`ky0Rq(+jT`Na6Gq1Azbm z6BCmFrV_z!Hi!)n()B6`iB#r-xB#_-_dy8ZfvnV;1yXiEGzUUTp9(=Gq!0)xeMsfy@MUzOTrd9S5 z{(OF`tpEfe{6Q^*x2?(Q_z--a$tVS6eV0L9ALET-z&=!N_rBqFc=}9D0Q#T{cj)#8f z#DEHPAt0O0a`DF>9ruw=Ag`L00=3Sct?UYbI^oo*2?p8>Hmt9NO>#C>i4hezQvgF9 zhG&CegNKU-o2CrrW@p*5^?tVYJ#cbc@Ai}H+Sdml{`SH9J{9m>Yh6g^u}6Q$f&Kf* zq|-dOb0_gP;>2H%gMfb=KhC~=`{?fOCXqNvcUKqj`0MQ0u>&W4IWV>xBSoc(Yt_Nt z`MtqV$bcf)d9aqDumIP}F|@ts_|@*H8`Q&kNFQF`-p()Xia3ER1uq>rQmNERg^;cR z5Q#+i=;Mz_XEKbAje!kguZ{7?mtSUle4LLz`GmLMd53sB&dA6JM~)ul+_`U^^kqBR zxEvI#%oWT{VTNg7mBB25RdV{}B7A)jx(|PkuIqtm$3iwsxm2X*zWY2S5Q4ecSw1^+ zhTk7NxMo+f?I6hM)2A888Ek0xxt2^-g56j$C@sTM7IIf%F$ZQD3MS0WLwIqKkghuw zTcy-|Ay0d_ohw&n>ZdkeZHT`SXKeJfx& zI#yNSr~^TbEA8InLfBaTtR+?enVo{O|Aoho0vmviTd$P6I*zf+I!2Ndr%p{UIQU4V zFncQ0aU~ISB~407Wy=Ez1iK6_3q6qU&cow}f!%6v+bFb+t-gp2sd;G4_V2i zB9Rr>+5UN!dn8{hRn)L7ECo_t23vyA>Q(k#z=r>TT>^WiF{>04!pmPVdAhNywo6l> z-2)vPL58Y9HUoAM?5mBtqlj`~<`rkcA6658*Vg$gq)b=Y$jwN3pA{Ju~ryY<`%PQiIyVkXGtpkuh1EnA86vS2l$^CoPH;vXE3$mOBYw3ng4F-F8 z>GlrXHX;ATE&Z$REq6^d8~fRP{hNkdMt8quI3W4v58Nt1>2f2l_!(-ZvfzP~ngmk% zshN-F-rB4siVgDh0+>Ien~IvsURpz8TD)B;;$F0BMeLgZvC6)A_ib)9AeMRk^mvE1 zAV|#PapWZ2QusK7`kkvgGCz zYW({HpWc!^&ViwecfK;otsaE$-K1*G{6$YQT5QNrZ@O0hnUlDIf+AKpceV5FI( $num) + { + if ($no_error) $no_error = false; + $error_msg .= get_display_name($member_id) . ": " . $num . " times
    "; + } + + if (!$no_error) + { + $f = array('ADD_TEST_INTO_GRADEBOOK', + $title, + $error_msg); + $msg->addFeedback($f); + } + + if ($no_error) // add into gradebook + { + $sql_insert = "INSERT INTO ".TABLE_PREFIX."gradebook_tests (id, type, grade_scale_id) + VALUES (". $test_id. ", 'ATutor Test', ".$_POST["selected_grade_scale_id"].")"; + $result_insert = mysql_query($sql_insert, $db) or die(mysql_error()); + } +} + +function add_assignment($assignment_id) +{ + global $db; + + $sql_insert = "INSERT INTO ".TABLE_PREFIX."gradebook_tests (id, type, grade_scale_id) + VALUES (". $assignment_id. ", 'ATutor Assignment', ".$_POST["selected_grade_scale_id"].")"; + $result_insert = mysql_query($sql_insert, $db) or die(mysql_error()); +} + +if (isset($_POST['cancel'])) +{ + $msg->addFeedback('CANCELLED'); + header('Location: gradebook_tests.php'); + exit; +} +else if (isset($_POST['addATutorTest'])) +{ + if (preg_match('/^at_(.*)$/', $_POST["id"], $matches) > 0) // add atutor test + { + if ($matches[1] == 0) // add all applicable tests + { + $sql = "SELECT * FROM ".TABLE_PREFIX."tests t". + " WHERE course_id=".$_SESSION["course_id"]. + " AND num_takes = 1". + " AND NOT EXISTS (SELECT 1". + " FROM ".TABLE_PREFIX."gradebook_tests g". + " WHERE g.id = t.test_id". + " AND g.type='ATutor Test')"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + while ($row = mysql_fetch_assoc($result)) + { + add_test($row["test_id"], $row["title"]); + } + } + else // add one atutor test + { + $sql = "SELECT * FROM ".TABLE_PREFIX."tests t". + " WHERE test_id=".$matches[1]; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + add_test($matches[1], $row["title"]); + } + } + else if (preg_match('/^aa_(.*)$/', $_POST["id"], $matches) > 0) // add atutor test + { + if ($matches[1] == 0) // add all applicable tests + { + $sql = "SELECT * FROM ".TABLE_PREFIX."assignments a". + " WHERE course_id=".$_SESSION["course_id"]. + " AND NOT EXISTS (SELECT 1". + " FROM ".TABLE_PREFIX."gradebook_tests g". + " WHERE g.id = a.assignment_id". + " AND g.type='ATutor Assignment')"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + while ($row = mysql_fetch_assoc($result)) + { + add_assignment($row["assignment_id"]); + } + } + else // add one test_id + { + add_assignment($matches[1]); + } + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: gradebook_tests.php'); + exit; +} +else if (isset($_POST['addExternalTest'])) +{ + $missing_fields = array(); + + if ($_POST['title'] == '') { + $missing_fields[] = _AT('title'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) + { + if ($_POST["has_due_date"] == 'true') + $date_due = $_POST["year_due"]. '-' .str_pad ($_POST["month_due"], 2, "0", STR_PAD_LEFT). '-' .str_pad ($_POST["day_due"], 2, "0", STR_PAD_LEFT). ' '.str_pad ($_POST["hour_due"], 2, "0", STR_PAD_LEFT). ':' .str_pad ($_POST["min_due"], 2, "0", STR_PAD_LEFT) . ':00'; + + $sql_insert = "INSERT INTO ".TABLE_PREFIX."gradebook_tests (course_id, type, title, due_date, grade_scale_id) + VALUES (".$_SESSION["course_id"].", 'External', '". $_POST["title"]. "', '".$date_due . "', ".$_POST["selected_grade_scale_id"].")"; + $result_insert = mysql_query($sql_insert, $db) or die(mysql_error()); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: gradebook_tests.php'); + exit; + } +} + +$onload .= ' disable_dates (true, \'_due\');'; +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    +
    +
    + +
    +

    +
    + + 0) ". + " ORDER BY title"; +$result_at = mysql_query($sql_at, $db) or die(mysql_error()); + +$sql_aa = "SELECT * FROM ".TABLE_PREFIX."assignments a". + " WHERE course_id=".$_SESSION["course_id"]. + " AND NOT EXISTS (SELECT 1". + " FROM ".TABLE_PREFIX."gradebook_tests g". + " WHERE g.id = a.assignment_id". + " AND g.type='ATutor Assignment')". + " ORDER BY title"; +$result_aa = mysql_query($sql_aa, $db) or die(mysql_error()); + +if (mysql_num_rows($result_at) == 0 && mysql_num_rows($result_aa) == 0) +{ + echo _AT('none_found'); +} +else +{ + echo '
    '."\n\r"; + echo '
    '."\n\r"; + echo ' '."\n\r"; + echo '
    '."\n\r"; + +?> +
    +
    + +
    + +
    + + +
    + +
    + +
    +
    + +
    +
    +
    + +
    + *
    + +
    + +
    +
    + +
    + +
    +
    + +
    + + + + + +
    + +
    + + +
    + +
    + +
    +
    + + + + diff --git a/mods/_standard/gradebook/gradebook_delete_tests.php b/mods/_standard/gradebook/gradebook_delete_tests.php new file mode 100644 index 000000000..da4d7ce27 --- /dev/null +++ b/mods/_standard/gradebook/gradebook_delete_tests.php @@ -0,0 +1,77 @@ +addFeedback('CANCELLED'); + header('Location: gradebook_tests.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + /* delete has been confirmed, delete this category */ + $gradebook_test_id = intval($_POST['gradebook_test_id']); + + $sql = "DELETE FROM ".TABLE_PREFIX."gradebook_tests WHERE gradebook_test_id=$gradebook_test_id"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $sql = "DELETE FROM ".TABLE_PREFIX."gradebook_detail WHERE gradebook_test_id=$gradebook_test_id"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: gradebook_tests.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$_GET['gradebook_test_id'] = intval($_GET['gradebook_test_id']); + +$sql = "(SELECT g.gradebook_test_id, t.title". + " FROM ".TABLE_PREFIX."gradebook_tests g, ".TABLE_PREFIX."tests t". + " WHERE g.type='ATutor Test'". + " AND g.id = t.test_id". + " AND g.gradebook_test_id=".$_GET['gradebook_test_id'].")". + " UNION (SELECT g.gradebook_test_id, a.title". + " FROM ".TABLE_PREFIX."gradebook_tests g, ".TABLE_PREFIX."assignments a". + " WHERE g.type='ATutor Assignment'". + " AND g.id = a.assignment_id". + " AND g.gradebook_test_id=".$_GET['gradebook_test_id'].")". + " UNION (SELECT gradebook_test_id, title ". + " FROM ".TABLE_PREFIX."gradebook_tests". + " WHERE type='External'". + " AND gradebook_test_id=".$_GET['gradebook_test_id'].")"; + +$result = mysql_query($sql,$db) or die(mysql_error()); + +if (mysql_num_rows($result) == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); +} else { + $row = mysql_fetch_assoc($result); + + $hidden_vars['title']= $row["title"]; + $hidden_vars['gradebook_test_id'] = $row['gradebook_test_id']; + + $confirm = array('DELETE_TEST_FROM_GRADEBOOK', $row["title"]); + $msg->addConfirm($confirm, $hidden_vars); + + $msg->printConfirm(); +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/gradebook/gradebook_edit_tests.php b/mods/_standard/gradebook/gradebook_edit_tests.php new file mode 100644 index 000000000..d63ee2f1d --- /dev/null +++ b/mods/_standard/gradebook/gradebook_edit_tests.php @@ -0,0 +1,195 @@ +addFeedback('CANCELLED'); + header('Location: gradebook_tests.php'); + exit; +} +else if (isset($_POST['save'])) +{ + $missing_fields = array(); + + if (isset($_POST['title']) && $_POST['title'] == '') { + $missing_fields[] = _AT('title'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors()) + { + $sql = "UPDATE ".TABLE_PREFIX."gradebook_tests SET "; + + if (isset($_POST["title"])) $sql .= "title = '".$_POST["title"]. "', "; + if ($_POST["has_due_date"] == 'true') + { + $due_date = $_POST["year_due"]. '-' .str_pad ($_POST["month_due"], 2, "0", STR_PAD_LEFT). '-' .str_pad ($_POST["day_due"], 2, "0", STR_PAD_LEFT). ' '.str_pad ($_POST["hour_due"], 2, "0", STR_PAD_LEFT). ':' .str_pad ($_POST["min_due"], 2, "0", STR_PAD_LEFT) . ':00'; + $sql .= "due_date = '".$due_date. "', "; + } + else + $sql .= "due_date = '', "; + + $sql .= "grade_scale_id=".$_POST["selected_grade_scale_id"]." WHERE gradebook_test_id = ". $_REQUEST["gradebook_test_id"]; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: gradebook_tests.php'); + exit; + } +} + +$sql = "SELECT * FROM ".TABLE_PREFIX."gradebook_tests WHERE gradebook_test_id=" . $_REQUEST["gradebook_test_id"]; +$result = mysql_query($sql, $db) or die(mysql_error()); + +$sql = "(SELECT g.gradebook_test_id, g.type, t.title, DATE_FORMAT(end_date, '%Y-%m-%d %H:%i:00') AS due_date, g.grade_scale_id". + " FROM ".TABLE_PREFIX."gradebook_tests g, ".TABLE_PREFIX."tests t". + " WHERE g.type='ATutor Test'". + " AND g.id = t.test_id". + " AND g.gradebook_test_id=".$_GET['gradebook_test_id'].")". + " UNION (SELECT g.gradebook_test_id, g.type, a.title, DATE_FORMAT(date_due, '%Y-%m-%d %H:%i:00') AS due_date, g.grade_scale_id". + " FROM ".TABLE_PREFIX."gradebook_tests g, ".TABLE_PREFIX."assignments a". + " WHERE g.type='ATutor Assignment'". + " AND g.id = a.assignment_id". + " AND g.gradebook_test_id=".$_GET['gradebook_test_id'].")". + " UNION (SELECT gradebook_test_id, type, title, DATE_FORMAT(due_date, '%Y-%m-%d %H:%i:00') AS due_date,grade_scale_id ". + " FROM ".TABLE_PREFIX."gradebook_tests". + " WHERE type='External'". + " AND gradebook_test_id=".$_GET['gradebook_test_id'].")"; +$result = mysql_query($sql, $db) or die(mysql_error()); +$row_this = mysql_fetch_assoc($result); + +if ($row_this["type"] == "External") +{ + $array1 = explode (' ', $row_this['due_date'], 2); + $array_date_due = explode ('-', $array1[0],3); + $array_time_due = explode (':', $array1[1]); + $today_year = $array_date_due[0]; + $today_mon = $array_date_due[1]; + $today_day = $array_date_due[2]; + $today_hour = $array_time_due[0]; + $today_min = $array_time_due[1]; + + if ($today_year == '0000') + { + $has_due_date = 'false'; + + // if due date is not set, use today's date as default + $today = getdate(); + $today_day = $today['mday']; + $today_mon = $today['mon']; + $today_year = $today['year']; + $today_hour = $today['hours']; + $today_min = $today['minutes']; + // round the minute to the next highest multiple of 5 + $today_min = round($today_min / '5' ) * '5' + '5'; + if ($today_min > '55') $today_min = '55'; + } + else + $has_due_date = 'true'; +} + +if ($has_due_date == 'false') $onload .= ' disable_dates (true, \'_due\');'; +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    +
    +
    + + +
    + *
    + " /> +
    + + +
    +
    + +
    + + +
    +
    + +
    + +
    +
    + + + + onfocus="disable_dates (true, '_due');" /> +
    + + onfocus="disable_dates (false, '_due');" /> + + + +
    + +
    + + +
    + +
    + +
    +
    + + + + diff --git a/mods/_standard/gradebook/gradebook_tests.php b/mods/_standard/gradebook/gradebook_tests.php new file mode 100644 index 000000000..4efa295c8 --- /dev/null +++ b/mods/_standard/gradebook/gradebook_tests.php @@ -0,0 +1,129 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> + +
    + + + + + + + + + + + + + + + + + + + + + +'; + } + + if ($whole_scale_value <> '') $scale_content[$row_scale_ids["grade_scale_id"]] = $whole_scale_value; + } + // End of initialize scale content array + + while ($row = mysql_fetch_assoc($result)) + { +?> + '].checked = true; rowselect(this);" id="r_"> + + + + + + + + +
     
    +
    + + +
    +
    " id="m" />
    +
    + + diff --git a/mods/_standard/gradebook/html/grade_scale_add_edit.inc.php b/mods/_standard/gradebook/html/grade_scale_add_edit.inc.php new file mode 100644 index 000000000..4aaeda645 --- /dev/null +++ b/mods/_standard/gradebook/html/grade_scale_add_edit.inc.php @@ -0,0 +1,221 @@ +addFeedback('CANCELLED'); + header('Location: grade_scale.php'); + exit; +} +else if (isset($_POST['submit'])) +{ + $_POST['scale_name'] = trim($_POST['scale_name']); + + $empty_fields = array(); + if ($_POST['scale_value'][0] == '') + { + $empty_fields[] = _AT('scale_value').' at line 1'; + } + + if ($_POST['percentage_from'][0] == '') + { + $empty_fields[] = _AT('percentage_from').' at line 1'; + } + + if ($_POST['percentage_to'][0] == '') + { + $empty_fields[] = _AT('percentage_to').' at line 1'; + } + + if (!empty($empty_fields)) + { + $msg->addError(array('EMPTY_FIELDS', implode(', ', $empty_fields))); + } + + if (!$msg->containsErrors()) + { + $_POST['scale_name'] = $addslashes($_POST['scale_name']); + + if ($action == "add") + { + $sql = "INSERT INTO ".TABLE_PREFIX."grade_scales + (member_id, scale_name, created_date) + VALUES (" . $_SESSION["member_id"] . ", '". $_POST["scale_name"] ."', now())"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $grade_scale_id = mysql_insert_id(); + } + else if ($action == "edit" && isset($_POST["grade_scale_id"])) + { + $grade_scale_id = $_POST["grade_scale_id"]; + + $sql = "UPDATE ".TABLE_PREFIX."grade_scales + SET scale_name = '".$_POST["scale_name"]."' + WHERE grade_scale_id = ". $grade_scale_id; + $result = mysql_query($sql, $db) or die(mysql_error()); + + // clean up scale details for new insertions + $sql = "DELETE FROM ".TABLE_PREFIX."grade_scales_detail WHERE grade_scale_id = ". $grade_scale_id; + $result = mysql_query($sql, $db) or die(mysql_error()); + } + + for ($i=0; $i<10; $i++) + { + if ($_POST['scale_value'][$i] <> "") + { + $_POST['scale_value'][$i] = $addslashes(trim($_POST['scale_value'][$i])); + $_POST['percentage_from'][$i] = intval($_POST['percentage_from'][$i]); + $_POST['percentage_to'][$i] = intval($_POST['percentage_to'][$i]); + + $sql = "INSERT INTO ".TABLE_PREFIX."grade_scales_detail + (grade_scale_id, scale_value, percentage_from, percentage_to) + VALUES (" . $grade_scale_id . ", '". $_POST['scale_value'][$i] ."', ".$_POST['percentage_from'][$i].", ".$_POST['percentage_to'][$i].")"; + +// print $sql; + $result = mysql_query($sql, $db) or die(mysql_error()); + } + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: grade_scale.php'); + exit; + } +} +else if (isset($_POST['preset']) || ($action == 'edit' && isset($_REQUEST['grade_scale_id']))) +{ + if (isset($_POST['selected_grade_scale_id'])) + { + // clean up values preset previously + unset($_POST["scale_value"]); + unset($_POST["percentage_from"]); + unset($_POST["percentage_to"]); + } + + if (!$msg->containsErrors() && $_POST['selected_grade_scale_id'] > 0) + { + // load preset + $_POST['selected_grade_scale_id'] = intval($_POST['selected_grade_scale_id']); + $sql = "SELECT * FROM ".TABLE_PREFIX."grade_scales_detail d, ".TABLE_PREFIX."grade_scales g WHERE d.grade_scale_id = g.grade_scale_id AND d.grade_scale_id=".$_POST[selected_grade_scale_id]." ORDER BY percentage_to DESC"; + } + else if ($action == 'edit' && isset($_REQUEST['grade_scale_id'])) + { + // edit existing + $sql = "SELECT * FROM ".TABLE_PREFIX."grade_scales_detail d, ".TABLE_PREFIX."grade_scales g WHERE d.grade_scale_id = g.grade_scale_id AND d.grade_scale_id=".$_REQUEST['grade_scale_id']." ORDER BY percentage_to DESC"; + } + + $result = mysql_query($sql, $db) or die(mysql_error()); + + $i = 0; + while ($row = mysql_fetch_assoc($result)) + { + $_POST["scale_name"] = $row["scale_name"]; + $_POST["scale_value"][$i] = $row["scale_value"]; + $_POST["percentage_from"][$i] = $row["percentage_from"]; + $_POST["percentage_to"][$i] = $row["percentage_to"]; + + $i++; + } +} + +$onload = 'document.form.selected_grade_scale_id.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); +?> + +
    " method="post" name="form"> + + + +
    +
    + +
    + +
    + +
    + +
    +
    + +
    + +
    +
    +

    +
    + + + + + + + + + + + + + + + + + + +
    + + + * + + + + + * + + + + + * + + +
    %%
    + +
    + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/gradebook/import_export_external_marks.php b/mods/_standard/gradebook/import_export_external_marks.php new file mode 100644 index 000000000..5b3846968 --- /dev/null +++ b/mods/_standard/gradebook/import_export_external_marks.php @@ -0,0 +1,277 @@ +addFeedback('CANCELLED'); + header('Location: gradebook_tests.php'); + exit; +} +else if (isset($_POST['export'])) +{ + // generate students array + $sql = "SELECT m.first_name, m.last_name, m.email, e.member_id FROM ".TABLE_PREFIX."members m, ".TABLE_PREFIX."course_enrollment e WHERE m.member_id = e.member_id AND e.course_id=".$_SESSION["course_id"]." AND e.approved='y' AND e.role<>'Instructor' ORDER BY m.first_name,m.last_name,m.email"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + if (mysql_num_rows($result)==0) + { + // nothing to send. empty file + $msg->addError('ENROLLMENT_NONE_FOUND'); + } + else + { + $this_row = "First Name, Last Name, Email, Grade\n"; + while ($row = mysql_fetch_assoc($result)) + { + // retrieve title + $sql_title = "(SELECT g.gradebook_test_id, t.title". + " FROM ".TABLE_PREFIX."gradebook_tests g, ".TABLE_PREFIX."tests t". + " WHERE g.type='ATutor Test'". + " AND g.id = t.test_id". + " AND g.gradebook_test_id=".$_POST['gradebook_test_id'].")". + " UNION (SELECT g.gradebook_test_id, a.title". + " FROM ".TABLE_PREFIX."gradebook_tests g, ".TABLE_PREFIX."assignments a". + " WHERE g.type='ATutor Assignment'". + " AND g.id = a.assignment_id". + " AND g.gradebook_test_id=".$_POST['gradebook_test_id'].")". + " UNION (SELECT gradebook_test_id, title ". + " FROM ".TABLE_PREFIX."gradebook_tests". + " WHERE type='External'". + " AND gradebook_test_id=".$_POST['gradebook_test_id'].")"; + $result_title = mysql_query($sql_title, $db) or die(mysql_error()); + $row_title = mysql_fetch_assoc($result_title); + + // retrieve grade + $sql_grade = "SELECT grade FROM ".TABLE_PREFIX."gradebook_detail WHERE gradebook_test_id=".$_POST["gradebook_test_id"]." AND member_id=".$row["member_id"]; + $result_grade = mysql_query($sql_grade, $db) or die(mysql_error()); + $row_grade = mysql_fetch_assoc($result_grade); + $grade=$row_grade["grade"]; + + $this_row .= quote_csv($row['first_name']).","; + $this_row .= quote_csv($row['last_name']).","; + $this_row .= quote_csv($row['email']).","; + $this_row .= quote_csv($grade)."\n"; + } + header('Content-Type: text/csv'); + header('Content-transfer-encoding: binary'); + header('Content-Disposition: attachment; filename="grade_'.$row_title["title"].'.csv"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + + echo $this_row; + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    +
    +
    +
    +

    +
    + +
    + +
    + +
    +
    + +
    + +
    + + +
    + +
    +
    +
    + +

    + +
    +
    +
    +
    +

    +
    + + +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + + +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/gradebook/lib/gradebook.inc.php b/mods/_standard/gradebook/lib/gradebook.inc.php new file mode 100644 index 000000000..231bdd393 --- /dev/null +++ b/mods/_standard/gradebook/lib/gradebook.inc.php @@ -0,0 +1,488 @@ + A+ - E +// [2] => Pass - Fail +// [3] => Excellent - Inadequate +// [grade_scale_id] => scale_value_max - scale_value_min (Value Explanation) +//) +define ('USE_HIGHER_GRADE', 1); +define ('USE_LOWER_GRADE', 2); +define ('NOT_OVERWRITE', 3); +define ('OVERWRITE', 4); + +function get_grade_scales_array($member_id = 0) +{ + global $db; + + $sql = "SELECT d.grade_scale_id, MIN(percentage_to) min, MAX(percentage_to) max FROM ".TABLE_PREFIX."grade_scales_detail d, ".TABLE_PREFIX."grade_scales g WHERE d.grade_scale_id = g.grade_scale_id AND g.member_id = ". $member_id ." GROUP BY d.grade_scale_id"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $preset_grade_scales = array(); + while ($row = mysql_fetch_assoc($result)) + { + $sql_min = "SELECT scale_value FROM ".TABLE_PREFIX."grade_scales_detail WHERE grade_scale_id=".$row["grade_scale_id"]." AND percentage_to=".$row["min"]; + $result_min = mysql_query($sql_min, $db) or die(mysql_error()); + $row_min = mysql_fetch_assoc($result_min); + $min_value = $row_min['scale_value']; + + $sql_max = "SELECT scale_value FROM ".TABLE_PREFIX."grade_scales_detail WHERE grade_scale_id=".$row["grade_scale_id"]." AND percentage_to=".$row["max"]; + $result_max = mysql_query($sql_max, $db) or die(mysql_error()); + $row_max = mysql_fetch_assoc($result_max); + $max_value = $row_max['scale_value']; + + $preset_grade_scales[$row["grade_scale_id"]] = $max_value . " - ". $min_value; + } + + return $preset_grade_scales; +} + +// generate html of dropdown list box on preset grade scales and grade scales created by current member Id +// parameter: $selected_grade_scale_id: the grade_scale_id that need to set to be selected. +// return: html text +function print_grade_scale_selectbox($selected_grade_scale_id = 0, $id_name="selected_grade_scale_id") +{ +?> + + + '' && $out_of <> 0) // raw final score + $mark_in_percentage = $score / $out_of * 100; + + mysql_data_seek($result_grade, 0); + while ($row_grade = mysql_fetch_assoc($result_grade)) + { + if ($mark_in_percentage <= $row_grade['percentage_to'] && $mark_in_percentage >= $row_grade['percentage_from']) + $mark = $row_grade['scale_value']; + } + } + } + + // in case grade definition does not cover all scores + if ($mark == '') $mark = $default_mark; + + return $mark; +} + +function get_member_grade($test_id, $member_id, $grade_scale_id) +{ + global $db; + + require_once(AT_INCLUDE_PATH.'../mods/_standard/tests/lib/test_result_functions.inc.php'); + + $grade = ""; + + // find out final_score, out_of + $sql = "SELECT t.random, t.out_of, r.result_id, r.final_score FROM ".TABLE_PREFIX."tests t, ".TABLE_PREFIX."tests_results r WHERE t.test_id=".$test_id." AND t.test_id=r.test_id AND r.member_id='".$member_id."'"; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + if (mysql_num_rows($result) > 0 && $row["final_score"] <> "") + { + if ($row['random']) { + $out_of = get_random_outof($test_id, $row['result_id']); + } else { + $out_of = $row['out_of']; + } + $grade = get_mark_by_grade($grade_scale_id, $row["final_score"], $out_of); + } + + return $grade; +} + +// Return array of students in the given course who take the given test more than once +// Parameter: $member_id, $test_id +// Return: an empty array or +//Array +//( +// [member_id1] => [num_takes1] +// [member_id2] => [num_takes2] +// ... +//) +function get_studs_take_more_than_once($course_id, $test_id) +{ + global $db; + + $rtn_array = array(); + + $sql = "SELECT m.member_id, count(result_id) num FROM ".TABLE_PREFIX."members m, ".TABLE_PREFIX."course_enrollment e, ".TABLE_PREFIX."tests_results t WHERE m.member_id = e.member_id AND e.course_id = ".$course_id." AND e.approved='y' AND e.role <> 'Instructor' AND e.member_id=t.member_id AND t.test_id=".$test_id." GROUP BY m.first_name, m.last_name having count(*) > 1"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + while ($row = mysql_fetch_assoc($result)) + $rtn_array[$row["member_id"]. " " . $row["last_name"]] = $row["num"]; + + return $rtn_array; +} + +// compare grades +// parameters: 2 grades to compare, grade_scale_id, "higher"/"lower": return higher or lower grade +// grade can be percentage like 70% or grade defined in grade_scale_id, like "A", "B"... +// return: higher or lower grade depending on 4th parameter +// or, -1 if grades are comparable +function compare_grades($grade1, $grade2, $gradebook_test_id, $mode = "higher") +{ + global $db; + + // get grade scale id + $sql = "SELECT grade_scale_id FROM ".TABLE_PREFIX."gradebook_tests WHERE gradebook_test_id = ".$gradebook_test_id; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + $grade_scale_id = $row["grade_scale_id"]; + + if ($grade_scale_id == 0) // compare raw scores + { + // retrieve raw score + $grade1 = trim(str_replace("%", "", $grade1)); + $grade2 = trim(str_replace("%", "", $grade2)); + + if ($grade1 > $grade2) return 1; + else if ($grade1 < $grade2) return -1; + else return 0; + } + else + { + $grade1 = get_mark_by_grade($grade_scale_id, $grade1); + $grade2 = get_mark_by_grade($grade_scale_id, $grade2); + + $grades = array(); + + $sql_grade = "SELECT scale_value from ".TABLE_PREFIX."grade_scales_detail WHERE grade_scale_id = ". $grade_scale_id. " ORDER BY percentage_to DESC"; + $result_grade = mysql_query($sql_grade, $db) or die(mysql_error()); + while ($row_grade = mysql_fetch_assoc($result_grade)) + { + $grades[] = $row_grade["scale_value"]; + } + + if (!in_array($grade1, $grades) || !in_array($grade2, $grades)) + return -1; // uncomparable + else + { + $grade1_key = array_search($grade1, $grades); + $grade2_key = array_search($grade2, $grades); + + if ($grade1_key > $grade2_key) + { + $higher_grade = $grade2; + $lower_grade = $grade1; + } + else if ($grade1_key < $grade2_key) + { + $higher_grade = $grade1; + $lower_grade = $grade2; + } + else $higher_grade = $lower_grade = $grade1; + } + } + + if ($mode == "higher") return $higher_grade; + else return $lower_grade; +} + +// check imported students and grades: +// 1. if the student exists in the class, if not, report error +// 2. if the grade already exists, if it is, report conflict +// parameter: an array of student/grade info +// Array +//( +// [member_id] => 1 +// [fname] => angelo (could be empty if [member_id] is given) +// [lname] => yuan (could be empty if [member_id] is given) +// [email] => angelo@hotmail.com (could be empty if [member_id] is given) +// [grade] => 70% +// [gradebook_test_id] => 4 +// [solve_conflict] => 0 +//) +// return: an array of processed student/grade/error info +// Array +//( +// [member_id] => 1 +// [fname] => angelo (could be empty if [member_id] is given) +// [lname] => yuan (could be empty if [member_id] is given) +// [email] => angelo@hotmail.com (could be empty if [member_id] is given) +// [grade] => 70% +// [gradebook_test_id] => 4 +// [solve_conflict] => 0 +// [error] => "Student not exists" +// [has_conflict] => 1 +//) +function check_user_info($record) +{ + global $db; + + $record['fname'] = htmlspecialchars(stripslashes(trim($record['fname']))); + $record['lname'] = htmlspecialchars(stripslashes(trim($record['lname']))); + $record['member_id'] = htmlspecialchars(stripslashes(trim($record['member_id']))); + $record['email'] = htmlspecialchars(stripslashes(trim($record['email']))); + $record['grade'] = htmlspecialchars(stripslashes(trim($record['grade']))); + + if (empty($record['remove'])) { + $record['remove'] = FALSE; + } + + if ($record['member_id'] == '') + { + $sql = "SELECT * FROM ".TABLE_PREFIX."members m, ".TABLE_PREFIX."course_enrollment e WHERE m.first_name='".$record['fname']."' AND m.last_name='".$record['lname']."' AND m.email='".$record['email']."' AND m.member_id = e.member_id AND e.course_id=".$_SESSION["course_id"]." AND e.approved='y' AND e.role<>'Instructor'"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + if (mysql_num_rows($result) == 0) + $record['error'] = _AT("student_not_exists"); + else + { + $row = mysql_fetch_assoc($result); + $record['member_id'] = $row["member_id"]; + } + } + + if ($record['error'] == "" && $record['member_id'] > 0) + { + $sql = "SELECT grade FROM ".TABLE_PREFIX."gradebook_detail WHERE gradebook_test_id=".$record['gradebook_test_id']. " AND member_id=".$record["member_id"]; + $result = mysql_query($sql, $db) or die(mysql_error()); + + if (mysql_num_rows($result) > 0 && $record['solve_conflict'] == 0) + { + $row = mysql_fetch_assoc($result); + $record['error'] = _AT("grade_already_exists", $row["grade"]); + $record['conflict'] = 1; + } + + if (mysql_num_rows($result) > 0 && $record['solve_conflict'] > 0) + { + $row = mysql_fetch_assoc($result); + + if ($record['solve_conflict'] == USE_HIGHER_GRADE || $record['solve_conflict'] == USE_LOWER_GRADE) + { + if ($record['solve_conflict'] == USE_HIGHER_GRADE) + $grade = compare_grades($record['grade'], $row['grade'], $record['gradebook_test_id'], "higher"); + + if ($record['solve_conflict'] == USE_LOWER_GRADE) + $grade = compare_grades($record['grade'], $row['grade'], $record['gradebook_test_id'], "lower"); + + if ($grade == -1) + { + $record["error"] = _AT("grades_uncomparable"); + $record['conflict'] = 1; + } + else + $record['grade'] = $grade; + } + + if ($record['solve_conflict'] == NOT_OVERWRITE) $record['grade'] = $row['grade']; + if ($record['solve_conflict'] == OVERWRITE) $record['grade'] = $record['grade']; + } + } + + if ($record['remove']) { + //unset errors + $record['error'] = ''; + } + + return $record; +} + +// update gradebook +function update_gradebook_external_test($students, $gradebook_test_id) +{ + global $db, $msg; + + foreach($students as $student) + { + if (!$student['remove']) + { + // retrieve member id + $sql = "SELECT member_id FROM ".TABLE_PREFIX."members m WHERE m.first_name='".$student['fname']."' AND m.last_name='".$student['lname']."' AND m.email='".$student['email']."'"; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + $member_id = $row["member_id"]; + + // retrieve grade scale id + $sql = "SELECT grade_scale_id FROM ".TABLE_PREFIX."gradebook_tests WHERE gradebook_test_id=".$gradebook_test_id; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + $grade_scale_id = $row["grade_scale_id"]; + + $grade = get_mark_by_grade($grade_scale_id, $student["grade"]); + $sql = "REPLACE INTO ".TABLE_PREFIX."gradebook_detail(gradebook_test_id, member_id, grade) VALUES(".$gradebook_test_id.", ".$member_id.", '".$grade."')"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $updated_list .= '
  • ' . $student['fname'] . ', '. $student['lname']. ': '. $grade. '
  • '; + } + } + + if ($updated_list) + { + $feedback = array('GRADEBOOK_UPDATED', $updated_list); + $msg->addFeedback($feedback); + } +} + +// return median value of the given array +function median($grade_array) +{ + $oe_value = count($grade_array); + + if ($oe_value % 2 == 0 ) + $position = 1; + else + $position = 2; + + if ($position == 2 ) + $median = $grade_array[(count($grade_array)/2)]; + else + $median= $grade_array[(count($grade_array)/2)-1]; + + return $median; +} + +// return class average of the given test_id +function get_class_avg($gradebook_test_id) +{ + global $db; + + $sql = "SELECT * FROM ".TABLE_PREFIX."gradebook_tests WHERE gradebook_test_id=".$gradebook_test_id; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + if ($row["id"]<>0) // internal atutor test + { + require_once (AT_INCLUDE_PATH.'../mods/_standard/tests/lib/test_result_functions.inc.php'); + + $sql_test = "SELECT * FROM ".TABLE_PREFIX."tests WHERE test_id=".$row["id"]; + $result_test = mysql_query($sql_test, $db) or die(mysql_error()); + $row_test = mysql_fetch_assoc($result_test); + + if ($row_test['out_of'] == 0 || $row_test['result_release']==AT_RELEASE_NEVER) + return _AT("na"); + + $sql_marks = "SELECT * FROM ".TABLE_PREFIX."tests_results WHERE test_id=".$row["id"]. " AND status=1"; + $result_marks = mysql_query($sql_marks, $db) or die(mysql_error()); + + $num_students = 0; + $total_final_score = 0; + $total_out_of = 0; + while ($row_marks = mysql_fetch_assoc($result_marks)) + { + if ($row_marks['final_score'] == '' ) continue; + + $num_students++; + $total_final_score += $row_marks["final_score"]; + + if ($row_test['random']) + $total_out_of += get_random_outof($row_marks['test_id'], $row_marks['result_id']); + else + $total_out_of += $row_test['out_of']; + } + + if ($num_students > 0) + { + $avg_final_score = round($total_final_score / $num_students); + $avg_out_of = round($total_out_of / $num_students); + } + + if ($avg_final_score <> "") + $avg_grade = get_mark_by_grade($row["grade_scale_id"], $avg_final_score, $avg_out_of); + else + $avg_grade = ""; + } + else // external test + { + $sql_grades = "SELECT * FROM ".TABLE_PREFIX."gradebook_detail WHERE gradebook_test_id=".$gradebook_test_id." ORDER BY grade"; + $result_grades = mysql_query($sql_grades, $db) or die(mysql_error()); + + $grade_array = array(); + while ($row_grades = mysql_fetch_assoc($result_grades)) + $grade_array[] = $row_grades["grade"]; + + $avg_grade = median($grade_array); + } + + if ($avg_grade == "") return _AT("na"); + else return $avg_grade; +} +?> diff --git a/mods/_standard/gradebook/module.php b/mods/_standard/gradebook/module.php new file mode 100644 index 000000000..37305576c --- /dev/null +++ b/mods/_standard/gradebook/module.php @@ -0,0 +1,102 @@ +getPrivilege()); +define('AT_ADMIN_PRIV_GRADEBOOK', $this->getAdminPrivilege()); + +/******* + * create a side menu box/stack. + */ +//$this->_stacks['gradebook'] = array('title_var'=>'gradebook', 'file'=>'mods/_standard/gradebook/side_menu.inc.php'); +// ** possible alternative: ** +// $this->addStack('gradebook', array('title_var' => 'gradebook', 'file' => './side_menu.inc.php'); + +/******* + * if this module is to be made available to students on the Home or Main Navigation. + */ +$_student_tool = 'mods/_standard/gradebook/my_gradebook.php'; + +/******* + * add the admin pages when needed. + */ +//if (admin_authenticate(AT_ADMIN_PRIV_GRADEBOOK, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { +// $this->_pages[AT_NAV_ADMIN] = array('mods/_standard/gradebook/index_admin.php'); +// $this->_pages['mods/_standard/gradebook/index_admin.php']['title_var'] = 'gradebook'; +// $this->_pages['mods/_standard/gradebook/index_admin.php']['parent'] = AT_NAV_ADMIN; +//} + +/******* + * instructor Manage section: + */ +$this->_pages['mods/_standard/gradebook/gradebook_tests.php']['title_var'] = 'gradebook'; +$this->_pages['mods/_standard/gradebook/gradebook_tests.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_standard/gradebook/gradebook_tests.php']['guide'] = 'instructor/?p=gradebook.php'; +$this->_pages['mods/_standard/gradebook/gradebook_tests.php']['children'] = array('mods/_standard/gradebook/gradebook_add_tests.php', 'mods/_standard/gradebook/update_gradebook.php', 'mods/_standard/gradebook/import_export_external_marks.php', 'mods/_standard/gradebook/edit_marks.php', 'mods/_standard/gradebook/grade_scale.php'); + +$this->_pages['mods/_standard/gradebook/gradebook_add_tests.php']['title_var'] = 'add_tests'; +$this->_pages['mods/_standard/gradebook/gradebook_add_tests.php']['parent'] = 'mods/_standard/gradebook/gradebook_tests.php'; +$this->_pages['mods/_standard/gradebook/gradebook_add_tests.php']['guide'] = 'instructor/?p=gradebook_add.php'; + +$this->_pages['mods/_standard/gradebook/gradebook_edit_tests.php']['title_var'] = 'edit_tests'; +$this->_pages['mods/_standard/gradebook/gradebook_edit_tests.php']['parent'] = 'mods/_standard/gradebook/gradebook_tests.php'; + +$this->_pages['mods/_standard/gradebook/gradebook_delete_tests.php']['title_var'] = 'delete_test'; +$this->_pages['mods/_standard/gradebook/gradebook_delete_tests.php']['parent'] = 'mods/_standard/gradebook/gradebook_tests.php'; + +$this->_pages['mods/_standard/gradebook/update_gradebook.php']['title_var'] = 'update_gradebook'; +$this->_pages['mods/_standard/gradebook/update_gradebook.php']['parent'] = 'mods/_standard/gradebook/gradebook_tests.php'; +$this->_pages['mods/_standard/gradebook/update_gradebook.php']['guide'] = 'instructor/?p=gradebook_update.php'; + +$this->_pages['mods/_standard/gradebook/verify_tests.php']['title_var'] = 'update_list'; +$this->_pages['mods/_standard/gradebook/verify_tests.php']['parent'] = 'mods/_standard/gradebook/update_gradebook.php'; + +$this->_pages['mods/_standard/gradebook/import_export_external_marks.php']['title_var'] = 'import_export_external_marks'; +$this->_pages['mods/_standard/gradebook/import_export_external_marks.php']['parent'] = 'mods/_standard/gradebook/gradebook_tests.php'; +$this->_pages['mods/_standard/gradebook/import_export_external_marks.php']['guide'] = 'instructor/?p=gradebook_external_marks.php'; + +$this->_pages['mods/_standard/gradebook/verify_list.php']['title_var'] = 'update_list'; +$this->_pages['mods/_standard/gradebook/verify_list.php']['parent'] = 'mods/_standard/gradebook/import_export_external_marks.php'; + +$this->_pages['mods/_standard/gradebook/edit_marks.php']['title_var'] = 'edit_marks'; +$this->_pages['mods/_standard/gradebook/edit_marks.php']['parent'] = 'mods/_standard/gradebook/gradebook_tests.php'; +$this->_pages['mods/_standard/gradebook/edit_marks.php']['guide'] = 'instructor/?p=gradebook_edit_marks.php'; + +$this->_pages['mods/_standard/gradebook/grade_scale.php']['title_var'] = 'grade_scale'; +$this->_pages['mods/_standard/gradebook/grade_scale.php']['parent'] = 'mods/_standard/gradebook/gradebook_tests.php'; +$this->_pages['mods/_standard/gradebook/grade_scale.php']['guide'] = 'instructor/?p=gradebook_scales.php'; +$this->_pages['mods/_standard/gradebook/grade_scale.php']['children'] = array('mods/_standard/gradebook/grade_scale_add.php'); + +$this->_pages['mods/_standard/gradebook/grade_scale_add.php']['title_var'] = 'add_grade_scale'; +$this->_pages['mods/_standard/gradebook/grade_scale_add.php']['parent'] = 'mods/_standard/gradebook/grade_scale.php'; +$this->_pages['mods/_standard/gradebook/grade_scale_add.php']['guide'] = 'instructor/?p=gradebook_scales.php'; + +$this->_pages['mods/_standard/gradebook/grade_scale_edit.php']['title_var'] = 'edit_grade_scale'; +$this->_pages['mods/_standard/gradebook/grade_scale_edit.php']['parent'] = 'mods/_standard/gradebook/grade_scale.php'; + +$this->_pages['mods/_standard/gradebook/grade_scale_delete.php']['title_var'] = 'delete_grade_scale'; +$this->_pages['mods/_standard/gradebook/grade_scale_delete.php']['parent'] = 'mods/_standard/gradebook/grade_scale.php'; + +$this->_pages['mods/_standard/gradebook/my_gradebook.php']['text'] = _AT('gradebook_text'); + +/******* + * student page. + */ +$this->_pages['mods/_standard/gradebook/my_gradebook.php']['title_var'] = 'gradebook'; +$this->_pages['mods/_standard/gradebook/my_gradebook.php']['img'] = 'mods/_standard/gradebook/gradebook.png'; + + +function gradebook_get_group_url($group_id) { + return 'mods/_standard/gradebook/index.php'; +} +?> \ No newline at end of file diff --git a/mods/_standard/gradebook/module.xml b/mods/_standard/gradebook/module.xml new file mode 100644 index 000000000..8c2daab37 --- /dev/null +++ b/mods/_standard/gradebook/module.xml @@ -0,0 +1,19 @@ + + + Gradebook + The Gradebook allows instructors to automatically gather scores from tests and quizzes taken through the ATutor Test Manager, and allows them to define external tests or assignments and manually record marks for those. It will allow students to view their marks for completed tests or assignments, list tests or assignments yet to be completed or yet to be marked, and print out a report of their marks. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + BSD + + 0.1 + 2008-05-27 + stable + + + \ No newline at end of file diff --git a/mods/_standard/gradebook/my_gradebook.php b/mods/_standard/gradebook/my_gradebook.php new file mode 100644 index 000000000..efc28f5ec --- /dev/null +++ b/mods/_standard/gradebook/my_gradebook.php @@ -0,0 +1,130 @@ + + +
    + + + + + + + + + + + + + + + + + + + + "") + { + $sql_tr = "SELECT R.result_id, R.date_taken, (UNIX_TIMESTAMP(R.end_time) - UNIX_TIMESTAMP(R.date_taken)) AS diff FROM ".TABLE_PREFIX."tests_results R WHERE R.status=1 AND R.test_id=".$row["id"]." AND R.member_id='".$_SESSION[member_id]."'"; + $result_tr = mysql_query($sql_tr, $db) or die(mysql_error()); + $row_tr = mysql_fetch_assoc($result_tr); + } +?> + +'.$row["title"].''."\n\r"; + else + echo ' '."\n\r"; +?> + + + + + + + + + + + + + + + + + + +
    '.$row["title"].'
    +
    + + diff --git a/mods/_standard/gradebook/update_gradebook.php b/mods/_standard/gradebook/update_gradebook.php new file mode 100644 index 000000000..8775c2870 --- /dev/null +++ b/mods/_standard/gradebook/update_gradebook.php @@ -0,0 +1,292 @@ + $num) + { + if ($no_error) $no_error = false; + $error_msg .= get_display_name($member_id) . ": " . $num . " times
    "; + } + + if (!$no_error) + { + $f = array('UPDATE_GRADEBOOK', + $row['title'], + $error_msg); + $msg->addFeedback($f); + } + + if ($no_error) + return true; + else + return false; +} + +function update_gradebook($gradebook_test_id, $member_id) +{ + global $db; + + $sql = "SELECT id, grade_scale_id FROM ".TABLE_PREFIX."gradebook_tests WHERE gradebook_test_id = ". $gradebook_test_id; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + $test_id = $row["id"]; + $grade_scale_id = $row["grade_scale_id"]; + + // get grade + $grade = get_member_grade($test_id, $member_id, $grade_scale_id); + + if ($grade <> "") + { + $sql = "REPLACE INTO ".TABLE_PREFIX."gradebook_detail(gradebook_test_id, member_id, grade) VALUES (".$gradebook_test_id.", ".$member_id.", '".$grade."')"; + $result = mysql_query($sql, $db) or die(mysql_error()); + } +} + +// Initialize all applicable tests array and all enrolled students array +$tests = array(); +$students = array(); + +// generate gradebook test array +$sql = "SELECT *, t.title FROM ".TABLE_PREFIX."gradebook_tests g, ".TABLE_PREFIX."tests t WHERE g.id = t.test_id AND g.type='ATutor Test' AND t.course_id=".$_SESSION["course_id"]; +$result = mysql_query($sql, $db) or die(mysql_error()); +while ($row = mysql_fetch_assoc($result)) +{ + $test["gradebook_test_id"] = $row["gradebook_test_id"]; + $test["title"] = $row["title"]; + + array_push($tests, $test); +} + +// generate students array +$sql = "SELECT m.first_name, m.last_name, e.member_id FROM ".TABLE_PREFIX."members m, ".TABLE_PREFIX."course_enrollment e WHERE m.member_id = e.member_id AND e.course_id=".$_SESSION["course_id"]." AND e.approved='y' AND e.role<>'Instructor' ORDER BY m.first_name,m.last_name"; +$result = mysql_query($sql, $db) or die(mysql_error()); + +while ($row = mysql_fetch_assoc($result)) +{ + $student["first_name"] = $row["first_name"]; + $student["last_name"] = $row["last_name"]; + $student["member_id"] = $row["member_id"]; + + array_push($students, $student); +} +// end of initialization + +if (isset($_POST['cancel'])) +{ + $msg->addFeedback('CANCELLED'); + header('Location: gradebook_tests.php'); + exit; +} +else if (isset($_POST['update'])) +{ + if (!$msg->containsErrors()) + { + if ($_POST["gradebook_test_id"] == 0) + { + foreach($tests as $test) + { + if (is_test_updatable($test["gradebook_test_id"])) + { + if ($_POST["member_id"]==0) + { + // delete old data for this test + $sql = "DELETE from ".TABLE_PREFIX."gradebook_detail WHERE gradebook_test_id = ".$test["gradebook_test_id"]; + $result = mysql_query($sql, $db) or die(mysql_error()); + + foreach($students as $student) + update_gradebook($test["gradebook_test_id"], $student["member_id"]); + } + else + update_gradebook($test["gradebook_test_id"], $_POST["member_id"]); + } + } + } + else + { + if (is_test_updatable($_POST["gradebook_test_id"])) + { + if ($_POST["member_id"]==0) + { + // delete old data for this test + $sql = "DELETE from ".TABLE_PREFIX."gradebook_detail WHERE gradebook_test_id = ".$_POST["gradebook_test_id"]; + $result = mysql_query($sql, $db) or die(mysql_error()); + + foreach($students as $student) + update_gradebook($_POST["gradebook_test_id"], $student["member_id"]); + } + else + update_gradebook($_POST["gradebook_test_id"], $_POST["member_id"]); + } + } + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    +
    +
    + + +
    + +
    +'."\n\r"; + echo '
    '."\n\r"; + echo ' '."\n\r"; + echo '
    '."\n\r"; + + // list of students + echo '
    '."\n\r"; + echo '
    '."\n\r"; + echo ' '."\n\r"; + echo '
    '."\n\r"; +?> + +
    + + +
    + + + + +
    + +
    +
    +
    +
    +

    +
    + + +
    + +
    +'."\n\r"; + echo '
    '."\n\r"; + echo ' '."\n\r"; + echo '
    '."\n\r"; + + // list of atutor tests that can be combined. + // These tests can only be taken once and are not in gradebook yet + // note: surveys are excluded by checking if question weights are defined + $sql_at = "SELECT * FROM ".TABLE_PREFIX."tests t". + " WHERE course_id=".$_SESSION["course_id"]. + " AND num_takes = 1". + " AND NOT EXISTS (SELECT 1". + " FROM ".TABLE_PREFIX."gradebook_tests g". + " WHERE g.id = t.test_id". + " AND g.type='ATutor Test')". + " AND test_id IN (SELECT test_id FROM ".TABLE_PREFIX."tests_questions_assoc ". + " GROUP BY test_id ". + " HAVING sum(weight) > 0) ". + " ORDER BY title"; + $result_at = mysql_query($sql_at, $db) or die(mysql_error()); + + if (mysql_num_rows($result_at) == 0) + echo _AT('none_found'); + else + { + echo '
    '."\n\r"; + echo '
    '."\n\r"; + echo ' '."\n\r"; + echo '
    '."\n\r"; + } +?> + +
    + + +
    + + + + +
    + + diff --git a/mods/_standard/gradebook/verify_list.php b/mods/_standard/gradebook/verify_list.php new file mode 100644 index 000000000..0856d94fe --- /dev/null +++ b/mods/_standard/gradebook/verify_list.php @@ -0,0 +1,201 @@ +addFeedback('CANCELLED'); + header('Location: import_export_external_marks.php'); + exit; +} +else if (isset($_POST['import'])) +{ + //IMPORT + if ($_FILES['file']['size'] < 1) + { + $msg->addError('FILE_EMPTY'); + header('Location: import_course_list.php'); + exit; + } + else + { + $fp = fopen($_FILES['file']['tmp_name'],'r'); + $line_number=0; + while ($data = fgetcsv($fp, 100000, ',')) { + $line_number++; + if ($line_number > 1) + { + $num_fields = count($data); + if ($num_fields == 4) + { + $students[] = check_user_info(array('fname' => $data[0], 'lname' => $data[1], 'email' => $data[2], 'grade' => $data[3], 'gradebook_test_id' => $_POST['gradebook_test_id'])); + } + else if ($num_fields != 1) + { + $errors = array('INCORRECT_FILE_FORMAT', $line_number); + $msg->addError($errors); + header('Location: import_course_list.php'); + exit; + } + else if (($num_fields == 1) && (trim($data[0]) != '')) + { + $errors = array('INCORRECT_FILE_FORMAT', $line_number); + $msg->addError($errors); + header('Location: import_course_list.php'); + exit; + } + } + } + } +} +/************* INFO GATHERED **************/ + +if ($_POST['verify']) { + for ($i=0; $i < $_POST['count']; $i++) + { + $info = array('fname' => $_POST['fname'.$i], 'lname' => $_POST['lname'.$i], 'email' => $_POST['email'.$i], 'grade' => $_POST['grade'.$i], 'remove' => $_POST['remove'.$i], 'gradebook_test_id' => $_POST["gradebook_test_id"], 'solve_conflict' => $_POST["solve_conflict"]); + $students[] = check_user_info($info); + + if (!empty($students[$i]['error'])) + $still_errors = TRUE; + } + + /**************************************************************************/ + // !!!!!!STEP 3 - INSERT INTO DB !!!!!!! + if (!$still_errors && isset($_POST['update'])) + { + update_gradebook_external_test($students, $_POST["gradebook_test_id"]); + header('Location: import_export_external_marks.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +// STEP 2 - INTERNAL VERIFICATION +if ($still_errors || !isset($_POST['verify']) || isset($_POST['resubmit'])) { +?> + +
    +
    +
    +

    +
    +
    + + + " /> + " /> + + + + + + + + + + + + + +'."\n\r"; + foreach ($students as $student) { + + if (!empty($student['conflict'])) + $has_conflict = TRUE; + + echo ' '."\n\r"; + echo ' '."\n\r"; + + echo ' '."\n\r"; + echo ' '."\n\r"; + echo ' '."\n\r"; + echo ' '."\n\r"; + echo ' '."\n\r"; + + $i++; + echo ' '."\n\r"; + } + echo ' '."\n\r"; + } + + $dsbld = ''; + if ($still_errors || $err_count>0) { + $dsbld = 'disabled="disabled"'; + } +?> + + + + + + + +
    '."\n\r"; + + //give status + if(!empty($student['error'])) { + echo ''.$student['error']; + } + + if (empty($student['error'])) + { + if ($student['remove']) + echo ''._AT('removed'); + else + echo ''._AT('ok'); + } + else + $err_count++; + + echo '
    + + /> + + | + + + +
    +
    \ No newline at end of file diff --git a/mods/_standard/gradebook/verify_tests.php b/mods/_standard/gradebook/verify_tests.php new file mode 100644 index 000000000..6d72ae0b7 --- /dev/null +++ b/mods/_standard/gradebook/verify_tests.php @@ -0,0 +1,225 @@ +addFeedback('CANCELLED'); + header('Location: update_gradebook.php'); + exit; +} +else if (isset($_POST['combine'])) +{ + //Check if the "combine from test" has students taking it more than once + $no_error = true; + + $sql = "SELECT title FROM ".TABLE_PREFIX."tests WHERE test_id=".$_POST["test_id"]; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + $studs_take_num = get_studs_take_more_than_once($_SESSION["course_id"], $_POST["test_id"]); + + foreach ($studs_take_num as $member_id => $num) + { + if ($no_error) $no_error = false; + $error_msg .= get_display_name($member_id) . ": " . $num . " times
    "; + } + + if (!$no_error) + { + $error = array('COMBINE_TESTS', + $row["title"], + $error_msg); + $msg->addError($error); + } + + if (!$msg->containsErrors()) + { + $sql = "SELECT id, grade_scale_id FROM ".TABLE_PREFIX."gradebook_tests WHERE gradebook_test_id = ". $_POST["gradebook_test_id"]; + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result) or die(mysql_error()); + $grade_scale_id = $row["grade_scale_id"]; + + $sql = "SELECT m.first_name, m.last_name, m.email, e.member_id FROM ".TABLE_PREFIX."members m, ".TABLE_PREFIX."course_enrollment e WHERE m.member_id = e.member_id AND e.course_id=".$_SESSION["course_id"]." AND e.approved='y' AND e.role<>'Instructor' ORDER BY m.first_name,m.last_name"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + while ($row = mysql_fetch_assoc($result)) + { + $grade = get_member_grade($_POST["test_id"], $row["member_id"], $grade_scale_id); + + if ($grade <> "") + $students[] = check_user_info(array('member_id' => $row["member_id"], 'fname' => $row["first_name"], 'lname' => $row["last_name"], 'email' => $row["email"], 'grade' => $grade, 'gradebook_test_id' => $_POST['gradebook_test_id'])); + } + + if (count($students) == 0) + { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: update_gradebook.php'); + exit; + } + + } + else + { + header('Location: update_gradebook.php'); + exit; + } +} + +/************* INFO GATHERED **************/ + +if ($_POST['verify']) { + for ($i=0; $i < $_POST['count']; $i++) + { + $info = array('fname' => $_POST['fname'.$i], 'lname' => $_POST['lname'.$i], 'email' => $_POST['email'.$i], 'grade' => $_POST['grade'.$i], 'remove' => $_POST['remove'.$i], 'gradebook_test_id' => $_POST["gradebook_test_id"], 'solve_conflict' => $_POST["solve_conflict"]); + $students[] = check_user_info($info); + + if (!empty($students[$i]['error'])) + $still_errors = TRUE; + } + + /**************************************************************************/ + // !!!!!!STEP 3 - INSERT INTO DB !!!!!!! + + if (!$still_errors && isset($_POST['update'])) + { + update_gradebook_external_test($students, $_POST["gradebook_test_id"]); + header('Location: update_gradebook.php'); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); +// STEP 2 - INTERNAL VERIFICATION +if ($still_errors || !isset($_POST['verify']) || isset($_POST['resubmit'])) { +?> + +
    +
    +
    +

    +
    +
    + + + " /> + " /> + " /> + + + + + + + + + + + + + +'."\n\r"; + foreach ($students as $student) { + + if (!empty($student['conflict'])) + $has_conflict = TRUE; + + echo ' '."\n\r"; + echo ' '."\n\r"; + + echo ' '."\n\r"; + echo ' '."\n\r"; + echo ' '."\n\r"; + echo ' '."\n\r"; + echo ' '."\n\r"; + + echo ' '."\n\r"; + echo ' '."\n\r"; + echo ' '."\n\r"; + + $i++; + echo ' '."\n\r"; + } + echo ' '."\n\r"; + } + + $dsbld = ''; + if ($still_errors || $err_count>0) { + $dsbld = 'disabled="disabled"'; + } +?> + + + + + + + +
    '."\n\r"; + + //give status + if(!empty($student['error'])) { + echo ''.$student['error']; + } + + if (empty($student['error'])) + { + if ($student['remove']) + echo ''._AT('removed'); + else + echo ''._AT('ok'); + } + else + $err_count++; + + echo ''.$student['fname'].''.$student['lname'].''.$student['email'].'
    + + /> + + | + + + +
    +
    \ No newline at end of file diff --git a/mods/_standard/links/add.php b/mods/_standard/links/add.php new file mode 100644 index 000000000..7fc30300b --- /dev/null +++ b/mods/_standard/links/add.php @@ -0,0 +1,140 @@ +addInfo('NOT_ENROLLED'); + require(AT_INCLUDE_PATH.'header.inc.php'); + require(AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +require (AT_INCLUDE_PATH.'../mods/_standard/links/lib/links.inc.php'); + +if (!manage_links()) { + $_pages['mods/_standard/links/index.php']['children'] = array('mods/_standard/links/add.php'); +} + +if (!isset($_POST['url'])) { + $_POST['url'] = "http://"; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; +} else if (isset($_POST['add_link']) && isset($_POST['submit'])) { + $missing_fields = array(); + if ($_POST['cat'] == 0 || $_POST['cat'] == '') { + $missing_fields[] = _AT('category'); + } + if (trim($_POST['title']) == '') { + $missing_fields[] = _AT('title'); + } + if (trim($_POST['url']) == '' || $_POST['url'] == 'http://') { + $missing_fields[] = _AT('url'); + } + if (trim($_POST['description']) == '') { + $missing_fields[] = _AT('description'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + + if (!$msg->containsErrors() && isset($_POST['submit'])) { + + $_POST['cat'] = intval($_POST['cat']); + $_POST['title'] = $addslashes($_POST['title']); + $_POST['url'] == $addslashes($_POST['url']); + $_POST['description'] = $addslashes($_POST['description']); + + $name = get_display_name($_SESSION['member_id']); + $email = ''; + + $approved = 0; //not approved for student submissions + + $sql = "INSERT INTO ".TABLE_PREFIX."links VALUES (NULL, $_POST[cat], '$_POST[url]', '$_POST[title]', '$_POST[description]', $approved, '$name', '$email', NOW(), 0)"; + mysql_query($sql, $db); + + $msg->addFeedback('LINK_ADDED'); + + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; + } else { + $_POST['title'] = stripslashes($_POST['title']); + $_POST['url'] == stripslashes($_POST['url']); + $_POST['description'] = stripslashes($_POST['description']); + } +} +$onload = 'document.form.title.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$categories = get_link_categories(); + +if (empty($categories)) { + $msg->addInfo('NO_LINK_CATEGORIES'); + $msg->printInfos(); + +} else { +?> +
    + + +
    +
    + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/links/index.php b/mods/_standard/links/index.php new file mode 100644 index 000000000..f8bcb50e7 --- /dev/null +++ b/mods/_standard/links/index.php @@ -0,0 +1,200 @@ + 'desc', 'desc' => 'asc'); +$cols = array('LinkName' => 1, 'name' => 1, 'description' => 1); + +if (isset($_GET['asc'])) { + $order = 'asc'; + $col = isset($cols[$_GET['asc']]) ? $_GET['asc'] : 'LinkName'; +} else if (isset($_GET['desc'])) { + $order = 'desc'; + $col = isset($cols[$_GET['desc']]) ? $_GET['desc'] : 'LinkName'; +} else { + // no order set + $order = 'asc'; + $col = 'LinkName'; +} + +//search +if ($_GET['search']) { + $_GET['search'] = trim($_GET['search']); + $page_string .= SEP.'search='.urlencode($_GET['search']); + $search = $addslashes($_GET['search']); + $search = str_replace(array('%','_'), array('\%', '\_'), $search); + $search = '%'.$search.'%'; + $search = "((LinkName LIKE '$search') OR (description LIKE '$search'))"; +} else { + $search = '1'; +} + +//view links of a child category +if ($_GET['cat_parent_id']) { + $children = get_child_categories ($_GET['cat_parent_id'], $categories); + $cat_sql = "C.cat_id IN ($children $_GET[cat_parent_id])"; + $parent_id = intval($_GET['cat_parent_id']); +} else { + $cat_sql = '1'; + $parent_id = 0; +} + +//get links +$tmp_groups = implode(',', $_SESSION['groups']); + +if (!empty($tmp_groups)) { + $sql = "SELECT * FROM ".TABLE_PREFIX."links L INNER JOIN ".TABLE_PREFIX."links_categories C USING (cat_id) WHERE ((owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.") OR (owner_id IN ($tmp_groups) AND owner_type=".LINK_CAT_GROUP.")) AND L.Approved=1 AND $search AND $cat_sql ORDER BY $col $order"; +} else { + $sql = "SELECT * FROM ".TABLE_PREFIX."links L INNER JOIN ".TABLE_PREFIX."links_categories C USING (cat_id) WHERE (owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.") AND L.Approved=1 AND $search AND $cat_sql ORDER BY $col $order"; +} +$result = mysql_query($sql, $db); +$num_results = mysql_num_rows($result); + +?> + 0 || isset($_GET['filter'])): ?> +
    + +
    +
    +

    +
    + +
    + +
    + + + +
    + +
    +
    + +
    + + +
    + + +
    +
    +
    + + + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + diff --git a/mods/_standard/links/lib/links.inc.php b/mods/_standard/links/lib/links.inc.php new file mode 100644 index 000000000..d64a7dfbe --- /dev/null +++ b/mods/_standard/links/lib/links.inc.php @@ -0,0 +1,230 @@ + 0) { + if (authenticate(AT_PRIV_GROUPS+AT_PRIV_LINKS, true)) { + return true; + } + + if ($owner_type == LINK_CAT_GROUP) { + //check if member of group + if ($_SESSION['valid_user'] && isset($_SESSION['groups'])) { + $sql="SELECT * FROM ".TABLE_PREFIX."groups_members WHERE group_id=".$owner_id." AND member_id=".$_SESSION['member_id']; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + return true; + } + } + } + + return false; +} + +/* return true if user is able to manage group or course links */ +function manage_links() { + global $db; + + if (authenticate(AT_PRIV_GROUPS, true) && authenticate(AT_PRIV_LINKS, true)) { //course and group links + return LINK_CAT_AUTH_ALL; + } else if (authenticate(AT_PRIV_GROUPS, true)) { //all group links + return LINK_CAT_AUTH_GROUP; + } else if (authenticate(AT_PRIV_LINKS, true)) { //course links + return LINK_CAT_AUTH_COURSE; + } else if (!empty($_SESSION['groups'])) { //particular group links + //find a group that uses links + foreach ($_SESSION['groups'] as $group_id) { + $sql = "SELECT modules FROM ".TABLE_PREFIX."groups WHERE group_id=$group_id"; + $result = mysql_query($sql, $db); + + $row = mysql_fetch_assoc($result); + $mods = explode('|', $row['modules']); + + if (in_array("_standard/links", $mods)) { + return LINK_CAT_AUTH_GROUP; + } + } + + return FALSE; + } + + return LINK_CAT_AUTH_NONE; +} + + +//if manage, then it's getting categories for only those that should see them +//if list, then filter out the uneditable group cats from the manage list (otherwise it's a dropdown of cats) +function get_link_categories($manage=false, $list=false) { + global $db, $_base_path; + $categories = array(); + + /* get all the categories: */ + /* $categories[category_id] = array(cat_name, cat_parent, num_courses, [array(children)]) */ + + if ($_SESSION['groups']) { + $groups = implode(',', $_SESSION['groups']); + } else { + // not in any groups + $groups = 0; + } + + //if suggest a link page + if ($_SERVER['PHP_SELF'] == $_base_path.'mods/links/add.php') { + $sql = "SELECT * FROM ".TABLE_PREFIX."links_categories WHERE (owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.") ORDER BY parent_id, name"; + } else if ($manage) { + $sql = "SELECT * FROM ".TABLE_PREFIX."links_categories WHERE "; + if ( authenticate(AT_PRIV_GROUPS, true) && authenticate(AT_PRIV_COURSE, true) ) { + if ($list) { + $sql .= "(owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.") OR (owner_id IN ($groups) AND owner_type=".LINK_CAT_GROUP." AND name<>'')"; + } else { + $sql .= "(owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.") OR (owner_id IN ($groups) AND owner_type=".LINK_CAT_GROUP.")"; + } + + } else if ( authenticate(AT_PRIV_LINKS, true) ) { + $sql .= "(owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.")"; + if (!empty($groups)) { + $sql .= " OR (owner_id IN ($groups) AND owner_type=".LINK_CAT_GROUP.")"; + } + } else if ( authenticate(AT_PRIV_GROUPS, true) || !empty($groups) ) { + if ($list) { + $sql .= "(owner_id IN ($groups) AND owner_type=".LINK_CAT_GROUP." AND name<>'')"; + } else { + $sql .= "(owner_id IN ($groups) AND owner_type=".LINK_CAT_GROUP.")"; + } + } + $sql .= " ORDER BY parent_id, name"; + } else { + if (!empty($groups)) { + $sql = "SELECT * FROM ".TABLE_PREFIX."links_categories WHERE (owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.") OR (owner_id IN ($groups) AND owner_type=".LINK_CAT_GROUP.") ORDER BY parent_id, name"; + } else { + $sql = "SELECT * FROM ".TABLE_PREFIX."links_categories WHERE (owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.") ORDER BY parent_id, name"; + } + } + $result = mysql_query($sql, $db); + + while ($row = mysql_fetch_assoc($result)) { + //if group, get name + if (empty($row['name'])) { + $row['name'] = get_group_name($row['owner_id']); + $categories[$row['cat_id']]['group'] = 1; + } + + $categories[$row['cat_id']]['cat_name'] = $row['name']; + $categories[$row['cat_id']]['cat_parent'] = $row['parent_id']; + + if ($row['parent_id'] > 0) { + $categories[$row['parent_id']]['children'][] = $row['cat_id']; + } else { + $categories[0][] = $row['cat_id']; + } + } + + return $categories; +} + +function select_link_categories($categories, $cat_id, $current_cat_id, $exclude, $depth=0, $owner=FALSE) { + global $db; + + if ($cat_id == 0 && is_array($categories[0])) { + foreach($categories[0] as $child_cat_id) { + select_link_categories($categories, $child_cat_id, $current_cat_id, $depth, 0, $owner); + } + } else { + $sql = "SELECT name, owner_type, owner_id FROM ".TABLE_PREFIX."links_categories WHERE cat_id=$cat_id"; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + + if ($exclude && ($cat_id == $current_cat_id)) { + return; + } + + if ($owner) { + echo ''; + + if (is_array($categories[$cat_id]['children'])) { + foreach($categories[$cat_id]['children'] as $child_cat_id) { + select_link_categories($categories, $child_cat_id, $current_cat_id, $exclude, $depth+1, $owner); + } + } + } +} + +/** + Given a $cat_id, return IDs of all children of that ID as a comma seperated + string. + */ +function get_child_categories ($cat_id, $categories) { + if (!isset ($categories)) { + $categories = get_link_categories(); + } + + $category = $categories[$cat_id]; + $children_string = ""; + if (is_array($categories[$cat_id]['children'])){ + foreach ($categories[$cat_id]['children'] as $child) { + $children_string = $child.","; + } + } + return $children_string; +} + +function get_group_name($owner_id) { + global $db; + + if (!$owner_id) { + return false; + } + + $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=".$owner_id; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + return $row['title']; +} + +function get_cat_info($cat_id) { + global $db; + + $sql = "SELECT * FROM ".TABLE_PREFIX."links_categories WHERE cat_id=".$cat_id; + $result = mysql_query($sql, $db); + $row = mysql_fetch_assoc($result); + + return $row; +} + +?> \ No newline at end of file diff --git a/mods/_standard/links/module.php b/mods/_standard/links/module.php new file mode 100644 index 000000000..0ad5cd440 --- /dev/null +++ b/mods/_standard/links/module.php @@ -0,0 +1,91 @@ +getPrivilege()); + +// if this module is to be made available to students on the Home or Main Navigation +$_group_tool = $_student_tool = 'mods/_standard/links/index.php'; + +//modules sub-content +$this->_list['links'] = array('title_var'=>'links','file'=>'mods/_standard/links/sublinks.php'); + +//tool manager +//$this->_tool['links'] = array('title_var'=>'links','file'=>'links_tool.php'); + +/*$this->_pages['mods/_standard/links/tools/index.php']['title_var'] = 'links'; +$this->_pages['mods/_standard/links/tools/index.php']['parent'] = 'tools/index.php'; +$this->_pages['mods/_standard/links/tools/index.php']['children'] = array('mods/_standard/links/tools/add.php', 'mods/_standard/links/tools/categories.php', 'mods/_standard/links/tools/categories_create.php'); +$this->_pages['mods/_standard/links/tools/index.php']['guide'] = 'instructor/?p=links.php'; + + $this->_pages['mods/_standard/links/tools/add.php']['title_var'] = 'add_link'; + $this->_pages['mods/_standard/links/tools/add.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/edit.php']['title_var'] = 'edit_link'; + $this->_pages['mods/_standard/links/tools/edit.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/delete.php']['title_var'] = 'delete_link'; + $this->_pages['mods/_standard/links/tools/delete.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/categories.php']['title_var'] = 'categories'; + $this->_pages['mods/_standard/links/tools/categories.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/categories_create.php']['title_var'] = 'create_category'; + $this->_pages['mods/_standard/links/tools/categories_create.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/categories_edit.php']['title_var'] = 'edit_category'; + $this->_pages['mods/_standard/links/tools/categories_edit.php']['parent'] = 'mods/_standard/links/tools/categories.php'; + + $this->_pages['mods/_standard/links/tools/categories_delete.php']['title_var'] = 'delete_category'; + $this->_pages['mods/_standard/links/tools/categories_delete.php']['parent'] = 'mods/_standard/links/tools/categories.php'; +*/ + +//instructor & group pages +$this->_pages['mods/_standard/links/tools/index.php']['title_var'] = 'manage_links'; +$this->_pages['mods/_standard/links/tools/index.php']['parent'] = 'mods/_standard/links/index.php'; +$this->_pages['mods/_standard/links/tools/index.php']['children'] = array('mods/_standard/links/tools/add.php', 'mods/_standard/links/tools/categories.php', 'mods/_standard/links/tools/categories_create.php'); +$this->_pages['mods/_standard/links/tools/index.php']['guide'] = 'instructor/?p=links.php'; + + $this->_pages['mods/_standard/links/tools/add.php']['title_var'] = 'add_link'; + $this->_pages['mods/_standard/links/tools/add.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/edit.php']['title_var'] = 'edit_link'; + $this->_pages['mods/_standard/links/tools/edit.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/delete.php']['title_var'] = 'delete_link'; + $this->_pages['mods/_standard/links/tools/delete.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/categories.php']['title_var'] = 'categories'; + $this->_pages['mods/_standard/links/tools/categories.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/categories_create.php']['title_var'] = 'create_category'; + $this->_pages['mods/_standard/links/tools/categories_create.php']['parent'] = 'mods/_standard/links/tools/index.php'; + + $this->_pages['mods/_standard/links/tools/categories_edit.php']['title_var'] = 'edit_category'; + $this->_pages['mods/_standard/links/tools/categories_edit.php']['parent'] = 'mods/_standard/links/tools/categories.php'; + + $this->_pages['mods/_standard/links/tools/categories_delete.php']['title_var'] = 'delete_category'; + $this->_pages['mods/_standard/links/tools/categories_delete.php']['parent'] = 'mods/_standard/links/tools/categories.php'; + +//student pages +$this->_pages['mods/_standard/links/index.php']['title_var'] = 'links'; +$this->_pages['mods/_standard/links/index.php']['children'] = array('mods/_standard/links/add.php', 'mods/_standard/links/tools/index.php'); +$this->_pages['mods/_standard/links/index.php']['img'] = 'images/home-links.png'; +$this->_pages['mods/_standard/links/index.php']['icon'] = 'images/home-links_sm.png'; + + $this->_pages['mods/_standard/links/add.php']['title_var'] = 'suggest_link'; + $this->_pages['mods/_standard/links/add.php']['parent'] = 'mods/_standard/links/index.php'; + + +function links_get_group_url($group_id) { + global $db; + $sql = "SELECT cat_id FROM ".TABLE_PREFIX."links_categories WHERE owner_id=$group_id and owner_type=".LINK_CAT_GROUP; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + return 'mods/_standard/links/index.php?cat_parent_id='.$row['cat_id'].'&search=&filter=Filter'; + } + + return 'mods/_standard/links/index.php'; +} + +?> \ No newline at end of file diff --git a/mods/_standard/links/module.xml b/mods/_standard/links/module.xml new file mode 100644 index 000000000..06c951e20 --- /dev/null +++ b/mods/_standard/links/module.xml @@ -0,0 +1,23 @@ + + + Links + It may be a useful resource to provide a list of external links to websites related to course material. Link categories can be defined and students may suggest links. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + GPL + + 0.1 + + create + + + 2005-08-22 + stable + This is a standard module. + + \ No newline at end of file diff --git a/mods/_standard/links/module_backup.php b/mods/_standard/links/module_backup.php new file mode 100644 index 000000000..410368900 --- /dev/null +++ b/mods/_standard/links/module_backup.php @@ -0,0 +1,39 @@ + \ No newline at end of file diff --git a/mods/_standard/links/module_delete.php b/mods/_standard/links/module_delete.php new file mode 100644 index 000000000..7a77e7fcb --- /dev/null +++ b/mods/_standard/links/module_delete.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/mods/_standard/links/module_groups.php b/mods/_standard/links/module_groups.php new file mode 100644 index 000000000..402da94f3 --- /dev/null +++ b/mods/_standard/links/module_groups.php @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/mods/_standard/links/module_news.php b/mods/_standard/links/module_news.php new file mode 100644 index 000000000..0d71663b9 --- /dev/null +++ b/mods/_standard/links/module_news.php @@ -0,0 +1,44 @@ + + */ +function links_news() { + global $db, $enrolled_courses, $system_courses; + $news = array(); + + if ($enrolled_courses == ''){ + return $news; + } + + $sql = "SELECT * FROM ".TABLE_PREFIX."links L INNER JOIN ".TABLE_PREFIX."links_categories C ON C.cat_id = L.cat_id WHERE owner_id IN $enrolled_courses AND L.Approved=1 ORDER BY SubmitDate DESC"; + $result = mysql_query($sql, $db); + if($result){ + while($row = mysql_fetch_assoc($result)){ + $news[] = array( + 'time'=>$row['SubmitDate'], + 'object'=>$row, + 'alt'=>_AT('links'), + 'course'=>$system_courses[$row['owner_id']]['title'], + 'thumb'=>'images/home-links_sm.png', + 'link'=>' SUBLINK_TEXT_LEN ? ' title="'.$row['LinkName'].'"' : '') .'>'. + validate_length($row['LinkName'], SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''); + } + } + return $news; +} + +?> \ No newline at end of file diff --git a/mods/_standard/links/sublinks.php b/mods/_standard/links/sublinks.php new file mode 100644 index 000000000..0a79768fb --- /dev/null +++ b/mods/_standard/links/sublinks.php @@ -0,0 +1,22 @@ + 0) { + + while ($row = mysql_fetch_assoc($result)) { + $list[] = ' SUBLINK_TEXT_LEN ? ' title="'.$row['LinkName'].'"' : '') .'>'. + validate_length($row['LinkName'], SUBLINK_TEXT_LEN, VALIDATE_LENGTH_FOR_DISPLAY) .''; + } + return $list; +} else { + return 0; +} +?> diff --git a/mods/_standard/links/tools/add.php b/mods/_standard/links/tools/add.php new file mode 100644 index 000000000..3184e6064 --- /dev/null +++ b/mods/_standard/links/tools/add.php @@ -0,0 +1,173 @@ +addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; +} + +if (!isset($_POST['approved'])) { + $_POST['approved'] = 1; +} + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/index.php'); + exit; +} + +if (isset($_POST['add_link']) && isset($_POST['submit'])) { + + //check category_id and see if user is allowed.. + $cat = explode('-', $_POST['cat']); + $cat_id = intval($cat[0]); + $owner_type = intval($cat[1]); + $owner_id = intval($cat[2]); + + if (!links_authenticate($owner_type, $owner_id)) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; + } + + $missing_fields = array(); + if ($_POST['cat'] == 0 || $_POST['cat'] == '') { + $missing_fields[] = _AT('category'); + } + if (trim($_POST['title']) == '') { + $missing_fields[] = _AT('title'); + } + if (trim($_POST['url']) == '' || $_POST['url'] == 'http://') { + $missing_fields[] = _AT('url'); + } + if (trim($_POST['description']) == '') { + $missing_fields[] = _AT('description'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors() && isset($_POST['submit'])) { + $_POST['title'] = $addslashes($_POST['title']); + $_POST['url'] == $addslashes($_POST['url']); + $_POST['description'] = $addslashes($_POST['description']); + + //Check length of the post, if it's exceeded 64 as defined in the db. + $_POST['title'] = validate_length($_POST['title'], 64); + $_POST['description'] = validate_length($_POST['description'], 250); + + $name = get_display_name($_SESSION['member_id']); + $email = ''; + + $approved = intval($_POST['approved']); + + $sql = "INSERT INTO ".TABLE_PREFIX."links VALUES (NULL, $cat_id, '$_POST[url]', '$_POST[title]', '$_POST[description]', $approved, '$name', '$email', NOW(), 0)"; + mysql_query($sql, $db); + + $msg->addFeedback('LINK_ADDED'); + + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/index.php'); + exit; + } else { + $_POST['title'] = stripslashes($_POST['title']); + $_POST['url'] == stripslashes($_POST['url']); + $_POST['description'] = stripslashes($_POST['description']); + } +} + +if (!isset($_POST['url'])) { + $_POST['url'] = 'http://'; +} + +$categories = get_link_categories(true); + +if (empty($categories)) { + $msg->addError('LINK_CAT_EMPTY'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/index.php'); + exit; +} + +$onload = 'document.form.title.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$msg->printErrors(); + +?> +
    + + +
    +
    + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    +
    + + > > +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/links/tools/categories.php b/mods/_standard/links/tools/categories.php new file mode 100644 index 000000000..e437da2ad --- /dev/null +++ b/mods/_standard/links/tools/categories.php @@ -0,0 +1,110 @@ +addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; +} + +if ((isset($_POST['delete']) || isset($_POST['edit'])) && !isset($_POST['cat_id'])) { + $msg->addError('NO_ITEM_SELECTED'); +} else if (isset($_POST['delete'])) { + //check if links are in the cat + $sql = "SELECT link_id FROM ".TABLE_PREFIX."links WHERE cat_id=$_POST[cat_id]"; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + $msg->addError('LINK_CAT_NOT_EMPTY'); + } else { + header('Location: categories_delete.php?cat_id='.$_POST['cat_id']); + exit; + } +} else if (isset($_POST['edit'])) { + header('Location: categories_edit.php?cat_id='.$_POST['cat_id']); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +?> +
    + + + + + + + + + + + + + + + + + +$row) { + + if (!empty($row['cat_name'])) { + + $parent_cat_name = ''; + if ($row['cat_parent']) { + $sql_cat = "SELECT name, owner_id, owner_type FROM ".TABLE_PREFIX."links_categories WHERE cat_id=".$row['cat_parent']; + $result_cat = mysql_query($sql_cat, $db); + $row_cat = mysql_fetch_assoc($result_cat); + $parent_cat_name = AT_print($row_cat['name'], 'links_categories.name'); + + if (empty($parent_cat_name)) { + $parent_cat_name = get_group_name($row_cat['owner_id']); + } + } else { + $parent_cat_name = ''._AT('none').''; + } + ?> + + + + + + + + + + + + + + + +
     
    +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/links/tools/categories_create.php b/mods/_standard/links/tools/categories_create.php new file mode 100644 index 000000000..8b4ea3521 --- /dev/null +++ b/mods/_standard/links/tools/categories_create.php @@ -0,0 +1,124 @@ +addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; +} + +if (isset($_POST['submit'])) { + $cat_parent_id = intval($_POST['cat_parent_id']); + $cat_name = trim($_POST['cat_name']); + $cat_name = $addslashes($cat_name); + + if ($cat_name == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('title'))); + } + + if (!$msg->containsErrors()) { + + if (!empty($cat_parent_id)) { + $cat_parent_id = explode('-', $_POST['cat_parent_id']); + $parent_id = intval($cat_parent_id[0]); + $owner_type = intval($cat_parent_id[1]); + $owner_id = intval($cat_parent_id[2]); + + if (!links_authenticate($owner_type, $owner_id)) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'index.php'); + exit; + } + } else { + $owner_type = LINK_CAT_COURSE; + $owner_id = $_SESSION['course_id']; + $parent_id = 0; + } + + //Check length of the post, if it's exceeded 100 as defined in the db. + if ($strlen($cat_name) > 100){ + $cat_name = $substr($cat_name, 0, 100); + } + + $sql = "INSERT INTO ".TABLE_PREFIX."links_categories VALUES (NULL, $owner_type, $owner_id, '$cat_name', $parent_id)"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: categories.php'); + exit; + } +} else if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: categories.php'); + exit; +} + + +/* $categories[category_id] = array(cat_name, cat_parent, num_courses, [array(children)]) */ +$categories = get_link_categories(true); + +$onload = 'document.form.category_name.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); +$msg->printAll(); + +?> + +
    + +
    +
    + *
    + +
    + +
    +
    + + + +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/links/tools/categories_delete.php b/mods/_standard/links/tools/categories_delete.php new file mode 100644 index 000000000..cbacf78b0 --- /dev/null +++ b/mods/_standard/links/tools/categories_delete.php @@ -0,0 +1,78 @@ +addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; +} + +$cat_id = intval($_REQUEST['cat_id']); + +if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: categories.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + /* delete has been confirmed, delete this category */ + $owner_type = intval($_POST['owner_type']); + $owner_id = intval($_POST['owner_id']); + //OR get_cat_info() again incase data has ben tampered? + + if (!links_authenticate($owner_type, $owner_id)) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/categories.php'); + exit; + } + + //check if there are sub cats within this cat, or links + $sql = "SELECT C.cat_id, L.link_id FROM ".TABLE_PREFIX."links_categories C, ".TABLE_PREFIX."links L WHERE C.parent_id=$cat_id OR L.cat_id=$cat_id"; + $result = mysql_query($sql, $db); + if (mysql_num_rows($result) == 0) { + $sql = "DELETE FROM ".TABLE_PREFIX."links_categories WHERE owner_id=$owner_id AND owner_type=$owner_type AND cat_id=$cat_id"; + $result = mysql_query($sql, $db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } else { + $msg->addError('LINK_CAT_NOT_EMPTY'); + } + + header('Location: categories.php'); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $row = get_cat_info($cat_id); + + if (empty($row)) { + $msg->printErrors('ITEM_NOT_FOUND'); + } else { + $hidden_vars['cat_name']= $row['name']; + $hidden_vars['cat_id'] = $row['cat_id']; + $hidden_vars['owner_type'] = $row['owner_type']; + $hidden_vars['owner_id'] = $row['owner_id']; + + $confirm = array('DELETE_CATEGORY', AT_print($row['name'], 'links_categories.name')); + $msg->addConfirm($confirm, $hidden_vars); + + $msg->printConfirm(); + } + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/links/tools/categories_edit.php b/mods/_standard/links/tools/categories_edit.php new file mode 100644 index 000000000..7d4a4d078 --- /dev/null +++ b/mods/_standard/links/tools/categories_edit.php @@ -0,0 +1,125 @@ +addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; +} + +$cat_id = intval($_REQUEST['cat_id']); + +if (isset($_POST['submit'])) { + + //check if cat name is empty + if ($_POST['cat_name'] == '') { + $msg->addError(array('EMPTY_FIELDS', _AT('title'))); + } + + if (!$msg->containsErrors()) { + //authorized cat parent? + $lid = explode('-', $_POST['cat_parent_id']); + $parent_id = intval($lid[0]); + $owner_type = intval($lid[1]); + $owner_id = intval($lid[2]); + + if (!links_authenticate($owner_type, $owner_id)) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/categories.php'); + exit; + } + + $cat_name = $addslashes($_POST['cat_name']); + + //Check length of the post, if it's exceeded 100 as defined in the db. + $cat_name = validate_length($cat_name, 100); + + $sql = "UPDATE ".TABLE_PREFIX."links_categories SET parent_id=$parent_id, name='$cat_name', owner_type=$owner_type, owner_id=$owner_id WHERE cat_id=".$cat_id; + + $result = mysql_query($sql, $db); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: categories.php'); + exit; + } +} else if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: categories.php'); + exit; +} else { + $row = get_cat_info($cat_id); + + //authorized to edit this cat? + if (!links_authenticate($row['owner_type'], $row['owner_id'])) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/categories.php'); + exit; + } +} + +/* get all the categories: */ +/* $categories[category_id] = array(cat_name, cat_parent, num_courses, [array(children)]) */ +$categories = get_link_categories(true); + +$onload = 'document.form.category_name.focus();'; + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$current_cat_id = $categories[$cat_id]['cat_parent']; + +?> +
    + + + +
    +
    + *
    + +
    + +
    +
    + +
    +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/links/tools/delete.php b/mods/_standard/links/tools/delete.php new file mode 100644 index 000000000..0d6f5e635 --- /dev/null +++ b/mods/_standard/links/tools/delete.php @@ -0,0 +1,74 @@ +addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; +} + +$lid = explode('-', $_REQUEST['lid']); +$link_id = intval($lid[0]); + +if (isset($_POST['submit_no'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/index.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + + $row = get_cat_info(intval($_POST['cat_id'])); + + if (!links_authenticate($row['owner_type'], $row['owner_id'])) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/index.php'); + exit; + } + + $sql = "DELETE FROM ".TABLE_PREFIX."links WHERE link_id=$_POST[link_id]"; + $result = mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/index.php'); + exit; +} + +$_section[0][0] = _AT('delete_link'); + +require(AT_INCLUDE_PATH.'header.inc.php'); + + $sql = "SELECT LinkName, cat_id FROM ".TABLE_PREFIX."links WHERE link_id=$link_id"; + + $result = mysql_query($sql,$db); + if (mysql_num_rows($result) == 0) { + $msg->printErrors('LINK_NOT_FOUND'); + } else { + $row = mysql_fetch_assoc($result); + + $hidden_vars['delete_link'] = TRUE; + $hidden_vars['link_id'] = $link_id; + $hidden_vars['cat_id'] = $row['cat_id']; + + $confirm = array('DELETE_LINK', AT_print(htmlentities_utf8($row['LinkName']), 'resource_links.LinkName')); + $msg->addConfirm($confirm, $hidden_vars); + + $msg->printConfirm(); + } + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/links/tools/edit.php b/mods/_standard/links/tools/edit.php new file mode 100644 index 000000000..34072c015 --- /dev/null +++ b/mods/_standard/links/tools/edit.php @@ -0,0 +1,158 @@ +addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; +} + +$lid = explode('-', $_REQUEST['lid']); +$link_id = intval($lid[0]); + +if (isset($_POST['cancel'])) { + $msg->addFeedback('CANCELLED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/index.php'); + exit; +} else if (isset($_POST['edit_link']) && isset($_POST['submit'])) { + + $missing_fields = array(); + if ($_POST['cat'] == 0 || $_POST['cat'] == '') { + $missing_fields[] = _AT('category'); + } + if (trim($_POST['title']) == '') { + $missing_fields[] = _AT('title'); + } + if (trim($_POST['url']) == '' || $_POST['url'] == 'http://') { + $missing_fields[] = _AT('url'); + } + if (trim($_POST['description']) == '') { + $missing_fields[] = _AT('description'); + } + + if ($missing_fields) { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!$msg->containsErrors() && isset($_POST['submit'])) { + + $_POST['cat'] = intval($_POST['cat']); + $_POST['title'] = $addslashes($_POST['title']); + $_POST['url'] == $addslashes($_POST['url']); + $_POST['description'] = $addslashes($_POST['description']); + //Check length of the post, if it's exceeded 64 as defined in the db. + $_POST['title'] = validate_length($_POST['title'], 64); + $_POST['description'] = validate_length($_POST['description'], 250); + +// $name = get_display_name($_SESSION['member_id']); + $email = ''; + + //check if new cat is auth? -- shouldn't be a prob. since cat dropdown is already filtered + + $sql = "UPDATE ".TABLE_PREFIX."links SET cat_id=$_POST[cat], Url='$_POST[url]', LinkName='$_POST[title]', Description='$_POST[description]', Approved=$_POST[approved] WHERE link_id=".$link_id; + mysql_query($sql, $db); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/index.php'); + exit; + } else { + $_POST['title'] = $stripslashes($_POST['title']); + $_POST['url'] = $stripslashes($_POST['url']); + $_POST['description'] = $stripslashes($_POST['description']); + } +} else { + $sql = "SELECT * FROM ".TABLE_PREFIX."links WHERE link_id=".$link_id; + $result = mysql_query($sql, $db); + if ($row = mysql_fetch_assoc($result)) { + + //auth based on the link's cat + $cat_row = get_cat_info($row['cat_id']); + + if (!links_authenticate($cat_row['owner_type'], $cat_row['owner_id'])) { + $msg->addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/tools/index.php'); + exit; + } + + $_POST['title'] = $row['LinkName']; + $_POST['cat'] = $row['cat_id']; + $_POST['url'] = $row['Url']; + $_POST['description'] = $row['Description']; + $_POST['approved'] = $row['Approved']; + } +} + +$onload = 'document.form.title.focus();'; +require(AT_INCLUDE_PATH.'header.inc.php'); + +$categories = get_link_categories(true); + +$msg->printErrors(); + +?> +
    + + + +
    +
    + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    + *
    + +
    + +
    +
    + + /> /> +
    + +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/links/tools/index.php b/mods/_standard/links/tools/index.php new file mode 100644 index 000000000..b37796785 --- /dev/null +++ b/mods/_standard/links/tools/index.php @@ -0,0 +1,174 @@ +addError('ACCESS_DENIED'); + header('Location: '.AT_BASE_HREF.'mods/_standard/links/index.php'); + exit; +} + +if (isset($_POST['edit']) && isset($_POST['link_id'])) { + header('Location: edit.php?lid='.$_POST['link_id']); + exit; +} else if (isset($_POST['delete']) && isset($_POST['link_id'])) { + header('Location: delete.php?lid='.$_POST['link_id']); + exit; +} else if (isset($_POST['view']) && isset($_POST['link_id'])) { + $onload = 'window.open(\''.AT_BASE_HREF.'mods/_standard/links/index.php?view='.$_POST['link_id'].'\',\'link\');'; +} else if (!empty($_POST)) { + $msg->addError('NO_ITEM_SELECTED'); +} + +$categories = get_link_categories(true); + +require(AT_INCLUDE_PATH.'header.inc.php'); + +if ($_GET['col']) { + $col = addslashes($_GET['col']); +} else { + $col = 'LinkName'; +} + +if ($_GET['order']) { + $order = addslashes($_GET['order']); +} else { + $order = 'asc'; +} + +if (!isset($_GET['cat_parent_id'])) { + $parent_id = 0; +} else { + $parent_id = intval($_GET['cat_parent_id']); +} + +if ($_SESSION['groups']) { + $groups = implode(',', $_SESSION['groups']); +} else { + // not in any groups + $groups = 0; +} + +$auth = manage_links(); + +if ($auth == LINK_CAT_AUTH_ALL) { + $sql = "SELECT * FROM ".TABLE_PREFIX."links L INNER JOIN ".TABLE_PREFIX."links_categories C USING (cat_id) WHERE ((owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.") OR (owner_id IN ($groups) AND owner_type=".LINK_CAT_GROUP."))"; +} else if ($auth == LINK_CAT_AUTH_GROUP) { + $sql = "SELECT * FROM ".TABLE_PREFIX."links L INNER JOIN ".TABLE_PREFIX."links_categories C USING (cat_id) WHERE owner_id IN ($groups) AND owner_type=".LINK_CAT_GROUP; +} else if ($auth == LINK_CAT_AUTH_COURSE) { + $sql = "SELECT * FROM ".TABLE_PREFIX."links L INNER JOIN ".TABLE_PREFIX."links_categories C USING (cat_id) WHERE ((owner_id=$_SESSION[course_id] AND owner_type=".LINK_CAT_COURSE.") OR (owner_id IN ($groups) AND owner_type=".LINK_CAT_GROUP."))"; +} + +if ($parent_id) { + $sql .= " AND L.cat_id=$parent_id"; +} +$sql .= " ORDER BY $col $order"; + +$result = mysql_query($sql, $db); + +if (!empty($categories)) { +?> + +
    +
    +
    +

    +
    + +
    + +
    + +
    + +
    +
    +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    +
    + + \ No newline at end of file diff --git a/mods/_standard/patcher/classes/Patch.class.php b/mods/_standard/patcher/classes/Patch.class.php new file mode 100644 index 000000000..ebd9d09c8 --- /dev/null +++ b/mods/_standard/patcher/classes/Patch.class.php @@ -0,0 +1,832 @@ +relative_to_atutor_root . $patch_array[files][$i]['location']; + } + + $this->patch_array = $patch_array; + $this->patch_summary_array = $patch_summary_array; + + $this->baseURL = $patch_folder; + $this ->backup_suffix = $patch_array['atutor_patch_id'] . ".old"; + $this ->patch_suffix = $patch_array['atutor_patch_id']; + $this->skipFilesModified = $skipFilesModified; + + $this->module_content_dir = AT_CONTENT_DIR . "patcher"; + + session_start(); + + if (!is_array($_SESSION['remove_permission'])) $_SESSION['remove_permission']=array(); + + } + + /** + * Main process to apply patch. + * @access public + * @return true if patch is successfully applied + * false if failed + * @author Cindy Qi Li + */ + function applyPatch() + { + global $msg; + + // Checks on + // 1. if svn server is up. If not, consider all files manipulated by patch as modified + // 2. if the local file is customized by user + // 3. if script has write priviledge on local file/folder + // 4. if dependent patches have been installed + if (!$this->pingDomain($this->svn_tag_folder)) + { + $msg->addInfo('CANNOT_CONNECT_SVN_SERVER'); + $msg->printInfos(); + $this->svn_server_connected = false; + } + else + $this->svn_server_connected = true; + + if (!$this->checkDependentPatches()) return false; + + if (!$this->checkAppliedVersion()) return false; + + if (!$this->skipFilesModified && $this->hasFilesModified()) return false; + + if (!$this->checkPriviledge()) return false; + // End of check + + if (strlen(trim($this->patch_array['sql'])) > 0) $this->runSQL(); + + // Start applying patch + $this->createPatchesRecord($this->patch_summary_array); + + // if no file action defined, update database and return true + if (!is_array($this->patch_array[files])) + { + $updateInfo = array("status"=>"Installed"); + updatePatchesRecord($this->patch_id, $updateInfo); + + return true; + } + + foreach ($this->patch_array[files] as $row_num => $patch_file) + { + $this->createPatchesFilesRecord($this->patch_array['files'][$row_num]); + + if ($patch_file['action'] == 'alter') + { + $this->alterFile($row_num); + } + else if ($patch_file['action'] == 'add') + { + $this->addFile($row_num); + } + else if ($patch_file['action'] == 'delete') + { + $this->deleteFile($row_num); + } + else if ($patch_file['action'] == 'overwrite') + { + $this->overwriteFile($row_num); + } + } + + // if only has backup files info, patch is considered successfully installed + // if has permission to remove, considered partly installed + $updateInfo = array(); + + if (count($this->backup_files) > 0) + { + foreach($this->backup_files as $backup_file) + $backup_files .= $backup_file. '|'; + + $updateInfo = array("backup_files"=>mysql_real_escape_string($backup_files)); + } + + if (count($this->patch_files) > 0) + { + foreach($this->patch_files as $patch_file) + $patch_files .= $patch_file. '|'; + + $updateInfo = array_merge($updateInfo, array("patch_files"=>mysql_real_escape_string($patch_files))); + } + + if (is_array($_SESSION['remove_permission']) && count($_SESSION['remove_permission'])) + { + foreach($_SESSION['remove_permission'] as $remove_permission_file) + $remove_permission_files .= $remove_permission_file. '|'; + + $updateInfo = array_merge($updateInfo, array("remove_permission_files"=>mysql_real_escape_string($remove_permission_files), "status"=>"Partly Installed")); + } + else + { + $updateInfo = array_merge($updateInfo, array("status"=>"Installed")); + } + + updatePatchesRecord($this->patch_id, $updateInfo); + + unset($_SESSION['remove_permission']); + + return true; + } + + /** + * return patch array + * @access public + * @return patch array + * @author Cindy Qi Li + */ + function getPatchArray() + { + return $this->patch_array; + } + + /** + * return patch id processed by this object + * @access public + * @return patch id + * @author Cindy Qi Li + */ + function getPatchID() + { + return $this->patch_id; + } + + /** + * Check if script has write permission to the files and folders that need to be written + * if no permission, warn user to give permission + * @access private + * @return true if there are files or folders that script has no permission + * false if permissions are in place + * @author Cindy Qi Li + */ + function checkPriviledge() + { + global $id, $who; + + // no file action is defined, return true; + if (!is_array($this->patch_array[files])) return true; + + foreach ($this->patch_array[files] as $row_num => $patch_file) + { + $real_location = realpath($patch_file['location']); + if (!is_writable($patch_file['location']) && !in_array($real_location, $this->need_access_to_folders)) + { + $this->need_access_to_folders[] = $real_location; + + if (!in_array($real_location, $_SESSION['remove_permission'])) + $_SESSION['remove_permission'][] = $real_location; + } + + if ($patch_file['action'] == 'alter' || $patch_file['action'] == 'delete' || $patch_file['action'] == 'overwrite') + { + $file = $patch_file['location'] . "/" . $patch_file['name']; + + $real_file = realpath($file); + if (file_exists($file) && !is_writable($file) && !in_array($real_file, $this->need_access_to_files)) + { + $this->need_access_to_files[] = $real_file; + + if (!in_array($real_file, $_SESSION['remove_permission']) && $patch_file['action'] <> 'delete') + $_SESSION['remove_permission'][] = $real_file; + } + } + } + + if (count($this->need_access_to_folders) > 0 || count($this->need_access_to_files) > 0) + { + $this->errors[] = _AT('grant_write_permission'); + + foreach($this->need_access_to_folders as $folder) + { + $this->errors[0] .= ''. $folder . "
    "; + } + + foreach($this->need_access_to_files as $file) + { + $this->errors[0] .= ''. $file . "
    "; + } + + $notes = '
    +
    + + + + + +
    +
    '; + + print_errors($this->errors, $notes); + + unset($this->errors); + return false; + } + + return true; + } + + /** + * Check if ATutor version is same as "applied version" defined in the patch. + * @access private + * @return true if versions match + * false if versions don't match + * @author Cindy Qi Li + */ + function checkAppliedVersion() + { + global $msg; + + if ($this->patch_summary_array["applied_version"] <> VERSION) + { + $this->errors[] = _AT("version_not_match", $this->patch_summary_array["applied_version"]); + + $notes = ' +
    +
    + + + + +
    +
    '; + + print_errors($this->errors, $notes); + + unset($this->errors); + + return false; + } + + return true; + } + + /** + * Check if all the dependent patches have been installed. + * @access private + * @return true if all the dependent patches have been installed + * false if any dependent patch has not been installed. + * @author Cindy Qi Li + */ + function checkDependentPatches() + { + global $msg; + + $dependent_patches_installed = true; + + // if no dependent patch defined, return true + if (!is_array($this->patch_summary_array["dependent_patches"])) return true; + + foreach($this->patch_summary_array["dependent_patches"] as $num => $dependent_patch) + { + if (!is_patch_installed($dependent_patch)) + { + $dependent_patches_installed = false; + $dependent_patches .= $dependent_patch. ", "; + } + } + + if (!$dependent_patches_installed) + { + $errors = array('PATCH_DEPENDENCY', substr($dependent_patches, 0, -2)); + $msg->addError($errors); + return false; + } + + return true; + } + + /** + * Loop thru all the patch files that will be overwitten or altered, + * to find out if they are modified by user. If it's modified, warn user. + * @access private + * @return true if there are files being modified + * false if no file is modified + * @author Cindy Qi Li + */ + function hasFilesModified() + { + $overwrite_modified_files = false; + $alter_modified_files = false; + $has_not_exist_files = false; + + // no file action is defined, return nothing is modified (false) + if (!is_array($this->patch_array[files])) return false; + + foreach ($this->patch_array[files] as $row_num => $patch_file) + { + if ($patch_file["action"]=='alter' || $patch_file["action"]=='overwrite') + { + if (!file_exists($patch_file['location'] . $patch_file['name'])) + { + $not_exist_files .= $patch_file['location'] . $patch_file['name'] . '
    '; + $has_not_exist_files = true; + } + else if ($this->isFileModified($patch_file['location'], $patch_file['name'])) + { + if ($patch_file['action']=='overwrite') + { + $overwrite_files .= realpath($patch_file['location'] . $patch_file['name']) . '
    '; + $overwrite_modified_files = true; + } + if ($patch_file['action']=='alter') + { + $alter_files .= realpath($patch_file['location'] . $patch_file['name']) . '
    '; + $alter_modified_files = true; + } + } + } + } + + if ($has_not_exist_files) $this->errors[] = _AT('patch_local_file_not_exist'). $not_exist_files; + if ($overwrite_modified_files) $this->errors[] = _AT('patcher_overwrite_modified_files') . $overwrite_files; + if ($alter_modified_files) $this->errors[] = _AT('patcher_alter_modified_files') . $alter_files; + if (count($this->errors) > 0) + { + if ($has_not_exist_files) + $notes = ''; + else + $notes = ' +
    +
    + + + + + +
    +
    '; + + print_errors($this->errors, $notes); + + unset($this->errors); + return true; + } + + return false; + } + + /** + * Compare user's local file with SVN backup for user's ATutor version, + * if different, check table at_patches_files to see if user's local file + * was altered by previous patch installation. If it is, return false + * (not modified), otherwise, return true (modified). + * @access private + * @param $folder folder of the file to be compared + * $file name of the file to be compared + * @return true if the file is modified + * false if the file is not modified + * @author Cindy Qi Li + */ + function isFileModified($folder, $file) + { + global $db; + + if (!$this->svn_server_connected) return true; + + $svn_file = $this->svn_tag_folder . 'atutor_' . str_replace('.', '_', VERSION) . + str_replace(substr($this->relative_to_atutor_root, 0, -1), '' , $folder) .$file; + $local_file = $folder.$file; + + // if svn script does not exist, consider the script is modified + if (!file_get_contents($svn_file)) return true; + + // check if the local file has been modified by user. if it is, don't overwrite + if ($this->compareFiles($svn_file, $local_file) <> 0) + { + // check if the file was changed by previous installed patches + $sql = "SELECT count(*) num_of_updates FROM " . TABLE_PREFIX. "patches patches, " . TABLE_PREFIX."patches_files patches_files " . + "WHERE patches.applied_version = '" . VERSION . "' ". + " AND patches.status = 'Installed' " . + " AND patches.patches_id = patches_files.patches_id " . + " AND patches_files.name = '" . $file . "'"; + + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + if ($row["num_of_updates"] == 0) return true; + } + return false; + } + + /** + * Run SQL defined in patch.xml + * @access private + * @author Cindy Qi Li + */ + function runSQL() + { + // run sql + // As sqlutility.class.php reads sql from a file, write sql to module content folder + $patch_sql_file = $this->module_content_dir . '/' . $this->sql_file; + + $fp = fopen($patch_sql_file, 'w'); + fwrite($fp, trim($this->patch_array['sql'])); + fclose($fp); + + require(AT_INCLUDE_PATH . 'classes/sqlutility.class.php'); + $sqlUtility = new SqlUtility(); + + $sqlUtility->queryFromFile($patch_sql_file, TABLE_PREFIX); + + @unlink($patch_sql_file); + + return true; + } + + /** + * Copy file from update.atutor.ca to user's computer + * @access private + * @param $row_num row number of patch record to be processed + * @author Cindy Qi Li + */ + function addFile($row_num) + { + $this->copyFile($this->baseURL . preg_replace('/.php$/', '.new', $this->patch_array['files'][$row_num]['name']), $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name']); + + return true; + } + + /** + * Delete file, backup before deletion + * @access private + * @param $row_num row number of patch record to be processed + * @author Cindy Qi Li + */ + function deleteFile($row_num) + { + $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name']; + $backup_file = $local_file . "." . $this->backup_suffix; + + if (file_exists($local_file)) + { + // move file to backup + $this->copyFile($local_file, $backup_file); + $this->backup_files[] = realpath($backup_file); + @unlink($local_file); + } + + return true; + + } + + /** + * Alter file based on + * If user's local file is modified and user agrees to proceed with applying patch, + * alter user's local file. + * @access private + * @param $row_num row number of patch record to be processed + * @author Cindy Qi Li + */ + function alterFile($row_num) + { + $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name']; + + // backup user's file + $backup_file = $local_file . "." . $this->backup_suffix; + + // Checking existence of $backup_file is to fix the bug when there are multiple alter/delete actions + // on the same file, the following backups overwrite the first backup which results in the loss of the + // original code. + if (!file_exists($backup_file)) + { + $this->copyFile($local_file, $backup_file); + $this->backup_files[] = realpath($backup_file); + } + + $local_file_content = file_get_contents($local_file); + + // Modify user's file + foreach ($this->patch_array['files'][$row_num]['action_detail'] as $garbage => $alter_file_action) + { + if ($alter_file_action['type'] == 'delete') + $modified_local_file_content = $this->strReplace($alter_file_action['code_from'], '', $local_file_content); + + if ($alter_file_action['type'] == 'replace') + $modified_local_file_content = $this->strReplace($alter_file_action['code_from'], $alter_file_action['code_to'], $local_file_content); + + // when code_from is not found, add in warning + if ($modified_local_file_content == $local_file_content) + { + for ($i = 0; $i < count($this->backup_files); $i++) + if ($this->backup_files[$i] == realpath($backup_file)) + $this->backup_files[$i] .= ' '._AT("chunks_not_found"); + } + else + $local_file_content = $modified_local_file_content; + + $this->createPatchesFilesActionsRecord($alter_file_action); + } + + $fp = fopen($local_file, 'w'); + fwrite($fp, $local_file_content); + fclose($fp); + + return true; + } + + /** + * Fetch file from update.atutor.ca and overwrite user's local file if the local file is not modified + * If user's local file is modified and user agrees to proceed with applying patch, + * copy the new file to user's local for them to merge manually. + * @access private + * @param $row_num row number of patch record to be processed + * @author Cindy Qi Li + */ + function overwriteFile($row_num) + { + $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name']; + $patch_file = $this->baseURL . preg_replace('/.php$/', '.new', $this->patch_array['files'][$row_num]['name']); + + // if local file is modified and user agrees to proceed with applying patch, + // copy the new file to user's local for them to merge manually + if ($this->skipFilesModified && $this->isFileModified($this->patch_array['files'][$row_num]['location'], $this->patch_array['files'][$row_num]['name'])) + { + $local_patch_file = $local_file . "." . $this->patch_suffix; + + $this->copyFile($patch_file, $local_patch_file); + + $this->patch_files[] = realpath($local_patch_file); + } + else + { + $backup_file = $local_file . "." . $this->backup_suffix; + + // backup user's file + $this->copyFile($local_file, $backup_file); + $this->backup_files[] = realpath($backup_file); + + // overwrite user's file + $this->copyFile($patch_file, $local_file); + } + + return true; + } + + /** + * Copy file $src to $dest. $src can be a local file or a remote file + * @access private + * @param $src location of the source file + * $dest location of the destination file + * @author Cindy Qi Li + */ + function copyFile($src, $dest) + { + $content = file_get_contents($src); + $fp = fopen($dest, 'w'); + fwrite($fp, $content); + fclose($fp); + + return true; + } + + /** + * Compare files $src against $dest + * @access private + * @param $src location of the source file + * $dest location of the destination file + * @return Returns < 0 if $src is less than $dest ; > 0 if $src is greater than $dest, and 0 if they are equal. + * @author Cindy Qi Li + */ + function compareFiles($src, $dest) + { + // use preg_replace to delete + // 1. the line starting with // $Id: + // 2. the line starting with $lm = '$LastChangedDate, ending with ; + // These lines are created by SVN. It could be different in different copies of the same file. + $pattern = '/\/\/ \$Id.*\$|\$lm = \'\$LastChangedDate.*;/'; + + $src_content = preg_replace($pattern, '', file_get_contents($src)); + $dest_content = preg_replace($pattern, '', file_get_contents($dest)); + + return strcasecmp($src_content, $dest_content); + } + + /** + * Replace single/multiple lines of string. + * This function handles different new line character at windows/unix platform + * @access private + * @param $search String to replace from + * $replace String to replace to + * $subject Subject to be handled + * @return return replaced string, if nothing is replaced, return original subject + * @author Cindy Qi Li + */ + function strReplace($search, $replace, $subject) + { + // Note: DO NOT change the order of the array elements. + // "\n\r", "\r\n" must come before "\n", "\r" in the array, + // otherwise, the new line replace underneath would wrongly replace "\n\r" to "\r\r" or "\n\n" + $new_line_array = array("\n\r", "\r\n", "\r", "\n"); + + foreach ($new_line_array as $new_line) + { + if (preg_match('/'.preg_quote($new_line).'/', $search) > 0) $search_new_lines[] = $new_line; + if (preg_match('/'.preg_quote($new_line).'/', $replace) > 0) $replace_new_lines[] = $new_line; + if (preg_match('/'.preg_quote($new_line).'/', $subject) > 0) $subject_new_lines[] = $new_line; + } + + // replace new line chars in $search, $replace, $subject to the last new line in $subject + if (is_array($subject_new_lines)) $new_line_replace_to = array_pop($subject_new_lines); + + if ($new_line_replace_to <> '') + { + if (count($search_new_lines) > 0) + foreach ($search_new_lines as $new_line) + if ($new_line <> $new_line_replace_to) + $search = preg_replace('/'.preg_quote($new_line).'/', $new_line_replace_to, $search); + + if (count($replace_new_lines) > 0) + foreach ($replace_new_lines as $new_line) + if ($new_line <> $new_line_replace_to) + $replace = preg_replace('/'.preg_quote($new_line).'/', $new_line_replace_to, $replace); + + if (count($subject_new_lines) > 0) + foreach ($subject_new_lines as $new_line) + $subject = preg_replace('/'.preg_quote($new_line).'/', $new_line_replace_to, $subject); + } + + return preg_replace('/'. preg_quote($search, '/') .'/', $replace, $subject); + } + + /** + * Check if the server is down + * @access private + * @param $domain Server Domain + * @return return false if server is down, otherwise, return true + * @author Cindy Qi Li + */ + function pingDomain($domain) + { + $file = @fopen ($domain, 'r'); + + if (!$file) + return false; + + return true; + } + + /** + * Insert record into table patches + * @access private + * @param $patch_summary_array Patch summary information + * @author Cindy Qi Li + */ + function createPatchesRecord($patch_summary_array) + { + global $db; + + $sql = "INSERT INTO " . TABLE_PREFIX. "patches " . + "(atutor_patch_id, + applied_version, + patch_folder, + description, + available_to, + sql_statement, + status, + remove_permission_files, + backup_files, + patch_files, + author, + installed_date) + VALUES + ('".$patch_summary_array["atutor_patch_id"]."', + '".$patch_summary_array["applied_version"]."', + '".mysql_real_escape_string($patch_summary_array["patch_folder"])."', + '".mysql_real_escape_string($patch_summary_array["description"])."', + '".$patch_summary_array["available_to"]."', + '".mysql_real_escape_string($patch_summary_array["sql"])."', + '".$patch_summary_array["status"]."', + '', + '', + '', + '".mysql_real_escape_string($patch_summary_array["author"])."', + now() + )"; + + $result = mysql_query($sql, $db) or die(mysql_error()); + + $this->patch_id = mysql_insert_id(); + + return true; + } + + /** + * Insert record into table patches_files + * @access private + * @param $patch_files_array Patch information + * @author Cindy Qi Li + */ + function createPatchesFilesRecord($patch_files_array) + { + global $db; + + $sql = "INSERT INTO " . TABLE_PREFIX. "patches_files " . + "(patches_id, + action, + name, + location) + VALUES + (".$this->patch_id.", + '".$patch_files_array['action']."', + '".mysql_real_escape_string($patch_files_array['name'])."', + '".mysql_real_escape_string($patch_files_array['location'])."' + )"; + + $result = mysql_query($sql, $db) or die(mysql_error()); + + $this->patch_file_id = mysql_insert_id(); + + return true; + } + + /** + * Insert record into table patches_files_actions + * @access private + * @param $patch_files_actions_array alter file actions and contents + * @author Cindy Qi Li + */ + function createPatchesFilesActionsRecord($patch_files_actions_array) + { + global $db; + + $sql = "INSERT INTO " . TABLE_PREFIX. "patches_files_actions " . + "(patches_files_id, + action, + code_from, + code_to) + VALUES + (".$this->patch_file_id.", + '".$patch_files_actions_array['type']."', + '".mysql_real_escape_string($patch_files_actions_array['code_from'])."', + '".mysql_real_escape_string($patch_files_actions_array['code_to'])."' + )"; + + $result = mysql_query($sql, $db) or die(mysql_error()); + + return true; + } +} + +?> \ No newline at end of file diff --git a/mods/_standard/patcher/classes/PatchCreator.class.php b/mods/_standard/patcher/classes/PatchCreator.class.php new file mode 100644 index 000000000..0987d53d4 --- /dev/null +++ b/mods/_standard/patcher/classes/PatchCreator.class.php @@ -0,0 +1,386 @@ + Patch001 + * [atutor_version_to_apply] => 1.6 + * [description] => this is a sample patch info array + * [sql_statement] => + * [dependent_patches] => Array + * ( + * [0] => P2 + * [1] => P3 + * ) + * [files] => Array + * ( + * [0] => Array + * ( + * [file_name] => script1.php + * [action] => add + * [directory] => admin/ + * [upload_tmp_name] => C:\xampp\tmp\php252.tmp + * ) + * + * [1] => Array + * ( + * [file_name] => script2.php + * [action] => delete + * [directory] => tools/ + * ) + * ) + * + * ) + */ + + function PatchCreator($patch_info_array, $patch_id) + { + // add slashes if magic_quotes_gpc is off + if (!get_magic_quotes_gpc()) + { + $patch_info_array["description"] = addslashes($patch_info_array["description"]); + $patch_info_array["sql_statement"] = addslashes($patch_info_array["sql_statement"]); + + for ($i = 0; $i < count($patch_info_array["files"]); $i++) + { + $patch_info_array["files"][$i]["directory"] = addslashes($patch_info_array["files"][$i]["directory"]); + $patch_info_array["files"][$i]["upload_tmp_name"] = addslashes($patch_info_array["files"][$i]["upload_tmp_name"]); + $patch_info_array["files"][$i]["code_from"] = addslashes($patch_info_array["files"][$i]["code_from"]); + $patch_info_array["files"][$i]["code_to"] = addslashes($patch_info_array["files"][$i]["code_to"]); + } + } + + $this->patch_info_array = $patch_info_array; + $this->current_patch_id = $patch_id; + + $this->patch_xml_file = AT_CONTENT_DIR . "patcher/patch.xml"; + + $this->version_folder = AT_CONTENT_DIR . "patcher/" . str_replace('.', '_', $this->patch_info_array["atutor_version_to_apply"]) . "/"; + $this->patch_folder = $this->version_folder . $this->patch_info_array["atutor_patch_id"] . "/"; + } + + /** + * Create Patch + * @access public + * @return true if created successfully + * false if error happens + * @author Cindy Qi Li + */ + function create_patch() + { + // save patch info into database & save uploaded files into content folder + $this->saveInfo(); + + // create patch.xml into $this->patch_xml_file + $fp = fopen($this->patch_xml_file,'w'); + fwrite($fp,$this->createXML()); + fclose($fp); + + // create zip package and force download + $this->createZIP(); + + // clean up + unlink($this->patch_xml_file); + + return true; + } + + /** + * Save patch info into database & save uploaded files into content folder + * @access public + * @return xml string + * @author Cindy Qi Li + */ + function saveInfo() + { + global $db; + + if ($this->current_patch_id == 0) + { + $sql = "INSERT INTO ".TABLE_PREFIX."myown_patches + (atutor_patch_id, + applied_version, + description, + sql_statement, + status, + last_modified) + VALUES ('".$this->patch_info_array["atutor_patch_id"]."', + '".$this->patch_info_array["atutor_version_to_apply"]."', + '".$this->patch_info_array["description"]."', + '".$this->patch_info_array["sql_statement"]."', + 'Created', + now())"; + } + else + { + $sql = "UPDATE ".TABLE_PREFIX."myown_patches + SET atutor_patch_id = '". $this->patch_info_array["atutor_patch_id"] ."', + applied_version = '". $this->patch_info_array["atutor_version_to_apply"] ."', + description = '". $this->patch_info_array["description"] ."', + sql_statement = '". $this->patch_info_array["sql_statement"] ."', + status = 'Created', + last_modified = now() + WHERE myown_patch_id = ". $this->current_patch_id; + } + + $result = mysql_query($sql, $db) or die(mysql_error()); + + if ($this->current_patch_id == 0) + { + $this->current_patch_id = mysql_insert_id(); + } + else // delete records for current_patch_id in tables myown_patches_dependent & myown_patches_files + { + $sql = "DELETE FROM ".TABLE_PREFIX."myown_patches_dependent WHERE myown_patch_id = " . $this->current_patch_id; + $result = mysql_query($sql, $db) or die(mysql_error()); + + $sql = "DELETE FROM ".TABLE_PREFIX."myown_patches_files WHERE myown_patch_id = " . $this->current_patch_id; + $result = mysql_query($sql, $db) or die(mysql_error()); + } + + // insert records into table myown_patches_dependent + if (is_array($this->patch_info_array["dependent_patches"])) + { + foreach ($this->patch_info_array["dependent_patches"] as $dependent_patch) + { + $sql = "INSERT INTO ".TABLE_PREFIX."myown_patches_dependent + (myown_patch_id, + dependent_patch_id) + VALUES ('".$this->current_patch_id."', + '".$dependent_patch."')"; + + $result = mysql_query($sql, $db) or die(mysql_error()); + } + } + + // insert records into table myown_patches_files + if (is_array($this->patch_info_array["files"])) + { + foreach ($this->patch_info_array["files"] as $file_info) + { + if ($file_info["upload_tmp_name"] <> "") + $upload_to = $this->saveFile($file_info); + else + $upload_to = ""; + + $sql = "INSERT INTO ".TABLE_PREFIX."myown_patches_files + (myown_patch_id, + action, + name, + location, + code_from, + code_to, + uploaded_file) + VALUES ('".$this->current_patch_id."', + '".$file_info["action"]."', + '".$file_info["file_name"]."', + '".$file_info["directory"]."', + '".$file_info["code_from"]."', + '".$file_info["code_to"]."', + '".addslashes($upload_to)."')"; + + $result = mysql_query($sql, $db) or die(mysql_error()); + } + } + } + + /** + * Save upload file into content folder + * @access private + * @return xml string + * @author Cindy Qi Li + */ + function saveFile($file_info) + { + // mkdir() function cannot create folder recursively, so have to acomplish the creation of patch folder by 2 steps. + if (!file_exists($this->version_folder)) mkdir($this->version_folder); + if (!file_exists($this->patch_folder)) mkdir($this->patch_folder); + + $upload_to = $this->patch_folder . $file_info['file_name']; + + // copy uploaded file into content folder + copy($file_info["upload_tmp_name"], $upload_to); + + return realpath($upload_to); + } + + /** + * Create patch.xml. + * @access private + * @return xml string + * @author Cindy Qi Li + */ + function createXML() + { + global $patch_xml, $dependent_patch_xml; + global $patch_action_detail_xml, $patch_file_xml; + + // generate content of section + if (is_array($this->patch_info_array["dependent_patches"])) + { + foreach ($this->patch_info_array["dependent_patches"] as $dependent_patch) + $dependent_patches .= str_replace('{DEPENDENT_PATCH}', $dependent_patch, $dependent_patch_xml); + } + + // generate content of section + if (is_array($this->patch_info_array["files"])) + { + foreach ($this->patch_info_array["files"] as $file_info) + { + $action_details = ""; + + if ($file_info["action"] == "alter") + { + $action_details .= str_replace(array('{TYPE}', '{CODE_FROM}', '{CODE_TO}'), + array('replace', + htmlspecialchars(stripslashes($file_info["code_from"]), ENT_QUOTES), + htmlspecialchars(stripslashes($file_info["code_to"]), ENT_QUOTES)), + $patch_action_detail_xml); + } + + $xml_files .= str_replace(array('{ACTION}', '{NAME}', '{LOCATION}', '{ACTION_DETAILS}'), + array($file_info["action"], $file_info["file_name"], $file_info["directory"], $action_details), + $patch_file_xml); + } + } + + // generate patch.xml + return str_replace(array('{ATUTOR_PATCH_ID}', + '{APPLIED_VERSION}', + '{DESCRIPTION}', + '{SQL}', + '{DEPENDENT_PATCHES}', + '{FILES}'), + array($this->patch_info_array["atutor_patch_id"], + $this->patch_info_array["atutor_version_to_apply"], + htmlspecialchars(stripslashes($this->htmlNewLine($this->patch_info_array["description"])), ENT_QUOTES), + htmlspecialchars(stripslashes($this->patch_info_array["sql_statement"]), ENT_QUOTES), + $dependent_patches, + $xml_files), + $patch_xml); + } + + /** + * Create xml for section in patch.xml. + * @access private + * @return xml string + * @author Cindy Qi Li + */ + function createXMLFiles($file_info) + { + + $action_details = ""; + + if ($file_info["action"] == "alter") + { + $action_details .= str_replace(array('{TYPE}', '{CODE_FROM}', '{CODE_TO}'), + array('replace', + htmlspecialchars(stripslashes($file_info["code_from"]), ENT_QUOTES), + htmlspecialchars(stripslashes($file_info["code_to"]), ENT_QUOTES)), + $patch_action_detail_xml); + } + + return str_replace(array('{ACTION}', '{NAME}', '{LOCATION}', '{ACTION_DETAILS}'), + array($file_info["action"], $file_info["file_name"], $file_info["directory"], $action_details), + $patch_file_xml); + } + + /** + * Create zip file which contains patch.xml and the files to be added, overwritten, altered; and force to download + * @access private + * @return true if successful + * false if errors + * @author Cindy Qi Li + */ + function createZIP() + { + require_once(AT_INCLUDE_PATH . '/classes/zipfile.class.php'); + + $zipfile = new zipfile(); + + $zipfile->add_file(file_get_contents($this->patch_xml_file), 'patch.xml'); + + if (is_array($this->patch_info_array["files"])) + { + foreach ($this->patch_info_array["files"] as $file_info) + { + if ($file_info["upload_tmp_name"] <> '') + { + $file_name = preg_replace('/.php$/', '.new', $file_info['file_name']); + $zipfile->add_file(file_get_contents($file_info['upload_tmp_name']), $file_name); + } + } + } + + $zipfile->send_file($this->patch_info_array["atutor_patch_id"]); + } + + /** + * replace new line string to html tag
    + * @access private + * @return converted string + * @author Cindy Qi Li + */ + function htmlNewLine($str) + { + $new_line_array = array("\n", "\r", "\n\r", "\r\n"); + + $found_match = false; + + if (strlen(trim($str))==0) return ""; + + foreach ($new_line_array as $new_line) + if (preg_match('/'.preg_quote($new_line).'/', $str) > 0) + { + $search_new_line = $new_line; + $found_match = true; + } + + if ($found_match) + return preg_replace('/'. preg_quote($search_new_line) .'/', "
    ", $str); + else + return $str; + } + +} + +?> \ No newline at end of file diff --git a/mods/_standard/patcher/classes/PatchListParser.class.php b/mods/_standard/patcher/classes/PatchListParser.class.php new file mode 100644 index 000000000..90dca6041 --- /dev/null +++ b/mods/_standard/patcher/classes/PatchListParser.class.php @@ -0,0 +1,132 @@ +parser = xml_parser_create(''); + + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($this->parser, 'startElement', 'endElement'); + xml_set_character_data_handler($this->parser, 'characterData'); + } + + // public + function parse($xml_data) { + $this->element_path = array(); + $this->patch_rows = array(); + $this->character_data = ''; + $this->row_num = 0; + xml_parse($this->parser, $xml_data, TRUE); + } + + // private + function startElement($parser, $name, $attributes) + { + array_push($this->element_path, $name); + } + + // private + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) { + if ($this->element_path == array('patch_list', 'patch', 'atutor_patch_id')) + { + $this->patch_rows[$this->row_num]['atutor_patch_id'] = trim($this->character_data); + } + else if ($this->element_path === array('patch_list', 'patch', 'applied_version')) + { + $this->patch_rows[$this->row_num]['applied_version'] = trim($this->character_data); + } + else if ($this->element_path === array('patch_list', 'patch', 'patch_folder')) + { + $this->patch_rows[$this->row_num]['patch_folder'] = trim($this->character_data); + } + else if ($this->element_path === array('patch_list', 'patch', 'description')) + { + $this->patch_rows[$this->row_num]['description'] = trim($this->character_data); + } + else if ($this->element_path === array('patch_list', 'patch', 'available_to')) + { + $this->patch_rows[$this->row_num]['available_to'] = trim($this->character_data); + } + else if ($this->element_path === array('patch_list', 'patch', 'author')) + { + $this->patch_rows[$this->row_num]['author'] = trim($this->character_data); + } + else if ($this->element_path === array('patch_list', 'patch', 'dependent_patches', 'dependent_patch')) + { + $this->patch_rows[$this->row_num]['dependent_patches'][$this->dependent_patches_num++] = trim($this->character_data); + } + else if ($this->element_path === array('patch_list', 'patch')) + { + $this->row_num++; + $this->dependent_patches_num = 0; + } + + array_pop($this->element_path); + $this->character_data = ''; + } + + // private + function characterData($parser, $data){ + $this->character_data .= $data; + } + + // public + function getNumPathes() + { + return count($this->patch_rows); + } + + // public + function getParsedArray() + { + return $this->patch_rows; + } + + // public + // return parsed array only for given name & version + function getMyParsedArrayForVersion($version, $who='public') + { + $my_array = array(); + + // filter out the patch for given version + foreach ($this->patch_rows as $key => $row) + { + if ($row['applied_version'] == $version && $row['available_to']==$who) + array_push($my_array, $row); + } + + return $my_array; + } +} + +?> \ No newline at end of file diff --git a/mods/_standard/patcher/classes/PatchParser.class.php b/mods/_standard/patcher/classes/PatchParser.class.php new file mode 100644 index 000000000..d16694541 --- /dev/null +++ b/mods/_standard/patcher/classes/PatchParser.class.php @@ -0,0 +1,139 @@ +parser = xml_parser_create(''); + + xml_set_object($this->parser, $this); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ + xml_set_element_handler($this->parser, 'startElement', 'endElement'); + xml_set_character_data_handler($this->parser, 'characterData'); + } + + // public + function parse($xml_data) { + $this->element_path = array(); + $this->patch_row = array(); + $this->character_data = ''; + $this->file_num = 0; + $this->action_detail_num = 0; + $this->dependent_patches_num = 0; + + xml_parse($this->parser, $xml_data, TRUE); + } + + // private + function startElement($parser, $name, $attributes) + { + array_push($this->element_path, $name); + } + + // private + /* called when an element ends */ + /* removed the current element from the $path */ + function endElement($parser, $name) + { + if ($this->element_path == array('patch', 'atutor_patch_id')) + { + $this->patch_row['atutor_patch_id'] = trim($this->character_data); + } + if ($this->element_path == array('patch', 'applied_version')) + { + $this->patch_row['applied_version'] = trim($this->character_data); + } + if ($this->element_path == array('patch', 'sequence')) + { + $this->patch_row['sequence'] = trim($this->character_data); + } + if ($this->element_path == array('patch', 'description')) + { + $this->patch_row['description'] = trim($this->character_data); + } + if ($this->element_path === array('patch', 'dependent_patches', 'dependent_patch')) + { + $this->patch_row['dependent_patches'][$this->dependent_patches_num++] = trim($this->character_data); + } + if ($this->element_path == array('patch', 'sql')) + { + $this->patch_row['sql'] = trim($this->character_data); + } + else if ($this->element_path === array('patch', 'files', 'file', 'action')) + { + $this->patch_row['files'][$this->file_num]['action'] = trim($this->character_data); + } + else if ($this->element_path === array('patch', 'files', 'file', 'name')) + { + $this->patch_row['files'][$this->file_num]['name'] = trim($this->character_data); + } + else if ($this->element_path === array('patch', 'files', 'file', 'location')) + { + $this->patch_row['files'][$this->file_num]['location'] = trim($this->character_data); + } + else if ($this->element_path === array('patch', 'files', 'file', 'action_detail', 'type')) + { + $this->patch_row['files'][$this->file_num]['action_detail'][$this->action_detail_num]['type'] = trim($this->character_data); + } + else if ($this->element_path === array('patch', 'files', 'file', 'action_detail', 'code_from')) + { + $this->patch_row['files'][$this->file_num]['action_detail'][$this->action_detail_num]['code_from'] = trim($this->character_data); + } + else if ($this->element_path === array('patch', 'files', 'file', 'action_detail', 'code_to')) + { + $this->patch_row['files'][$this->file_num]['action_detail'][$this->action_detail_num]['code_to'] = trim($this->character_data); + } + else if ($this->element_path === array('patch', 'files', 'file')) + { + $this->file_num++; + } + else if ($this->element_path === array('patch', 'files', 'file', 'action_detail')) + { + $this->action_detail_num++; + } + + array_pop($this->element_path); + $this->character_data = ''; + } + + // private + function characterData($parser, $data) + { + $this->character_data .= $data; + } + + // public + function getParsedArray() + { + return $this->patch_row; + } +} + +?> \ No newline at end of file diff --git a/mods/_standard/patcher/include/common.inc.php b/mods/_standard/patcher/include/common.inc.php new file mode 100644 index 000000000..ab083b05b --- /dev/null +++ b/mods/_standard/patcher/include/common.inc.php @@ -0,0 +1,145 @@ + +
    + + + + + + + +
    +

    Warning

    + '; + foreach ($errors as $p) { + echo '
  • '.$p.'
  • '; + } + echo ''; + ?> +
    + +
    +
    + +
    + + + + + + + +

    The patch has been installed successfully!

    + '; + foreach ($feedback as $p) { + echo '
  • '.$p.'
  • '; + } + echo ''; + ?>
    + +
    +
    + $value) + { + $sql_middle .= $key . "='" . $value . "', "; + } + + $sql = substr($sql_prefix . $sql_middle, 0, -2) . " where patches_id = " . $patch_id; + + $result = mysql_query($sql, $db) or die(mysql_error()); + + return true; +} + +/** +* This function deletes $dir recrusively without deleting $dir itself. +* @access public +* @param string $charsets_array The name of the directory where all files and folders under needs to be deleted +* @author Cindy Qi Li +*/ +function clear_dir($dir) { + require_once(AT_INCLUDE_PATH.'../mods/_core/file_manager/filemanager.inc.php'); + + if(!$opendir = @opendir($dir)) { + return false; + } + + while(($readdir=readdir($opendir)) !== false) { + if (($readdir !== '..') && ($readdir !== '.')) { + $readdir = trim($readdir); + + clearstatcache(); /* especially needed for Windows machines: */ + + if (is_file($dir.'/'.$readdir)) { + if(!@unlink($dir.'/'.$readdir)) { + return false; + } + } else if (is_dir($dir.'/'.$readdir)) { + /* calls lib function to clear subdirectories recrusively */ + if(!clr_dir($dir.'/'.$readdir)) { + return false; + } + } + } + } /* end while */ + + @closedir($opendir); + + return true; +} + +/** + * Check if the patch has been installed + */ +function is_patch_installed($patch_id) +{ + global $db; + + // Only displays the patches that are not installed + $sql = "select count(*) num_of_installed from ".TABLE_PREFIX."patches " . + "where atutor_patch_id = '" . $patch_id ."'". + " and applied_version = '".VERSION."'". + " and status like '%Installed'"; + + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + if ($row["num_of_installed"] > 0) return true; + else return false; +} + +?> diff --git a/mods/_standard/patcher/include/json.inc.php b/mods/_standard/patcher/include/json.inc.php new file mode 100644 index 000000000..0a8cb3005 --- /dev/null +++ b/mods/_standard/patcher/include/json.inc.php @@ -0,0 +1,84 @@ + $val) { + if($str == "") + $str = $col .":'" . escapeString($val) . "'"; + else + $str = $str . "," . $col .":'" . escapeString($val) . "'"; + } + + return "{" . $str . "}"; +} + +// convert a PHP object to javascript object +function php2js_array($phparr) { + $str = ""; + + if (!is_array($phparr)) return "[]"; + + foreach ($phparr as $e) { + if($str == "") + $str = php2js_object($e) ; + else + $str = $str . "," . php2js_object($e); + } + + return "[" . $str . "]"; +} + +// convert a SQL result object to javascript object +function php2js_sqlresult($phpsql) { + // Printing results + $rows = array(); + while ($line = mysql_fetch_assoc($phpsql)) { + $rows[] = $line; + } + mysql_free_result($phpsql); + return php2js_array($rows); +} + +function escapeString($string) { + $escape = array( + "\r\n" => '\n', + "\r" => '\n', + "\n" => '\n', + "/" => '\/' + ); + + return str_replace(array_keys($escape), array_values($escape), addslashes($string)); +} + +?> \ No newline at end of file diff --git a/mods/_standard/patcher/include/patch_xml_template.inc.php b/mods/_standard/patcher/include/patch_xml_template.inc.php new file mode 100644 index 000000000..2e3c1e267 --- /dev/null +++ b/mods/_standard/patcher/include/patch_xml_template.inc.php @@ -0,0 +1,56 @@ + + + {ATUTOR_PATCH_ID} + {APPLIED_VERSION} + {DESCRIPTION} + +{DEPENDENT_PATCHES} + + + +{SQL} + + + +{FILES} + +'; + +$dependent_patch_xml = +' {DEPENDENT_PATCH} +'; + +$patch_file_xml = +' + {ACTION} + {NAME} + {LOCATION} +{ACTION_DETAILS} + + +'; + +$patch_action_detail_xml = +' + {TYPE} + {CODE_FROM} + {CODE_TO} + + +'; +?> diff --git a/mods/_standard/patcher/index_admin.php b/mods/_standard/patcher/index_admin.php new file mode 100644 index 000000000..c307632fb --- /dev/null +++ b/mods/_standard/patcher/index_admin.php @@ -0,0 +1,465 @@ +" . $dependent_patches . ""; +?> + > + /> + + + + + + + + 0 && ($patch_row["remove_permission_files"]<> "" || $patch_row["backup_files"]<>"" || $patch_row["patch_files"]<> "")) + echo ' +
    + +
    '; + ?> + + +addFeedback('CANCELLED'); + header('Location: index_admin.php'); + exit; +} + +require (AT_INCLUDE_PATH.'header.inc.php'); + +if (trim($_POST['who']) != '') $who = trim($_POST['who']); +elseif (trim($_REQUEST['who']) != '') $who = trim($_REQUEST['who']); +else $who = "public"; + +// check the connection to server update.atutor.ca +$update_server = "http://update.atutor.ca"; +$connection_test_file = $update_server . '/index.php'; +$connection = @file_get_contents($connection_test_file); + +if (!$connection) +{ + $infos = array('CANNOT_CONNECT_PATCH_SERVER', $update_server); + $msg->addInfo($infos); + $server_connected = false; +} +else + $server_connected = true; + +// get patch list if successfully connect to patch server +if ($server_connected) +{ + $patch_folder = $update_server . '/patch/' . str_replace('.', '_', VERSION) . '/'; + + $patch_list_xml = @file_get_contents($patch_folder . 'patch_list.xml'); + + if ($patch_list_xml) + { + $patchListParser = new PatchListParser(); + $patchListParser->parse($patch_list_xml); + $patch_list_array = $patchListParser->getMyParsedArrayForVersion(VERSION); + } +} +// end of get patch list + +$module_content_folder = AT_CONTENT_DIR . "patcher"; + +if ($_POST['install_upload'] && $_POST['uploading']) +{ + include_once(AT_INCLUDE_PATH . '/classes/pclzip.lib.php'); + + // clean up module content folder + clear_dir($module_content_folder); + + // 1. unzip uploaded file to module's content directory + $archive = new PclZip($_FILES['patchfile']['tmp_name']); + + if ($archive->extract(PCLZIP_OPT_PATH, $module_content_folder) == 0) + { + clear_dir($module_content_folder); + $msg->addError('CANNOT_UNZIP'); + } +} + +// Installation process +if ($_POST['install'] || $_POST['install_upload'] && !isset($_POST["not_ignore_version"])) +{ + + if (isset($_POST['id'])) $id=$_POST['id']; + else $id = $_REQUEST['id']; + + if ($_POST['install'] && $id == "") + { + $msg->addError('CHOOSE_UNINSTALLED_PATCH'); + } + else + { + if ($_POST['install']) + { + $patchURL = $patch_folder . $patch_list_array[$id][patch_folder] . "/"; + } + else if ($_POST['install_upload']) + { + $patchURL = $module_content_folder . "/"; + } + + $patch_xml = @file_get_contents($patchURL . 'patch.xml'); + + if ($patch_xml === FALSE) + { + $msg->addError('PATCH_XML_NOT_FOUND'); + } + else + { + require_once('classes/PatchParser.class.php'); + require_once('classes/Patch.class.php'); + + $patchParser = new PatchParser(); + $patchParser->parse($patch_xml); + + $patch_array = $patchParser->getParsedArray(); + + if ($_POST["ignore_version"]) $patch_array["applied_version"] = VERSION; + + if ($_POST["install_upload"]) + { + $current_patch_list = array('atutor_patch_id' => $patch_array['atutor_patch_id'], + 'applied_version' => $patch_array['applied_version'], + 'patch_folder' => $patchURL, + 'available_to' => 'private', + 'author' => $patch_array['author'], + 'sql' => $patch_array['sql'], + 'description' => $patch_array['description'], + 'dependent_patches' => $patch_array['dependent_patches']); + } + + if ($_POST["install"]) + { + $current_patch_list = $patch_list_array[$id]; + $current_patch_list["sql"] = $patch_array["sql"]; + } + + if ($_POST["install_upload"] && is_patch_installed($patch_array["atutor_patch_id"])) + $msg->addError('PATCH_ALREADY_INSTALLED'); + else + { + $patch = new Patch($patch_array, $current_patch_list, $skipFilesModified, $patchURL); + + if ($patch->applyPatch()) $patch_id = $patch->getPatchID(); + } + } + } +} +// end of patch installation + +// display permission and backup files message +if (isSet($_REQUEST['patch_id'])) $patch_id = $_REQUEST['patch_id']; +elseif ($_POST['patch_id']) $patch_id=$_POST['patch_id']; + +if ($patch_id > 0) +{ + // clicking on button "Done" at displaying remove permission info page + if ($_POST['done']) + { + $permission_files = array(); + + if (is_array($_SESSION['remove_permission'])) + { + foreach ($_SESSION['remove_permission'] as $file) + { + if (is_writable($file)) $permission_files[] = $file; + } + } + + if (count($permission_files) == 0) + { + $updateInfo = array("remove_permission_files"=>"", "status"=>"Installed"); + + updatePatchesRecord($patch_id, $updateInfo); + } + else + { + foreach($permission_files as $permission_file) + $remove_permission_files .= $permission_file. '|'; + + $updateInfo = array("remove_permission_files"=>preg_quote($remove_permission_files), "status"=>"Partly Installed"); + + updatePatchesRecord($patch_id, $updateInfo); + } + + } + + // display remove permission info + unset($_SESSION['remove_permission']); + + $sql = "SELECT * FROM ".TABLE_PREFIX."patches + WHERE patches_id = " . $patch_id; + + $result = mysql_query($sql, $db) or die(mysql_error()); + $row = mysql_fetch_assoc($result); + + if ($row["remove_permission_files"]<> "") + { + $remove_permission_files = $_SESSION['remove_permission'] = get_array_by_delimiter($row["remove_permission_files"], "|"); + + if (count($_SESSION['remove_permission']) > 0) + { + if ($_POST['done']) $msg->printErrors('REMOVE_WRITE_PERMISSION'); + else $msg->printInfos('PATCH_INSTALLED_AND_REMOVE_PERMISSION'); + + $feedbacks[] = _AT('remove_write_permission'); + + foreach($remove_permission_files as $remove_permission_file) + if ($remove_permission_file <> "") $feedbacks[count($feedbacks)-1] .= "" . $remove_permission_file . "
    "; + + $notes = '
    +
    + + +
    +
    '; + } + + print_errors($feedbacks, $notes); + } + + // display backup file info after remove permission step + if ($row["remove_permission_files"] == "") + { + $msg->printFeedbacks('PATCH_INSTALLED_SUCCESSFULLY'); + + if ($row["backup_files"]<> "") + { + $backup_files = get_array_by_delimiter($row["backup_files"], "|"); + + if (count($backup_files) > 0) + { + $feedbacks[] = _AT('patcher_show_backup_files'); + + foreach($backup_files as $backup_file) + if ($backup_file <> "") $feedbacks[count($feedbacks)-1] .= "" . $backup_file . "
    "; + } + } + + if ($row["patch_files"]<> "") + { + $patch_files = get_array_by_delimiter($row["patch_files"], "|"); + + if (count($patch_files) > 0) + { + $feedbacks[] = _AT('patcher_show_patch_files'); + + foreach($patch_files as $patch_file) + if ($patch_file <> "") $feedbacks[count($feedbacks)-1] .= "" . $patch_file . "
    "; + + } + } + + if (count($feedbacks)> 0) + print_feedback($feedbacks); + else + print_feedback(array()); + } +} + +$msg->printAll(); +?> + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + $new_patch) + { + if (!is_patch_installed($new_patch['atutor_patch_id'])) + { + $dependent_patches_installed = true; + $dependent_patches = ""; + + // check if the dependent patches are installed + if (is_array($new_patch["dependent_patches"])) + { + + foreach ($new_patch["dependent_patches"] as $num => $dependent_patch) + { + if (!is_patch_installed($dependent_patch)) + { + $dependent_patches_installed = false; + $dependent_patches .= $dependent_patch. ", "; + } + } + + // remove the last comma in the string + if ($dependent_patches <> "") $dependent_patches = substr($dependent_patches, 0, -2); + } + + // display patch row + if ($dependent_patches_installed) + print_patch_row($new_patch, $array_id++, true); + else + { + print_patch_row($new_patch, $array_id++, false); + $dependent_patches_installed = true; + } + } + else + $array_id++; + } + } +?> + + + + + + + + +
     
    + +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + + +
    + +
    + + +
    +
    + +
    + + + + diff --git a/mods/_standard/patcher/module.php b/mods/_standard/patcher/module.php new file mode 100644 index 000000000..90570e03c --- /dev/null +++ b/mods/_standard/patcher/module.php @@ -0,0 +1,43 @@ +getPrivilege()); +define('AT_ADMIN_PRIV_PATCHER', $this->getAdminPrivilege()); + +/******* + * add the admin pages when needed. + */ +if (admin_authenticate(AT_ADMIN_PRIV_PATCHER, TRUE) || admin_authenticate(AT_ADMIN_PRIV_ADMIN, TRUE)) { + $this->_pages[AT_NAV_ADMIN] = array('mods/_standard/patcher/index_admin.php'); + $this->_pages['mods/_standard/patcher/index_admin.php']['title_var'] = 'patcher'; + $this->_pages['mods/_standard/patcher/index_admin.php']['parent'] = AT_NAV_ADMIN; + + $this->_pages['mods/_standard/patcher/index_admin.php']['children'] = array('mods/_standard/patcher/myown_patches.php','mods/_standard/patcher/patch_create.php'); + $this->_pages['mods/_standard/patcher/myown_patches.php']['title_var'] = 'myown_patches'; + $this->_pages['mods/_standard/patcher/myown_patches.php']['parent'] = 'mods/_standard/patcher/index_admin.php'; + + $this->_pages['mods/_standard/patcher/myown_patches.php']['children'] = array('mods/_standard/patcher/patch_create.php'); + $this->_pages['mods/_standard/patcher/patch_create.php']['title_var'] = 'create_patch'; + $this->_pages['mods/_standard/patcher/patch_create.php']['parent'] = 'mods/_standard/patcher/myown_patches.php'; + + $this->_pages['mods/_standard/patcher/patch_edit.php']['title_var'] = 'edit_patch'; + $this->_pages['mods/_standard/patcher/patch_edit.php']['parent'] = 'mods/_standard/patcher/myown_patches.php'; + + $this->_pages['mods/_standard/patcher/patch_delete.php']['title_var'] = 'delete_patch'; + $this->_pages['mods/_standard/patcher/patch_delete.php']['parent'] = 'mods/_standard/patcher/myown_patches.php'; +} +$this->_pages['mods/_standard/patcher/index_admin.php']['guide'] = '../documentation/admin/?p=patcher.php'; +$this->_pages['mods/_standard/patcher/patch_create.php']['guide'] = '../documentation/admin/?p=create_patches.php'; + +?> diff --git a/mods/_standard/patcher/module.xml b/mods/_standard/patcher/module.xml new file mode 100644 index 000000000..2bd36ef52 --- /dev/null +++ b/mods/_standard/patcher/module.xml @@ -0,0 +1,19 @@ + + + Patcher + This module is used by the ATutor administrator to apply patches (either uploaded locally, or imported from update.atutor.ca), or create your own ATutor patch. + + + ATutor Team + info@atutor.ca + + + http://atutor.ca + BSD + + 0.1 + 2008-02-08 + stable + + + diff --git a/mods/_standard/patcher/myown_patches.php b/mods/_standard/patcher/myown_patches.php new file mode 100644 index 000000000..37c6ff007 --- /dev/null +++ b/mods/_standard/patcher/myown_patches.php @@ -0,0 +1,99 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +?> +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    +
    + + +
    +
    />
    + +
    + + diff --git a/mods/_standard/patcher/patch_create.php b/mods/_standard/patcher/patch_create.php new file mode 100644 index 000000000..be373f5b1 --- /dev/null +++ b/mods/_standard/patcher/patch_create.php @@ -0,0 +1,23 @@ + diff --git a/mods/_standard/patcher/patch_creator.php b/mods/_standard/patcher/patch_creator.php new file mode 100644 index 000000000..7512c3fbe --- /dev/null +++ b/mods/_standard/patcher/patch_creator.php @@ -0,0 +1,134 @@ +$action) + { + // must upload a file if action is add or overwrite + if ($action == "add" && $_FILES['add_upload_file']['name'][$i] == "" && $_POST['add_uploaded_file'] == "") + $missing_fields[] = _AT("upload_file") . " for ". _AT("file_name") . " " . $_POST['add_filename'][$i] . ""; + + if ($action == "overwrite" && $_FILES['overwrite_upload_file']['name'][$i] == "" && $_POST['overwrite_uploaded_file'] == "") + $missing_fields[] = _AT("upload_file") . " for ". _AT("file_name") . " " . $_POST['overwrite_filename'][$i] . ""; + } + } + // end of checking missing fields + + if ($missing_fields) + { + $missing_fields = implode(', ', $missing_fields); + $msg->addError(array('EMPTY_FIELDS', $missing_fields)); + } + + if (!(preg_match("/^[a-zA-Z0-9_.-]([a-zA-Z0-9_.-])*$/i", $_POST['atutor_patch_id']))) + $msg->addError('LOGIN_CHARS'); + + // main process + if (!$msg->containsErrors()) + { + $patch_info = array("atutor_patch_id"=>$_POST["atutor_patch_id"], + "atutor_version_to_apply"=>$_POST["atutor_version_to_apply"], + "description"=>$_POST["description"], + "sql_statement"=>$_POST["sql_statement"]); + + // remove empty dependent patches + if (is_array($_POST["dependent_patch"])) + { + foreach ($_POST["dependent_patch"] as $dependent_patch) + if (trim($dependent_patch) <> "") + $dependent_patches[] = $dependent_patch; + } + + if (is_array($dependent_patches)) + $patch_info["dependent_patches"] = $dependent_patches; + + if (is_array($_POST['rb_action'])) + { + foreach ($_POST['rb_action'] as $i=>$action) + { + if ($action == "add" && $_POST['add_filename'][$i] <> "") + { + if ($_FILES['add_upload_file']['tmp_name'][$i] <> "") + $upload_file = $_FILES['add_upload_file']['tmp_name'][$i]; + else + $upload_file = $_POST['add_uploaded_file'][$i]; + + $patch_info["files"][] = array("action"=>$action, + "file_name"=>$_POST['add_filename'][$i], + "directory"=>$_POST['add_dir'][$i], + "upload_tmp_name"=>$upload_file); + } + + if ($action == "alter" && $_POST['alter_filename'][$i] <> "") + $patch_info["files"][] = array("action"=>$action, + "file_name"=>$_POST['alter_filename'][$i], + "directory"=>$_POST['alter_dir'][$i], + "code_from"=>$_POST['alter_code_from'][$i], + "code_to"=>$_POST['alter_code_to'][$i]); + + if ($action == "delete" && $_POST['delete_filename'][$i] <> "") + $patch_info["files"][] = array("action"=>$action, + "file_name"=>$_POST['delete_filename'][$i], + "directory"=>$_POST['delete_dir'][$i]); + + if ($action == "overwrite" && $_POST['overwrite_filename'][$i] <> "") + { + if ($_FILES['overwrite_upload_file']['tmp_name'][$i] <> "") + $upload_file = $_FILES['overwrite_upload_file']['tmp_name'][$i]; + else + $upload_file = $_POST['overwrite_uploaded_file'][$i]; + + $patch_info["files"][] = array("action"=>$action, + "file_name"=>$_POST['overwrite_filename'][$i], + "directory"=>$_POST['overwrite_dir'][$i], + "upload_tmp_name"=>$upload_file); + } + } + } + + require_once("classes/PatchCreator.class.php"); + + $patch_creator = new PatchCreator($patch_info, $patch_id); + + if ($_POST['create']) + $patch_creator->create_patch(); + else if ($_POST['save']) + { + $patch_creator->saveInfo(); + header('Location: myown_patches.php'); + } + + } +} + +$msg->printErrors(); +?> diff --git a/mods/_standard/patcher/patch_delete.php b/mods/_standard/patcher/patch_delete.php new file mode 100644 index 000000000..fc7cee514 --- /dev/null +++ b/mods/_standard/patcher/patch_delete.php @@ -0,0 +1,71 @@ +addFeedback('CANCELLED'); + header('Location: myown_patches.php'); + exit; +} else if (isset($_POST['submit_yes'])) { + /* delete has been confirmed, delete this category */ + $myown_patch_id = intval($_POST['myown_patch_id']); + + $sql = "DELETE FROM ".TABLE_PREFIX."myown_patches WHERE myown_patch_id=$myown_patch_id"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + write_to_log(AT_ADMIN_LOG_DELETE, 'myown_patches', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."myown_patches_dependent WHERE myown_patch_id=$myown_patch_id"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + write_to_log(AT_ADMIN_LOG_DELETE, 'myown_patches_dependent', mysql_affected_rows($db), $sql); + + $sql = "DELETE FROM ".TABLE_PREFIX."myown_patches_files WHERE myown_patch_id=$myown_patch_id"; + $result = mysql_query($sql, $db) or die(mysql_error()); + + write_to_log(AT_ADMIN_LOG_DELETE, 'myown_patches_files', mysql_affected_rows($db), $sql); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: myown_patches.php'); + exit; +} + +//require('../../include/header.inc.php'); +require(AT_INCLUDE_PATH.'header.inc.php'); + +$_GET['myown_patch_id'] = intval($_GET['myown_patch_id']); + +$sql = "SELECT myown_patch_id, atutor_patch_id FROM ".TABLE_PREFIX."myown_patches m WHERE m.myown_patch_id=$_GET[myown_patch_id]"; +$result = mysql_query($sql,$db) or die(mysql_error()); + +if (mysql_num_rows($result) == 0) { + $msg->printErrors('ITEM_NOT_FOUND'); +} else { + $row = mysql_fetch_assoc($result); + + $hidden_vars['atutor_patch_id']= $row['atutor_patch_id']; + $hidden_vars['myown_patch_id'] = $row['myown_patch_id']; + + $confirm = array('DELETE_MYOWN_PATCH', $row['atutor_patch_id']); + $msg->addConfirm($confirm, $hidden_vars); + + $msg->printConfirm(); +} + +require(AT_INCLUDE_PATH.'footer.inc.php'); + +?> \ No newline at end of file diff --git a/mods/_standard/patcher/patch_edit.php b/mods/_standard/patcher/patch_edit.php new file mode 100644 index 000000000..442f48c87 --- /dev/null +++ b/mods/_standard/patcher/patch_edit.php @@ -0,0 +1,42 @@ +addError('NO_ITEM_SELECTED'); + exit; +} + +$myown_patch_id = $_REQUEST["myown_patch_id"]; + +// URL called by form action +$url = dirname($_SERVER['PHP_SELF']) . "/patch_creator.php?myown_patch_id=" . $myown_patch_id; + +$sql_patches = "SELECT * from ".TABLE_PREFIX."myown_patches m where myown_patch_id=". $myown_patch_id; +$result_patches = mysql_query($sql_patches, $db) or die(mysql_error()); +$row_patches = mysql_fetch_assoc($result_patches); + +$sql_patch_dependent = "SELECT * from ".TABLE_PREFIX."myown_patches_dependent m where myown_patch_id=". $myown_patch_id; +$result_patch_dependent = mysql_query($sql_patch_dependent, $db) or die(mysql_error()); + +$sql_patch_files = "SELECT * from ".TABLE_PREFIX."myown_patches_files m where myown_patch_id=". $myown_patch_id . " order by myown_patches_files_id"; +$result_patch_files = mysql_query($sql_patch_files, $db) or die(mysql_error()); + +require ('patch_edit_interface.tmpl.php'); + +?> diff --git a/mods/_standard/patcher/patch_edit_interface.tmpl.php b/mods/_standard/patcher/patch_edit_interface.tmpl.php new file mode 100644 index 000000000..b842f9e81 --- /dev/null +++ b/mods/_standard/patcher/patch_edit_interface.tmpl.php @@ -0,0 +1,349 @@ + + +
    + +
    + + +
    + +
    + *
    + ·
    +
    +
    + +
    + *
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    +
    + +





    +
    +
    + · +
    + +
    +
    + +
    + +
    + +

    + +
    + + + +
    + +
    +
    + + + + + + + diff --git a/mods/_standard/patcher/sample_patch.xml b/mods/_standard/patcher/sample_patch.xml new file mode 100644 index 000000000..2be52d0e0 --- /dev/null +++ b/mods/_standard/patcher/sample_patch.xml @@ -0,0 +1,51 @@ + + + 00001 + 1.6 + <a href="http://atutor.ca">ATutor</a> + Sample patch XML + + 00002 + 00003 + + + + ALTER TABLE `tests` ADD `description` TEXT NOT NULL AFTER `title`; + + + + + alter + create_test.php + tools/tests/ + + delete + + + + + replace + + + + + + + add + calendar.gif + docs/images/ + + + + delete + results_all_csv.php + tools/tests/ + + + + overwrite + results_all.php + tools/tests/ + + + diff --git a/mods/_standard/patcher/sample_patch_list.xml b/mods/_standard/patcher/sample_patch_list.xml new file mode 100644 index 000000000..312f78dfa --- /dev/null +++ b/mods/_standard/patcher/sample_patch_list.xml @@ -0,0 +1,25 @@ + + + + 00001 + 1.6 + 1_6-1 + Patch No. 1 for ATutor 1.6 + public + <a href="http://atutor.ca">ATutor</a> + + + + + 00002 + 1.6 + 1_6-2 + Patch No. 2 for ATutor 1.6 + public + <a href="http://atutor.ca">ATutor</a> + + 00001 + 00003 + + + diff --git a/mods/_standard/patcher/xml_special_chars.txt b/mods/_standard/patcher/xml_special_chars.txt new file mode 100644 index 000000000..a72031528 --- /dev/null +++ b/mods/_standard/patcher/xml_special_chars.txt @@ -0,0 +1,5 @@ +< < less than +> > greater than +& & ampersand +' ' apostrophe +" " quotation mark \ No newline at end of file diff --git a/mods/_standard/photos/addComment.php b/mods/_standard/photos/addComment.php new file mode 100644 index 000000000..cc47b77b7 --- /dev/null +++ b/mods/_standard/photos/addComment.php @@ -0,0 +1,54 @@ +0){ + $isPhoto = true; + $id = $pid; +} else { + $isPhoto = false; + $id = $aid; +} + + +//Error checking +if (trim($_POST['comment']) == ''){ + //if comment is empty + $msg->addError('PA_EMPTY_COMMENT'); //sql +} else { + $pa = new PhotoAlbum(); + $result = $pa->addComment($id, $_POST['comment'], $_SESSION['member_id'], $isPhoto); + + if ($result){ + //TODO: AJAX + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + } else { + $msg->addError('PA_ADD_COMMENT_FAILED'); //sql + } +} + +if ($isPhoto){ + header('Location: photo.php?pid='.$pid.SEP.'aid='.$aid); +} else { + header('Location: albums.php?id='.$aid); +} +exit; +?> diff --git a/mods/_standard/photos/admin/edit_album.php b/mods/_standard/photos/admin/edit_album.php new file mode 100644 index 000000000..35537e79b --- /dev/null +++ b/mods/_standard/photos/admin/edit_album.php @@ -0,0 +1,29 @@ +printAll(); +} +require(AT_PA_INCLUDE.'edit_album.inc.php'); +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/photos/admin/edit_photos.php b/mods/_standard/photos/admin/edit_photos.php new file mode 100644 index 000000000..63ca1168a --- /dev/null +++ b/mods/_standard/photos/admin/edit_photos.php @@ -0,0 +1,29 @@ +printAll(); +} +require(AT_PA_INCLUDE.'edit_photos.inc.php'); +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/photos/admin/preferences.php b/mods/_standard/photos/admin/preferences.php new file mode 100644 index 000000000..3fbe7d308 --- /dev/null +++ b/mods/_standard/photos/admin/preferences.php @@ -0,0 +1,39 @@ +addError('PA_MEMORY_INPUT_ERROR'); + } else { + $sql = 'UPDATE '.TABLE_PREFIX."config SET value='$max_memory' WHERE name='pa_max_memory_per_member'"; + $result = mysql_query($sql, $db); + if ($reuslt===false){ + $msg->addError('PA_MEMORY_SQL_ERROR'); + } else { + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: preferences.php'); + exit; + } + } +} + +require (AT_INCLUDE_PATH.'header.inc.php'); +$savant->assign('max_memory', $_config['pa_max_memory_per_member']); +$savant->display('photos/admin/pa_preferences.tmpl.php'); +require (AT_INCLUDE_PATH.'footer.inc.php'); +?> diff --git a/mods/_standard/photos/albums.php b/mods/_standard/photos/albums.php new file mode 100644 index 000000000..82684958f --- /dev/null +++ b/mods/_standard/photos/albums.php @@ -0,0 +1,193 @@ +'; + +$id = intval($_REQUEST['id']); +$pa = new PhotoAlbum($id); +$info = $pa->getAlbumInfo(); +$action_permission = $pa->checkAlbumPriv($_SESSION['member_id']); + +//TODO: Validate users, using permission and course album control. +if ($info['member_id'] != $_SESSION['member_id'] && $info['type_id']!=AT_PA_TYPE_PERSONAL){ + $visible_albums = $pa->getAlbums($_SESSION['member_id'], $info['type_id']); + if(!isset($visible_albums[$id]) && $info['permission']==AT_PA_PRIVATE_ALBUM){ + //TODO msg; + $msg->addError("ACCESS_DENIED"); + header('location: index.php'); + exit; + } +} + +//Set pages/submenu +$_pages[AT_PA_BASENAME.'index.php']['children'] = array(AT_PA_BASENAME.'albums.php'); + +$_pages[AT_PA_BASENAME.'albums.php']['title'] = _AT('pa_albums') .' - '.$info['name']; +if ($info['type_id']==AT_PA_TYPE_MY_ALBUM){ + $_pages[AT_PA_BASENAME.'albums.php']['parent'] = AT_PA_BASENAME.'index.php'; +} elseif ($info['type_id']==AT_PA_TYPE_COURSE_ALBUM){ + $_pages[AT_PA_BASENAME.'albums.php']['parent'] = AT_PA_BASENAME.'course_albums.php'; +} +if (admin_authenticate(AT_ADMIN_PRIV_PHOTO_ALBUM, true)) { + //this is admin + $_pages[AT_PA_BASENAME.'albums.php']['parent'] = AT_PA_BASENAME.'index_admin.php'; + +} + +//if this member has the permission to edit the album, show the edit/organize menu +if ($action_permission){ + $_pages[AT_PA_BASENAME.'albums.php']['children'] = array( + AT_PA_BASENAME.'edit_photos.php?aid='.$id, + AT_PA_BASENAME.'edit_photos.php?aid='.$id.SEP.'org=1', + ); + $_pages[AT_PA_BASENAME.'edit_photos.php?aid='.$id]['title_var'] = 'pa_edit_photos'; + $_pages[AT_PA_BASENAME.'edit_photos.php?aid='.$id]['parent'] = AT_PA_BASENAME.'albums.php'; + $_pages[AT_PA_BASENAME.'edit_photos.php?aid='.$id.SEP.'org=1']['title_var'] = 'pa_organize_photos'; + $_pages[AT_PA_BASENAME.'edit_photos.php?aid='.$id.SEP.'org=1']['parent'] = AT_PA_BASENAME.'albums.php'; +} + +//TODO: handle add_photo +if(isset($_POST['upload'])){ + //check file size, filename, and extension + $_FILES['photo'] = checkPhoto($_FILES['photo']); + if ($_FILES['photo']===false || (!$action_permission && $info['type_id']!=AT_PA_TYPE_COURSE_ALBUM)){ + //owner and course members can upload pictures. Not edit though. + echo json_encode(array( + 'aid'=>$id, + 'pid'=>-1, + 'msg'=>htmlentities($msg->printErrors()), + 'error'=>true)); + exit; + } + + //computer album folder name and photo filename, if exist, shift bits + //goal: generate a random yet computable file structure to disallow + // users to browse through others' photos through URLs. + $album_file_path = getAlbumFilePath($id, $info['created_date']); + $album_file_path_tn = $album_file_path.'_tn'.DIRECTORY_SEPARATOR; + $album_file_path .= DIRECTORY_SEPARATOR; + + if (!is_dir(AT_PA_CONTENT_DIR.$album_file_path)){ + mkdir(AT_PA_CONTENT_DIR.$album_file_path); + } + if (!is_dir(AT_PA_CONTENT_DIR.$album_file_path_tn)){ + mkdir(AT_PA_CONTENT_DIR.$album_file_path_tn); + } + + //add the photo + $added_photo_id = $pa->addPhoto($_FILES['photo']['name'], $_POST['photo_comment'], $_SESSION['member_id']); + if ($added_photo_id <= 0){ + $msg->addError('PA_ADD_PHOTO_FAILED'); + } + + if (!$msg->containsErrors()){ + //get photo filepath + $photo_info = $pa->getPhotoInfo($added_photo_id); + $photo_file_path = getPhotoFilePath($added_photo_id, $_FILES['photo']['name'], $photo_info['created_date']); + + //resize images to a specific size, and its thumbnail + $si = new SimpleImage(); + $si->load($_FILES['photo']['tmp_name']); + $image_w = $si->getWidth(); + $image_h = $si->getHeight(); + + //picture is horizontal + if($image_w > $image_h){ + //don't stretch images + if ($image_w > AT_PA_IMAGE){ + $si->resizeToWidth(AT_PA_IMAGE); + $si->save(AT_PA_CONTENT_DIR.$album_file_path.$photo_file_path); + } else { + move_uploaded_file($_FILES['photo']['tmp_name'], AT_PA_CONTENT_DIR.$album_file_path.$photo_file_path); + } + $si->resizeToWidth(AT_PA_IMAGE_THUMB); + $si->save(AT_PA_CONTENT_DIR.$album_file_path_tn.$photo_file_path); + } else { + if ($image_h > AT_PA_IMAGE){ + $si->resizeToHeight(AT_PA_IMAGE); + $si->save(AT_PA_CONTENT_DIR.$album_file_path.$photo_file_path); + } else { + move_uploaded_file($_FILES['photo']['tmp_name'], AT_PA_CONTENT_DIR.$album_file_path.$photo_file_path); + } + $si->resizeToHeight(AT_PA_IMAGE_THUMB); + $si->save(AT_PA_CONTENT_DIR.$album_file_path_tn.$photo_file_path); + } + if ($_POST['upload'] == 'ajax'){ + $photo_file_hash = getPhotoFilePath($added_photo_id, '', $photo_info['created_date']); + //return JSON, relying on jQuery to convert entries to html entities. + echo json_encode(array( + 'aid'=>$id, + 'pid'=>$added_photo_id, + 'ph'=>$photo_file_hash, + 'size'=>number_format(filesize(AT_PA_CONTENT_DIR.$album_file_path.$photo_file_path)/1024, 2), + 'title'=>$photo_info['title'], + 'alt'=>$photo_info['alt'])); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + exit; + } + //if this is profile picture upload, sets it to the default profile + if ($info['type_id']==AT_PA_TYPE_PERSONAL){ + header('Location: ../profile_pictures/profile_picture.php?pid='.$added_photo_id.SEP.'aid='.$id); + exit; + } + } //if msg contain error + header('location: albums.php?id='.$id); + exit; +} + +//If this is a profile album, redirect it to the profile album page +if ($info['type_id'] == AT_PA_TYPE_PERSONAL && $info['member_id']==$_SESSION['member_id']){ + header('Location: profile_album.php'); + exit; +} + +//paginator settings +$page = intval($_GET['p']); +$photos_count = sizeof($pa->getAlbumPhotos()); +$last_page = ceil($photos_count/AT_PA_PHOTOS_PER_PAGE); + +if (!$page || $page < 0) { + $page = 1; +} elseif ($page > $last_page){ + $page = $last_page; +} + +$count = (($page-1) * AT_PA_PHOTOS_PER_PAGE) + 1; +$offset = ($page-1) * AT_PA_PHOTOS_PER_PAGE; + +//get details +$photos = $pa->getAlbumPhotos($offset); +$comments = $pa->getComments($id, false); +//TODO: Can improve performance by adding this to a session variable +$memory_usage = memoryUsage($_SESSION['member_id']); + +include (AT_INCLUDE_PATH.'header.inc.php'); +$savant->assign('album_info', $info); +$savant->assign('photos', $photos); +$savant->assign('comments', $comments); +$savant->assign('page', $page); +$savant->assign('num_rows', $photos_count); +$savant->assign('memory_usage', $memory_usage/(1024*1024)); //mb +$savant->assign('allowable_memory_usage', $_config['pa_max_memory_per_member']); //mb +$savant->assign('action_permission', $action_permission); +$savant->display('photos/pa_albums.tmpl.php'); +include (AT_INCLUDE_PATH.'footer.inc.php'); +?> diff --git a/mods/_standard/photos/course_albums.php b/mods/_standard/photos/course_albums.php new file mode 100644 index 000000000..cc556bcaa --- /dev/null +++ b/mods/_standard/photos/course_albums.php @@ -0,0 +1,47 @@ +getAlbums($_SESSION['member_id'], $type)); + +//paginator settings +$page = intval($_GET['p']); +$last_page = ceil($album_count/AT_PA_ALBUMS_PER_PAGE); + +if (!$page || $page < 0) { + $page = 1; +} elseif ($page > $last_page){ + $page = $last_page; +} + +$count = (($page-1) * AT_PA_ALBUMS_PER_PAGE) + 1; +$offset = ($page-1) * AT_PA_ALBUMS_PER_PAGE; + +$albums = $pa->getAlbums($_SESSION['member_id'], $type, $offset); + +include (AT_INCLUDE_PATH.'header.inc.php'); +$savant->assign('albums', $albums); +$savant->assign('page', $page); +$savant->assign('type', $type); +$savant->assign('num_rows', $album_count); +$savant->display('photos/pa_index.tmpl.php'); +include (AT_INCLUDE_PATH.'footer.inc.php'); +?> diff --git a/mods/_standard/photos/create_album.php b/mods/_standard/photos/create_album.php new file mode 100644 index 000000000..1c2dc30f0 --- /dev/null +++ b/mods/_standard/photos/create_album.php @@ -0,0 +1,74 @@ +createAlbum($_POST['album_name'], $_POST['album_location'], $_POST['album_description'], $album_type, $album_permission, $_SESSION['member_id'], 0); + + if (!$result){ + //TODO: sql failure. + $msg->addError('PA_CREATE_ALBUM_FAILED'); + } + } else { + //album name can't be empty + //TODO: user input failure + $msg->addError('PA_EMTPY_ALBUM_NAME'); + } + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + if($_POST['album_type'] == AT_PA_TYPE_COURSE_ALBUM){ + header('Location: course_albums.php'); + exit; + }else{ + header('Location: index.php'); + exit; + + } + +} elseif (isset($_POST['cancel'])){ + $msg->addFeedback('CANCELLED'); + header('Location: '.AT_PA_BASE); + exit; +} + +include (AT_INCLUDE_PATH.'header.inc.php'); +$savant->display('photos/pa_create_album.tmpl.php'); +include (AT_INCLUDE_PATH.'footer.inc.php'); +?> diff --git a/mods/_standard/photos/delete_album.php b/mods/_standard/photos/delete_album.php new file mode 100644 index 000000000..2f847309b --- /dev/null +++ b/mods/_standard/photos/delete_album.php @@ -0,0 +1,63 @@ +getAlbumInfo(); + +if (!$pa->checkAlbumPriv($_SESSION['member_id'])){ + $msg->addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; +} + +if ($_POST['submit_no']) { + $msg->addFeedback('CANCELLED'); + Header('Location: index.php'); + exit; +} + +if ($_POST['submit_yes']) { + //delete + $pa->deleteAlbum(); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + + if($info['type_id']== AT_PA_TYPE_COURSE_ALBUM){ + Header('Location: course_albums.php'); + exit; + }else{ + Header('Location: index.php'); + exit; + } + +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$hidden_vars['id'] = $id; +$msg->addConfirm(array('PA_DELETE_ALBUM', htmlentities_utf82($info['name'])), $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> diff --git a/mods/_standard/photos/delete_comment.php b/mods/_standard/photos/delete_comment.php new file mode 100644 index 000000000..e8ae07492 --- /dev/null +++ b/mods/_standard/photos/delete_comment.php @@ -0,0 +1,88 @@ +checkCommentPriv($comment_id, $_SESSION['member_id'], $isPhoto) && + !$pa->checkAlbumPriv($_SESSION['member_id'])){ + $msg->addError('ACCESS_DENIED'); + header('Location: index.php'); + exit; +} + +if ($_POST['submit_no']) { + $msg->addFeedback('CANCELLED'); + if ($isPhoto){ + header('Location: photo.php?pid='.$pid.SEP.'aid='.$aid); + } else { + header('Location: albums.php?id='.$aid); + } + exit; +} + +if ($_POST['submit_yes']) { + //delete + if ($pid==0){ + //not a photo + $pa->deleteComment($comment_id, false); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + Header('Location: albums.php?id='.$aid); + exit; + } else { + $pa->deleteComment($comment_id, true); + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + Header('Location: photo.php?pid='.$pid.SEP.'aid='.$aid); + exit; + } +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$hidden_vars['comment_id'] = $comment_id; +$hidden_vars['aid'] = $aid; +$hidden_vars['pid'] = $pid; + + +$msg->addConfirm(array('PA_DELETE_COMMENT'), $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> diff --git a/mods/_standard/photos/delete_photo.php b/mods/_standard/photos/delete_photo.php new file mode 100644 index 000000000..55bc7db72 --- /dev/null +++ b/mods/_standard/photos/delete_photo.php @@ -0,0 +1,72 @@ +addError('PA_PHOTO_NOT_FOUND'); //no such picture + header('Location: index.php'); + exit; +} elseif (!$pa->checkPhotoPriv($pid, $_SESSION['member_id']) && !$pa->checkAlbumPriv($_SESSION['member_id'])){ + $msg->addError('ACCESS_DENIED'); + header('Location: albums.php?id='.$aid); + exit; +} + +if ($_POST['submit_no']) { + $msg->addFeedback('CANCELLED'); + Header('Location: photo.php?aid='.$aid.SEP.'pid='.$pid); + exit; +} + +if ($_POST['submit_yes']) { + //delete + $pa->deletePhoto($pid); + + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + header('Location: albums.php?id='.$aid); + exit; +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + +$hidden_vars['pid'] = $pid; +$hidden_vars['aid'] = $aid; + +$msg->addConfirm(array('PA_DELETE_PHOTO'), $hidden_vars); +$msg->printConfirm(); + +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> diff --git a/mods/_standard/photos/edit_album.php b/mods/_standard/photos/edit_album.php new file mode 100644 index 000000000..12db106a7 --- /dev/null +++ b/mods/_standard/photos/edit_album.php @@ -0,0 +1,93 @@ +checkAlbumPriv($_SESSION['member_id'])){ + $msg->addError('ACCESS_DENIED'); + header('location: index.php'); + exit; +} + +//Set pages/submenu +if (admin_authenticate(AT_ADMIN_PRIV_PHOTO_ALBUM, true)) { + //this is admin + $_pages[AT_PA_BASENAME.'edit_album.php']['parent'] = AT_PA_BASENAME.'index_admin.php'; + +} + +$album_info = $pa->getAlbumInfo(); + +//handle Edit album info. +if(isset($_POST['submit'])){ + $pa = new PhotoAlbum($_POST['aid']); //new object + if (isset($_POST['album_type'])){ + $album_type = (intval($_POST['album_type'])==AT_PA_TYPE_MY_ALBUM)?AT_PA_TYPE_MY_ALBUM:AT_PA_TYPE_COURSE_ALBUM; + } else { + //default is "my album" 'cause normally user can't create course album. + $album_type = AT_PA_TYPE_MY_ALBUM; + } + + //private or shared album? + if (isset($_POST['album_permission'])){ + $album_permission = ($_POST['album_permission']==AT_PA_SHARED_ALBUM)?AT_PA_SHARED_ALBUM:AT_PA_PRIVATE_ALBUM; + } else { + $album_permission = AT_PA_PRIVATE_ALBUM; + } + + if (isset($_POST['album_name']) && $_POST['album_name']!=''){ + //TODO: photo_id = 0, should default to use the first one after multi-file uploader works + $result = $pa->editAlbum($_POST['album_name'], $_POST['album_location'], $_POST['album_description'], $album_type, $album_permission); + + if (!$result){ + $msg->addError('PA_EDIT_ALBUM_FAILED'); + } + } else { + //album name can't be empty + $msg->addError('EMPTY_ALBUM_NAME'); + + } + $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); + if (admin_authenticate(AT_ADMIN_PRIV_PHOTO_ALBUM, true)) { + //if admin + header('Location: index_admin.php'); + exit; + } + //header('Location: albums.php?id='.intval($_POST['aid'])); + header('Location: index.php'); + exit; +} elseif (isset($_POST['cancel'])){ + $msg->addFeedback('CANCELLED'); + if (admin_authenticate(AT_ADMIN_PRIV_PHOTO_ALBUM, true)) { + //if admin + header('Location: index_admin.php'); + exit; + } + header('Location: '.AT_PA_BASE); + exit; +} + + +include (AT_INCLUDE_PATH.'header.inc.php'); +$savant->assign('album_info', $album_info); +$savant->display('photos/pa_edit_album.tmpl.php'); +include (AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/photos/edit_comment.php b/mods/_standard/photos/edit_comment.php new file mode 100644 index 000000000..013e31639 --- /dev/null +++ b/mods/_standard/photos/edit_comment.php @@ -0,0 +1,46 @@ +0){ + $isPhoto = true; +} else { + $isPhoto = false; +} +$cid = intval(str_replace('cid_', '', $cid)); + +$pa = new PhotoAlbum($aid); +//validates +if ($pa->checkAlbumPriv($_SESSION['member_id']) || $pa->checkCommentPriv($cid, $_SESSION['member_id'], $isPhoto)){ + $result = $pa->editComment($cid, $comment, $isPhoto); +} + +if ($result===true){ + //TODO: AJAX + header('HTTP/1.0 200 OK'); +} else { + header('HTTP/1.0 404 Not Found'); +} +exit; +?> \ No newline at end of file diff --git a/mods/_standard/photos/edit_photos.php b/mods/_standard/photos/edit_photos.php new file mode 100644 index 000000000..35638cdd8 --- /dev/null +++ b/mods/_standard/photos/edit_photos.php @@ -0,0 +1,97 @@ +checkPhotoPriv($pid, $_SESSION['member_id']) || $pa->checkAlbumPriv($_SESSION['member_id']))){ + $msg->addError("ACCESS_DENIED"); + header('location: index.php'); + exit; +} + +//get details +if ($pid > 0){ + //get only 1 photo + $photos = array($pa->getPhotoInfo($pid)); +} else { + $photos = $pa->getAlbumPhotos(); +} +$album_info = $pa->getAlbumInfo(); + +$isadmin = FALSE; + +$_pages[AT_PA_BASENAME.'albums.php?id='.$aid]['title'] = _AT('pa_albums') .' - '.$album_info['name']; +$_pages[AT_PA_BASENAME.'albums.php?id='.$aid]['parent'] = AT_PA_BASENAME.'index.php'; +$_pages[AT_PA_BASENAME.'albums.php?id='.$aid]['children'] = array( + AT_PA_BASENAME.'edit_photos.php', + ); +$_pages[AT_PA_BASENAME.'edit_photos.php']['parent'] = AT_PA_BASENAME.'albums.php?id='.$aid; +if ($isadmin) { + $_pages[AT_PA_BASENAME.'albums.php?id='.$aid]['parent'] = AT_PA_BASENAME.'index_admin.php'; + +} + +//handle organize +if(isset($_GET['org'])){ + $_custom_head .= ''; + $_custom_css = $_base_path . AT_PA_BASENAME . 'module.css'; // use a custom stylesheet + //reset pages/submenu + $_pages[AT_PA_BASENAME.'edit_photos.php']['title'] = _AT('pa_organize_photos'); + + if (isset($_POST['submit'])){ + foreach($photos as $index=>$photo_array){ + $ordering = $_POST['image_'.$photo_array['id']]; + if(isset($ordering)){ + $result = $pa->editPhotoOrder($photo_array['id'], $ordering); + if (!$result){ + $msg->addError('PA_EDIT_PHOTO_FAILED'); + } + } + } + exit; + } + include (AT_INCLUDE_PATH.'header.inc.php'); + $savant->assign('album_info', $album_info); + $savant->assign('photos', $photos); + $savant->display('photos/pa_organize_photos.tmpl.php'); + include (AT_INCLUDE_PATH.'footer.inc.php'); + exit; +} + +//printer header iff this is not a POST request +//a hack to avoid 'header already sent...' error. +if (!isset($_POST['submit']) && !isset($_POST['cancel'])){ + $_custom_css = $_base_path . AT_PA_BASENAME . 'module.css'; // use a custom stylesheet + require(AT_INCLUDE_PATH.'header.inc.php'); + $msg->printAll(); +} +require(AT_PA_INCLUDE.'edit_photos.inc.php'); +require(AT_INCLUDE_PATH.'footer.inc.php'); +?> \ No newline at end of file diff --git a/mods/_standard/photos/get_photo.php b/mods/_standard/photos/get_photo.php new file mode 100644 index 000000000..da104d984 --- /dev/null +++ b/mods/_standard/photos/get_photo.php @@ -0,0 +1,103 @@ +getAlbumInfo(); +$photo_info = $pa->getPhotoInfo($pid); +$album_file_path = getAlbumFilePath($album_info['id'], $album_info['created_date']); +if (isset($_GET['size']) && $_GET['size'] == 'o') { + //if original + $album_file_path .= DIRECTORY_SEPARATOR; +} else { + //if thumbnail + $album_file_path .= '_tn'.DIRECTORY_SEPARATOR; +} +$photo_file_path = getPhotoFilePath($photo_info['id'], $photo_info['name'], $photo_info['created_date']); +$photo_file_hash = getPhotoFilePath($photo_info['id'], '', $photo_info['created_date']); + +$file = AT_PA_CONTENT_DIR . $album_file_path . $photo_file_path; + +//if file does not exist, quit. +if (!file_exists($file)){ + //TODO: Clean files silently, cleaned but garbaged link remains on page. + //Remove node from the DOM tree? + $pa->deletePhoto($pid); + header('HTTP/1.1 404 Not Found', TRUE); + exit; +} +//if hash doesn't match, then don't load the picture. +//to prevent trial and error on URL for photos +if ($ph !== $photo_file_hash){ + header('HTTP/1.1 404 Not Found', TRUE); + exit; +} + +$pathinfo = pathinfo($file); +$ext = $pathinfo['extension']; +if ($ext == '') { + $ext = 'application/octet-stream'; +} else { + $ext = $mime[$ext][0]; +} + +$real = realpath($file); + +if (file_exists($real) && (substr($real, 0, strlen(AT_CONTENT_DIR)) == AT_CONTENT_DIR)) { + + header('Content-Disposition: filename="'.$photo_file_path.'"'); + + /** + * although we can check if mod_xsendfile is installed in apache2 + * we can't actually check if it's enabled. also, we can't check if + * it's enabled and installed in lighty, so instead we send the + * header anyway, if it works then the line after it will not + * execute. if it doesn't work, then the line after it will replace + * it so that the full server path is not exposed. + * + * x-sendfile is supported in apache2 and lighttpd 1.5+ (previously + * named x-send-file in lighttpd 1.4) + */ + header('x-Sendfile: '.$real); + header('x-Sendfile: ', TRUE); // if we get here then it didn't work + + header('Content-Type: '.$ext); + + @readfile($real); + exit; +} else { + header('HTTP/1.1 404 Not Found', TRUE); + exit; +} + +?> \ No newline at end of file diff --git a/mods/_standard/photos/images/loading.gif b/mods/_standard/photos/images/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4 GIT binary patch literal 1849 zcma*odr(tX9tZI2z31lM+(&YVk%mZ}5P~KlG2s=WSbGzm0!x7^P##Mnh7t-jP!X0Q zk_SQ}Po-L1tlDK;6l?(>v)e5ZBQx4|Y-Q?nr@Px3?9h(3ZWr3^tj=`TP57gKr87N$ zp2wWee1GRRCwo_xahnw)5cxNPJbCg2L6DV|6`#+yw6v6!mDS$f9-JvFD^n;GQ&UrZ zzh5jCkByB101O60U0q#p_1BM>Cv-vP?&s4@g_((4_1L=L$(a91)0=J91Gas#R{McE znYG^9*0A5YZ>#;~+Wkn(W5B0^yELIYLP!K}mB~<)AM@1&nqekynuaEGqPrzoH|KodRXJy)%+w_fu3nE5>@Bd_b zqC$EQ;{c`T&?EsNO|igL9gC7Ygxv?aQUEXMq?~>wg{EyW;VcJ37CUF#HjrT=KQO_* zS>M9yydXk18D(+QDJ1>r);Lav_uYKp$T?4vr{Q$lTo&pKv^?(>L-)G2*lwH!Ah7k? z7oH<8h-(KTKt5V6$8gF)C7Io&P5=SjTh)=zV=E2EUhQZP##L8S{d%UK>>+y82>+FV+#^BzW7u3F)Bb>=lYQ%%j`F>ASe zo*cw@V#u6T`A2He;70mR(V&iV&-7{qP~=SRf&jm9-T{*ZeZ}$rd0#6c&fLG^xJcf5 z+p<`wJYgW+_s*V{uI$nMB;%8`S_3>PfGOj3Rq}@Cx^+j?rk92fANSFDBYnOqQ>Vdj z)(|$AhP4t&Lb=Gvo2#3Gl%9<=Gv`Mz?Po@P4iLF!x}GUWJICDlFk-hS^Whyh7x~VH z@0vD1>HYD4&e+~yzS*-sFR{9`{QEEZO1zg7>R&7cHts-6j!xHVdA8eI+ZlVzd%`es zJT@$#GX(gvCJ1oJN%yLBK}{V=V;seo;!w|Yte!W1%5qLNFWqvZW>h&IiH+oPT=b@E zPhGzv5=(Un*X>v`>%8h_nj^NdYcE6NHS_ifkCV$*D)Tqrbu`s;<=t<4 zAHNqNV?6(g<1PY-w@#I-WYFViz?9TrkMr)u0g`O`u|>T;k|2sV*YF^punvT;$SuTy{j3Gv)yqD!R_CF>yR)MzmmYS5v+~R zXAdD%ng9?df;wd8GxR#%3O+gz};Vo;)sK%Bj-q>Oq%R7JU-KD?vYu>#2UjaDo z&8$>5xW~?KPD_#XFToU1hIb*VOMidUr6iYiO0N|i-7s`T8!cFT`rN!^1Pt78J93i6 z5HI1wIM$94m{3SLDvISDe6$ZG1;eq_D9RTaaC>=cO{@Bs>$IlPCPJJ$h$)-3vzNUQ6OsN#_zWxey!_9%hxwH2_dEJi=yY|1c7nDm2_Lm!Cof8-R_+9UkS zcBE(o47yE)oMR(Q=dp1a2wTX5KvvGyLqlWTa7V&!A*|w|)ax~1_~aJ0=_Lilg*0iQk7#ZD EAHN$8j{pDw literal 0 HcmV?d00001 diff --git a/mods/_standard/photos/images/next.png b/mods/_standard/photos/images/next.png new file mode 100644 index 0000000000000000000000000000000000000000..facc49220f94e33cc2abd7704298f9b71cbbb78b GIT binary patch literal 5946 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 F0RRv1<|F_B literal 0 HcmV?d00001 diff --git a/mods/_standard/photos/images/photo_gallery.png b/mods/_standard/photos/images/photo_gallery.png new file mode 100644 index 0000000000000000000000000000000000000000..f18d9954aede100787e9010d40e7c51f60889a9d GIT binary patch literal 7430 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

    Q$^{0RJ=H&;U8mu+-Xy^zn8X6itu$FhS2V4S{ zPo6ya?#+Z@dmc5t&!1{Q6X*l+#~CeMpU(VWKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000a!NklLe~1oVULJzTsv^~*PgFW}(9oOiT=a|Nmp0K6mN!X%ku*W>4+@ zFC`)VpW+|~00030|ICvyO9Md^gy-ERXL11z=|v1i@PAlX31VeoYnj5r%737Zl@?-c zCxVS3f{I|N0TDY0MU@KfIuDQanQ2(dFxJ3W zOU#__p;n#3%JR~JfY9y6NRk0*W5De4fzz||c4MxN?X4!>-apVw`q0MWd%GjRW^-fl z;_?PUg3fIV+8D8x;hf9=QdBNseQlNEIFYDShWA4NBq?S2ri;Z8N-5N;layr{p6;*U z{SfnwS;+;I3J|COLV!p{t@bsJ_IL1mf&Z|rU+k1UYg9oLhMzO{-q}@1f)OhTqJ_jt zf*-*Ti1`D82!Z792!f4vrm@i~AU2lTWV=+$YBX-Z-=NuKvwQD7XJ#xmqzdR2vC-4b zVc?y4&wHNV+Pc32#(!5NKdJfT={jbHs#2?>s;C;OhM6WcBUr#fB!obSfy_BJUOty! z7V$+6Bw`G>;1FPNFifo?M=fA#s2OH~5CSnJsX}aKSG(P&-EQH%r`!7!fpogvLiLDV zWkpriT)H^7T-PdfT~n(z6Pcc#;^X$t>df?1TDrR^31M7T91IT`4u{|*9F9iP?S0-H zmnGE+1o^35vu9?~%ES9dr$j<{^!OPuMVw@UBoZLUH$aePKRI`HRyJP0tuD-+=gRzL zZrxa*^ZpIrz7N>jAEf1_+pFWz_!yd1Ra4bfvyK0<`xRg0c<t;HA#G0^XI==VD84fY8!rnQv^<$pl_k@~@o`NLRML}C0p zGjs2I2@A`*Z((=M8WS)?42V(?Lo`y5Xh>*GEHt5k*gzx!3yp=16dL~(1P#r0TiBQw z3JVulBP1J9!H@+60xYb-Xk-l$bcx+grGBRKc( zZFl?5-G%idEmBpe2vQmwL~;>QV*d90A0JLkJU*tT5K)L$ zC?tZ78=fpK{QBFQ!adMR2mvAZB<3859G;jMf8Qhtu%uQ+7OWmvWph`qH$@m?QvXb{ zEC2>bP%tydk(s5oaK&1Y8D(VLUtDx53N?eO))&e197F_#VP)lSBM{USEZt!!(J4v* zMj{F`gPH-_!-lBY$kAvv@bKZ14X#;(5IlqxouWjiC{Pp;DoSOPs;cg!_0X2-m+EfB110OUuhP(i%pykz;sh2xH^x@$8P> z$g&LHIU<)xJt!7Roxf@1_r zRf+aE!d&OwM#QFRh*ohdiwC8121d!1GT5?)}6cX z-lq)nsj4gloceCsUfcUBoKFc&L{=K#<>lf8R()8(tz zp=Ol{6Gb? znmq3MnA#WlZ$m+6GCrAcVUgbH;}=yZ4g# z6>)tax9R3?Hv2IDnKOI-=Xd6RiLNVVwyHv^LaIWlLaIXk)@c9H-wQ8qhhYX+T;`Gz z+rSu`dSO%X&Y3flcIJ$FNX2YnA$HcXg|)5>Q!W5ZTsbj}rAroX{B`OqWfEFSX{dxx zkof$xkV~Ah6Ej>K{jyXl*`}sBJ}lA!vq?mh2X0u}I#CqkriHB&hM9T}_kQ$OB;c|_ zE0@z?Lofi4V;~kgZDG-r69okdDm?V z!jKZsl)P)w5H#5c@jVZ|mxt$j@I4&rujGz?YVktxvg-8&EB5k86R-~b|+1%t&`m0AVp4Vc%Fy4x>`K=z--*!zD^D&Il4Rh>RCZ=U;j{&Z!S3s zMK1f;y@@CGP$H z;GPWfNKQa%&Rse$Ty53eE9jNic5E9tHJ*Nf*zwIIs}J^eVW4+!>am7~hRLwC8`iIx zN^OhF_$GA$8RZeJ9SGDKN(mTD;{Vxap4dA7`X+k*#aGXkN+G}~J6UT}Cp_4<*NGIj z`|+;N>Oo{TcRbu_SS$>)NsVPDq4m>n(cx4%OO}YxN`n;1nFf+)Z|?knpX%5|$@>XQ zS1yzk2(3%nkc1XMn0w7EoIW#2W%CL2{|V>4O!q5;C#}*!DOHYfxKBR;c=GXw)EhhA zZ~5jxx6~Vu8iP}2%oym~mzqXc#>DBLVXd|8%a`%L^;`dFMT)1!pdgzU$;vonuM~mY zJ*l!#B88preAu~m&8m$}jdP$N`6t=XNeR$;b6dL6;a2bIjz=aocRboyC5=@fRUuU& dSFVuX2LLrTK^oNQum=DD002ovPDHLkV1hhIMjQYD literal 0 HcmV?d00001 diff --git a/mods/_standard/photos/images/search_icon.png b/mods/_standard/photos/images/search_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..81c5ae078add9e04ec080218def6ed08d0b7a4b3 GIT binary patch literal 3443 zcmV-(4UF=MP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0007>Nkl(^bh zw6yl*<>ej5P;~$P{r{gnePa0d@gu|Q*RL5WDk|RIzI|JSfq{X61*UrH)T#NRqM{So z*w`4ROqs&)`t@sJxE07AL&tu8ek=I+`2GU`00960e2_5?f=~cM8%Kvf8B83UOqf{R zI06@dBbaauz6*;XVdQUc7d-$!B+Lwckif)JCyfTQYn!~bvyw}CX&uM$ z`jz4G<;x6v_wL=cbLUQPbc2|XIX7?Kv}I#uW%%&n z0~~++_>tkypFa!|;^GV}EG!H+Z{D=U<{}h>l$4ZC&6zv5=*^oq3~%4OVR-ZA4TG|> zGQ-cGKN)uK*~2h(>Qsir#KcsrfrKr}T3T8HJ>1>5s;a8Kke8SLWMN@(x~#M`+tbr? z6CWSne?C6G|4WuEkww;nH=p22Kz@FHEBN{O|HIM;Sq5P 1) { + clientTop = 0; + clientLeft = 0; + } + + var top = box.top / zoom + (window.pageYOffset || docElem && docElem.scrollTop / zoom || body.scrollTop / zoom) - clientTop, left = box.left / zoom + (window.pageXOffset || docElem && docElem.scrollLeft / zoom || body.scrollLeft / zoom) - clientLeft; + + return { + top: top, + left: left + }; + }; + } else { + // Get offset adding all offsets + var getOffset = function(el){ + var top = 0, left = 0; + do { + top += el.offsetTop || 0; + left += el.offsetLeft || 0; + el = el.offsetParent; + } while (el); + + return { + left: left, + top: top + }; + }; + } + + /** + * Returns left, top, right and bottom properties describing the border-box, + * in pixels, with the top-left relative to the body + * @param {Element} el + * @return {Object} Contains left, top, right,bottom + */ + function getBox(el){ + var left, right, top, bottom; + var offset = getOffset(el); + left = offset.left; + top = offset.top; + + right = left + el.offsetWidth; + bottom = top + el.offsetHeight; + + return { + left: left, + right: right, + top: top, + bottom: bottom + }; + } + + /** + * Helper that takes object literal + * and add all properties to element.style + * @param {Element} el + * @param {Object} styles + */ + function addStyles(el, styles){ + for (var name in styles) { + if (styles.hasOwnProperty(name)) { + el.style[name] = styles[name]; + } + } + } + + /** + * Function places an absolutely positioned + * element on top of the specified element + * copying position and dimentions. + * @param {Element} from + * @param {Element} to + */ + function copyLayout(from, to){ + var box = getBox(from); + +/* addStyles(to, { + position: 'absolute', + left : box.left + 'px', + top : box.top + 'px', + width : from.offsetWidth + 'px', + height : from.offsetHeight + 'px' + }); */ + } + + /** + * Creates and returns element from html chunk + * Uses innerHTML to create an element + */ + var toElement = (function(){ + var div = document.createElement('div'); + return function(html){ + div.innerHTML = html; + var el = div.firstChild; + return div.removeChild(el); + }; + })(); + + /** + * Function generates unique id + * @return unique id + */ + var getUID = (function(){ + var id = 0; + return function(){ + return 'ValumsAjaxUpload' + id++; + }; + })(); + + /** + * Get file name from path + * @param {String} file path to file + * @return filename + */ + function fileFromPath(file){ + return file.replace(/.*(\/|\\)/, ""); + } + + /** + * Get file extension lowercase + * @param {String} file name + * @return file extenstion + */ + function getExt(file){ + return (-1 !== file.indexOf('.')) ? file.replace(/.*[.]/, '') : ''; + } + + function hasClass(el, name){ + var re = new RegExp('\\b' + name + '\\b'); + return re.test(el.className); + } + function addClass(el, name){ + if ( ! hasClass(el, name)){ + el.className += ' ' + name; + } + } + function removeClass(el, name){ + var re = new RegExp('\\b' + name + '\\b'); + el.className = el.className.replace(re, ''); + } + + function removeNode(el){ + el.parentNode.removeChild(el); + } + + /** + * Easy styling and uploading + * @constructor + * @param button An element you want convert to + * upload button. Tested dimentions up to 500x500px + * @param {Object} options See defaults below. + */ + window.AjaxUpload = function(button, options){ + this._settings = { + // Location of the server-side upload script + action: 'upload.php', + // File upload name + name: 'userfile', + // File Title + title: '', + // Additional data to send + data: {}, + // Submit file as soon as it's selected + autoSubmit: true, + // The type of data that you're expecting back from the server. + // html and xml are detected automatically. + // Only useful when you are using json data as a response. + // Set to "json" in that case. + responseType: false, + // Class applied to button when mouse is hovered + hoverClass: 'hover', + // Class applied to button when AU is disabled + disabledClass: 'disabled', + // When user selects a file, useful with autoSubmit disabled + // You can return false to cancel upload + onChange: function(file, extension){ + }, + // Callback to fire before file is uploaded + // You can return false to cancel upload + onSubmit: function(file, extension){ + }, + // Fired when file upload is completed + // WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE! + onComplete: function(file, response){ + } + }; + + // Merge the users options with our defaults + for (var i in options) { + if (options.hasOwnProperty(i)){ + this._settings[i] = options[i]; + } + } + + // button isn't necessary a dom element + if (button.jquery){ + // jQuery object was passed + button = button[0]; + } else if (typeof button == "string") { + if (/^#.*/.test(button)){ + // If jQuery user passes #elementId don't break it + button = button.slice(1); + } + + button = document.getElementById(button); + } + + if ( ! button || button.nodeType !== 1){ + throw new Error("Please make sure that you're passing a valid element"); + } + + if ( button.nodeName.toUpperCase() == 'A'){ + // disable link + addEvent(button, 'click', function(e){ + if (e && e.preventDefault){ + e.preventDefault(); + } else if (window.event){ + window.event.returnValue = false; + } + }); + } + + // DOM element + this._button = button; + // DOM element + this._input = null; + // If disabled clicking on button won't do anything + this._disabled = false; + + // if the button was disabled before refresh if will remain + // disabled in FireFox, let's fix it + this.enable(); + + // create the first input button on the fly + this._createInput(); + + this._rerouteClicks(); + }; + + // assigning methods to our class + AjaxUpload.prototype = { + setData: function(data){ + this._settings.data = data; + }, + disable: function(){ + addClass(this._button, this._settings.disabledClass); + this._disabled = true; + + var nodeName = this._button.nodeName.toUpperCase(); + if (nodeName == 'INPUT' || nodeName == 'BUTTON'){ + this._button.setAttribute('disabled', 'disabled'); + } + + // hide input + if (this._input){ + // We use visibility instead of display to fix problem with Safari 4 + // The problem is that the value of input doesn't change if it + // has display none when user selects a file +// this._input.parentNode.style.visibility = 'hidden'; + } + }, + enable: function(){ + removeClass(this._button, this._settings.disabledClass); + this._button.removeAttribute('disabled'); + this._disabled = false; + + }, + /** + * Creates invisible file input + * that will hover above the button + *

    + */ + _createInput: function(){ + var self = this; + + var input = document.createElement("input"); +// var input = document.getElementById("upload_button"); + var div = document.getElementById("upload_button_div"); + input.setAttribute('type', 'file'); + input.setAttribute('name', this._settings.name); + input.setAttribute('id', 'add_more_photos'); + input.setAttribute('title', this._settings.title); + + addStyles(input, { +// 'position' : 'absolute', + // in Opera only 'browse' button + // is clickable and it is located at + // the right side of the input + 'right' : 0, + 'margin' : 0, + 'padding' : 0, +// 'fontSize' : '480px', + 'cursor' : 'pointer' + }); + +// var div = document.createElement("div"); +/* addStyles(div, { + 'display' : 'block', + 'position' : 'absolute', +// 'overflow' : 'hidden', + 'margin' : 0, + 'padding' : 0, + 'opacity' : 0, + // Make sure browse button is in the right side + // in Internet Explorer + 'direction' : 'ltr', + //Max zIndex supported by Opera 9.0-9.2 + 'zIndex': 2147483583 + });*/ + + // Make sure that element opacity exists. + // Otherwise use IE filter +/* if ( div.style.opacity !== "0") { + if (typeof(div.filters) == 'undefined'){ + throw new Error('Opacity not supported by the browser'); + } + div.style.filter = "alpha(opacity=0)"; + } +*/ + addEvent(input, 'change', function(){ + + if ( ! input || input.value === ''){ + return; + } + + // Get filename from input, required + // as some browsers have path instead of it + var file = fileFromPath(input.value); + + if (false === self._settings.onChange.call(self, file, getExt(file))){ + self._clearInput(); + return; + } + + // Submit form when value is changed + if (self._settings.autoSubmit) { + self.submit(); + } + }); + + addEvent(input, 'mouseover', function(){ + addClass(self._button, self._settings.hoverClass); + }); + + addEvent(input, 'mouseout', function(){ + removeClass(self._button, self._settings.hoverClass); + + // We use visibility instead of display to fix problem with Safari 4 + // The problem is that the value of input doesn't change if it + // has display none when user selects a file +// input.parentNode.style.visibility = 'hidden'; + + }); + + div.appendChild(input); +// ajax_uploader = document.getElementById('ajax_uploader'); +// document.body.appendChild(div); +// ajax_uploader.appendChild(div); + + this._input = input; + }, + _clearInput : function(){ + if (!this._input){ + return; + } + + // this._input.value = ''; Doesn't work in IE6 + removeNode(this._input.parentNode); + this._input = null; + this._createInput(); + + removeClass(this._button, this._settings.hoverClass); + }, + /** + * Function makes sure that when user clicks upload button, + * the this._input is clicked instead + */ + _rerouteClicks: function(){ + var self = this; + + // IE will later display 'access denied' error + // if you use using self._input.click() + // other browsers just ignore click() + + /* Accessibility, use onClick + addEvent(self._button, 'mouseover', function(){ + if (self._disabled){ + return; + } + + if ( ! self._input){ + self._createInput(); + } + + var div = self._input.parentNode; + copyLayout(self._button, div); + div.style.visibility = 'visible'; + + }); + */ + + + addEvent(self._button, 'click', function(){ + if (self._disabled){ + return; + } + + if ( ! self._input){ + self._createInput(); + } + + var div = self._input.parentNode; + copyLayout(self._button, div); + div.style.visibility = 'visible'; + + }); + + + + // commented because we now hide input on mouseleave + /** + * When the window is resized the elements + * can be misaligned if button position depends + * on window size + */ + //addResizeEvent(function(){ + // if (self._input){ + // copyLayout(self._button, self._input.parentNode); + // } + //}); + + }, + /** + * Creates iframe with unique name + * @return {Element} iframe + */ + _createIframe: function(){ + // We can't use getTime, because it sometimes return + // same value in safari :( + var id = getUID(); + + // We can't use following code as the name attribute + // won't be properly registered in IE6, and new window + // on form submit will open + // var iframe = document.createElement('iframe'); + // iframe.setAttribute('name', id); + + var iframe = toElement(' + + + \ No newline at end of file diff --git a/mods/_standard/tests/html/tests_questions.inc.php b/mods/_standard/tests/html/tests_questions.inc.php new file mode 100644 index 000000000..edcc5f999 --- /dev/null +++ b/mods/_standard/tests/html/tests_questions.inc.php @@ -0,0 +1,168 @@ + +
    +
    + +
    +
    + +
    +
    + + +
    +
    +
    + + += 0) { + $sql = "SELECT * FROM ".TABLE_PREFIX."tests_questions_categories WHERE course_id=$_SESSION[course_id] AND category_id=$_GET[category_id] ORDER BY title"; +} else { + $sql = "SELECT * FROM ".TABLE_PREFIX."tests_questions_categories WHERE course_id=$_SESSION[course_id] ORDER BY title"; +} + +$result = mysql_query($sql, $db); +if ($_GET['category_id'] <= 0) { + $cats[] = array('title' => _AT('cats_uncategorized'), 'category_id' => 0); +} + +while ($row = mysql_fetch_assoc($result)) { + $cats[] = $row; +} + + $cols = 3; +?> + +
    + + + + + + + + + + + + + + + + + + + + + + + +'; + echo ''; + echo ''; + + do { + echo ''; + echo ''; + echo ''; + echo ''; + + echo ''; + + } while ($row = mysql_fetch_assoc($result)); + } +} +if (!$question_flag) { + echo ''; +} +?> + +
     
    + + + +
    + + + + + +
    '; + + echo ''; + echo '
    '; + echo ''; + echo ''; + echo AT_PRINT(htmlentities(validate_length($row['question'], 100, VALIDATE_LENGTH_FOR_DISPLAY), ENT_QUOTES, 'UTF-8'), 'tests_questions.question'); + echo ''; + echo ''; + $o = TestQuestions::getQuestion($row['type']); + $o->printName(); + + echo '
    '._AT('none_found').'
    +
    + + diff --git a/mods/_standard/tests/import_test.php b/mods/_standard/tests/import_test.php new file mode 100644 index 000000000..f6ce6c1dc --- /dev/null +++ b/mods/_standard/tests/import_test.php @@ -0,0 +1,339 @@ +addFeedback('IMPORT_CANCELLED'); + header('Location: index.php'); + exit; +} + +/* functions */ +/* called at the start of en element */ +/* builds the $path array which is the path from the root to the current element */ +function startElement($parser, $name, $attrs) { + global $attributes, $element_path, $resource_num; + //save attributes. + switch($name) { + case 'resource': + $attributes[$name.$resource_num]['identifier'] = $attrs['identifier']; + $attributes[$name.$resource_num]['href'] = $attrs['href']; + $attributes[$name.$resource_num]['type'] = $attrs['type']; + $resource_num++; + break; + case 'file': + if(in_array('resource', $element_path)){ + $attributes['resource'.($resource_num-1)]['file'][] = $attrs['href']; + } + break; + case 'dependency': + if(in_array('resource', $element_path)){ + $attributes['resource'.($resource_num-1)]['dependency'][] = $attrs['identifierref']; + } + break; + + } + array_push($element_path, $name); +} + +/* called when an element ends */ +/* removed the current element from the $path */ +function endElement($parser, $name) { + global $element_path, $test_title, $character_data; + switch($name) { + case 'title': + if (in_array('organization', $element_path)){ + $test_title = $character_data; + } + } + $character_data = ''; + array_pop($element_path); +} + +/* called when there is character data within elements */ +/* constructs the $items array using the last entry in $path as the parent element */ +function characterData($parser, $data){ + global $character_data; + if (trim($data)!=''){ + $character_data .= preg_replace('/[\t\0\x0B]*/', '', $data); + } +} + +//If overwrite hasn't been set to true, then the file has not been exported and still in the cache. +//otherwise, the zip file is extracted but has not been deleted (due to the confirmation). +if (!$overwrite){ + if (!isset($_POST['submit_import'])) { + /* just a catch all */ + + $errors = array('FILE_MAX_SIZE', ini_get('post_max_size')); + $msg->addError($errors); + + header('Location: ./index.php'); + exit; + } + + + //Handles import + /* + if (isset($_POST['url']) && ($_POST['url'] != 'http://') ) { + if ($content = @file_get_contents($_POST['url'])) { + + // save file to /content/ + $filename = substr(time(), -6). '.zip'; + $full_filename = AT_CONTENT_DIR . $filename; + + if (!$fp = fopen($full_filename, 'w+b')) { + echo "Cannot open file ($filename)"; + exit; + } + + if (fwrite($fp, $content, strlen($content) ) === FALSE) { + echo "Cannot write to file ($filename)"; + exit; + } + fclose($fp); + } + $_FILES['file']['name'] = $filename; + $_FILES['file']['tmp_name'] = $full_filename; + $_FILES['file']['size'] = strlen($content); + unset($content); + $url_parts = pathinfo($_POST['url']); + $package_base_name_url = $url_parts['basename']; + } + */ + $ext = pathinfo($_FILES['file']['name']); + $ext = $ext['extension']; + + if ($ext != 'zip') { + $msg->addError('IMPORTDIR_IMS_NOTVALID'); + } else if ($_FILES['file']['error'] == 1) { + $errors = array('FILE_MAX_SIZE', ini_get('upload_max_filesize')); + $msg->addError($errors); + } else if ( !$_FILES['file']['name'] || (!is_uploaded_file($_FILES['file']['tmp_name']) && !$_POST['url'])) { + $msg->addError('FILE_NOT_SELECTED'); + } else if ($_FILES['file']['size'] == 0) { + $msg->addError('IMPORTFILE_EMPTY'); + } +} + +if ($msg->containsErrors()) { + if (isset($_GET['tile'])) { + header('Location: '.$_base_path.'mods/_standard/tile/index.php'); + } else { + header('Location: index.php'); + } + exit; +} + +/* check if ../content/import/ exists */ +$import_path = AT_CONTENT_DIR . 'import/'; +$content_path = AT_CONTENT_DIR; + +if (!is_dir($import_path)) { + if (!@mkdir($import_path, 0700)) { + $msg->addError('IMPORTDIR_FAILED'); + } +} + +$import_path .= $_SESSION['course_id'].'/'; +if (!$overwrite){ + if (is_dir($import_path)) { + clr_dir($import_path); + } + + if (!@mkdir($import_path, 0700)) { + $msg->addError('IMPORTDIR_FAILED'); + } + + /* extract the entire archive into AT_COURSE_CONTENT . import/$course using the call back function to filter out php files */ + error_reporting(0); + $archive = new PclZip($_FILES['file']['tmp_name']); + if ($archive->extract( PCLZIP_OPT_PATH, $import_path, + PCLZIP_CB_PRE_EXTRACT, 'preImportCallBack') == 0) { + $msg->addError('IMPORT_FAILED'); + echo 'Error : '.$archive->errorInfo(true); + clr_dir($import_path); + header('Location: questin_db.php'); + exit; + } + error_reporting(AT_ERROR_REPORTING); +} +/* get the course's max_quota */ +$sql = "SELECT max_quota FROM ".TABLE_PREFIX."courses WHERE course_id=$_SESSION[course_id]"; +$result = mysql_query($sql, $db); +$q_row = mysql_fetch_assoc($result); + +if ($q_row['max_quota'] != AT_COURSESIZE_UNLIMITED) { + + if ($q_row['max_quota'] == AT_COURSESIZE_DEFAULT) { + $q_row['max_quota'] = $MaxCourseSize; + } + $totalBytes = dirsize($import_path); + $course_total = dirsize(AT_CONTENT_DIR . $_SESSION['course_id'].'/'); + $total_after = $q_row['max_quota'] - $course_total - $totalBytes + $MaxCourseFloat; + + if ($total_after < 0) { + /* remove the content dir, since there's no space for it */ + $errors = array('NO_CONTENT_SPACE', number_format(-1*($total_after/AT_KBYTE_SIZE), 2 ) ); + $msg->addError($errors); + + clr_dir($import_path); + + if (isset($_GET['tile'])) { + header('Location: '.$_base_path.'mods/_standard/tile/index.php'); + } else { + header('Location: index.php'); + } + exit; + } +} + +$ims_manifest_xml = @file_get_contents($import_path.'imsmanifest.xml'); + +if ($ims_manifest_xml === false) { + $msg->addError('NO_IMSMANIFEST'); + + if (file_exists($import_path . 'atutor_backup_version')) { + $msg->addError('NO_IMS_BACKUP'); + } + + clr_dir($import_path); + + if (isset($_GET['tile'])) { + header('Location: '.$_base_path.'mods/_standard/tile/index.php'); + } else { + header('Location: index.php'); + } + exit; +} + +$xml_parser = xml_parser_create(); + +xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); /* conform to W3C specs */ +xml_set_element_handler($xml_parser, 'startElement', 'endElement'); +xml_set_character_data_handler($xml_parser, 'characterData'); + +if (!xml_parse($xml_parser, $ims_manifest_xml, true)) { + die(sprintf("XML error: %s at line %d", + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser))); +} + +xml_parser_free($xml_parser); + +//assign folder names +//if (!$package_base_name){ +// $package_base_name = substr($_FILES['file']['name'], 0, -4); +//} + +//$package_base_name = strtolower($package_base_name); +//$package_base_name = str_replace(array('\'', '"', ' ', '|', '\\', '/', '<', '>', ':'), '_' , $package_base_name); +//$package_base_name = preg_replace("/[^A-Za-z0-9._\-]/", '', $package_base_name); + +//if (is_dir(AT_CONTENT_DIR . $_SESSION['course_id'].'/'.$package_base_name)) { +// echo 'Already exist: Quitting. (Need better msg here)'; +// exit; +// $package_base_name .= '_'.date('ymdHis'); +//} + +if ($package_base_path) { + $package_base_path = implode('/', $package_base_path); +} + +//debug($attributes); +//Dependency handling +//$media_items = array(); +$xml_items = array(); +//foreach($attributes as $resource=>$attrs){ +// if ($attrs['type'] != 'webcontent'){ +// $media_items[$attrs['identifier']] = $attrs['file']; +// } +//} + +//Check if the files exist, if so, warn the user. +$existing_files = isQTIFileExist($attributes); +//debug($existing_files); +if (!$overwrite && !empty($existing_files)){ + $existing_files = implode('
    ', $existing_files); + require(AT_INCLUDE_PATH.'header.inc.php'); +// $msg->addConfirm(array('MEDIA_FILE_EXISTED', $existing_files)); +// $msg->printConfirm(); + echo '
    '; + echo '
    '; + echo '
    '; + $msg->printInfos(array('MEDIA_FILE_EXISTED', $existing_files)); + echo '
    '; + echo '
    '; + echo ''; + echo ''; + echo ''; + ECHO ''; + echo '
    '; + echo '
    '; + require (AT_INCLUDE_PATH.'footer.inc.php'); + + exit; +} + +//Get the XML file out and start importing them into our database. +//TODO: See question_import.php 287-289. +$qti_import = new QTIImport($import_path); +$qids = $qti_import->importQuestions($attributes); + +//import test +$tid = $qti_import->importTest(); + +//associate question and tests +foreach ($qids as $order=>$qid){ + if (isset($qti_import->weights[$order])){ + $weight = round($qti_import->weights[$order]); + } else { + $weight = 0; + } + $new_order = $order + 1; + $sql = "INSERT INTO " . TABLE_PREFIX . "tests_questions_assoc" . + "(test_id, question_id, weight, ordering, required) " . + "VALUES ($tid, $qid, $weight, $new_order, 0)"; + $result = mysql_query($sql, $db); +} +//debug('imported test'); +if (!$msg->containsErrors()) { + $msg->addFeedback('IMPORT_SUCCEEDED'); +} + +//clear directory +clr_dir(AT_CONTENT_DIR . 'import/'.$_SESSION['course_id']); + +header('Location: index.php'); +exit; +?> \ No newline at end of file diff --git a/mods/_standard/tests/index.php b/mods/_standard/tests/index.php new file mode 100644 index 000000000..76818e69b --- /dev/null +++ b/mods/_standard/tests/index.php @@ -0,0 +1,177 @@ +addError('NO_ITEM_SELECTED'); +} + +require(AT_INCLUDE_PATH.'header.inc.php'); + + +/* get a list of all the tests we have, and links to create, edit, delete, preview */ + +$sql = "SELECT *, UNIX_TIMESTAMP(start_date) AS us, UNIX_TIMESTAMP(end_date) AS ue FROM ".TABLE_PREFIX."tests WHERE course_id=$_SESSION[course_id] ORDER BY start_date DESC"; +$result = mysql_query($sql, $db); +$num_tests = mysql_num_rows($result); + +$cols=6; +?> +
    +
    +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    + + + +
    + + + + +
    = time() ) ) { + echo ''._AT('ongoing').''; + } else if ($row['ue'] < time() ) { + echo ''._AT('expired').''; + } else if ($row['us'] > time() ) { + echo ''._AT('pending').''; + } ?> + +
    +
    + + \ No newline at end of file diff --git a/mods/_standard/tests/lib/likert_presets.inc.php b/mods/_standard/tests/lib/likert_presets.inc.php new file mode 100644 index 000000000..4d9e5b2f2 --- /dev/null +++ b/mods/_standard/tests/lib/likert_presets.inc.php @@ -0,0 +1,28 @@ + \ No newline at end of file diff --git a/mods/_standard/tests/lib/take_test.js b/mods/_standard/tests/lib/take_test.js new file mode 100644 index 000000000..a9511e06c --- /dev/null +++ b/mods/_standard/tests/lib/take_test.js @@ -0,0 +1,23 @@ +function iframeSetHeight(id, height) { + document.getElementById("qframe" + id).style.height = (height + 20) + "px"; +} + +/** + * jQuery - Mimic confirm alert box + * @param DOM input element The input submit button. + * @param String The message that confirms submission + */ +function confirmSubmit(input, confirmMsg){ + input_button = jQuery(input); + submit_row = input_button.parent(); + //jquery submit button alternation + input_button.attr('id', 'submit_test'); + input_button.removeAttr('onclick'); + + //label for the modified submit button + input_label = jQuery('