From b245e3475f66c94afb94f8b2287bf0185a343732 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Thu, 28 May 2009 06:07:27 +0800 Subject: Restructure things so that the application is now just another module. Kohana makes this type of transition fairly straightforward in that all controllers/helpers/etc are still located in the cascading filesystem without any extra effort, except that I've temporarily added a hack to force modules/gallery into the module path. Rename what's left of "core" to be "application" so that it conforms more closely to the Kohana standard (basically, just application/config/config.php which is the minimal thing that you need in the application directory) There's still considerable work left to be done here. Signed-off-by: Gallery Role Account --- application/config/config.php | 136 +++++ core/config/config.php | 135 ----- core/config/cookie.php | 49 -- core/config/database.php | 23 - core/config/locale.php | 46 -- core/config/routes.php | 31 - core/config/sendmail.php | 29 - core/config/session.php | 66 --- core/config/upload.php | 36 -- core/controllers/admin.php | 52 -- core/controllers/admin_advanced_settings.php | 53 -- core/controllers/admin_dashboard.php | 93 --- core/controllers/admin_graphics.php | 63 --- core/controllers/admin_languages.php | 136 ----- core/controllers/admin_maintenance.php | 181 ------ core/controllers/admin_modules.php | 65 --- core/controllers/admin_theme_details.php | 67 --- core/controllers/admin_themes.php | 79 --- core/controllers/after_install.php | 30 - core/controllers/albums.php | 229 -------- core/controllers/file_proxy.php | 120 ---- core/controllers/items.php | 30 - core/controllers/l10n_client.php | 128 ----- core/controllers/maintenance.php | 24 - core/controllers/move.php | 64 --- core/controllers/movies.php | 114 ---- core/controllers/permissions.php | 80 --- core/controllers/photos.php | 116 ---- core/controllers/quick.php | 122 ---- core/controllers/rest.php | 183 ------ core/controllers/scaffold.php | 437 -------------- core/controllers/simple_uploader.php | 86 --- core/css/debug.css | 28 - core/css/l10n_client.css | 185 ------ core/css/quick.css | 40 -- core/helpers/MY_remote.php | 163 ------ core/helpers/MY_url.php | 81 --- core/helpers/access.php | 628 --------------------- core/helpers/album.php | 132 ----- core/helpers/batch.php | 40 -- core/helpers/block_manager.php | 67 --- core/helpers/core.php | 52 -- core/helpers/core_block.php | 100 ---- core/helpers/core_event.php | 46 -- core/helpers/core_installer.php | 278 --------- core/helpers/core_menu.php | 162 ------ core/helpers/core_search.php | 24 - core/helpers/core_task.php | 166 ------ core/helpers/core_theme.php | 137 ----- core/helpers/dir.php | 40 -- core/helpers/graphics.php | 387 ------------- core/helpers/item.php | 105 ---- core/helpers/l10n_client.php | 203 ------- core/helpers/l10n_scanner.php | 154 ----- core/helpers/locale.php | 119 ---- core/helpers/log.php | 108 ---- core/helpers/message.php | 108 ---- core/helpers/model_cache.php | 46 -- core/helpers/module.php | 357 ------------ core/helpers/movie.php | 153 ----- core/helpers/photo.php | 171 ------ core/helpers/rest.php | 116 ---- core/helpers/site_status.php | 132 ----- core/helpers/task.php | 83 --- core/helpers/theme.php | 61 -- core/helpers/xml.php | 35 -- core/hooks/init_gallery.php | 44 -- core/images/gallery.png | Bin 22178 -> 0 bytes core/images/gd.png | Bin 5531 -> 0 bytes core/images/graphicsmagick.png | Bin 1486 -> 0 bytes core/images/imagemagick.jpg | Bin 20337 -> 0 bytes core/js/albums_form_add.js | 12 - core/js/fullsize.js | 78 --- core/js/l10n_client.js | 195 ------- core/js/quick.js | 95 ---- core/libraries/Admin_View.php | 126 ----- core/libraries/Block.php | 30 - core/libraries/I18n.php | 410 -------------- core/libraries/MY_Database.php | 92 --- core/libraries/MY_Forge.php | 59 -- core/libraries/MY_ORM.php | 46 -- core/libraries/MY_Pagination.php | 35 -- core/libraries/MY_View.php | 46 -- core/libraries/Menu.php | 187 ------ core/libraries/ORM_MPTT.php | 307 ---------- core/libraries/Sendmail.php | 97 ---- core/libraries/Task_Definition.php | 50 -- core/libraries/Theme_View.php | 221 -------- core/models/access_cache.php | 21 - core/models/access_intent.php | 21 - core/models/graphics_rule.php | 21 - core/models/incoming_translation.php | 21 - core/models/item.php | 497 ---------------- core/models/log.php | 22 - core/models/message.php | 21 - core/models/module.php | 21 - core/models/outgoing_translation.php | 21 - core/models/permission.php | 21 - core/models/task.php | 46 -- core/models/theme.php | 21 - core/models/var.php | 21 - core/module.info | 3 - core/tests/Access_Helper_Test.php | 323 ----------- core/tests/Album_Helper_Test.php | 87 --- core/tests/Albums_Controller_Test.php | 76 --- core/tests/Core_Installer_Test.php | 50 -- core/tests/Database_Test.php | 134 ----- core/tests/Dir_Helper_Test.php | 32 -- core/tests/DrawForm_Test.php | 84 --- core/tests/File_Structure_Test.php | 235 -------- core/tests/I18n_Test.php | 108 ---- core/tests/Item_Model_Test.php | 143 ----- core/tests/Menu_Test.php | 32 -- core/tests/Movie_Helper_Test.php | 46 -- core/tests/ORM_MPTT_Test.php | 221 -------- core/tests/Photo_Helper_Test.php | 109 ---- core/tests/Photos_Controller_Test.php | 74 --- core/tests/REST_Controller_Test.php | 197 ------- core/tests/REST_Helper_Test.php | 45 -- core/tests/Sendmail_Test.php | 115 ---- core/tests/Var_Test.php | 49 -- core/tests/images/DSC_0003.jpg | Bin 735609 -> 0 bytes core/tests/images/DSC_0005.jpg | Bin 687555 -> 0 bytes core/tests/images/DSC_0017.jpg | Bin 1246655 -> 0 bytes core/tests/images/DSC_0019.jpg | Bin 649556 -> 0 bytes core/tests/images/DSC_0067.jpg | Bin 1245526 -> 0 bytes core/tests/images/DSC_0072.jpg | Bin 1014511 -> 0 bytes core/tests/images/P4050088.jpg | Bin 1774906 -> 0 bytes core/tests/selenium/Add_Album.html | 52 -- core/tests/selenium/Add_Comment.html | 52 -- core/tests/selenium/Add_Item.html | 62 -- core/tests/selenium/Login.html | 47 -- core/tests/test.jpg | Bin 6232 -> 0 bytes core/views/admin_advanced_settings.html.php | 34 -- core/views/admin_block_log_entries.html.php | 11 - core/views/admin_block_news.html.php | 11 - core/views/admin_block_photo_stream.html.php | 14 - core/views/admin_block_platform.html.php | 18 - core/views/admin_block_stats.html.php | 12 - core/views/admin_block_welcome.html.php | 20 - core/views/admin_dashboard.html.php | 38 -- core/views/admin_graphics.html.php | 28 - core/views/admin_graphics_gd.html.php | 29 - core/views/admin_graphics_graphicsmagick.html.php | 21 - core/views/admin_graphics_imagemagick.html.php | 21 - core/views/admin_graphics_none.html.php | 7 - core/views/admin_languages.html.php | 15 - core/views/admin_maintenance.html.php | 181 ------ core/views/admin_maintenance_task.html.php | 32 -- core/views/admin_modules.html.php | 32 -- core/views/admin_theme_details.html.php | 6 - core/views/admin_themes.html.php | 89 --- core/views/admin_themes_preview.html.php | 7 - core/views/after_install.html.php | 29 - core/views/after_install_loader.html.php | 7 - core/views/form.html.php | 75 --- core/views/kohana_error_page.php | 118 ---- core/views/kohana_profiler.php | 35 -- core/views/l10n_client.html.php | 31 - core/views/maintenance.html.php | 50 -- core/views/move_browse.html.php | 47 -- core/views/move_tree.html.php | 19 - core/views/permissions_browse.html.php | 56 -- core/views/permissions_form.html.php | 94 --- core/views/quick_pane.html.php | 108 ---- core/views/scaffold.html.php | 169 ------ core/views/simple_uploader.html.php | 249 -------- index.php | 2 +- modules/gallery/config/cookie.php | 49 ++ modules/gallery/config/database.php | 23 + modules/gallery/config/locale.php | 46 ++ modules/gallery/config/routes.php | 31 + modules/gallery/config/sendmail.php | 29 + modules/gallery/config/session.php | 66 +++ modules/gallery/config/upload.php | 36 ++ modules/gallery/controllers/admin.php | 52 ++ .../controllers/admin_advanced_settings.php | 53 ++ modules/gallery/controllers/admin_dashboard.php | 93 +++ modules/gallery/controllers/admin_graphics.php | 63 +++ modules/gallery/controllers/admin_languages.php | 136 +++++ modules/gallery/controllers/admin_maintenance.php | 181 ++++++ modules/gallery/controllers/admin_modules.php | 65 +++ .../gallery/controllers/admin_theme_details.php | 67 +++ modules/gallery/controllers/admin_themes.php | 79 +++ modules/gallery/controllers/after_install.php | 30 + modules/gallery/controllers/albums.php | 229 ++++++++ modules/gallery/controllers/file_proxy.php | 120 ++++ modules/gallery/controllers/items.php | 30 + modules/gallery/controllers/l10n_client.php | 128 +++++ modules/gallery/controllers/maintenance.php | 24 + modules/gallery/controllers/move.php | 64 +++ modules/gallery/controllers/movies.php | 114 ++++ modules/gallery/controllers/permissions.php | 80 +++ modules/gallery/controllers/photos.php | 116 ++++ modules/gallery/controllers/quick.php | 122 ++++ modules/gallery/controllers/rest.php | 183 ++++++ modules/gallery/controllers/scaffold.php | 437 ++++++++++++++ modules/gallery/controllers/simple_uploader.php | 86 +++ modules/gallery/css/debug.css | 28 + modules/gallery/css/l10n_client.css | 185 ++++++ modules/gallery/css/quick.css | 40 ++ modules/gallery/helpers/MY_remote.php | 163 ++++++ modules/gallery/helpers/MY_url.php | 81 +++ modules/gallery/helpers/access.php | 628 +++++++++++++++++++++ modules/gallery/helpers/album.php | 132 +++++ modules/gallery/helpers/batch.php | 40 ++ modules/gallery/helpers/block_manager.php | 67 +++ modules/gallery/helpers/core.php | 52 ++ modules/gallery/helpers/core_block.php | 100 ++++ modules/gallery/helpers/core_event.php | 46 ++ modules/gallery/helpers/core_installer.php | 278 +++++++++ modules/gallery/helpers/core_menu.php | 162 ++++++ modules/gallery/helpers/core_search.php | 24 + modules/gallery/helpers/core_task.php | 166 ++++++ modules/gallery/helpers/core_theme.php | 137 +++++ modules/gallery/helpers/dir.php | 40 ++ modules/gallery/helpers/graphics.php | 387 +++++++++++++ modules/gallery/helpers/item.php | 105 ++++ modules/gallery/helpers/l10n_client.php | 203 +++++++ modules/gallery/helpers/l10n_scanner.php | 154 +++++ modules/gallery/helpers/locale.php | 119 ++++ modules/gallery/helpers/log.php | 108 ++++ modules/gallery/helpers/message.php | 108 ++++ modules/gallery/helpers/model_cache.php | 46 ++ modules/gallery/helpers/module.php | 357 ++++++++++++ modules/gallery/helpers/movie.php | 153 +++++ modules/gallery/helpers/photo.php | 171 ++++++ modules/gallery/helpers/rest.php | 116 ++++ modules/gallery/helpers/site_status.php | 132 +++++ modules/gallery/helpers/task.php | 83 +++ modules/gallery/helpers/theme.php | 61 ++ modules/gallery/helpers/xml.php | 35 ++ modules/gallery/hooks/init_gallery.php | 44 ++ modules/gallery/images/gallery.png | Bin 0 -> 22178 bytes modules/gallery/images/gd.png | Bin 0 -> 5531 bytes modules/gallery/images/graphicsmagick.png | Bin 0 -> 1486 bytes modules/gallery/images/imagemagick.jpg | Bin 0 -> 20337 bytes modules/gallery/js/albums_form_add.js | 12 + modules/gallery/js/fullsize.js | 78 +++ modules/gallery/js/l10n_client.js | 195 +++++++ modules/gallery/js/quick.js | 95 ++++ modules/gallery/libraries/Admin_View.php | 126 +++++ modules/gallery/libraries/Block.php | 30 + modules/gallery/libraries/I18n.php | 410 ++++++++++++++ modules/gallery/libraries/MY_Database.php | 92 +++ modules/gallery/libraries/MY_Forge.php | 59 ++ modules/gallery/libraries/MY_ORM.php | 46 ++ modules/gallery/libraries/MY_Pagination.php | 35 ++ modules/gallery/libraries/MY_View.php | 46 ++ modules/gallery/libraries/Menu.php | 187 ++++++ modules/gallery/libraries/ORM_MPTT.php | 307 ++++++++++ modules/gallery/libraries/Sendmail.php | 97 ++++ modules/gallery/libraries/Task_Definition.php | 50 ++ modules/gallery/libraries/Theme_View.php | 221 ++++++++ modules/gallery/models/access_cache.php | 21 + modules/gallery/models/access_intent.php | 21 + modules/gallery/models/graphics_rule.php | 21 + modules/gallery/models/incoming_translation.php | 21 + modules/gallery/models/item.php | 497 ++++++++++++++++ modules/gallery/models/log.php | 22 + modules/gallery/models/message.php | 21 + modules/gallery/models/module.php | 21 + modules/gallery/models/outgoing_translation.php | 21 + modules/gallery/models/permission.php | 21 + modules/gallery/models/task.php | 46 ++ modules/gallery/models/theme.php | 21 + modules/gallery/models/var.php | 21 + modules/gallery/module.info | 3 + modules/gallery/tests/Access_Helper_Test.php | 323 +++++++++++ modules/gallery/tests/Album_Helper_Test.php | 87 +++ modules/gallery/tests/Albums_Controller_Test.php | 76 +++ modules/gallery/tests/Core_Installer_Test.php | 50 ++ modules/gallery/tests/Database_Test.php | 134 +++++ modules/gallery/tests/Dir_Helper_Test.php | 32 ++ modules/gallery/tests/DrawForm_Test.php | 84 +++ modules/gallery/tests/File_Structure_Test.php | 235 ++++++++ modules/gallery/tests/I18n_Test.php | 108 ++++ modules/gallery/tests/Item_Model_Test.php | 143 +++++ modules/gallery/tests/Menu_Test.php | 32 ++ modules/gallery/tests/Movie_Helper_Test.php | 46 ++ modules/gallery/tests/ORM_MPTT_Test.php | 221 ++++++++ modules/gallery/tests/Photo_Helper_Test.php | 109 ++++ modules/gallery/tests/Photos_Controller_Test.php | 74 +++ modules/gallery/tests/REST_Controller_Test.php | 197 +++++++ modules/gallery/tests/REST_Helper_Test.php | 45 ++ modules/gallery/tests/Sendmail_Test.php | 115 ++++ modules/gallery/tests/Var_Test.php | 49 ++ modules/gallery/tests/images/DSC_0003.jpg | Bin 0 -> 735609 bytes modules/gallery/tests/images/DSC_0005.jpg | Bin 0 -> 687555 bytes modules/gallery/tests/images/DSC_0017.jpg | Bin 0 -> 1246655 bytes modules/gallery/tests/images/DSC_0019.jpg | Bin 0 -> 649556 bytes modules/gallery/tests/images/DSC_0067.jpg | Bin 0 -> 1245526 bytes modules/gallery/tests/images/DSC_0072.jpg | Bin 0 -> 1014511 bytes modules/gallery/tests/images/P4050088.jpg | Bin 0 -> 1774906 bytes modules/gallery/tests/selenium/Add_Album.html | 52 ++ modules/gallery/tests/selenium/Add_Comment.html | 52 ++ modules/gallery/tests/selenium/Add_Item.html | 62 ++ modules/gallery/tests/selenium/Login.html | 47 ++ modules/gallery/tests/test.jpg | Bin 0 -> 6232 bytes .../gallery/views/admin_advanced_settings.html.php | 34 ++ .../gallery/views/admin_block_log_entries.html.php | 11 + modules/gallery/views/admin_block_news.html.php | 11 + .../views/admin_block_photo_stream.html.php | 14 + .../gallery/views/admin_block_platform.html.php | 18 + modules/gallery/views/admin_block_stats.html.php | 12 + modules/gallery/views/admin_block_welcome.html.php | 20 + modules/gallery/views/admin_dashboard.html.php | 38 ++ modules/gallery/views/admin_graphics.html.php | 28 + modules/gallery/views/admin_graphics_gd.html.php | 29 + .../views/admin_graphics_graphicsmagick.html.php | 21 + .../views/admin_graphics_imagemagick.html.php | 21 + modules/gallery/views/admin_graphics_none.html.php | 7 + modules/gallery/views/admin_languages.html.php | 15 + modules/gallery/views/admin_maintenance.html.php | 181 ++++++ .../gallery/views/admin_maintenance_task.html.php | 32 ++ modules/gallery/views/admin_modules.html.php | 32 ++ modules/gallery/views/admin_theme_details.html.php | 6 + modules/gallery/views/admin_themes.html.php | 89 +++ .../gallery/views/admin_themes_preview.html.php | 7 + modules/gallery/views/after_install.html.php | 29 + .../gallery/views/after_install_loader.html.php | 7 + modules/gallery/views/form.html.php | 75 +++ modules/gallery/views/kohana_error_page.php | 118 ++++ modules/gallery/views/kohana_profiler.php | 35 ++ modules/gallery/views/l10n_client.html.php | 31 + modules/gallery/views/maintenance.html.php | 50 ++ modules/gallery/views/move_browse.html.php | 47 ++ modules/gallery/views/move_tree.html.php | 19 + modules/gallery/views/permissions_browse.html.php | 56 ++ modules/gallery/views/permissions_form.html.php | 94 +++ modules/gallery/views/quick_pane.html.php | 108 ++++ modules/gallery/views/scaffold.html.php | 169 ++++++ modules/gallery/views/simple_uploader.html.php | 249 ++++++++ 333 files changed, 14670 insertions(+), 14669 deletions(-) create mode 100644 application/config/config.php delete mode 100644 core/config/config.php delete mode 100644 core/config/cookie.php delete mode 100644 core/config/database.php delete mode 100644 core/config/locale.php delete mode 100644 core/config/routes.php delete mode 100644 core/config/sendmail.php delete mode 100644 core/config/session.php delete mode 100644 core/config/upload.php delete mode 100644 core/controllers/admin.php delete mode 100644 core/controllers/admin_advanced_settings.php delete mode 100644 core/controllers/admin_dashboard.php delete mode 100644 core/controllers/admin_graphics.php delete mode 100644 core/controllers/admin_languages.php delete mode 100644 core/controllers/admin_maintenance.php delete mode 100644 core/controllers/admin_modules.php delete mode 100644 core/controllers/admin_theme_details.php delete mode 100644 core/controllers/admin_themes.php delete mode 100644 core/controllers/after_install.php delete mode 100644 core/controllers/albums.php delete mode 100644 core/controllers/file_proxy.php delete mode 100644 core/controllers/items.php delete mode 100644 core/controllers/l10n_client.php delete mode 100644 core/controllers/maintenance.php delete mode 100644 core/controllers/move.php delete mode 100644 core/controllers/movies.php delete mode 100644 core/controllers/permissions.php delete mode 100644 core/controllers/photos.php delete mode 100644 core/controllers/quick.php delete mode 100644 core/controllers/rest.php delete mode 100644 core/controllers/scaffold.php delete mode 100644 core/controllers/simple_uploader.php delete mode 100644 core/css/debug.css delete mode 100644 core/css/l10n_client.css delete mode 100644 core/css/quick.css delete mode 100644 core/helpers/MY_remote.php delete mode 100644 core/helpers/MY_url.php delete mode 100644 core/helpers/access.php delete mode 100644 core/helpers/album.php delete mode 100644 core/helpers/batch.php delete mode 100644 core/helpers/block_manager.php delete mode 100644 core/helpers/core.php delete mode 100644 core/helpers/core_block.php delete mode 100644 core/helpers/core_event.php delete mode 100644 core/helpers/core_installer.php delete mode 100644 core/helpers/core_menu.php delete mode 100644 core/helpers/core_search.php delete mode 100644 core/helpers/core_task.php delete mode 100644 core/helpers/core_theme.php delete mode 100644 core/helpers/dir.php delete mode 100644 core/helpers/graphics.php delete mode 100644 core/helpers/item.php delete mode 100644 core/helpers/l10n_client.php delete mode 100644 core/helpers/l10n_scanner.php delete mode 100644 core/helpers/locale.php delete mode 100644 core/helpers/log.php delete mode 100644 core/helpers/message.php delete mode 100644 core/helpers/model_cache.php delete mode 100644 core/helpers/module.php delete mode 100644 core/helpers/movie.php delete mode 100644 core/helpers/photo.php delete mode 100644 core/helpers/rest.php delete mode 100644 core/helpers/site_status.php delete mode 100644 core/helpers/task.php delete mode 100644 core/helpers/theme.php delete mode 100644 core/helpers/xml.php delete mode 100644 core/hooks/init_gallery.php delete mode 100644 core/images/gallery.png delete mode 100644 core/images/gd.png delete mode 100644 core/images/graphicsmagick.png delete mode 100644 core/images/imagemagick.jpg delete mode 100644 core/js/albums_form_add.js delete mode 100644 core/js/fullsize.js delete mode 100644 core/js/l10n_client.js delete mode 100644 core/js/quick.js delete mode 100644 core/libraries/Admin_View.php delete mode 100644 core/libraries/Block.php delete mode 100644 core/libraries/I18n.php delete mode 100644 core/libraries/MY_Database.php delete mode 100644 core/libraries/MY_Forge.php delete mode 100644 core/libraries/MY_ORM.php delete mode 100644 core/libraries/MY_Pagination.php delete mode 100644 core/libraries/MY_View.php delete mode 100644 core/libraries/Menu.php delete mode 100644 core/libraries/ORM_MPTT.php delete mode 100644 core/libraries/Sendmail.php delete mode 100644 core/libraries/Task_Definition.php delete mode 100644 core/libraries/Theme_View.php delete mode 100644 core/models/access_cache.php delete mode 100644 core/models/access_intent.php delete mode 100644 core/models/graphics_rule.php delete mode 100644 core/models/incoming_translation.php delete mode 100644 core/models/item.php delete mode 100644 core/models/log.php delete mode 100644 core/models/message.php delete mode 100644 core/models/module.php delete mode 100644 core/models/outgoing_translation.php delete mode 100644 core/models/permission.php delete mode 100644 core/models/task.php delete mode 100644 core/models/theme.php delete mode 100644 core/models/var.php delete mode 100644 core/module.info delete mode 100644 core/tests/Access_Helper_Test.php delete mode 100644 core/tests/Album_Helper_Test.php delete mode 100644 core/tests/Albums_Controller_Test.php delete mode 100644 core/tests/Core_Installer_Test.php delete mode 100644 core/tests/Database_Test.php delete mode 100644 core/tests/Dir_Helper_Test.php delete mode 100644 core/tests/DrawForm_Test.php delete mode 100644 core/tests/File_Structure_Test.php delete mode 100644 core/tests/I18n_Test.php delete mode 100644 core/tests/Item_Model_Test.php delete mode 100644 core/tests/Menu_Test.php delete mode 100644 core/tests/Movie_Helper_Test.php delete mode 100644 core/tests/ORM_MPTT_Test.php delete mode 100644 core/tests/Photo_Helper_Test.php delete mode 100644 core/tests/Photos_Controller_Test.php delete mode 100644 core/tests/REST_Controller_Test.php delete mode 100644 core/tests/REST_Helper_Test.php delete mode 100644 core/tests/Sendmail_Test.php delete mode 100644 core/tests/Var_Test.php delete mode 100644 core/tests/images/DSC_0003.jpg delete mode 100644 core/tests/images/DSC_0005.jpg delete mode 100644 core/tests/images/DSC_0017.jpg delete mode 100644 core/tests/images/DSC_0019.jpg delete mode 100644 core/tests/images/DSC_0067.jpg delete mode 100644 core/tests/images/DSC_0072.jpg delete mode 100644 core/tests/images/P4050088.jpg delete mode 100644 core/tests/selenium/Add_Album.html delete mode 100644 core/tests/selenium/Add_Comment.html delete mode 100644 core/tests/selenium/Add_Item.html delete mode 100644 core/tests/selenium/Login.html delete mode 100644 core/tests/test.jpg delete mode 100644 core/views/admin_advanced_settings.html.php delete mode 100644 core/views/admin_block_log_entries.html.php delete mode 100644 core/views/admin_block_news.html.php delete mode 100644 core/views/admin_block_photo_stream.html.php delete mode 100644 core/views/admin_block_platform.html.php delete mode 100644 core/views/admin_block_stats.html.php delete mode 100644 core/views/admin_block_welcome.html.php delete mode 100644 core/views/admin_dashboard.html.php delete mode 100644 core/views/admin_graphics.html.php delete mode 100644 core/views/admin_graphics_gd.html.php delete mode 100644 core/views/admin_graphics_graphicsmagick.html.php delete mode 100644 core/views/admin_graphics_imagemagick.html.php delete mode 100644 core/views/admin_graphics_none.html.php delete mode 100644 core/views/admin_languages.html.php delete mode 100644 core/views/admin_maintenance.html.php delete mode 100644 core/views/admin_maintenance_task.html.php delete mode 100644 core/views/admin_modules.html.php delete mode 100644 core/views/admin_theme_details.html.php delete mode 100644 core/views/admin_themes.html.php delete mode 100644 core/views/admin_themes_preview.html.php delete mode 100644 core/views/after_install.html.php delete mode 100644 core/views/after_install_loader.html.php delete mode 100644 core/views/form.html.php delete mode 100644 core/views/kohana_error_page.php delete mode 100644 core/views/kohana_profiler.php delete mode 100644 core/views/l10n_client.html.php delete mode 100644 core/views/maintenance.html.php delete mode 100644 core/views/move_browse.html.php delete mode 100644 core/views/move_tree.html.php delete mode 100644 core/views/permissions_browse.html.php delete mode 100644 core/views/permissions_form.html.php delete mode 100644 core/views/quick_pane.html.php delete mode 100644 core/views/scaffold.html.php delete mode 100644 core/views/simple_uploader.html.php create mode 100644 modules/gallery/config/cookie.php create mode 100644 modules/gallery/config/database.php create mode 100644 modules/gallery/config/locale.php create mode 100644 modules/gallery/config/routes.php create mode 100644 modules/gallery/config/sendmail.php create mode 100644 modules/gallery/config/session.php create mode 100644 modules/gallery/config/upload.php create mode 100644 modules/gallery/controllers/admin.php create mode 100644 modules/gallery/controllers/admin_advanced_settings.php create mode 100644 modules/gallery/controllers/admin_dashboard.php create mode 100644 modules/gallery/controllers/admin_graphics.php create mode 100644 modules/gallery/controllers/admin_languages.php create mode 100644 modules/gallery/controllers/admin_maintenance.php create mode 100644 modules/gallery/controllers/admin_modules.php create mode 100644 modules/gallery/controllers/admin_theme_details.php create mode 100644 modules/gallery/controllers/admin_themes.php create mode 100644 modules/gallery/controllers/after_install.php create mode 100644 modules/gallery/controllers/albums.php create mode 100644 modules/gallery/controllers/file_proxy.php create mode 100644 modules/gallery/controllers/items.php create mode 100644 modules/gallery/controllers/l10n_client.php create mode 100644 modules/gallery/controllers/maintenance.php create mode 100644 modules/gallery/controllers/move.php create mode 100644 modules/gallery/controllers/movies.php create mode 100644 modules/gallery/controllers/permissions.php create mode 100644 modules/gallery/controllers/photos.php create mode 100644 modules/gallery/controllers/quick.php create mode 100644 modules/gallery/controllers/rest.php create mode 100644 modules/gallery/controllers/scaffold.php create mode 100644 modules/gallery/controllers/simple_uploader.php create mode 100644 modules/gallery/css/debug.css create mode 100644 modules/gallery/css/l10n_client.css create mode 100644 modules/gallery/css/quick.css create mode 100644 modules/gallery/helpers/MY_remote.php create mode 100644 modules/gallery/helpers/MY_url.php create mode 100644 modules/gallery/helpers/access.php create mode 100644 modules/gallery/helpers/album.php create mode 100644 modules/gallery/helpers/batch.php create mode 100644 modules/gallery/helpers/block_manager.php create mode 100644 modules/gallery/helpers/core.php create mode 100644 modules/gallery/helpers/core_block.php create mode 100644 modules/gallery/helpers/core_event.php create mode 100644 modules/gallery/helpers/core_installer.php create mode 100644 modules/gallery/helpers/core_menu.php create mode 100644 modules/gallery/helpers/core_search.php create mode 100644 modules/gallery/helpers/core_task.php create mode 100644 modules/gallery/helpers/core_theme.php create mode 100644 modules/gallery/helpers/dir.php create mode 100644 modules/gallery/helpers/graphics.php create mode 100644 modules/gallery/helpers/item.php create mode 100644 modules/gallery/helpers/l10n_client.php create mode 100644 modules/gallery/helpers/l10n_scanner.php create mode 100644 modules/gallery/helpers/locale.php create mode 100644 modules/gallery/helpers/log.php create mode 100644 modules/gallery/helpers/message.php create mode 100644 modules/gallery/helpers/model_cache.php create mode 100644 modules/gallery/helpers/module.php create mode 100644 modules/gallery/helpers/movie.php create mode 100644 modules/gallery/helpers/photo.php create mode 100644 modules/gallery/helpers/rest.php create mode 100644 modules/gallery/helpers/site_status.php create mode 100644 modules/gallery/helpers/task.php create mode 100644 modules/gallery/helpers/theme.php create mode 100644 modules/gallery/helpers/xml.php create mode 100644 modules/gallery/hooks/init_gallery.php create mode 100644 modules/gallery/images/gallery.png create mode 100644 modules/gallery/images/gd.png create mode 100644 modules/gallery/images/graphicsmagick.png create mode 100644 modules/gallery/images/imagemagick.jpg create mode 100644 modules/gallery/js/albums_form_add.js create mode 100644 modules/gallery/js/fullsize.js create mode 100644 modules/gallery/js/l10n_client.js create mode 100644 modules/gallery/js/quick.js create mode 100644 modules/gallery/libraries/Admin_View.php create mode 100644 modules/gallery/libraries/Block.php create mode 100644 modules/gallery/libraries/I18n.php create mode 100644 modules/gallery/libraries/MY_Database.php create mode 100644 modules/gallery/libraries/MY_Forge.php create mode 100644 modules/gallery/libraries/MY_ORM.php create mode 100644 modules/gallery/libraries/MY_Pagination.php create mode 100644 modules/gallery/libraries/MY_View.php create mode 100644 modules/gallery/libraries/Menu.php create mode 100644 modules/gallery/libraries/ORM_MPTT.php create mode 100644 modules/gallery/libraries/Sendmail.php create mode 100644 modules/gallery/libraries/Task_Definition.php create mode 100644 modules/gallery/libraries/Theme_View.php create mode 100644 modules/gallery/models/access_cache.php create mode 100644 modules/gallery/models/access_intent.php create mode 100644 modules/gallery/models/graphics_rule.php create mode 100644 modules/gallery/models/incoming_translation.php create mode 100644 modules/gallery/models/item.php create mode 100644 modules/gallery/models/log.php create mode 100644 modules/gallery/models/message.php create mode 100644 modules/gallery/models/module.php create mode 100644 modules/gallery/models/outgoing_translation.php create mode 100644 modules/gallery/models/permission.php create mode 100644 modules/gallery/models/task.php create mode 100644 modules/gallery/models/theme.php create mode 100644 modules/gallery/models/var.php create mode 100644 modules/gallery/module.info create mode 100644 modules/gallery/tests/Access_Helper_Test.php create mode 100644 modules/gallery/tests/Album_Helper_Test.php create mode 100644 modules/gallery/tests/Albums_Controller_Test.php create mode 100644 modules/gallery/tests/Core_Installer_Test.php create mode 100644 modules/gallery/tests/Database_Test.php create mode 100644 modules/gallery/tests/Dir_Helper_Test.php create mode 100644 modules/gallery/tests/DrawForm_Test.php create mode 100644 modules/gallery/tests/File_Structure_Test.php create mode 100644 modules/gallery/tests/I18n_Test.php create mode 100644 modules/gallery/tests/Item_Model_Test.php create mode 100644 modules/gallery/tests/Menu_Test.php create mode 100644 modules/gallery/tests/Movie_Helper_Test.php create mode 100644 modules/gallery/tests/ORM_MPTT_Test.php create mode 100644 modules/gallery/tests/Photo_Helper_Test.php create mode 100644 modules/gallery/tests/Photos_Controller_Test.php create mode 100644 modules/gallery/tests/REST_Controller_Test.php create mode 100644 modules/gallery/tests/REST_Helper_Test.php create mode 100644 modules/gallery/tests/Sendmail_Test.php create mode 100644 modules/gallery/tests/Var_Test.php create mode 100644 modules/gallery/tests/images/DSC_0003.jpg create mode 100644 modules/gallery/tests/images/DSC_0005.jpg create mode 100644 modules/gallery/tests/images/DSC_0017.jpg create mode 100644 modules/gallery/tests/images/DSC_0019.jpg create mode 100644 modules/gallery/tests/images/DSC_0067.jpg create mode 100644 modules/gallery/tests/images/DSC_0072.jpg create mode 100644 modules/gallery/tests/images/P4050088.jpg create mode 100644 modules/gallery/tests/selenium/Add_Album.html create mode 100644 modules/gallery/tests/selenium/Add_Comment.html create mode 100644 modules/gallery/tests/selenium/Add_Item.html create mode 100644 modules/gallery/tests/selenium/Login.html create mode 100644 modules/gallery/tests/test.jpg create mode 100644 modules/gallery/views/admin_advanced_settings.html.php create mode 100644 modules/gallery/views/admin_block_log_entries.html.php create mode 100644 modules/gallery/views/admin_block_news.html.php create mode 100644 modules/gallery/views/admin_block_photo_stream.html.php create mode 100644 modules/gallery/views/admin_block_platform.html.php create mode 100644 modules/gallery/views/admin_block_stats.html.php create mode 100644 modules/gallery/views/admin_block_welcome.html.php create mode 100644 modules/gallery/views/admin_dashboard.html.php create mode 100644 modules/gallery/views/admin_graphics.html.php create mode 100644 modules/gallery/views/admin_graphics_gd.html.php create mode 100644 modules/gallery/views/admin_graphics_graphicsmagick.html.php create mode 100644 modules/gallery/views/admin_graphics_imagemagick.html.php create mode 100644 modules/gallery/views/admin_graphics_none.html.php create mode 100644 modules/gallery/views/admin_languages.html.php create mode 100644 modules/gallery/views/admin_maintenance.html.php create mode 100644 modules/gallery/views/admin_maintenance_task.html.php create mode 100644 modules/gallery/views/admin_modules.html.php create mode 100644 modules/gallery/views/admin_theme_details.html.php create mode 100644 modules/gallery/views/admin_themes.html.php create mode 100644 modules/gallery/views/admin_themes_preview.html.php create mode 100644 modules/gallery/views/after_install.html.php create mode 100644 modules/gallery/views/after_install_loader.html.php create mode 100644 modules/gallery/views/form.html.php create mode 100644 modules/gallery/views/kohana_error_page.php create mode 100644 modules/gallery/views/kohana_profiler.php create mode 100644 modules/gallery/views/l10n_client.html.php create mode 100644 modules/gallery/views/maintenance.html.php create mode 100644 modules/gallery/views/move_browse.html.php create mode 100644 modules/gallery/views/move_tree.html.php create mode 100644 modules/gallery/views/permissions_browse.html.php create mode 100644 modules/gallery/views/permissions_form.html.php create mode 100644 modules/gallery/views/quick_pane.html.php create mode 100644 modules/gallery/views/scaffold.html.php create mode 100644 modules/gallery/views/simple_uploader.html.php diff --git a/application/config/config.php b/application/config/config.php new file mode 100644 index 00000000..92886dbe --- /dev/null +++ b/application/config/config.php @@ -0,0 +1,136 @@ + email address that appears as the from address - * line-length => word wrap length (PHP documentations suggest no larger tha 70 characters - * reply-to => what goes into the reply to header - */ -$config["from"] = "admin@gallery3.com"; -$config["line_length"] = 70; -$config["reply_to"] = "public@gallery3.com"; -$config["header_separator"] = "\n"; diff --git a/core/config/session.php b/core/config/session.php deleted file mode 100644 index 990fa31f..00000000 --- a/core/config/session.php +++ /dev/null @@ -1,66 +0,0 @@ -admin)) { - throw new Exception("@todo UNAUTHORIZED", 401); - } - parent::__construct(); - } - - public function __call($controller_name, $args) { - if (request::method() == "post") { - access::verify_csrf(); - } - - if ($controller_name == "index") { - $controller_name = "dashboard"; - } - $controller_name = "Admin_{$controller_name}_Controller"; - if ($args) { - $method = array_shift($args); - } else { - $method = "index"; - } - - if (!method_exists($controller_name, $method)) { - return kohana::show_404(); - } - - call_user_func_array(array(new $controller_name, $method), $args); - } -} - diff --git a/core/controllers/admin_advanced_settings.php b/core/controllers/admin_advanced_settings.php deleted file mode 100644 index 79bc1183..00000000 --- a/core/controllers/admin_advanced_settings.php +++ /dev/null @@ -1,53 +0,0 @@ -content = new View("admin_advanced_settings.html"); - $view->content->vars = ORM::factory("var") - ->orderby("module_name", "name") - ->find_all(); - print $view; - } - - public function edit($module_name, $var_name) { - $value = module::get_var($module_name, $var_name); - $form = new Forge("admin/advanced_settings/save/$module_name/$var_name", "", "post"); - $group = $form->group("edit_var")->label( - t("Edit %var (%module_name)", - array("module_name" => $module_name, "var" => $var_name))); - $group->input("module_name")->label(t("Module"))->value($module_name)->disabled(1); - $group->input("var_name")->label(t("Setting"))->value($var_name)->disabled(1); - $group->textarea("value")->label(t("Value"))->value($value); - $group->submit("")->value(t("Save")); - print $form; - } - - public function save($module_name, $var_name) { - access::verify_csrf(); - - module::set_var($module_name, $var_name, Input::instance()->post("value")); - message::success( - t("Saved value for %var (%module_name)", - array("var" => $var_name, "module_name" => $module_name))); - - print json_encode(array("result" => "success")); - } -} diff --git a/core/controllers/admin_dashboard.php b/core/controllers/admin_dashboard.php deleted file mode 100644 index d2d2f79b..00000000 --- a/core/controllers/admin_dashboard.php +++ /dev/null @@ -1,93 +0,0 @@ -content = new View("admin_dashboard.html"); - $view->content->blocks = block_manager::get_html("dashboard_center"); - $view->sidebar = "
" . - block_manager::get_html("dashboard_sidebar") . - "
"; - print $view; - } - - public function add_block() { - $form = core_block::get_add_block_form(); - if ($form->validate()) { - list ($module_name, $id) = explode(":", $form->add_block->id->value); - $available = block_manager::get_available(); - - if ($form->add_block->center->value) { - block_manager::add("dashboard_center", $module_name, $id); - message::success( - t("Added %title block to the dashboard center", - array("title" => $available["$module_name:$id"]))); - } else { - block_manager::add("dashboard_sidebar", $module_name, $id); - message::success( - t("Added %title to the dashboard sidebar", - array("title" => $available["$module_name:$id"]))); - } - } - url::redirect("admin/dashboard"); - } - - public function remove_block($id) { - access::verify_csrf(); - $blocks_center = block_manager::get_active("dashboard_center"); - $blocks_sidebar = block_manager::get_active("dashboard_sidebar"); - - if (array_key_exists($id, $blocks_sidebar)) { - $deleted = $blocks_sidebar[$id]; - block_manager::remove("dashboard_sidebar", $id); - } else if (array_key_exists($id, $blocks_center)) { - $deleted = $blocks_center[$id]; - block_manager::remove("dashboard_center", $id); - } - - if (!empty($deleted)) { - $available = block_manager::get_available(); - $title = $available[join(":", $deleted)]; - message::success(t("Removed %title block", array("title" => $title))); - } - - url::redirect("admin"); - } - - public function reorder() { - access::verify_csrf(); - $active_set = array(); - foreach (array("dashboard_sidebar", "dashboard_center") as $location) { - foreach (block_manager::get_active($location) as $id => $info) { - $active_set[$id] = $info; - } - } - - foreach (array("dashboard_sidebar", "dashboard_center") as $location) { - $new_blocks = array(); - foreach ($this->input->get($location, array()) as $id) { - $new_blocks[$id] = $active_set[$id]; - } - block_manager::set_active($location, $new_blocks); - } - - $this->_force_block_adder(); - } -} diff --git a/core/controllers/admin_graphics.php b/core/controllers/admin_graphics.php deleted file mode 100644 index 0b3014f0..00000000 --- a/core/controllers/admin_graphics.php +++ /dev/null @@ -1,63 +0,0 @@ -content = new View("admin_graphics.html"); - $view->content->available = ""; - - $tk = new ArrayObject(graphics::detect_toolkits(), ArrayObject::ARRAY_AS_PROPS); - $active = module::get_var("core", "graphics_toolkit", "none"); - foreach (array("gd", "imagemagick", "graphicsmagick", "none") as $id) { - if ($id == $active) { - $view->content->active = new View("admin_graphics_$id.html"); - $view->content->active->tk = $tk; - $view->content->active->is_active = true; - } else if ($id != "none") { - $v = new View("admin_graphics_$id.html"); - $v->tk = $tk; - $v->is_active = false; - $view->content->available .= $v; - } - } - - print $view; - } - - public function choose($toolkit) { - access::verify_csrf(); - if ($toolkit != module::get_var("core", "graphics_toolkit")) { - module::set_var("core", "graphics_toolkit", $toolkit); - - $toolkit_info = graphics::detect_toolkits(); - if ($toolkit == "graphicsmagick" || $toolkit == "imagemagick") { - module::set_var("core", "graphics_toolkit_path", $toolkit_info[$toolkit]); - } - - site_status::clear("missing_graphics_toolkit"); - message::success(t("Updated Graphics Toolkit")); - log::success("graphics", t("Changed graphics toolkit to: %toolkit", - array("toolkit" => $toolkit))); - } - - url::redirect("admin/graphics"); - } -} - diff --git a/core/controllers/admin_languages.php b/core/controllers/admin_languages.php deleted file mode 100644 index 37d335a3..00000000 --- a/core/controllers/admin_languages.php +++ /dev/null @@ -1,136 +0,0 @@ -content = new View("admin_languages.html"); - $v->content->settings_form = $this->_languages_form(); - if (empty($share_translations_form)) { - $share_translations_form = $this->_share_translations_form(); - } - $v->content->share_translations_form = $share_translations_form; - $this->_outgoing_translations_count(); - print $v; - } - - public function save() { - $form = $this->_languages_form(); - if ($form->validate()) { - module::set_var("core", "default_locale", $form->choose_language->locale->value); - locale::update_installed($form->choose_language->installed_locales->value); - message::success(t("Settings saved")); - } - url::redirect("admin/languages"); - } - - public function share() { - $form = $this->_share_translations_form(); - if (!$form->validate()) { - // Show the page with form errors - return $this->index($form); - } - - if ($form->sharing->share) { - l10n_client::submit_translations(); - message::success(t("Translations submitted")); - } else { - return $this->_save_api_key($form); - } - url::redirect("admin/languages"); - } - - private function _save_api_key($form) { - $new_key = $form->sharing->api_key->value; - if ($new_key && !l10n_client::validate_api_key($new_key)) { - $form->sharing->api_key->add_error("invalid", 1); - $valid = false; - } else { - $valid = true; - } - - if ($valid) { - $old_key = l10n_client::api_key(); - l10n_client::api_key($new_key); - if ($old_key && !$new_key) { - message::success(t("Your API key has been cleared.")); - } else if ($old_key && $new_key && $old_key != $new_key) { - message::success(t("Your API key has been changed.")); - } else if (!$old_key && $new_key) { - message::success(t("Your API key has been saved.")); - } - - log::success(t("core"), t("l10n_client API key changed.")); - url::redirect("admin/languages"); - } else { - // Show the page with form errors - $this->index($form); - } - } - - private function _languages_form() { - $all_locales = locale::available(); - $installed_locales = locale::installed(); - $form = new Forge("admin/languages/save", "", "post", array("id" => "gLanguageSettingsForm")); - $group = $form->group("choose_language") - ->label(t("Language settings")); - $group->dropdown("locale") - ->options($installed_locales) - ->selected(module::get_var("core", "default_locale")) - ->label(t("Default language")) - ->rules('required'); - - $installation_options = array(); - foreach ($all_locales as $code => $display_name) { - $installation_options[$code] = array($display_name, isset($installed_locales->$code)); - } - $group->checklist("installed_locales") - ->label(t("Installed Languages")) - ->options($installation_options) - ->rules("required"); - $group->submit("save")->value(t("Save settings")); - return $form; - } - - private function _outgoing_translations_count() { - return ORM::factory("outgoing_translation")->count_all(); - } - - private function _share_translations_form() { - $form = new Forge("admin/languages/share", "", "post", array("id" => "gShareTranslationsForm")); - $group = $form->group("sharing") - ->label(t("Sharing you own translations with the Gallery community is easy. Please do!")); - $api_key = l10n_client::api_key(); - $server_link = l10n_client::server_api_key_url(); - $group->input("api_key") - ->label(empty($api_key) - ? t("This is a unique key that will allow you to send translations to the remote server. To get your API key go to %server-link.", - array("server-link" => html::anchor($server_link))) - : t("API Key")) - ->value($api_key) - ->error_messages("invalid", t("The API key you provided is invalid.")); - $group->submit("save")->value(t("Save settings")); - if ($api_key && $this->_outgoing_translations_count()) { - // TODO: UI improvement: hide API key / save button when API key is set. - $group->submit("share")->value(t("Submit translations")); - } - return $form; - } -} - diff --git a/core/controllers/admin_maintenance.php b/core/controllers/admin_maintenance.php deleted file mode 100644 index c169de75..00000000 --- a/core/controllers/admin_maintenance.php +++ /dev/null @@ -1,181 +0,0 @@ -query( - "UPDATE {tasks} SET `state` = 'stalled' " . - "WHERE done = 0 " . - "AND state <> 'stalled' " . - "AND unix_timestamp(now()) - updated > 15"); - $stalled_count = $query->count(); - if ($stalled_count) { - log::warning("tasks", - t2("One task is stalled", - "%count tasks are stalled", - $stalled_count), - t('view', - array("url" => url::site("admin/maintenance")))); - } - - $view = new Admin_View("admin.html"); - $view->content = new View("admin_maintenance.html"); - $view->content->task_definitions = task::get_definitions(); - $view->content->running_tasks = ORM::factory("task") - ->where("done", 0)->orderby("updated", "DESC")->find_all(); - $view->content->finished_tasks = ORM::factory("task") - ->where("done", 1)->orderby("updated", "DESC")->find_all(); - print $view; - } - - /** - * Start a new task - * @param string $task_callback - */ - public function start($task_callback) { - access::verify_csrf(); - - $tasks = task::get_definitions(); - $task = task::create($tasks[$task_callback], array()); - $view = new View("admin_maintenance_task.html"); - $view->task = $task; - - log::info("tasks", t("Task %task_name started (task id %task_id)", - array("task_name" => $task->name, "task_id" => $task->id)), - html::anchor(url::site("admin/maintenance"), t("maintenance"))); - print $view; - } - - /** - * Resume a stalled task - * @param string $task_id - */ - public function resume($task_id) { - access::verify_csrf(); - - $task = ORM::factory("task", $task_id); - if (!$task->loaded) { - throw new Exception("@todo MISSING_TASK"); - } - $view = new View("admin_maintenance_task.html"); - $view->task = $task; - - log::info("tasks", t("Task %task_name resumed (task id %task_id)", - array("task_name" => $task->name, "task_id" => $task->id)), - html::anchor(url::site("admin/maintenance"), t("maintenance"))); - print $view; - } - - /** - * Cancel a task. - * @param string $task_id - */ - public function cancel($task_id) { - access::verify_csrf(); - - task::cancel($task_id); - - message::success(t("Task cancelled")); - url::redirect("admin/maintenance"); - } - - public function cancel_running_tasks() { - access::verify_csrf(); - Database::instance()->update( - "tasks", - array("done" => 1, "state" => "cancelled"), - array("done" => 0)); - message::success(t("All running tasks cancelled")); - url::redirect("admin/maintenance"); - } - - /** - * Remove a task. - * @param string $task_id - */ - public function remove($task_id) { - access::verify_csrf(); - - task::remove($task_id); - - message::success(t("Task removed")); - url::redirect("admin/maintenance"); - } - - public function remove_finished_tasks() { - access::verify_csrf(); - Database::instance()->delete("tasks", array("done" => 1)); - message::success(t("All finished tasks removed")); - url::redirect("admin/maintenance"); - } - - /** - * Run a task. This will trigger the task to do a small amount of work, then it will report - * back with status on the task. - * @param string $task_id - */ - public function run($task_id) { - access::verify_csrf(); - - try { - $task = task::run($task_id); - } catch (Exception $e) { - Kohana::log( - "error", - sprintf( - "%s in %s at line %s:\n%s", $e->getMessage(), $e->getFile(), - $e->getLine(), $e->getTraceAsString())); - throw $e; - } - - if ($task->done) { - switch ($task->state) { - case "success": - log::success("tasks", t("Task %task_name completed (task id %task_id)", - array("task_name" => $task->name, "task_id" => $task->id)), - html::anchor(url::site("admin/maintenance"), t("maintenance"))); - message::success(t("Task completed successfully")); - break; - - case "error": - log::error("tasks", t("Task %task_name failed (task id %task_id)", - array("task_name" => $task->name, "task_id" => $task->id)), - html::anchor(url::site("admin/maintenance"), t("maintenance"))); - message::success(t("Task failed")); - break; - } - print json_encode(array("result" => "success", - "task" => array( - "percent_complete" => $task->percent_complete, - "status" => $task->status, - "done" => $task->done), - "location" => url::site("admin/maintenance"))); - - } else { - print json_encode(array("result" => "in_progress", - "task" => array( - "percent_complete" => $task->percent_complete, - "status" => $task->status, - "done" => $task->done))); - } - } -} diff --git a/core/controllers/admin_modules.php b/core/controllers/admin_modules.php deleted file mode 100644 index f7dd909d..00000000 --- a/core/controllers/admin_modules.php +++ /dev/null @@ -1,65 +0,0 @@ -content = new View("admin_modules.html"); - $view->content->available = module::available(); - print $view; - } - - public function save() { - access::verify_csrf(); - - $changes->activate = array(); - $changes->deactivate = array(); - $activated_names = array(); - $deactivated_names = array(); - foreach (module::available() as $module_name => $info) { - if ($info->locked) { - continue; - } - - $desired = $this->input->post($module_name) == 1; - if ($info->active && !$desired && module::is_active($module_name)) { - $changes->deactivate[] = $module_name; - $deactivated_names[] = $info->name; - module::deactivate($module_name); - } else if (!$info->active && $desired && !module::is_active($module_name)) { - $changes->activate[] = $module_name; - $activated_names[] = $info->name; - module::install($module_name); - module::activate($module_name); - } - } - - module::event("module_change", $changes); - - // @todo this type of collation is questionable from a i18n perspective - if ($activated_names) { - message::success(t("Activated: %names", array("names" => join(", ", $activated_names)))); - } - if ($deactivated_names) { - message::success(t("Deactivated: %names", array("names" => join(", ", $deactivated_names)))); - } - url::redirect("admin/modules"); - } -} - diff --git a/core/controllers/admin_theme_details.php b/core/controllers/admin_theme_details.php deleted file mode 100644 index 542ec31c..00000000 --- a/core/controllers/admin_theme_details.php +++ /dev/null @@ -1,67 +0,0 @@ -content = new View("admin_theme_details.html"); - $view->content->form = theme::get_edit_form_admin(); - print $view; - } - - public function save() { - $form = theme::get_edit_form_admin(); - if ($form->validate()) { - module::set_var("core", "page_size", $form->edit_theme->page_size->value); - - $thumb_size = $form->edit_theme->thumb_size->value; - $thumb_dirty = false; - if (module::get_var("core", "thumb_size") != $thumb_size) { - graphics::remove_rule("core", "thumb", "resize"); - graphics::add_rule( - "core", "thumb", "resize", - array("width" => $thumb_size, "height" => $thumb_size, "master" => Image::AUTO), - 100); - module::set_var("core", "thumb_size", $thumb_size); - } - - $resize_size = $form->edit_theme->resize_size->value; - $resize_dirty = false; - if (module::get_var("core", "resize_size") != $resize_size) { - graphics::remove_rule("core", "resize", "resize"); - graphics::add_rule( - "core", "resize", "resize", - array("width" => $resize_size, "height" => $resize_size, "master" => Image::AUTO), - 100); - module::set_var("core", "resize_size", $resize_size); - } - - module::set_var("core", "header_text", $form->edit_theme->header_text->value); - module::set_var("core", "footer_text", $form->edit_theme->footer_text->value); - - message::success(t("Updated theme details")); - url::redirect("admin/theme_details"); - } else { - $view = new Admin_View("admin.html"); - $view->content = $form; - print $view; - } - } -} - diff --git a/core/controllers/admin_themes.php b/core/controllers/admin_themes.php deleted file mode 100644 index 05c134d1..00000000 --- a/core/controllers/admin_themes.php +++ /dev/null @@ -1,79 +0,0 @@ -content = new View("admin_themes.html"); - $view->content->admin = module::get_var("core", "active_admin_theme"); - $view->content->site = module::get_var("core", "active_site_theme"); - $view->content->themes = $this->_get_themes(); - print $view; - } - - private function _get_themes() { - $themes = array(); - foreach (scandir(THEMEPATH) as $theme_name) { - if ($theme_name[0] == ".") { - continue; - } - - $file = THEMEPATH . "$theme_name/theme.info"; - $theme_info = new ArrayObject(parse_ini_file($file), ArrayObject::ARRAY_AS_PROPS); - $themes[$theme_name] = $theme_info; - } - return $themes; - } - - public function preview($type, $theme_name) { - $view = new View("admin_themes_preview.html"); - $theme_name = preg_replace("/[^\w]/", "", $theme_name); - $view->info = new ArrayObject( - parse_ini_file(THEMEPATH . "$theme_name/theme.info"), ArrayObject::ARRAY_AS_PROPS); - $view->theme_name = $theme_name; - $view->type = $type; - if ($type == "admin") { - $view->url = url::site("admin?theme=$theme_name"); - } else { - $view->url = url::site("albums/1?theme=$theme_name"); - } - print $view; - } - - public function choose($type, $theme_name) { - access::verify_csrf(); - - $theme_name = preg_replace("/[^\w]/", "", $theme_name); - $info = new ArrayObject( - parse_ini_file(THEMEPATH . "$theme_name/theme.info"), ArrayObject::ARRAY_AS_PROPS); - - if ($type == "admin" && $info->admin) { - module::set_var("core", "active_admin_theme", $theme_name); - message::success(t("Successfully changed your admin theme to %theme_name", - array("theme_name" => $info->name))); - } else if ($type == "site" && $info->site) { - module::set_var("core", "active_site_theme", $theme_name); - message::success(t("Successfully changed your Gallery theme to %theme_name", - array("theme_name" => $info->name))); - } - - url::redirect("admin/themes"); - } -} - diff --git a/core/controllers/after_install.php b/core/controllers/after_install.php deleted file mode 100644 index f066afe4..00000000 --- a/core/controllers/after_install.php +++ /dev/null @@ -1,30 +0,0 @@ -admin) { - url::redirect("albums/1"); - } - - $v = new View("after_install.html"); - $v->user = user::active(); - print $v; - } -} diff --git a/core/controllers/albums.php b/core/controllers/albums.php deleted file mode 100644 index 5b4d5979..00000000 --- a/core/controllers/albums.php +++ /dev/null @@ -1,229 +0,0 @@ -id != 1) { - access::forbidden(); - } else { - print new Theme_View("login_page.html", "album"); - return; - } - } - - $page_size = module::get_var("core", "page_size", 9); - $show = $this->input->get("show"); - - if ($show) { - $index = $album->get_position($show); - $page = ceil($index / $page_size); - if ($page == 1) { - url::redirect("albums/$album->id"); - } else { - url::redirect("albums/$album->id?page=$page"); - } - } - - $page = $this->input->get("page", "1"); - $children_count = $album->viewable()->children_count(); - $offset = ($page - 1) * $page_size; - $max_pages = max(ceil($children_count / $page_size), 1); - - // Make sure that the page references a valid offset - if ($page < 1) { - url::redirect("albums/$album->id"); - } else if ($page > $max_pages) { - url::redirect("albums/$album->id?page=$max_pages"); - } - - $template = new Theme_View("page.html", "album"); - $template->set_global("page_size", $page_size); - $template->set_global("item", $album); - $template->set_global("children", $album->viewable()->children($page_size, $offset)); - $template->set_global("children_count", $children_count); - $template->set_global("parents", $album->parents()); - $template->content = new View("album.html"); - - // We can't use math in ORM or the query builder, so do this by hand. It's important - // that we do this with math, otherwise concurrent accesses will damage accuracy. - Database::instance()->query( - "UPDATE {items} SET `view_count` = `view_count` + 1 WHERE `id` = $album->id"); - - print $template; - } - - /** - * @see REST_Controller::_create($resource) - */ - public function _create($album) { - access::required("add", $album); - - switch ($this->input->post("type")) { - case "album": - return $this->_create_album($album); - - case "photo": - return $this->_create_photo($album); - - default: - access::forbidden(); - } - } - - private function _create_album($album) { - access::required("add", $album); - - $form = album::get_add_form($album); - if ($form->validate()) { - $new_album = album::create( - $album, - $this->input->post("name"), - $this->input->post("title", $this->input->post("name")), - $this->input->post("description"), - user::active()->id); - - log::success("content", "Created an album", - html::anchor("albums/$new_album->id", "view album")); - message::success(t("Created album %album_title", array("album_title" => $new_album->title))); - - print json_encode( - array("result" => "success", - "location" => url::site("albums/$new_album->id"), - "resource" => url::site("albums/$new_album->id"))); - } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString() . html::script("core/js/albums_form_add.js"))); - } - } - - private function _create_photo($album) { - access::required("add", $album); - - // If we set the content type as JSON, it triggers saving the result as - // a document in the browser (well, in Chrome at least). - // @todo figure out why and fix this. - $form = photo::get_add_form($album); - if ($form->validate()) { - $photo = photo::create( - $album, - $this->input->post("file"), - $_FILES["file"]["name"], - $this->input->post("title", $this->input->post("name")), - $this->input->post("description"), - user::active()->id); - - log::success("content", "Added a photo", html::anchor("photos/$photo->id", "view photo")); - message::success(t("Added photo %photo_title", array("photo_title" => $photo->title))); - - print json_encode( - array("result" => "success", - "resource" => url::site("photos/$photo->id"), - "location" => url::site("photos/$photo->id"))); - } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); - } - } - - /** - * @see REST_Controller::_update($resource) - */ - public function _update($album) { - access::required("edit", $album); - - $form = album::get_edit_form($album); - if ($valid = $form->validate()) { - // Make sure that there's not a conflict - if (Database::instance() - ->from("items") - ->where("parent_id", $album->parent_id) - ->where("id <>", $album->id) - ->where("name", $form->edit_album->dirname->value) - ->count_records()) { - $form->edit_album->dirname->add_error("conflict", 1); - $valid = false; - } - } - - // @todo - // @todo we need to make sure that filename / dirname components can't contain a / - // @todo - - if ($valid) { - $orig = clone $album; - $album->title = $form->edit_album->title->value; - $album->description = $form->edit_album->description->value; - $album->sort_column = $form->edit_album->sort_order->column->value; - $album->sort_order = $form->edit_album->sort_order->direction->value; - $album->rename($form->edit_album->dirname->value); - $album->save(); - - module::event("item_updated", $orig, $album); - - log::success("content", "Updated album", "id\">view"); - message::success(t("Saved album %album_title", array("album_title" => $album->title))); - - print json_encode( - array("result" => "success", - "location" => url::site("albums/$album->id"))); - } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); - } - } - - /** - * @see REST_Controller::_form_add($parameters) - */ - public function _form_add($album_id) { - $album = ORM::factory("item", $album_id); - access::required("add", $album); - - switch ($this->input->get("type")) { - case "album": - print album::get_add_form($album) . - html::script("core/js/albums_form_add.js"); - break; - - case "photo": - print photo::get_add_form($album); - break; - - default: - kohana::show_404(); - } - } - - /** - * @see REST_Controller::_form_add($parameters) - */ - public function _form_edit($album) { - access::required("edit", $album); - - print album::get_edit_form($album); - } -} diff --git a/core/controllers/file_proxy.php b/core/controllers/file_proxy.php deleted file mode 100644 index f3c5f109..00000000 --- a/core/controllers/file_proxy.php +++ /dev/null @@ -1,120 +0,0 @@ -input->server("REQUEST_URI"); - $request_uri = preg_replace("/\?.*/", "", $request_uri); - - // var_uri: http://example.com/gallery3/var/ - $var_uri = url::file("var/"); - - // Make sure that the request is for a file inside var - $offset = strpos($request_uri, $var_uri); - if ($offset === false) { - kohana::show_404(); - } - - $file = substr($request_uri, strlen($var_uri)); - - // Make sure that we don't leave the var dir - if (strpos($file, "..") !== false) { - kohana::show_404(); - } - - // We only handle var/resizes and var/albums - $paths = explode("/", $file); - $type = $paths[0]; - if ($type != "resizes" && $type != "albums" && $type != "thumbs") { - kohana::show_404(); - } - - // If the last element is .album.jpg, pop that off since it's not a real item - if ($paths[count($paths)-1] == ".album.jpg") { - array_pop($paths); - } - if ($paths[count($paths)-1] == "") { - array_pop($paths); - } - - // Find all items that match the level and name, then iterate over those to find a match. - // In most cases we'll get it in one. Note that for the level calculation, we just count the - // size of $paths. $paths includes the type ("thumbs", etc) but it doesn't include the root, - // so it's a wash. - $count = count($paths); - $compare_file = VARPATH . $file; - $item = null; - foreach (ORM::factory("item") - ->where("name", $paths[$count - 1]) - ->where("level", $count) - ->find_all() as $match) { - if ($type == "albums") { - $match_file = $match->file_path(); - } else if ($type == "resizes") { - $match_file = $match->resize_path(); - } else { - $match_file = $match->thumb_path(); - } - if ($match_file == $compare_file) { - $item = $match; - break; - } - } - - if (!$item) { - kohana::show_404(); - } - - // Make sure we have access to the item - if (!access::can("view", $item)) { - kohana::show_404(); - } - - // Make sure we have view_full access to the original - if ($type == "albums" && !access::can("view_full", $item)) { - kohana::show_404(); - } - - // Don't try to load a directory - if ($type == "albums" && $item->is_album()) { - kohana::show_404(); - } - - if (!file_exists($match_file)) { - kohana::show_404(); - } - - // Dump out the image - header("Content-Type: $item->mime_type"); - Kohana::close_buffers(false); - $fd = fopen($match_file, "rb"); - fpassthru($fd); - fclose($fd); - } -} diff --git a/core/controllers/items.php b/core/controllers/items.php deleted file mode 100644 index 13891726..00000000 --- a/core/controllers/items.php +++ /dev/null @@ -1,30 +0,0 @@ -url(array(), true)); - } -} diff --git a/core/controllers/l10n_client.php b/core/controllers/l10n_client.php deleted file mode 100644 index 17520051..00000000 --- a/core/controllers/l10n_client.php +++ /dev/null @@ -1,128 +0,0 @@ -admin or access::forbidden(); - - $input = Input::instance(); - $message = $input->post("l10n-message-source"); - $translation = $input->post("l10n-edit-target"); - $key = I18n::get_message_key($message); - $locale = I18n::instance()->locale(); - - $entry = ORM::factory("outgoing_translation") - ->where(array("key" => $key, - "locale" => $locale)) - ->find(); - - if (!$entry->loaded) { - $entry->key = $key; - $entry->locale = $locale; - $entry->message = serialize($message); - $entry->base_revision = null; - } - - $entry->translation = serialize($translation); - - $entry_from_incoming = ORM::factory("incoming_translation") - ->where(array("key" => $key, - "locale" => $locale)) - ->find(); - - if (!$entry_from_incoming->loaded) { - $entry->base_revision = $entry_from_incoming->revision; - } - - $entry->save(); - - print json_encode(new stdClass()); - } - - public function toggle_l10n_mode() { - access::verify_csrf(); - - $session = Session::instance(); - $session->set("l10n_mode", - !$session->get("l10n_mode", false)); - - url::redirect("albums/1"); - } - - private static function _l10n_client_form() { - $form = new Forge("l10n_client/save", "", "post", array("id" => "gL10nClientSaveForm")); - $group = $form->group("l10n_message"); - $group->hidden("l10n-message-source")->value(""); - $group->textarea("l10n-edit-target"); - $group->submit("l10n-edit-save")->value(t("Save translation")); - // TODO(andy_st): Avoiding multiple submit buttons for now (hassle with jQuery form plugin). - // $group->submit("l10n-edit-copy")->value(t("Copy source")); - // $group->submit("l10n-edit-clear")->value(t("Clear")); - - return $form; - } - - private static function _l10n_client_search_form() { - $form = new Forge("l10n_client/search", "", "post", array("id" => "gL10nSearchForm")); - $group = $form->group("l10n_search"); - $group->input("l10n-search")->id("gL10nSearch"); - $group->submit("l10n-search-filter-clear")->value(t("X")); - - return $form; - } - - public static function l10n_form() { - $calls = I18n::instance()->call_log(); - - if ($calls) { - $string_list = array(); - foreach ($calls as $call) { - list ($message, $options) = $call; - // Note: Don't interpolate placeholders for the actual translation input field. - // TODO: Use $options to generate a preview. - if (is_array($message)) { - // TODO: Handle plural forms. - // Translate each message. If it has a plural form, get - // the current locale's plural rules and all plural translations. - continue; - } - $source = $message; - $translation = ''; - $options_for_raw_translation = array(); - if (isset($options['count'])) { - $options_for_raw_translation['count'] = $options['count']; - } - if (I18n::instance()->has_translation($message, $options_for_raw_translation)) { - $translation = I18n::instance()->translate($message, $options_for_raw_translation); - } - $string_list[] = array('source' => $source, - 'translation' => $translation); - } - - $v = new View('l10n_client.html'); - $v->string_list = $string_list; - $v->l10n_form = self::_l10n_client_form(); - $v->l10n_search_form = self::_l10n_client_search_form(); - return $v; - } - - return ''; - } -} diff --git a/core/controllers/maintenance.php b/core/controllers/maintenance.php deleted file mode 100644 index b5f39bed..00000000 --- a/core/controllers/maintenance.php +++ /dev/null @@ -1,24 +0,0 @@ -source = $source; - $view->tree = $this->_get_tree_html($source, ORM::factory("item", 1)); - print $view; - } - - public function save($source_id) { - access::verify_csrf(); - $source = ORM::factory("item", $source_id); - $target = ORM::factory("item", $this->input->post("target_id")); - - item::move($source, $target); - - print json_encode( - array("result" => "success", - "location" => url::site("albums/{$target->id}"))); - } - - public function show_sub_tree($source_id, $target_id) { - $source = ORM::factory("item", $source_id); - $target = ORM::factory("item", $target_id); - access::required("edit", $source); - access::required("view", $target); - - print $this->_get_tree_html($source, $target); - } - - private function _get_tree_html($source, $target) { - $view = new View("move_tree.html"); - $view->source = $source; - $view->parent = $target; - $view->children = ORM::factory("item") - ->viewable() - ->where("type", "album") - ->where("parent_id", $target->id) - ->find_all(); - return $view; - } - -} diff --git a/core/controllers/movies.php b/core/controllers/movies.php deleted file mode 100644 index 55bbb0e5..00000000 --- a/core/controllers/movies.php +++ /dev/null @@ -1,114 +0,0 @@ -viewable() - ->where("parent_id", $photo->parent_id) - ->where("id >", $photo->id) - ->orderby("id", "ASC") - ->find(); - $previous_item = ORM::factory("item") - ->viewable() - ->where("parent_id", $photo->parent_id) - ->where("id <", $photo->id) - ->orderby("id", "DESC") - ->find(); - $position = ORM::factory("item") - ->viewable() - ->where("parent_id", $photo->parent_id) - ->where("id <=", $photo->id) - ->count_all(); - - $template = new Theme_View("page.html", "photo"); - $template->set_global("item", $photo); - $template->set_global("children", array()); - $template->set_global("children_count", $photo->children_count()); - $template->set_global("parents", $photo->parents()); - $template->set_global("next_item", $next_item->loaded ? $next_item : null); - $template->set_global("previous_item", $previous_item->loaded ? $previous_item : null); - $template->set_global("sibling_count", $photo->parent()->children_count()); - $template->set_global("position", $position); - - $template->content = new View("movie.html"); - - $photo->view_count++; - $photo->save(); - - print $template; - } - - /** - * @see REST_Controller::_update($resource) - */ - public function _update($photo) { - access::required("edit", $photo); - - $form = photo::get_edit_form($photo); - if ($valid = $form->validate()) { - // Make sure that there's not a conflict - if (Database::instance() - ->from("items") - ->where("parent_id", $photo->parent_id) - ->where("id <>", $photo->id) - ->where("name", $form->edit_photo->filename->value) - ->count_records()) { - $form->edit_photo->filename->add_error("conflict", 1); - $valid = false; - } - } - - if ($valid) { - $orig = clone $photo; - $photo->title = $form->edit_photo->title->value; - $photo->description = $form->edit_photo->description->value; - $photo->rename($form->edit_photo->filename->value); - $photo->save(); - - module::event("item_updated", $orig, $photo); - - log::success("content", "Updated photo", "id\">view"); - message::success(t("Saved photo %photo_title", array("photo_title" => $photo->title))); - - print json_encode( - array("result" => "success", - "location" => url::site("photos/$photo->id"))); - } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); - } - } - - /** - * @see REST_Controller::_form_edit($resource) - */ - public function _form_edit($photo) { - access::required("edit", $photo); - print photo::get_edit_form($photo); - } -} diff --git a/core/controllers/permissions.php b/core/controllers/permissions.php deleted file mode 100644 index b0cee303..00000000 --- a/core/controllers/permissions.php +++ /dev/null @@ -1,80 +0,0 @@ -is_album()) { - access::forbidden(); - } - - $view = new View("permissions_browse.html"); - $view->htaccess_works = access::htaccess_works(); - $view->item = $item; - $view->parents = $item->parents(); - $view->form = $this->_get_form($item); - - print $view; - } - - function form($id) { - $item = ORM::factory("item", $id); - access::required("edit", $item); - - if (!$item->is_album()) { - access::forbidden(); - } - - print $this->_get_form($item); - } - - function change($command, $group_id, $perm_id, $item_id) { - access::verify_csrf(); - $group = ORM::factory("group", $group_id); - $perm = ORM::factory("permission", $perm_id); - $item = ORM::factory("item", $item_id); - access::required("edit", $item); - - if ($group->loaded && $perm->loaded && $item->loaded) { - switch($command) { - case "allow": - access::allow($group, $perm->name, $item); - break; - - case "deny": - access::deny($group, $perm->name, $item); - break; - - case "reset": - access::reset($group, $perm->name, $item); - break; - } - } - } - - function _get_form($item) { - $view = new View("permissions_form.html"); - $view->item = $item; - $view->groups = ORM::factory("group")->find_all(); - $view->permissions = ORM::factory("permission")->find_all(); - return $view; - } -} diff --git a/core/controllers/photos.php b/core/controllers/photos.php deleted file mode 100644 index 5d4040cf..00000000 --- a/core/controllers/photos.php +++ /dev/null @@ -1,116 +0,0 @@ -viewable() - ->where("parent_id", $photo->parent_id) - ->where("id >", $photo->id) - ->orderby("id", "ASC") - ->find(); - $previous_item = ORM::factory("item") - ->viewable() - ->where("parent_id", $photo->parent_id) - ->where("id <", $photo->id) - ->orderby("id", "DESC") - ->find(); - $position = ORM::factory("item") - ->viewable() - ->where("parent_id", $photo->parent_id) - ->where("id <=", $photo->id) - ->count_all(); - - $template = new Theme_View("page.html", "photo"); - $template->set_global("item", $photo); - $template->set_global("children", array()); - $template->set_global("children_count", $photo->children_count()); - $template->set_global("parents", $photo->parents()); - $template->set_global("next_item", $next_item->loaded ? $next_item : null); - $template->set_global("previous_item", $previous_item->loaded ? $previous_item : null); - $template->set_global("sibling_count", $photo->parent()->children_count()); - $template->set_global("position", $position); - - $template->content = new View("photo.html"); - - $photo->view_count++; - $photo->save(); - - print $template; - } - - /** - * @see REST_Controller::_update($resource) - */ - public function _update($photo) { - access::required("edit", $photo); - - $form = photo::get_edit_form($photo); - if ($valid = $form->validate()) { - if ($form->edit_photo->filename->value != $photo->name) { - // Make sure that there's not a conflict - if (Database::instance() - ->from("items") - ->where("parent_id", $photo->parent_id) - ->where("id <>", $photo->id) - ->where("name", $form->edit_photo->filename->value) - ->count_records()) { - $form->edit_photo->filename->add_error("conflict", 1); - $valid = false; - } - } - } - - if ($valid) { - $orig = clone $photo; - $photo->title = $form->edit_photo->title->value; - $photo->description = $form->edit_photo->description->value; - $photo->rename($form->edit_photo->filename->value); - $photo->save(); - - module::event("item_updated", $orig, $photo); - - log::success("content", "Updated photo", "id\">view"); - message::success(t("Saved photo %photo_title", array("photo_title" => $photo->title))); - - print json_encode( - array("result" => "success", - "location" => url::site("photos/$photo->id"))); - } else { - print json_encode( - array("result" => "error", - "form" => $form->__toString())); - } - } - - /** - * @see REST_Controller::_form_edit($resource) - */ - public function _form_edit($photo) { - access::required("edit", $photo); - print photo::get_edit_form($photo); - } -} diff --git a/core/controllers/quick.php b/core/controllers/quick.php deleted file mode 100644 index 643dce30..00000000 --- a/core/controllers/quick.php +++ /dev/null @@ -1,122 +0,0 @@ -loaded) { - return ""; - } - - $view = new View("quick_pane.html"); - $view->item = $item; - $view->page_type = Input::instance()->get("page_type"); - print $view; - } - - public function rotate($id, $dir) { - access::verify_csrf(); - $item = ORM::factory("item", $id); - if (!$item->loaded) { - return ""; - } - - $degrees = 0; - switch($dir) { - case "ccw": - $degrees = -90; - break; - - case "cw": - $degrees = 90; - break; - } - - if ($degrees) { - graphics::rotate($item->file_path(), $item->file_path(), array("degrees" => $degrees)); - - list($item->width, $item->height) = getimagesize($item->file_path()); - $item->resize_dirty= 1; - $item->thumb_dirty= 1; - $item->save(); - - graphics::generate($item); - - $parent = $item->parent(); - if ($parent->album_cover_item_id == $item->id) { - copy($item->thumb_path(), $parent->thumb_path()); - $parent->thumb_width = $item->thumb_width; - $parent->thumb_height = $item->thumb_height; - $parent->save(); - } - } - - if (Input::instance()->get("page_type") == "album") { - print json_encode( - array("src" => $item->thumb_url() . "?rnd=" . rand(), - "width" => $item->thumb_width, - "height" => $item->thumb_height)); - } else { - print json_encode( - array("src" => $item->resize_url() . "?rnd=" . rand(), - "width" => $item->resize_width, - "height" => $item->resize_height)); - } - } - - public function make_album_cover($id) { - access::verify_csrf(); - item::make_album_cover(ORM::factory("item", $id)); - - print json_encode(array("result" => "success")); - } - - public function delete($id) { - access::verify_csrf(); - $item = ORM::factory("item", $id); - access::required("edit", $item); - - if ($item->is_album()) { - $msg = t("Deleted album %title", array("title" => $item->title)); - } else { - $msg = t("Deleted photo %title", array("title" => $item->title)); - } - - $item->delete(); - message::success($msg); - - if (Input::instance()->get("page_type") == "album") { - print json_encode(array("result" => "success", "reload" => 1)); - } else { - print json_encode(array("result" => "success", - "location" => url::site("albums/$parent->id"))); - } - } - - public function form_edit($id) { - $item = ORM::factory("item", $id); - access::required("edit", $item); - if ($item->is_album()) { - $form = album::get_edit_form($item); - } else { - $form = photo::get_edit_form($item); - } - print $form; - } -} diff --git a/core/controllers/rest.php b/core/controllers/rest.php deleted file mode 100644 index 11a6bbac..00000000 --- a/core/controllers/rest.php +++ /dev/null @@ -1,183 +0,0 @@ -resource_type == null) { - throw new Exception("@todo ERROR_MISSING_RESOURCE_TYPE"); - } - parent::__construct(); - } - - /** - * Handle dispatching for all REST controllers. - */ - public function __call($function, $args) { - // If no parameter was provided after the controller name (eg "/albums") then $function will - // be set to "index". Otherwise, $function is the first parameter, and $args are all - // subsequent parameters. - $request_method = rest::request_method(); - if ($function == "index" && $request_method == "get") { - return $this->_index(); - } - - $resource = ORM::factory($this->resource_type, (int)$function); - if (!$resource->loaded && $request_method != "post") { - return Kohana::show_404(); - } - - if ($request_method != "get") { - access::verify_csrf(); - } - - switch ($request_method) { - case "get": - return $this->_show($resource); - - case "put": - return $this->_update($resource); - - case "delete": - return $this->_delete($resource); - - case "post": - return $this->_create($resource); - } - } - - /* We're editing an existing item, load it from the database. */ - public function form_edit($resource_id) { - if ($this->resource_type == null) { - throw new Exception("@todo ERROR_MISSING_RESOURCE_TYPE"); - } - - // @todo this needs security checks - $resource = ORM::factory($this->resource_type, $resource_id); - if (!$resource->loaded) { - return Kohana::show_404(); - } - - return $this->_form_edit($resource); - } - - /* We're adding a new item, pass along any additional parameters. */ - public function form_add($parameters) { - return $this->_form_add($parameters); - } - - /** - * Perform a GET request on the controller root - * (e.g. http://www.example.com/gallery3/comments) - */ - public function _index() { - throw new Exception("@todo _create NOT IMPLEMENTED"); - } - - /** - * Perform a POST request on this resource - * @param ORM $resource the instance of this resource type - */ - public function _create($resource) { - throw new Exception("@todo _create NOT IMPLEMENTED"); - } - - /** - * Perform a GET request on this resource - * @param ORM $resource the instance of this resource type - */ - public function _show($resource) { - throw new Exception("@todo _show NOT IMPLEMENTED"); - } - - /** - * Perform a PUT request on this resource - * @param ORM $resource the instance of this resource type - */ - public function _update($resource) { - throw new Exception("@todo _update NOT IMPLEMENTED"); - } - - /** - * Perform a DELETE request on this resource - * @param ORM $resource the instance of this resource type - */ - public function _delete($resource) { - throw new Exception("@todo _delete NOT IMPLEMENTED"); - } - - /** - * Present a form for adding a new resource - * @param string part of the URI after the controller name - */ - public function _form_add($parameter) { - throw new Exception("@todo _form_add NOT IMPLEMENTED"); - } - - /** - * Present a form for editing an existing resource - * @param ORM $resource the resource container for instances of this resource type - */ - public function _form_edit($resource) { - throw new Exception("@todo _form_edit NOT IMPLEMENTED"); - } -} diff --git a/core/controllers/scaffold.php b/core/controllers/scaffold.php deleted file mode 100644 index f0063725..00000000 --- a/core/controllers/scaffold.php +++ /dev/null @@ -1,437 +0,0 @@ -template->album_count = ORM::factory("item")->where("type", "album")->count_all(); - $this->template->photo_count = ORM::factory("item")->where("type", "photo")->count_all(); - $this->template->album_tree = $this->_load_album_tree(); - $this->template->add_photo_html = $this->_get_add_photo_html(); - } catch (Exception $e) { - $this->template->album_count = 0; - $this->template->photo_count = 0; - $this->template->deepest_photo = null; - $this->template->album_tree = array(); - $this->template->add_photo_html = ""; - } - - $this->_load_comment_info(); - $this->_load_tag_info(); - - restore_error_handler(); - - if (!empty($session) && $session->get("profiler", false)) { - $profiler = new Profiler(); - $profiler->render(); - } - } - - - function add_photos() { - $path = trim($this->input->post("path")); - $parent_id = (int)$this->input->post("parent_id"); - $parent = ORM::factory("item", $parent_id); - if (!$parent->loaded) { - throw new Exception("@todo BAD_ALBUM"); - } - - batch::start(); - cookie::set("add_photos_path", $path); - $photo_count = 0; - foreach (glob("$path/*.[Jj][Pp][Gg]") as $file) { - set_time_limit(30); - photo::create($parent, $file, basename($file), basename($file)); - $photo_count++; - } - batch::stop(); - - if ($photo_count > 0) { - log::success("content", "(scaffold) Added $photo_count photos", - html::anchor("albums/$parent_id", "View album")); - } - - url::redirect("scaffold"); - } - - function add_albums_and_photos($count, $desired_type=null) { - srand(time()); - $parents = ORM::factory("item")->where("type", "album")->find_all()->as_array(); - $owner_id = user::active()->id; - - $test_images = glob(APPPATH . "tests/images/*.[Jj][Pp][Gg]"); - - batch::start(); - $album_count = $photo_count = 0; - for ($i = 0; $i < $count; $i++) { - set_time_limit(30); - - $parent = $parents[array_rand($parents)]; - $parent->reload(); - $type = $desired_type; - if (!$type) { - $type = rand(0, 10) ? "photo" : "album"; - } - if ($type == "album") { - $thumb_size = module::get_var("core", "thumb_size"); - $parents[] = album::create( - $parent, "rnd_" . rand(), "Rnd $i", "random album $i", $owner_id) - ->save(); - $album_count++; - } else { - $photo_index = rand(0, count($test_images) - 1); - photo::create($parent, $test_images[$photo_index], basename($test_images[$photo_index]), - "rnd_" . rand(), "sample thumb", $owner_id); - $photo_count++; - } - } - batch::stop(); - - if ($photo_count > 0) { - log::success("content", "(scaffold) Added $photo_count photos"); - } - - if ($album_count > 0) { - log::success("content", "(scaffold) Added $album_count albums"); - } - url::redirect("scaffold"); - } - - function random_phrase($count) { - static $words; - if (empty($words)) { - $sample_text = "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium - laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi - architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas - sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione - voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, - amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt, ut - labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis - nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi - consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam - nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla - pariatur? At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis - praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi - sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt - mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et - expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque - nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas - assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis - debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et - molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut - reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores - repellat."; - $words = preg_split('/\s+/', $sample_text); - } - - $chosen = array(); - for ($i = 0; $i < $count; $i++) { - $chosen[] = $words[array_rand($words)]; - } - - return implode(' ', $chosen); - } - - function add_comments($count) { - srand(time()); - $photos = ORM::factory("item")->where("type", "photo")->find_all()->as_array(); - $users = ORM::factory("user")->find_all()->as_array(); - - if (empty($photos)) { - url::redirect("scaffold"); - } - - if (module::is_active("akismet")) { - akismet::$test_mode = 1; - } - for ($i = 0; $i < $count; $i++) { - $photo = $photos[array_rand($photos)]; - $author = $users[array_rand($users)]; - $guest_name = ucfirst($this->random_phrase(rand(1, 3))); - $guest_email = sprintf("%s@%s.com", $this->random_phrase(1), $this->random_phrase(1)); - $guest_url = sprintf("http://www.%s.com", $this->random_phrase(1)); - comment::create($photo, $author, $this->random_phrase(rand(8, 500)), - $guest_name, $guest_email, $guest_url); - } - - url::redirect("scaffold"); - } - - function add_tags($count) { - $items = ORM::factory("item")->find_all()->as_array(); - - if (!empty($items)) { - $tags = $this->_generateTags($count); - - while ($count-- > 0) { - $tag_name = $tags[array_rand($tags)]; - $item = $items[array_rand($items)]; - - tag::add($item, $tag_name); - } - } - - url::redirect("scaffold"); - } - - private function _generateTags($number){ - // Words from lorem2.com - $words = explode( - " ", - "Lorem ipsum dolor sit amet consectetuer adipiscing elit Donec odio Quisque volutpat " . - "mattis eros Nullam malesuada erat ut turpis Suspendisse urna nibh viverra non " . - "semper suscipit posuere a pede Donec nec justo eget felis facilisis " . - "fermentum Aliquam porttitor mauris sit amet orci Aenean dignissim pellentesque " . - "felis Morbi in sem quis dui placerat ornare Pellentesque odio nisi euismod in " . - "pharetra a ultricies in diam Sed arcu Cras consequat Praesent dapibus neque " . - "id cursus faucibus tortor neque egestas augue eu vulputate magna eros eu " . - "erat Aliquam erat volutpat Nam dui mi tincidunt quis accumsan porttitor " . - "facilisis luctus metus Phasellus ultrices nulla quis nibh Quisque a " . - "lectus Donec consectetuer ligula vulputate sem tristique cursus Nam nulla quam " . - "gravida non commodo a sodales sit amet nisi Pellentesque fermentum " . - "dolor Aliquam quam lectus facilisis auctor ultrices ut elementum vulputate " . - "nunc Sed adipiscing ornare risus Morbi est est blandit sit amet sagittis vel " . - "euismod vel velit Pellentesque egestas sem Suspendisse commodo ullamcorper " . - "magna"); - - while ($number--) { - $results[] = $words[array_rand($words, 1)]; - } - return $results; - } - - function _error_handler($x) { - } - - private function _load_comment_info() { - if (class_exists("Comment_Model")) { - $this->template->comment_count = ORM::factory("comment")->count_all(); - } else { - $this->template->comment_count = 0; - } - } - - private function _load_tag_info() { - if (class_exists("Tag_Model")) { - $this->template->tag_count = ORM::factory("tag")->count_all(); - $this->template->most_tagged = Database::instance() - ->select("item_id AS id", "COUNT(tag_id) AS count") - ->from("items_tags") - ->groupby("item_id") - ->orderby("count", "DESC") - ->limit(1) - ->get() - ->current(); - } else { - $this->template->tag_count = 0; - $this->template->most_tagged = 0; - } - } - - function install($module_name, $redirect=true) { - $to_install = array(); - if ($module_name == "*") { - foreach (module::available() as $module_name => $info) { - if (empty($info->installed)) { - $to_install[] = $module_name; - } - } - } else { - $to_install[] = $module_name; - } - - foreach ($to_install as $module_name) { - if ($module_name != "core") { - require_once(DOCROOT . "modules/${module_name}/helpers/${module_name}_installer.php"); - } - module::install($module_name); - } - - if ($redirect) { - url::redirect("scaffold"); - } - } - - - public function package() { - $this->auto_render = false; - $db = Database::instance(); - - // Drop all tables - foreach ($db->list_tables() as $table) { - $db->query("DROP TABLE IF EXISTS `$table`"); - } - - // Clean out data - dir::unlink(VARPATH . "uploads"); - dir::unlink(VARPATH . "albums"); - dir::unlink(VARPATH . "resizes"); - dir::unlink(VARPATH . "thumbs"); - dir::unlink(VARPATH . "modules"); - dir::unlink(VARPATH . "tmp"); - - $db->clear_cache(); - module::$modules = array(); - module::$active = array(); - - // Use a known random seed so that subsequent packaging runs will reuse the same random - // numbers, keeping our install.sql file more stable. - srand(0); - - try { - core_installer::install(true); - module::load_modules(); - - foreach (array("user", "comment", "organize", "info", "rss", - "search", "slideshow", "tag") as $module_name) { - module::install($module_name); - module::activate($module_name); - } - } catch (Exception $e) { - Kohana::log("error", $e->getTraceAsString()); - print $e->getTrace(); - throw $e; - } - - url::redirect("scaffold/dump_database"); - } - - public function dump_database() { - $this->auto_render = false; - - // We now have a clean install with just the packages that we want. Make sure that the - // database is clean too. - $db = Database::instance(); - $db->query("TRUNCATE {sessions}"); - $db->query("TRUNCATE {logs}"); - $db->query("DELETE FROM {vars} WHERE `module_name` = 'core' AND `name` = '_cache'"); - $db->update("users", array("password" => ""), array("id" => 1)); - $db->update("users", array("password" => ""), array("id" => 2)); - - $dbconfig = Kohana::config('database.default'); - $conn = $dbconfig["connection"]; - $pass = $conn["pass"] ? "-p{$conn['pass']}" : ""; - $sql_file = DOCROOT . "installer/install.sql"; - if (!is_writable($sql_file)) { - print "$sql_file is not writeable"; - return; - } - $command = "mysqldump --compact --add-drop-table -h{$conn['host']} " . - "-u{$conn['user']} $pass {$conn['database']} > $sql_file"; - exec($command, $output, $status); - if ($status) { - print "
";
-      print "$command\n";
-      print "Failed to dump database\n";
-      print implode("\n", $output);
-      return;
-    }
-
-    // Post-process the sql file
-    $buf = "";
-    $root_timestamp = ORM::factory("item", 1)->created;
-    foreach (file($sql_file) as $line) {
-      // Prefix tables
-      $line = preg_replace(
-        "/(CREATE TABLE|IF EXISTS|INSERT INTO) `{$dbconfig['table_prefix']}(\w+)`/", "\\1 {\\2}",
-        $line);
-
-      // Normalize dates
-      $line = preg_replace("/,$root_timestamp,/", ",UNIX_TIMESTAMP(),", $line);
-      $buf .= $line;
-    }
-    $fd = fopen($sql_file, "wb");
-    fwrite($fd, $buf);
-    fclose($fd);
-
-    url::redirect("scaffold/dump_var");
-  }
-
-  public function dump_var() {
-    $this->auto_render = false;
-
-    $objects = new RecursiveIteratorIterator(
-      new RecursiveDirectoryIterator(VARPATH),
-      RecursiveIteratorIterator::SELF_FIRST);
-
-    $var_file = DOCROOT . "installer/init_var.php";
-    if (!is_writable($var_file)) {
-      print "$var_file is not writeable";
-      return;
-    }
-
-    $paths = array();
-    foreach($objects as $name => $file){
-      if ($file->getBasename() == "database.php") {
-        continue;
-      } else if (basename($file->getPath()) == "logs") {
-        continue;
-      }
-
-      if ($file->isDir()) {
-        $paths[] = "VARPATH . \"" . substr($name, strlen(VARPATH)) . "\"";
-      } else {
-        // @todo: serialize non-directories
-        print "Unknown file: $name";
-        return;
-      }
-    }
-    // Sort the paths so that the var file is stable
-    sort($paths);
-
-    $fd = fopen($var_file, "w");
-    fwrite($fd, "\n");
-    fwrite($fd, "where("type", "album")->find_all() as $album) {
-      if ($album->parent_id) {
-        $tree[$album->parent_id]->children[] = $album->id;
-      }
-      $tree[$album->id]->album = $album;
-      $tree[$album->id]->children = array();
-    }
-
-    return $tree;
-  }
-
-  public function form($arg1, $arg2) {
-    if ($arg1 == "add" && $arg2 == "photos") {
-      print $this->_get_add_photo_html();
-    }
-    $this->auto_render = false;
-  }
-
-  public function _get_add_photo_html($parent_id=1) {
-    $parent = ORM::factory("item", $parent_id);
-    return photo::get_add_form($parent);
-  }
-}
diff --git a/core/controllers/simple_uploader.php b/core/controllers/simple_uploader.php
deleted file mode 100644
index bdf9582f..00000000
--- a/core/controllers/simple_uploader.php
+++ /dev/null
@@ -1,86 +0,0 @@
-item = $item;
-    print $v;
-  }
-
-  public function start() {
-    batch::start();
-  }
-
-  public function add_photo($id) {
-    $album = ORM::factory("item", $id);
-    access::required("add", $album);
-    access::verify_csrf();
-
-    $file_validation = new Validation($_FILES);
-    $file_validation->add_rules("Filedata", "upload::valid", "upload::type[gif,jpg,png,flv,mp4]");
-    if ($file_validation->validate()) {
-
-      // SimpleUploader.swf does not yet call /start directly, so simulate it here for now.
-      if (!batch::in_progress()) {
-        batch::start();
-      }
-
-      $temp_filename = upload::save("Filedata");
-      try {
-        $name = substr(basename($temp_filename), 10);  // Skip unique identifier Kohana adds
-        $title = $this->convert_filename_to_title($name);
-        $path_info = pathinfo($temp_filename);
-        if (array_key_exists("extension", $path_info) &&
-            in_array(strtolower($path_info["extension"]), array("flv", "mp4"))) {
-          $movie = movie::create($album, $temp_filename, $name, $title);
-          log::success("content", t("Added a movie"),
-                       html::anchor("movies/$movie->id", t("view movie")));
-        } else {
-          $photo = photo::create($album, $temp_filename, $name, $title);
-          log::success("content", t("Added a photo"),
-                       html::anchor("photos/$photo->id", t("view photo")));
-        }
-      } catch (Exception $e) {
-        unlink($temp_filename);
-        throw $e;
-      }
-      unlink($temp_filename);
-    }
-    print "File Received";
-  }
-
-  /**
-   * We should move this into a helper somewhere.. but where is appropriate?
-   */
-  private function convert_filename_to_title($filename) {
-    $title = strtr($filename, "_", " ");
-    $title = preg_replace("/\..*?$/", "", $title);
-    $title = preg_replace("/ +/", " ", $title);
-    return $title;
-  }
-
-  public function finish() {
-    batch::stop();
-    print json_encode(array("result" => "success"));
-  }
-}
diff --git a/core/css/debug.css b/core/css/debug.css
deleted file mode 100644
index fe5665ad..00000000
--- a/core/css/debug.css
+++ /dev/null
@@ -1,28 +0,0 @@
-.gAnnotatedThemeBlock {
-  border: 1px solid #C00;
-  clear: both;
-  margin: 1em;
-  padding: 1em;
-  position: relative;
-}
-
-.gAnnotatedThemeBlock_album_top {
-  float: right;
-}
-
-.gAnnotatedThemeBlock_header_bottom {
-  float: right;
-}
-
-.gAnnotatedThemeBlock div.title {
-  background: #C00;
-  border: 1px solid black;
-  color: white;
-  font-size: 110%;
-  padding: 4px;
-  position: absolute;
-  right: -1em;
-  top: -1em;
-  text-align: left;
-  -moz-border-radius: 5%;
-}
diff --git a/core/css/l10n_client.css b/core/css/l10n_client.css
deleted file mode 100644
index 8973715f..00000000
--- a/core/css/l10n_client.css
+++ /dev/null
@@ -1,185 +0,0 @@
-// TODO(andy_st): Add original copyright notice from Drupal l10_client.
-// TODO(andy_st): Add G3 copyright notice.
-// TODO(andy_st): clean up formatting to match our other CSS files.
-
-/* $Id: l10n_client.css,v 1.6 2008/09/09 10:48:20 goba Exp $ */
-
-/* width percentages add to 99% rather than 100% to prevent float
-overflows from occurring in an unnamed browser that can't decide
-how it wants to round. */
-
-/* l10n_client container */
-#l10n-client {
-  text-align:left;
-  z-index:99;
-  line-height:1em;
-  color:#000; background:#fff;
-  position:fixed;
-  width:100%; height: 2em;
-  bottom:0px; left:0px;
-  overflow:hidden;}
-
-  * html #l10n-client {
-    position:static;}
-
-#l10n-client-string-select .string-list,
-#l10n-client-string-editor .source,
-#l10n-client-string-editor .editor {
-  height:20em;}
-
-#l10n-client .labels {
-  overflow:hidden;
-  position:relative;
-  height:2em;
-  color:#fff;
-  background:#37a;}
-
-  #l10n-client .labels .label {
-    display:none;}
-
-  /* Panel toggle button (span) */
-  #l10n-client .labels .toggle {
-    cursor:pointer;
-    display:block;
-    position:absolute; right:0em;
-    padding: 0em .75em; height:2em; line-height:2em;
-    text-transform:uppercase;
-    text-align:center; background:#000;}
-
-  /* Panel labels */
-  #l10n-client h2 {
-    border-left:1px solid #fff;
-    height:1em; line-height:1em;
-    padding: .5em; margin:0px;
-    font-size:1em;
-    text-transform:uppercase;}
-
-    #l10n-client .strings h2 {
-      border:0px;}
-
-  /* 25 + 37 + 37 = 99 */
-  #l10n-client .strings {
-    width:25%; float:left;}
-
-  #l10n-client .source {
-    width:37%; float:left;}
-
-  #l10n-client .translation {
-    width:37%; float:left;}
-
-/* Translatable string list */
-#l10n-client-string-select {
-  display:none;
-  float:left;
-  width:25%;}
-
-  #l10n-client .string-list {
-    height:17em;
-    overflow:auto;
-    list-style:none; list-style-image:none;
-    margin:0em; padding:0em;}
-
-  #l10n-client .string-list li {
-    font-size:.9em;
-    line-height:1.5em;
-    cursor:default;
-    background:transparent;
-    list-style:none; list-style-image:none;
-    border-bottom:1px solid #ddd;
-    padding:.25em .5em;
-    margin:0em;}
-
-  /* Green for translated */
-  #l10n-client .string-list li.translated {
-    border-bottom-color:#9c3;
-    background:#cf6; color:#360;}
-
-    #l10n-client .string-list li.translated:hover {
-      background: #df8;}
-
-    #l10n-client .string-list li.translated:active {
-      background: #9c3;}
-
-  #l10n-client .string-list li.hidden {
-    display:none;}
-
-  /* Gray + Blue hover for untranslated */
-  #l10n-client .string-list li.untranslated {}
-
-    #l10n-client .string-list li.untranslated:hover {
-      background: #ace;}
-
-    #l10n-client .string-list li.untranslated:active {
-      background: #8ac;}
-
-  /* Selected string is indicated by bold text */
-  #l10n-client .string-list li.active {
-    font-weight:bold;}
-
-  #l10n-client #gL10nSearchForm {
-    background:#eee;
-    text-align:center;
-    height:2em; line-height:2em;
-    margin:0em; padding:.5em .5em;
-  }
-
-  #l10n-client #gL10nSearchForm .form-item,
-  #l10n-client #gL10nSearchForm input.form-text,
-  #l10n-client #gL10nSearchForm #search-filter-go,
-  #l10n-client #gL10nSearchForm #search-filter-clear {
-    display:inline;
-    vertical-align:middle;
-  }
-
-    #l10n-client #gL10nSearchForm .form-item {
-      margin:0em;
-      padding:0em;
-    }
-
-    #l10n-client #gL10nSearchForm input.form-text {
-      width:80%;
-    }
-
-    #l10n-client #gL10nSearchForm #search-filter-clear {
-      width:10%;
-      margin:0em;
-    }
-
-
-#l10n-client-string-editor {
-  display:none;
-  float:left;
-  width:74%;}
-
-  #l10n-client-string-editor .source {
-    overflow:hidden;
-    width:50%; float:left;}
-
-    #l10n-client-string-editor .source .source-text {
-      line-height:1.5em;
-      background:#eee;
-      height:16em; margin:1em; padding:1em;
-      overflow:auto;}
-
-  #l10n-client-string-editor .translation {
-    overflow:hidden;
-    width:49%; float:right;}
-
-#gL10nClientSaveForm {
-  padding:0em;}
-
-  #gL10nClientSaveForm .form-textarea {
-    height:13em;
-    font-size:1em; line-height:1.25em;
-    width:95%;}
-
-  #gL10nClientSaveForm .form-submit {
-    margin-top: 0em;}
-
-
-#l10n-client form ul,
-#l10n-client form li,
-#l10n-client form input[type=submit],
-#l10n-client form input[type=text] {
-  display: inline ! important ;
-}
diff --git a/core/css/quick.css b/core/css/quick.css
deleted file mode 100644
index 02f9953e..00000000
--- a/core/css/quick.css
+++ /dev/null
@@ -1,40 +0,0 @@
-.gItem:hover {
-  background-color: #cfdeff;
-}
-
-.gQuick {
-  border: none !important;
-  margin: 0 !important;
-  padding: 0 !important;
-}
-
-#gQuickPane {
-  background: #000;
-  border-bottom: 1px solid #ccc;
-  opacity: 0.9;
-}
-
-#gQuickPane a {
-  cursor: pointer;
-  float: left;
-  margin: 4px;
-}
-
-#gQuickPaneOptions {
-  background: #000;
-  float: left;
-  width: 100%;
-}
-
-#gQuickPaneOptions li a {
-  display: block;
-  float: none;
-  width: auto;
-  margin: 0;
-  padding: .5em .5em .5em .8em;
-  text-align: left;
-}
-
-#gQuickPaneOptions li a:hover {
-  background-color: #4d4d4d;
-}
diff --git a/core/helpers/MY_remote.php b/core/helpers/MY_remote.php
deleted file mode 100644
index 4abf5bf1..00000000
--- a/core/helpers/MY_remote.php
+++ /dev/null
@@ -1,163 +0,0 @@
- $value) {
-      if (!empty($post_data_raw)) {
-        $post_data_raw .= '&';
-      }
-      $post_data_raw .= urlencode($key) . '=' . urlencode($value);
-    }
-    
-    $extra_headers['Content-Type'] = 'application/x-www-form-urlencoded';
-    $extra_headers['Content-Length'] = strlen($post_data_raw);
-    
-    return $post_data_raw;
-  }
-
-  /**
-   * A single request, without following redirects
-   *
-   * @todo: Handle redirects? If so, only for GET (i.e. not for POST), and use G2's WebHelper_simple::_parseLocation logic.
-   */
-  static function do_request($url, $method='GET', $headers=array(), $body='') {
-    /* Convert illegal characters */
-    $url = str_replace(' ', '%20', $url);
-    
-    $url_components = self::_parse_url_for_fsockopen($url);
-    $handle = fsockopen(
-      $url_components['fsockhost'], $url_components['port'], $errno, $errstr, 5);
-    if (empty($handle)) {
-      // log "Error $errno: '$errstr' requesting $url";
-      return array(null, null, null);
-    }
-    
-    $header_lines = array('Host: ' . $url_components['host']);
-    foreach ($headers as $key => $value) {
-      $header_lines[] = $key . ': ' . $value;
-    }
-    
-    $success = fwrite($handle, sprintf("%s %s HTTP/1.0\r\n%s\r\n\r\n%s",
-                                       $method,
-                                       $url_components['uri'],
-                                       implode("\r\n", $header_lines),
-                                       $body));
-    if (!$success) {
-      // Zero bytes written or false was returned
-      // log "fwrite failed in requestWebPage($url)" . ($success === false ? ' - false' : ''
-      return array(null, null, null);
-    }
-    fflush($handle);
-    
-    /*
-     * Read the status line.  fgets stops after newlines.  The first line is the protocol
-     * version followed by a numeric status code and its associated textual phrase.
-     */
-    $response_status = trim(fgets($handle, 4096));
-    if (empty($response_status)) {
-      // 'Empty http response code, maybe timeout'
-      return array(null, null, null);
-    }
-    
-    /* Read the headers */
-    $response_headers = array();
-    while (!feof($handle)) {
-      $line = trim(fgets($handle, 4096));
-      if (empty($line)) {
-        break;
-      }
-      
-      /* Normalize the line endings */
-      $line = str_replace("\r", '', $line);
-      
-      list ($key, $value) = explode(':', $line, 2);
-      if (isset($response_headers[$key])) {
-        if (!is_array($response_headers[$key])) {
-          $response_headers[$key] = array($response_headers[$key]);
-        }
-        $response_headers[$key][] = trim($value);
-      } else {
-        $response_headers[$key] = trim($value);
-      }
-    }
-    
-    /* Read the body */
-    $response_body = '';
-    while (!feof($handle)) {
-      $response_body .= fread($handle, 4096);
-    }
-    fclose($handle);
-
-    return array($response_status, $response_headers, $response_body);
-  }
-
-  /**
-   * Prepare for fsockopen call.
-   * @param string $url
-   * @return array url components
-   * @access private
-   */
-  private static function _parse_url_for_fsockopen($url) {
-    $url_components = parse_url($url);
-    if (strtolower($url_components['scheme']) == 'https') {
-      $url_components['fsockhost'] = 'ssl://' . $url_components['host'];
-      $default_port = 443;
-    } else {
-      $url_components['fsockhost'] = $url_components['host'];
-      $default_port = 80;
-    }
-    if (empty($url_components['port'])) {
-      $url_components['port'] = $default_port;
-    }
-    if (empty($url_components['path'])) {
-      $url_components['path'] = '/';
-    }
-    $uri = $url_components['path']
-      . (empty($url_components['query']) ? '' : '?' . $url_components['query']);
-    /* Unescape ampersands, since if the url comes from form input it will be escaped */
-    $url_components['uri'] = str_replace('&', '&', $uri);
-    return $url_components;
-  }
-}
-
diff --git a/core/helpers/MY_url.php b/core/helpers/MY_url.php
deleted file mode 100644
index 81dcbe1e..00000000
--- a/core/helpers/MY_url.php
+++ /dev/null
@@ -1,81 +0,0 @@
-relative_path();
-    }
-    return parent::site($uri . $query, $protocol);
-  }
-
-  static function parse_url() {
-    if (Router::$controller) {
-      return;
-    }
-
-    $count = count(Router::$segments);
-    foreach (ORM::factory("item")
-             ->where("name", html_entity_decode(Router::$segments[$count - 1], ENT_QUOTES))
-             ->where("level", $count + 1)
-             ->find_all() as $match) {
-      if ($match->relative_path() == html_entity_decode(Router::$current_uri, ENT_QUOTES)) {
-        $item = $match;
-      }
-    }
-
-    if (!empty($item)) {
-      Router::$controller = "{$item->type}s";
-      Router::$controller_path = APPPATH . "controllers/{$item->type}s.php";
-      Router::$method = $item->id;
-    }
-  }
-
-  /**
-   * Just like url::file() except that it returns an absolute URI
-   */
-  static function abs_file($path) {
-    return url::base(
-      false, (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') ? 'http' : 'https') . $path;
-  }
-
-  /**
-   * Just like url::site() except that it returns an absolute URI and
-   * doesn't take a protocol parameter.
-   */
-  static function abs_site($path) {
-    return url::site(
-      $path, (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') ? 'http' : 'https');
-  }
-
-  /**
-   * Just like url::current except that it returns an absolute URI
-   */
-  static function abs_current($qs=false) {
-    return self::abs_site(url::current($qs));
-  }
-}
diff --git a/core/helpers/access.php b/core/helpers/access.php
deleted file mode 100644
index 64ce91fa..00000000
--- a/core/helpers/access.php
+++ /dev/null
@@ -1,628 +0,0 @@
- tuples.  It would be inefficient to check
- *   these tuples every time we want to do a lookup, so we use these intents to create an entire
- *   table of permissions for easy lookup in the Access_Cache_Model.  There's a 1:1 mapping
- *   between Item_Model and Access_Cache_Model entries.
- *
- * o For efficiency, we create columns in Access_Intent_Model and Access_Cache_Model for each of
- *   the possible Group_Model and Permission_Model combinations.  This may lead to performance
- *   issues for very large Gallery installs, but for small to medium sized ones (5-10 groups, 5-10
- *   permissions) it's especially efficient because there's a single field value for each
- *   group/permission/item combination.
- *
- * o For efficiency, we store the cache columns for view permissions directly in the Item_Model.
- *   This means that we can filter items by group/permission combination without doing any table
- *   joins making for an especially efficient permission check at the expense of having to
- *   maintain extra columns for each item.
- *
- * o If at any time the Access_Cache_Model becomes invalid, we can rebuild the entire table from
- *   the Access_Intent_Model
- */
-class access_Core {
-  const DENY      = 0;
-  const ALLOW     = 1;
-  const UNKNOWN   = 2;
-
-  /**
-   * Does the active user have this permission on this item?
-   *
-   * @param  string     $perm_name
-   * @param  Item_Model $item
-   * @return boolean
-   */
-  static function can($perm_name, $item) {
-    if (!$item->loaded) {
-      return false;
-    }
-
-    if (user::active()->admin) {
-      return true;
-    }
-
-    $resource = $perm_name == "view" ?
-      $item : model_cache::get("access_cache", $item->id, "item_id");
-    foreach (user::group_ids() as $id) {
-      if ($resource->__get("{$perm_name}_$id") === self::ALLOW) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * If the active user does not have this permission, failed with an access::forbidden().
-   *
-   * @param  string     $perm_name
-   * @param  Item_Model $item
-   * @return boolean
-   */
-  static function required($perm_name, $item) {
-    if (!self::can($perm_name, $item)) {
-      self::forbidden();
-    }
-  }
-
-  /**
-   * Does this group have this permission on this item?
-   *
-   * @param  Group_Model $group
-   * @param  string      $perm_name
-   * @param  Item_Model  $item
-   * @return boolean
-   */
-  static function group_can($group, $perm_name, $item) {
-    $resource = $perm_name == "view" ?
-      $item : model_cache::get("access_cache", $item->id, "item_id");
-    return $resource->__get("{$perm_name}_{$group->id}") === self::ALLOW;
-  }
-
-  /**
-   * Return this group's intent for this permission on this item.
-   *
-   * @param  Group_Model $group
-   * @param  string      $perm_name
-   * @param  Item_Model  $item
-   * @return integer     access::ALLOW, access::DENY or null for no intent
-   */
-  static function group_intent($group, $perm_name, $item) {
-    $intent = model_cache::get("access_intent", $item->id, "item_id");
-    return $intent->__get("{$perm_name}_{$group->id}");
-  }
-
-  /**
-   * Is the permission on this item locked by a parent?  If so return the nearest parent that
-   * locks it.
-   *
-   * @param  Group_Model $group
-   * @param  string      $perm_name
-   * @param  Item_Model  $item
-   * @return ORM_Model   item that locks this one
-   */
-  static function locked_by($group, $perm_name, $item) {
-    if ($perm_name != "view") {
-      return null;
-    }
-
-    // For view permissions, if any parent is self::DENY, then those parents lock this one.
-    // Return
-    $lock = ORM::factory("item")
-      ->where("`left` <= $item->left")
-      ->where("`right` >= $item->right")
-      ->where("items.id <> $item->id")
-      ->join("access_intents", "items.id", "access_intents.item_id")
-      ->where("access_intents.view_$group->id", 0)
-      ->orderby("level", "DESC")
-      ->limit(1)
-      ->find();
-
-    if ($lock->loaded) {
-      return $lock;
-    } else {
-      return null;
-    }
-  }
-
-  /**
-   * Terminate immediately with an HTTP 503 Forbidden response.
-   */
-  static function forbidden() {
-    throw new Exception("@todo FORBIDDEN", 503);
-  }
-
-  /**
-   * Internal method to set a permission
-   *
-   * @param  Group_Model $group
-   * @param  string      $perm_name
-   * @param  Item_Model  $item
-   * @param  boolean     $value
-   */
-  private static function _set(Group_Model $group, $perm_name, $album, $value) {
-    if (get_class($group) != "Group_Model") {
-      throw new Exception("@todo PERMISSIONS_ONLY_WORK_ON_GROUPS");
-    }
-    if (!$album->loaded) {
-      throw new Exception("@todo INVALID_ALBUM $album->id");
-    }
-    if (!$album->is_album()) {
-      throw new Exception("@todo INVALID_ALBUM_TYPE not an album");
-    }
-    $access = model_cache::get("access_intent", $album->id, "item_id");
-    $access->__set("{$perm_name}_{$group->id}", $value);
-    $access->save();
-
-    if ($perm_name == "view") {
-      self::_update_access_view_cache($group, $album);
-    } else {
-      self::_update_access_non_view_cache($group, $perm_name, $album);
-    }
-
-    self::_update_htaccess_files($album, $group, $perm_name, $value);
-  }
-
-  /**
-   * Allow a group to have a permission on an item.
-   *
-   * @param  Group_Model $group
-   * @param  string  $perm_name
-   * @param  Item_Model $item
-   */
-  static function allow($group, $perm_name, $item) {
-    self::_set($group, $perm_name, $item, self::ALLOW);
-  }
-
-  /**
-   * Deny a group the given permission on an item.
-   *
-   * @param  Group_Model $group
-   * @param  string  $perm_name
-   * @param  Item_Model $item
-   */
-  static function deny($group, $perm_name, $item) {
-    self::_set($group, $perm_name, $item, self::DENY);
-  }
-
-  /**
-   * Unset the given permission for this item and use inherited values
-   *
-   * @param  Group_Model $group
-   * @param  string  $perm_name
-   * @param  Item_Model $item
-   */
-  static function reset($group, $perm_name, $item) {
-    if ($item->id == 1) {
-      throw new Exception("@todo CANT_RESET_ROOT_PERMISSION");
-    }
-    self::_set($group, $perm_name, $item, null);
-  }
-
-  /**
-   * Register a permission so that modules can use it.
-   *
-   * @param  string $name           The internal name for for this permission
-   * @param  string $display_name   The internationalized version of the displayable name
-   * @return void
-  */
-  static function register_permission($name, $display_name) {
-    $permission = ORM::factory("permission", $name);
-    if ($permission->loaded) {
-      throw new Exception("@todo PERMISSION_ALREADY_EXISTS $name");
-    }
-    $permission->name = $name;
-    $permission->display_name = $display_name;
-    $permission->save();
-
-    foreach (self::_get_all_groups() as $group) {
-      self::_add_columns($name, $group);
-    }
-  }
-
-  /**
-   * Delete a permission.
-   *
-   * @param  string $perm_name
-   * @return void
-   */
-  static function delete_permission($name) {
-    foreach (self::_get_all_groups() as $group) {
-      self::_drop_columns($name, $group);
-    }
-    $permission = ORM::factory("permission")->where("name", $name)->find();
-    if ($permission->loaded) {
-      $permission->delete();
-    }
-  }
-
-  /**
-   * Add the appropriate columns for a new group
-   *
-   * @param Group_Model $group
-   * @return void
-   */
-  static function add_group($group) {
-    foreach (ORM::factory("permission")->find_all() as $perm) {
-      self::_add_columns($perm->name, $group);
-    }
-  }
-
-  /**
-   * Remove a group's permission columns (usually when it's deleted)
-   *
-   * @param Group_Model $group
-   * @return void
-   */
-  static function delete_group($group) {
-    foreach (ORM::factory("permission")->find_all() as $perm) {
-      self::_drop_columns($perm->name, $group);
-    }
-  }
-
-  /**
-   * Add new access rows when a new item is added.
-   *
-   * @param Item_Model $item
-   * @return void
-   */
-  static function add_item($item) {
-    $access_intent = ORM::factory("access_intent", $item->id);
-    if ($access_intent->loaded) {
-      throw new Exception("@todo ITEM_ALREADY_ADDED $item->id");
-    }
-    $access_intent = ORM::factory("access_intent");
-    $access_intent->item_id = $item->id;
-    $access_intent->save();
-
-    // Create a new access cache entry and copy the parents values.
-    $access_cache = ORM::factory("access_cache");
-    $access_cache->item_id = $item->id;
-    if ($item->id != 1) {
-      $parent_access_cache =
-        ORM::factory("access_cache")->where("item_id", $item->parent()->id)->find();
-      foreach (self::_get_all_groups() as $group) {
-        foreach (ORM::factory("permission")->find_all() as $perm) {
-          $field = "{$perm->name}_{$group->id}";
-          if ($perm->name == "view") {
-            $item->$field = $item->parent()->$field;
-          } else {
-            $access_cache->$field = $parent_access_cache->$field;
-          }
-        }
-      }
-    }
-    $item->save();
-    $access_cache->save();
-  }
-
-  /**
-   * Delete appropriate access rows when an item is deleted.
-   *
-   * @param Item_Model $item
-   * @return void
-   */
-  static function delete_item($item) {
-    ORM::factory("access_intent")->where("item_id", $item->id)->find()->delete();
-    ORM::factory("access_cache")->where("item_id", $item->id)->find()->delete();
-  }
-
-  /**
-   * Verify our Cross Site Request Forgery token is valid, else throw an exception.
-   */
-  static function verify_csrf() {
-    $input = Input::instance();
-    if ($input->post("csrf", $input->get("csrf", null)) !== Session::instance()->get("csrf")) {
-      self::forbidden();
-    }
-  }
-
-  /**
-   * Get the Cross Site Request Forgery token for this session.
-   * @return string
-   */
-  static function csrf_token() {
-    $session = Session::instance();
-    $csrf = $session->get("csrf");
-    if (empty($csrf)) {
-      $csrf = md5(rand());
-      $session->set("csrf", $csrf);
-    }
-    return $csrf;
-  }
-
-  /**
-   * Generate an  element containing the Cross Site Request Forgery token for this session.
-   * @return string
-   */
-  static function csrf_form_field() {
-    return "";
-  }
-
-  /**
-   * Internal method to get all available groups.
-   *
-   * @return ORM_Iterator
-   */
-  private static function _get_all_groups() {
-    // When we build the core package, it's possible that the user module is not installed yet.
-    // This is ok at packaging time, so work around it.
-    if (module::is_active("user")) {
-      return ORM::factory("group")->find_all();
-    } else {
-      return array();
-    }
-  }
-
-  /**
-   * Internal method to  remove Permission/Group columns
-   *
-   * @param  Group_Model $group
-   * @param  string      $perm_name
-   * @return void
-   */
-  private static function _drop_columns($perm_name, $group) {
-    $db = Database::instance();
-    $field = "{$perm_name}_{$group->id}";
-    $cache_table = $perm_name == "view" ? "items" : "access_caches";
-    $db->query("ALTER TABLE {{$cache_table}} DROP `$field`");
-    $db->query("ALTER TABLE {access_intents} DROP `$field`");
-    ORM::factory("access_intent")->clear_cache();
-  }
-
-  /**
-   * Internal method to add Permission/Group columns
-   *
-   * @param  Group_Model $group
-   * @param  string  $perm_name
-   * @return void
-   */
-  private static function _add_columns($perm_name, $group) {
-    $db = Database::instance();
-    $field = "{$perm_name}_{$group->id}";
-    $cache_table = $perm_name == "view" ? "items" : "access_caches";
-    $db->query("ALTER TABLE {{$cache_table}} ADD `$field` SMALLINT NOT NULL DEFAULT 0");
-    $db->query("ALTER TABLE {access_intents} ADD `$field` BOOLEAN DEFAULT NULL");
-    $db->update("access_intents", array($field => 0), array("item_id" => 1));
-    ORM::factory("access_intent")->clear_cache();
-  }
-
-  /**
-   * Update the Access_Cache model based on information from the Access_Intent model for view
-   * permissions only.
-   *
-   * @todo: use database locking
-   *
-   * @param  Group_Model $group
-   * @param  Item_Model $item
-   * @return void
-   */
-  private static function _update_access_view_cache($group, $item) {
-    $access = ORM::factory("access_intent")->where("item_id", $item->id)->find();
-
-    $db = Database::instance();
-    $field = "view_{$group->id}";
-
-    // With view permissions, deny values in the parent can override allow values in the child,
-    // so start from the bottom of the tree and work upwards overlaying negative on top of
-    // positive.
-    //
-    // If the item's intent is ALLOW or DEFAULT, it's possible that some ancestor has specified
-    // DENY and this ALLOW cannot be obeyed.  So in that case, back up the tree and find any
-    // non-DEFAULT and non-ALLOW parent and propagate from there.  If we can't find a matching
-    // item, then its safe to propagate from here.
-    if ($access->$field !== self::DENY) {
-      $tmp_item = ORM::factory("item")
-        ->where("left <", $item->left)
-        ->where("right >", $item->right)
-        ->join("access_intents", "access_intents.item_id", "items.id")
-        ->where("access_intents.$field", self::DENY)
-        ->orderby("left", "DESC")
-        ->limit(1)
-        ->find();
-      if ($tmp_item->loaded) {
-        $item = $tmp_item;
-      }
-    }
-
-    // We will have a problem if we're trying to change a DENY to an ALLOW because the
-    // access_caches table will already contain DENY values and we won't be able to overwrite
-    // them according the rule above.  So mark every permission below this level as UNKNOWN so
-    // that we can tell which permissions have been changed, and which ones need to be updated.
-    $db->update("items", array($field => self::UNKNOWN),
-                array("left >=" => $item->left, "right <=" => $item->right));
-
-    $query = ORM::factory("access_intent")
-      ->select(array("access_intents.$field", "items.left", "items.right", "items.id"))
-      ->join("items", "items.id", "access_intents.item_id")
-      ->where("left >=", $item->left)
-      ->where("right <=", $item->right)
-      ->where("type", "album")
-      ->where("access_intents.$field IS NOT", null)
-      ->orderby("level", "DESC")
-      ->find_all();
-    foreach ($query as $row) {
-      if ($row->$field == self::ALLOW) {
-        // Propagate ALLOW for any row that is still UNKNOWN.
-        $db->update("items", array($field => $row->$field),
-          array($field => self::UNKNOWN, "left >=" => $row->left, "right <=" => $row->right));
-      } else if ($row->$field == self::DENY) {
-        // DENY overwrites everything below it
-        $db->update("items", array($field => $row->$field),
-                    array("left >=" => $row->left, "right <=" => $row->right));
-      }
-    }
-
-    // Finally, if our intent is DEFAULT at this point it means that we were unable to find a
-    // DENY parent in the hierarchy to propagate from.  So we'll still have a UNKNOWN values in
-    // the hierarchy, and all of those are safe to change to ALLOW.
-    $db->update("items", array($field => self::ALLOW),
-                array($field => self::UNKNOWN, "left >=" => $item->left, "right <=" => $item->right));
-  }
-
-  /**
-   * Update the Access_Cache model based on information from the Access_Intent model for non-view
-   * permissions.
-   *
-   * @todo: use database locking
-   *
-   * @param  Group_Model $group
-   * @param  string  $perm_name
-   * @param  Item_Model $item
-   * @return void
-   */
-  private static function _update_access_non_view_cache($group, $perm_name, $item) {
-    $access = ORM::factory("access_intent")->where("item_id", $item->id)->find();
-
-    $db = Database::instance();
-    $field = "{$perm_name}_{$group->id}";
-
-    // If the item's intent is DEFAULT, then we need to back up the chain to find the nearest
-    // parent with an intent and propagate from there.
-    //
-    // @todo To optimize this, we wouldn't need to propagate from the parent, we could just
-    //       propagate from here with the parent's intent.
-    if ($access->$field === null) {
-      $tmp_item = ORM::factory("item")
-        ->join("access_intents", "items.id", "access_intents.item_id")
-        ->where("left <", $item->left)
-        ->where("right >", $item->right)
-        ->where("$field IS NOT", null)
-        ->orderby("left", "DESC")
-        ->limit(1)
-        ->find();
-      if ($tmp_item->loaded) {
-        $item = $tmp_item;
-      }
-    }
-
-    // With non-view permissions, each level can override any permissions that came above it
-    // so start at the top and work downwards, overlaying permissions as we go.
-    $query = ORM::factory("access_intent")
-      ->select(array("access_intents.$field", "items.left", "items.right"))
-      ->join("items", "items.id", "access_intents.item_id")
-      ->where("left >=", $item->left)
-      ->where("right <=", $item->right)
-      ->where("$field IS NOT", null)
-      ->orderby("level", "ASC")
-      ->find_all();
-    foreach  ($query as $row) {
-      $db->query(
-        "UPDATE {access_caches} SET `$field` = {$row->$field} " .
-        "WHERE `item_id` IN " .
-        "  (SELECT `id` FROM {items} " .
-        "  WHERE `left` >= $row->left " .
-        "  AND `right` <= $row->right)");
-    }
-  }
-
-  /**
-   * Maintain .htacccess files to prevent direct access to albums, resizes and thumbnails when we
-   * apply the view and view_full permissions to guest users.
-   */
-  private static function _update_htaccess_files($album, $group, $perm_name, $value) {
-    if ($group->id != 1 || !($perm_name == "view" || $perm_name == "view_full")) {
-      return;
-    }
-
-    $dirs = array($album->file_path());
-    if ($perm_name == "view") {
-      $dirs[] = dirname($album->resize_path());
-      $dirs[] = dirname($album->thumb_path());
-    }
-
-    $base_url = url::site("file_proxy");
-    foreach ($dirs as $dir) {
-      if ($value === self::DENY) {
-        $fp = fopen("$dir/.htaccess", "w+");
-        fwrite($fp, "\n");
-        fwrite($fp, "  RewriteEngine On\n");
-        fwrite($fp, "  RewriteRule (.*) $base_url/\$1 [L]\n");
-        fwrite($fp, "\n");
-        fwrite($fp, "\n");
-        fwrite($fp, "  Order Deny,Allow\n");
-        fwrite($fp, "  Deny from All\n");
-        fwrite($fp, "\n");
-        fclose($fp);
-      } else {
-        @unlink($dir . "/.htaccess");
-      }
-    }
-  }
-
-  static function private_key() {
-    return module::get_var("core", "private_key");
-  }
-
-  /**
-   * Verify that our htaccess based permission system actually works.  Create a temporary
-   * directory containing an .htaccess file that uses mod_rewrite to redirect /verify to
-   * /success.  Then request that url.  If we retrieve it successfully, then our redirects are
-   * working and our permission system works.
-   */
-  static function htaccess_works() {
-    $success_url = url::file("var/tmp/security_test/success");
-
-    @mkdir(VARPATH . "tmp/security_test");
-    if ($fp = @fopen(VARPATH . "tmp/security_test/.htaccess", "w+")) {
-      fwrite($fp, "RewriteEngine On\n");
-      fwrite($fp, "RewriteRule verify $success_url [L]\n");
-      fclose($fp);
-    }
-
-    if ($fp = @fopen(VARPATH . "tmp/security_test/success", "w+")) {
-      fwrite($fp, "success");
-      fclose($fp);
-    }
-
-    list ($response) = remote::do_request(url::abs_file("var/tmp/security_test/verify"));
-    $works = $response == "HTTP/1.1 200 OK";
-    @dir::unlink(VARPATH . "tmp/security_test");
-
-    return $works;
-  }
-}
diff --git a/core/helpers/album.php b/core/helpers/album.php
deleted file mode 100644
index 362b93d0..00000000
--- a/core/helpers/album.php
+++ /dev/null
@@ -1,132 +0,0 @@
-loaded || !$parent->is_album()) {
-      throw new Exception("@todo INVALID_PARENT");
-    }
-
-    if (strpos($name, "/")) {
-      throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH");
-    }
-
-    // We don't allow trailing periods as a security measure
-    // ref: http://dev.kohanaphp.com/issues/684
-    if (rtrim($name, ".") != $name) {
-      throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD");
-    }
-
-    $album = ORM::factory("item");
-    $album->type = "album";
-    $album->title = $title;
-    $album->description = $description;
-    $album->name = $name;
-    $album->owner_id = $owner_id;
-    $album->thumb_dirty = 1;
-    $album->resize_dirty = 1;
-    $album->rand_key = ((float)mt_rand()) / (float)mt_getrandmax();
-    $album->sort_column = "weight";
-    $album->sort_order = "ASC";
-
-    while (ORM::factory("item")
-           ->where("parent_id", $parent->id)
-           ->where("name", $album->name)
-           ->find()->id) {
-      $album->name = "{$name}-" . rand();
-    }
-
-    $album = $album->add_to_parent($parent);
-    mkdir($album->file_path());
-    mkdir(dirname($album->thumb_path()));
-    mkdir(dirname($album->resize_path()));
-
-    module::event("item_created", $album);
-
-    return $album;
-  }
-
-  static function get_add_form($parent) {
-    $form = new Forge("albums/{$parent->id}", "", "post", array("id" => "gAddAlbumForm"));
-    $group = $form->group("add_album")
-      ->label(t("Add an album to %album_title", array("album_title" => $parent->title)));
-    $group->input("title")->label(t("Title"));
-    $group->textarea("description")->label(t("Description"));
-    $group->input("name")->label(t("Directory Name"))
-      ->callback("item::validate_no_slashes")
-      ->error_messages("no_slashes", t("The directory name can't contain the \"/\" character"));
-    $group->hidden("type")->value("album");
-    $group->submit("")->value(t("Create"));
-    $form->add_rules_from(ORM::factory("item"));
-    return $form;
-  }
-
-  static function get_edit_form($parent) {
-    $form = new Forge("albums/{$parent->id}", "", "post", array("id" => "gEditAlbumForm"));
-    $form->hidden("_method")->value("put");
-    $group = $form->group("edit_album")->label(t("Edit Album"));
-
-    $group->input("title")->label(t("Title"))->value($parent->title);
-    $group->textarea("description")->label(t("Description"))->value($parent->description);
-    if ($parent->id != 1) {
-      $group->input("dirname")->label(t("Directory Name"))->value($parent->name)
-        ->callback("item::validate_no_slashes")
-        ->error_messages("no_slashes", t("The directory name can't contain a \"/\""))
-        ->callback("item::validate_no_trailing_period")
-        ->error_messages("no_trailing_period", t("The directory name can't end in \".\""));
-    }
-
-    $sort_order = $group->group("sort_order", array("id" => "gAlbumSortOrder"))
-      ->label(t("Sort Order"));
-
-    $sort_order->dropdown("column", array("id" => "gAlbumSortColumn"))
-      ->label(t("Sort by"))
-      ->options(array("weight" => t("Default"),
-                      "captured" => t("Capture Date"),
-                      "created" => t("Creation Date"),
-                      "title" => t("Title"),
-                      "updated" => t("Updated Date"),
-                      "view_count" => t("Number of views"),
-                      "rand_key" => t("Random")))
-      ->selected($parent->sort_column);
-    $sort_order->dropdown("direction", array("id" => "gAlbumSortDirection"))
-      ->label(t("Order"))
-      ->options(array("ASC" => t("Ascending"),
-                      "DESC" => t("Descending")))
-      ->selected($parent->sort_order);
-    $group->hidden("type")->value("album");
-    $group->submit("")->value(t("Modify"));
-    $form->add_rules_from(ORM::factory("item"));
-    return $form;
-  }
-}
diff --git a/core/helpers/batch.php b/core/helpers/batch.php
deleted file mode 100644
index 0faa3369..00000000
--- a/core/helpers/batch.php
+++ /dev/null
@@ -1,40 +0,0 @@
-set("batch_level", $session->get("batch_level", 0) + 1);
-  }
-
-  static function stop() {
-    $session = Session::instance();
-    $batch_level = $session->get("batch_level", 0) - 1;
-    if ($batch_level > 0) {
-      $session->set("batch_level", $batch_level);
-    } else {
-      $session->delete("batch_level");
-      module::event("batch_complete");
-    }
-  }
-
-  static function in_progress() {
-    return Session::instance()->get("batch_level", 0) > 0;
-  }
-}
diff --git a/core/helpers/block_manager.php b/core/helpers/block_manager.php
deleted file mode 100644
index 022626e5..00000000
--- a/core/helpers/block_manager.php
+++ /dev/null
@@ -1,67 +0,0 @@
-name}_block";
-      if (method_exists($class_name, "get_list")) {
-        foreach (call_user_func(array($class_name, "get_list")) as $id => $title) {
-          $blocks["{$module->name}:$id"] = $title;
-        }
-      }
-    }
-    return $blocks;
-  }
-
-  static function get_html($location) {
-    $active = self::get_active($location);
-    $result = "";
-    foreach ($active as $id => $desc) {
-      if (method_exists("$desc[0]_block", "get")) {
-        $block = call_user_func(array("$desc[0]_block", "get"), $desc[1]);
-        $block->id = $id;
-        $result .= $block;
-      }
-    }
-    return $result;
-  }
-}
diff --git a/core/helpers/core.php b/core/helpers/core.php
deleted file mode 100644
index 63f51f86..00000000
--- a/core/helpers/core.php
+++ /dev/null
@@ -1,52 +0,0 @@
-admin) {
-      Router::$controller = "maintenance";
-      Router::$controller_path = APPPATH . "controllers/maintenance.php";
-      Router::$method = "index";
-    }
-  }
-
-  /**
-   * This function is called when the Gallery is fully initialized.  We relay it to modules as the
-   * "gallery_ready" event.  Any module that wants to perform an action at the start of every
-   * request should implement the _event::gallery_ready() handler.
-   */
-  static function ready() {
-    module::event("gallery_ready");
-  }
-
-  /**
-   * This function is called right before the Kohana framework shuts down.  We relay it to modules
-   * as the "gallery_shutdown" event.  Any module that wants to perform an action at the start of
-   * every request should implement the _event::gallery_shutdown() handler.
-   */
-  static function shutdown() {
-    module::event("gallery_shutdown");
-  }
-}
\ No newline at end of file
diff --git a/core/helpers/core_block.php b/core/helpers/core_block.php
deleted file mode 100644
index 0e2e9c54..00000000
--- a/core/helpers/core_block.php
+++ /dev/null
@@ -1,100 +0,0 @@
- t("Welcome to Gallery 3!"),
-      "photo_stream" => t("Photo Stream"),
-      "log_entries" => t("Log Entries"),
-      "stats" => t("Gallery Stats"),
-      "platform_info" => t("Platform Information"),
-      "project_news" => t("Gallery Project News"));
-  }
-
-  static function get($block_id) {
-    $block = new Block();
-    switch($block_id) {
-    case "welcome":
-      $block->css_id = "gWelcome";
-      $block->title = t("Welcome to Gallery3");
-      $block->content = new View("admin_block_welcome.html");
-      break;
-
-    case "photo_stream":
-      $block->css_id = "gPhotoStream";
-      $block->title = t("Photo Stream");
-      $block->content = new View("admin_block_photo_stream.html");
-      $block->content->photos =
-        ORM::factory("item")->where("type", "photo")->orderby("created", "DESC")->find_all(10);
-      break;
-
-    case "log_entries":
-      $block->css_id = "gLogEntries";
-      $block->title = t("Log Entries");
-      $block->content = new View("admin_block_log_entries.html");
-      $block->content->entries = ORM::factory("log")->orderby("timestamp", "DESC")->find_all(5);
-        break;
-
-    case "stats":
-      $block->css_id = "gStats";
-      $block->title = t("Gallery Stats");
-      $block->content = new View("admin_block_stats.html");
-      $block->content->album_count = ORM::factory("item")->where("type", "album")->count_all();
-      $block->content->photo_count = ORM::factory("item")->where("type", "photo")->count_all();
-      break;
-
-    case "platform_info":
-      $block->css_id = "gPlatform";
-      $block->title = t("Platform Information");
-      $block->content = new View("admin_block_platform.html");
-      if (is_readable("/proc/loadavg")) {
-        $block->content->load_average =
-          join(" ", array_slice(split(" ", array_shift(file("/proc/loadavg"))), 0, 3));
-      } else {
-        $block->content->load_average = t("Unavailable");
-      }
-      break;
-
-    case "project_news":
-      $block->css_id = "gProjectNews";
-      $block->title = t("Gallery Project News");
-      $block->content = new View("admin_block_news.html");
-      $block->content->feed = feed::parse("http://gallery.menalto.com/node/feed", 3);
-      break;
-
-    case "block_adder":
-      $block->css_id = "gBlockAdder";
-      $block->title = t("Dashboard Content");
-      $block->content = self::get_add_block_form();
-    }
-
-    return $block;
-  }
-
-  static function get_add_block_form() {
-    $form = new Forge("admin/dashboard/add_block", "", "post",
-                      array("id" => "gAddDashboardBlockForm"));
-    $group = $form->group("add_block")->label(t("Add Block"));
-    $group->dropdown("id")->label("Available Blocks")->options(block_manager::get_available());
-    $group->submit("center")->value(t("Add to center"));
-    $group->submit("sidebar")->value(t("Add to sidebar"));
-    return $form;
-  }
-}
\ No newline at end of file
diff --git a/core/helpers/core_event.php b/core/helpers/core_event.php
deleted file mode 100644
index bbb53cc9..00000000
--- a/core/helpers/core_event.php
+++ /dev/null
@@ -1,46 +0,0 @@
-admin && module::get_var("core", "choose_default_tookit", null)) {
-      graphics::choose_default_toolkit();
-      module::clear_var("core", "choose_default_tookit");
-    }
-  }
-}
diff --git a/core/helpers/core_installer.php b/core/helpers/core_installer.php
deleted file mode 100644
index cffcbedb..00000000
--- a/core/helpers/core_installer.php
+++ /dev/null
@@ -1,278 +0,0 @@
-query("CREATE TABLE {access_caches} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `item_id` int(9),
-                   PRIMARY KEY (`id`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {access_intents} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `item_id` int(9),
-                   PRIMARY KEY (`id`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {graphics_rules} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `active` BOOLEAN default 0,
-                   `args` varchar(255) default NULL,
-                   `module_name` varchar(64) NOT NULL,
-                   `operation` varchar(64) NOT NULL,
-                   `priority` int(9) NOT NULL,
-                   `target`  varchar(32) NOT NULL,
-                   PRIMARY KEY (`id`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {items} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `album_cover_item_id` int(9) default NULL,
-                   `captured` int(9) default NULL,
-                   `created` int(9) default NULL,
-                   `description` varchar(2048) default NULL,
-                   `height` int(9) default NULL,
-                   `left` int(9) NOT NULL,
-                   `level` int(9) NOT NULL,
-                   `mime_type` varchar(64) default NULL,
-                   `name` varchar(255) default NULL,
-                   `owner_id` int(9) default NULL,
-                   `parent_id` int(9) NOT NULL,
-                   `rand_key` float default NULL,
-                   `relative_path_cache` varchar(255) default NULL,
-                   `resize_dirty` boolean default 1,
-                   `resize_height` int(9) default NULL,
-                   `resize_width` int(9) default NULL,
-                   `right` int(9) NOT NULL,
-                   `sort_column` varchar(64) default NULL,
-                   `sort_order` char(4) default 'ASC',
-                   `thumb_dirty` boolean default 1,
-                   `thumb_height` int(9) default NULL,
-                   `thumb_width` int(9) default NULL,
-                   `title` varchar(255) default NULL,
-                   `type` varchar(32) NOT NULL,
-                   `updated` int(9) default NULL,
-                   `view_count` int(9) default 0,
-                   `weight` int(9) NOT NULL default 0,
-                   `width` int(9) default NULL,
-                   PRIMARY KEY (`id`),
-                   KEY `parent_id` (`parent_id`),
-                   KEY `type` (`type`),
-                   KEY `random` (`rand_key`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {logs} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `category` varchar(64) default NULL,
-                   `html` varchar(255) default NULL,
-                   `message` text default NULL,
-                   `referer` varchar(255) default NULL,
-                   `severity` int(9) default 0,
-                   `timestamp` int(9) default 0,
-                   `url` varchar(255) default NULL,
-                   `user_id` int(9) default 0,
-                   PRIMARY KEY (`id`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {messages} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `key` varchar(255) default NULL,
-                   `severity` varchar(32) default NULL,
-                   `value` varchar(255) default NULL,
-                   PRIMARY KEY (`id`),
-                   UNIQUE KEY(`key`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {modules} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `active` BOOLEAN default 0,
-                   `name` varchar(64) default NULL,
-                   `version` int(9) default NULL,
-                   PRIMARY KEY (`id`),
-                   UNIQUE KEY(`name`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {themes} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `name` varchar(64) default NULL,
-                   `version` int(9) default NULL,
-                   PRIMARY KEY (`id`),
-                   UNIQUE KEY(`name`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {permissions} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `display_name` varchar(64) default NULL,
-                   `name` varchar(64) default NULL,
-                   PRIMARY KEY (`id`),
-                   UNIQUE KEY(`name`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {incoming_translations} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `key` char(32) NOT NULL,
-                   `locale` char(10) NOT NULL,
-                   `message` text NOT NULL,
-                   `revision` int(9) DEFAULT NULL,
-                   `translation` text,
-                   PRIMARY KEY (`id`),
-                   UNIQUE KEY(`key`, `locale`),
-                   KEY `locale_key` (`locale`, `key`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {outgoing_translations} (
-                   `id` int(9) NOT NULL auto_increment,
-                   `base_revision` int(9) DEFAULT NULL,
-                   `key` char(32) NOT NULL,
-                   `locale` char(10) NOT NULL,
-                   `message` text NOT NULL,
-                   `translation` text,
-                   PRIMARY KEY (`id`),
-                   UNIQUE KEY(`key`, `locale`),
-                   KEY `locale_key` (`locale`, `key`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {sessions} (
-                  `session_id` varchar(127) NOT NULL,
-                  `data` text NOT NULL,
-                  `last_activity` int(10) UNSIGNED NOT NULL,
-                  PRIMARY KEY (`session_id`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {tasks} (
-                  `id` int(9) NOT NULL auto_increment,
-                  `callback` varchar(128) default NULL,
-                  `context` text NOT NULL,
-                  `done` boolean default 0,
-                  `name` varchar(128) default NULL,
-                  `owner_id` int(9) default NULL,
-                  `percent_complete` int(9) default 0,
-                  `state` varchar(32) default NULL,
-                  `status` varchar(255) default NULL,
-                  `updated` int(9) default NULL,
-                  PRIMARY KEY (`id`),
-                  KEY (`owner_id`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      $db->query("CREATE TABLE {vars} (
-                  `id` int(9) NOT NULL auto_increment,
-                  `module_name` varchar(64) NOT NULL,
-                  `name` varchar(64) NOT NULL,
-                  `value` text,
-                  PRIMARY KEY (`id`),
-                  UNIQUE KEY(`module_name`, `name`))
-                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
-
-      foreach (array("albums", "logs", "modules", "resizes", "thumbs", "tmp", "uploads") as $dir) {
-        @mkdir(VARPATH . $dir);
-      }
-
-      access::register_permission("view", "View");
-      access::register_permission("view_full", "View Full Size");
-      access::register_permission("edit", "Edit");
-      access::register_permission("add", "Add");
-
-      $root = ORM::factory("item");
-      $root->type = "album";
-      $root->title = "Gallery";
-      $root->description = "";
-      $root->left = 1;
-      $root->right = 2;
-      $root->parent_id = 0;
-      $root->level = 1;
-      $root->thumb_dirty = 1;
-      $root->resize_dirty = 1;
-      $root->sort_column = "weight";
-      $root->sort_order = "ASC";
-      $root->save();
-      access::add_item($root);
-
-      module::set_var("core", "active_site_theme", "default");
-      module::set_var("core", "active_admin_theme", "admin_default");
-      module::set_var("core", "page_size", 9);
-      module::set_var("core", "thumb_size", 200);
-      module::set_var("core", "resize_size", 640);
-      module::set_var("core", "default_locale", "en_US");
-      module::set_var("core", "image_quality", 75);
-
-      // Add rules for generating our thumbnails and resizes
-      graphics::add_rule(
-        "core", "thumb", "resize",
-        array("width" => 200, "height" => 200, "master" => Image::AUTO),
-        100);
-      graphics::add_rule(
-        "core", "resize", "resize",
-        array("width" => 640, "height" => 480, "master" => Image::AUTO),
-        100);
-
-      // Instantiate default themes (site and admin)
-      foreach (array("default", "admin_default") as $theme_name) {
-        $theme_info = new ArrayObject(parse_ini_file(THEMEPATH . $theme_name . "/theme.info"),
-                                      ArrayObject::ARRAY_AS_PROPS);
-        $theme = ORM::factory("theme");
-        $theme->name = $theme_name;
-        $theme->version = $theme_info->version;
-        $theme->save();
-      }
-
-      block_manager::add("dashboard_sidebar", "core", "block_adder");
-      block_manager::add("dashboard_sidebar", "core", "stats");
-      block_manager::add("dashboard_sidebar", "core", "platform_info");
-      block_manager::add("dashboard_sidebar", "core", "project_news");
-      block_manager::add("dashboard_center", "core", "welcome");
-      block_manager::add("dashboard_center", "core", "photo_stream");
-      block_manager::add("dashboard_center", "core", "log_entries");
-
-      module::set_version("core", 1);
-      module::set_var("core", "version", "3.0 pre-beta svn");
-      module::set_var("core", "choose_default_tookit", 1);
-    }
-  }
-
-  static function uninstall() {
-    $db = Database::instance();
-    $db->query("DROP TABLE IF EXISTS {access_caches}");
-    $db->query("DROP TABLE IF EXISTS {access_intents}");
-    $db->query("DROP TABLE IF EXISTS {graphics_rules}");
-    $db->query("DROP TABLE IF EXISTS {items}");
-    $db->query("DROP TABLE IF EXISTS {logs}");
-    $db->query("DROP TABLE IF EXISTS {messages}");
-    $db->query("DROP TABLE IF EXISTS {modules}");
-    $db->query("DROP TABLE IF EXISTS {themes}");
-    $db->query("DROP TABLE IF EXISTS {incoming_translations}");
-    $db->query("DROP TABLE IF EXISTS {outgoing_translations}");
-    $db->query("DROP TABLE IF EXISTS {permissions}");
-    $db->query("DROP TABLE IF EXISTS {sessions}");
-    $db->query("DROP TABLE IF EXISTS {tasks}");
-    $db->query("DROP TABLE IF EXISTS {vars}");
-    foreach (array("albums", "resizes", "thumbs", "uploads",
-                   "modules", "logs", "database.php") as $entry) {
-      system("/bin/rm -rf " . VARPATH . $entry);
-    }
-  }
-}
diff --git a/core/helpers/core_menu.php b/core/helpers/core_menu.php
deleted file mode 100644
index eb208560..00000000
--- a/core/helpers/core_menu.php
+++ /dev/null
@@ -1,162 +0,0 @@
-admin) {
-      $menu->append($scaffold_menu = Menu::factory("submenu")
-                    ->id("scaffold")
-                    ->label("Scaffold"));
-      $scaffold_menu->append(Menu::factory("link")
-                             ->id("scaffold_home")
-                             ->label("Dashboard")
-                             ->url(url::site("scaffold")));
-    }
-
-    $menu->append(Menu::factory("link")
-                  ->id("home")
-                  ->label(t("Home"))
-                  ->url(url::site("albums/1")));
-
-    $item = $theme->item();
-
-    if (user::active()->admin || ($item && access::can("edit", $item))) {
-      $menu->append($options_menu = Menu::factory("submenu")
-                    ->id("options_menu")
-                    ->label(t("Options")));
-
-      if ($item && access::can("edit", $item)) {
-        $options_menu
-          ->append(Menu::factory("dialog")
-                   ->id("edit_item")
-                   ->label($item->is_album() ? t("Edit album") : t("Edit photo"))
-                   ->url(url::site("form/edit/{$item->type}s/$item->id")));
-
-        // @todo Move album options menu to the album quick edit pane
-        // @todo Create resized item quick edit pane menu
-        if ($item->is_album()) {
-          $options_menu
-            ->append(Menu::factory("dialog")
-                     ->id("add_item")
-                     ->label(t("Add a photo"))
-                     ->url(url::site("simple_uploader/app/$item->id")))
-            ->append(Menu::factory("dialog")
-                     ->id("add_album")
-                     ->label(t("Add an album"))
-                     ->url(url::site("form/add/albums/$item->id?type=album")))
-            ->append(Menu::factory("dialog")
-                     ->id("edit_permissions")
-                     ->label(t("Edit permissions"))
-                     ->url(url::site("permissions/browse/$item->id")));
-        }
-      }
-    }
-
-    if (user::active()->admin) {
-      $menu->append($admin_menu = Menu::factory("submenu")
-                    ->id("admin_menu")
-                    ->label(t("Admin")));
-      self::admin($admin_menu, $theme);
-      foreach (module::active() as $module) {
-        if ($module->name == "core") {
-          continue;
-        }
-        $class = "{$module->name}_menu";
-        if (method_exists($class, "admin")) {
-          call_user_func_array(array($class, "admin"), array(&$admin_menu, $theme));
-        }
-      }
-    }
-  }
-
-  static function album($menu, $theme) {
-  }
-
-  static function photo($menu, $theme) {
-    if (access::can("view_full", $theme->item())) {
-      $menu
-        ->append(Menu::factory("link")
-               ->id("fullsize")
-               ->label(t("View full size"))
-               ->url("#")
-               ->css_class("gFullSizeLink"));
-    }
-    $menu
-      ->append(Menu::factory("link")
-               ->id("album")
-               ->label(t("Return to album"))
-               ->url($theme->item()->parent()->url("show={$theme->item->id}"))
-               ->css_id("gAlbumLink"));
-  }
-
-  static function admin($menu, $theme) {
-    $menu
-      ->append(Menu::factory("link")
-               ->id("dashboard")
-               ->label(t("Dashboard"))
-               ->url(url::site("admin")))
-      ->append(Menu::factory("submenu")
-               ->id("settings_menu")
-               ->label(t("Settings"))
-               ->append(Menu::factory("link")
-                        ->id("graphics_toolkits")
-                        ->label(t("Graphics"))
-                        ->url(url::site("admin/graphics")))
-               ->append(Menu::factory("link")
-                        ->id("languages")
-                        ->label(t("Languages"))
-                        ->url(url::site("admin/languages")))
-               ->append(Menu::factory("link")
-                        ->id("l10n_mode")
-                        ->label(Session::instance()->get("l10n_mode", false)
-                                ? t("Stop translating") : t("Start translating"))
-                        ->url(url::site("l10n_client/toggle_l10n_mode?csrf=" .
-                                        access::csrf_token())))
-               ->append(Menu::factory("link")
-                        ->id("advanced")
-                        ->label("Advanced")
-                        ->url(url::site("admin/advanced_settings"))))
-      ->append(Menu::factory("link")
-               ->id("modules")
-               ->label(t("Modules"))
-               ->url(url::site("admin/modules")))
-      ->append(Menu::factory("submenu")
-               ->id("content_menu")
-               ->label(t("Content")))
-      ->append(Menu::factory("submenu")
-               ->id("appearance_menu")
-               ->label(t("Appearance"))
-               ->append(Menu::factory("link")
-                        ->id("themes")
-                        ->label(t("Theme Choice"))
-                        ->url(url::site("admin/themes")))
-               ->append(Menu::factory("link")
-                        ->id("theme_details")
-                        ->label(t("Theme Options"))
-                        ->url(url::site("admin/theme_details"))))
-      ->append(Menu::factory("link")
-               ->id("maintenance")
-               ->label(t("Maintenance"))
-               ->url(url::site("admin/maintenance")))
-      ->append(Menu::factory("submenu")
-               ->id("statistics_menu")
-               ->label(t("Statistics"))
-               ->url("#"));
-  }
-}
diff --git a/core/helpers/core_search.php b/core/helpers/core_search.php
deleted file mode 100644
index 9957a493..00000000
--- a/core/helpers/core_search.php
+++ /dev/null
@@ -1,24 +0,0 @@
-description, $item->name, $item->title));
-  }
-}
diff --git a/core/helpers/core_task.php b/core/helpers/core_task.php
deleted file mode 100644
index e078192c..00000000
--- a/core/helpers/core_task.php
+++ /dev/null
@@ -1,166 +0,0 @@
-count();
-    $tasks = array();
-    $tasks[] = Task_Definition::factory()
-                 ->callback("core_task::rebuild_dirty_images")
-                 ->name(t("Rebuild Images"))
-                 ->description($dirty_count ?
-                               t2("You have one out of date photo",
-                                  "You have %count out of date photos",
-                                  $dirty_count)
-                               : t("All your photos are up to date"))
-      ->severity($dirty_count ? log::WARNING : log::SUCCESS);
-
-    $tasks[] = Task_Definition::factory()
-                 ->callback("core_task::update_l10n")
-                 ->name(t("Update translations"))
-                 ->description(t("Download new and updated translated strings"))
-      ->severity(log::SUCCESS);
-
-    return $tasks;
-  }
-
-  /**
-   * Task that rebuilds all dirty images.
-   * @param Task_Model the task
-   */
-  static function rebuild_dirty_images($task) {
-    $result = graphics::find_dirty_images_query();
-    $remaining = $result->count();
-    $completed = $task->get("completed", 0);
-
-    $i = 0;
-    foreach ($result as $row) {
-      $item = ORM::factory("item", $row->id);
-      if ($item->loaded) {
-        graphics::generate($item);
-      }
-
-      $completed++;
-      $remaining--;
-
-      if (++$i == 2) {
-        break;
-      }
-    }
-
-    $task->status = t2("Updated: 1 image. Total: %total_count.",
-                       "Updated: %count images. Total: %total_count.",
-                       $completed,
-                       array("total_count" => ($remaining + $completed)));
-
-    if ($completed + $remaining > 0) {
-      $task->percent_complete = (int)(100 * $completed / ($completed + $remaining));
-    } else {
-      $task->percent_complete = 100;
-    }
-
-    $task->set("completed", $completed);
-    if ($remaining == 0) {
-      $task->done = true;
-      $task->state = "success";
-      site_status::clear("graphics_dirty");
-    }
-  }
-
-  static function update_l10n(&$task) {
-    $start = microtime(true);
-    $dirs = $task->get("dirs");
-    $files = $task->get("files");
-    $cache = $task->get("cache", array());
-    $i = 0;
-
-    switch ($task->get("mode", "init")) {
-    case "init":  // 0%
-      $dirs = array("core", "modules", "themes", "installer");
-      $files = array();
-      $task->set("mode", "find_files");
-      $task->status = t("Finding files");
-      break;
-
-    case "find_files":  // 0% - 10%
-      while (($dir = array_pop($dirs)) && microtime(true) - $start < 0.5) {
-        if (basename($dir) == "tests") {
-          continue;
-        }
-
-        foreach (glob(DOCROOT . "$dir/*") as $path) {
-          $relative_path = str_replace(DOCROOT, "", $path);
-          if (is_dir($path)) {
-            $dirs[] = $relative_path;
-          } else {
-            $files[] = $relative_path;
-          }
-        }
-      }
-
-      $task->status = t2("Finding files: found 1 file",
-                         "Finding files: found %count files", count($files));
-
-      if (!$dirs) {
-        $task->set("mode", "scan_files");
-        $task->set("total_files", count($files));
-        $task->status = t("Scanning files");
-        $task->percent_complete = 10;
-      }
-      break;
-
-    case "scan_files": // 10% - 90%
-      while (($file = array_pop($files)) && microtime(true) - $start < 0.5) {
-        $file = DOCROOT . $file;
-        switch (pathinfo($file, PATHINFO_EXTENSION)) {
-        case "php":
-          l10n_scanner::scan_php_file($file, $cache);
-          break;
-
-        case "info":
-          l10n_scanner::scan_info_file($file, $cache);
-          break;
-        }
-      }
-
-      $total_files = $task->get("total_files");
-      $task->status = t2("Scanning files: scanned 1 file",
-                         "Scanning files: scanned %count files", $total_files - count($files));
-
-      $task->percent_complete = 10 + 80 * ($total_files - count($files)) / $total_files;
-      if (empty($files)) {
-        $task->set("mode", "fetch_updates");
-        $task->status = t("Fetching updates");
-        $task->percent_complete = 90;
-      }
-      break;
-
-    case "fetch_updates":  // 90% - 100%
-      l10n_client::fetch_updates();
-      $task->done = true;
-      $task->state = "success";
-      $task->status = t("Translations installed/updated");
-      $task->percent_complete = 100;
-    }
-
-    $task->set("files", $files);
-    $task->set("dirs", $dirs);
-    $task->set("cache", $cache);
-  }
-}
\ No newline at end of file
diff --git a/core/helpers/core_theme.php b/core/helpers/core_theme.php
deleted file mode 100644
index 28f544a1..00000000
--- a/core/helpers/core_theme.php
+++ /dev/null
@@ -1,137 +0,0 @@
-get("debug")) {
-      $buf .= "";
-    }
-    if (($theme->page_type == "album" || $theme->page_type == "photo")
-        && access::can("edit", $theme->item())) {
-      $buf .= "";
-      $buf .= html::script("core/js/quick.js");
-    }
-    if ($theme->page_type == "photo" && access::can("view_full", $theme->item())) {
-      $buf .= "";
-      $buf .= html::script("core/js/fullsize.js");
-    }
-
-    if ($session->get("l10n_mode", false)) {
-      $buf .= "";
-      $buf .= html::script("lib/jquery.cookie.js");
-      $buf .= html::script("core/js/l10n_client.js");
-    }
-
-    return $buf;
-  }
-
-  static function resize_top($theme, $item) {
-    if (access::can("edit", $item)) {
-      $edit_link = url::site("quick/pane/$item->id?page_type=photo");
-      return "
"; - } - } - - static function resize_bottom($theme, $item) { - if (access::can("edit", $item)) { - return "
"; - } - } - - static function thumb_top($theme, $child) { - if (access::can("edit", $child)) { - $edit_link = url::site("quick/pane/$child->id?page_type=album"); - return "
"; - } - } - - static function thumb_bottom($theme, $child) { - if (access::can("edit", $child)) { - return "
"; - } - } - - static function admin_head($theme) { - $session = Session::instance(); - $buf = ""; - if ($session->get("debug")) { - $buf .= ""; - } - - if ($session->get("l10n_mode", false)) { - $buf .= ""; - $buf .= html::script("lib/jquery.cookie.js"); - $buf .= html::script("core/js/l10n_client.js"); - } - - return $buf; - } - - static function page_bottom($theme) { - $session = Session::instance(); - if ($session->get("profiler", false)) { - $profiler = new Profiler(); - $profiler->render(); - } - if ($session->get("l10n_mode", false)) { - return L10n_Client_Controller::l10n_form(); - } - - if ($session->get("after_install")) { - $session->delete("after_install"); - return new View("after_install_loader.html"); - } - } - - static function admin_page_bottom($theme) { - $session = Session::instance(); - if ($session->get("profiler", false)) { - $profiler = new Profiler(); - $profiler->render(); - } - if ($session->get("l10n_mode", false)) { - return L10n_Client_Controller::l10n_form(); - } - } - - static function credits() { - return "
  • " . - t("Powered by Gallery %version", - array("url" => "http://gallery.menalto.com", - "version" => module::get_var("core", "version"))) . - "
  • "; - } - - static function admin_credits() { - return core_theme::credits(); - } -} \ No newline at end of file diff --git a/core/helpers/dir.php b/core/helpers/dir.php deleted file mode 100644 index 1717bdd9..00000000 --- a/core/helpers/dir.php +++ /dev/null @@ -1,40 +0,0 @@ -isDot()) { - unset($resource); - continue; - } else if ($resource->isFile()) { - unlink($resource->getPathName()); - } else if ($resource->isDir()) { - dir::unlink($resource->getRealPath()); - } - unset($resource); - } - return @rmdir($path); - } - return false; - } - - -} diff --git a/core/helpers/graphics.php b/core/helpers/graphics.php deleted file mode 100644 index 805a95c0..00000000 --- a/core/helpers/graphics.php +++ /dev/null @@ -1,387 +0,0 @@ - 200, "height" => 200, "master" => Image::AUTO), 100); - * - * Specifies that "core" is adding a rule to resize thumbnails down to a max of 200px on - * the longest side. The core module adds default rules at a priority of 100. You can set - * higher and lower priorities to perform operations before or after this fires. - * - * @param string $module_name the module that added the rule - * @param string $target the target for this operation ("thumb" or "resize") - * @param string $operation the name of the operation - * @param array $args arguments to the operation - * @param integer $priority the priority for this rule (lower priorities are run first) - */ - static function add_rule($module_name, $target, $operation, $args, $priority) { - $rule = ORM::factory("graphics_rule"); - $rule->module_name = $module_name; - $rule->target = $target; - $rule->operation = $operation; - $rule->priority = $priority; - $rule->args = serialize($args); - $rule->active = true; - $rule->save(); - - self::mark_dirty($target == "thumb", $target == "resize"); - } - - /** - * Remove any matching graphics rules - * @param string $module_name the module that added the rule - * @param string $target the target for this operation ("thumb" or "resize") - * @param string $operation the name of the operation - */ - static function remove_rule($module_name, $target, $operation) { - ORM::factory("graphics_rule") - ->where("module_name", $module_name) - ->where("target", $target) - ->where("operation", $operation) - ->delete_all(); - - self::mark_dirty($target == "thumb", $target == "resize"); - } - - /** - * Remove all rules for this module - * @param string $module_name - */ - static function remove_rules($module_name) { - $status = Database::instance()->delete("graphics_rules", array("module_name" => $module_name)); - if (count($status)) { - self::mark_dirty(true, true); - } - } - - /** - * Activate the rules for this module, typically done when the module itself is deactivated. - * Note that this does not mark images as dirty so that if you deactivate and reactivate a - * module it won't cause all of your images to suddenly require a rebuild. - */ - static function activate_rules($module_name) { - Database::instance() - ->update("graphics_rules",array("active" => true), array("module_name" => $module_name)); - } - - /** - * Deactivate the rules for this module, typically done when the module itself is deactivated. - * Note that this does not mark images as dirty so that if you deactivate and reactivate a - * module it won't cause all of your images to suddenly require a rebuild. - */ - static function deactivate_rules($module_name) { - Database::instance() - ->update("graphics_rules",array("active" => false), array("module_name" => $module_name)); - } - - /** - * Rebuild the thumb and resize for the given item. - * @param Item_Model $item - */ - static function generate($item) { - if ($item->is_album()) { - if (!$cover = $item->album_cover()) { - return; - } - $input_file = $cover->file_path(); - $input_item = $cover; - } else { - $input_file = $item->file_path(); - $input_item = $item; - } - - if ($item->thumb_dirty) { - $ops["thumb"] = $item->thumb_path(); - } - if ($item->resize_dirty && !$item->is_album() && !$item->is_movie()) { - $ops["resize"] = $item->resize_path(); - } - - if (empty($ops)) { - return; - } - - try { - foreach ($ops as $target => $output_file) { - if ($input_item->is_movie()) { - // Convert the movie to a JPG first - $output_file = preg_replace("/...$/", "jpg", $output_file); - movie::extract_frame($input_file, $output_file); - $working_file = $output_file; - } else { - $working_file = $input_file; - } - - foreach (ORM::factory("graphics_rule") - ->where("target", $target) - ->where("active", true) - ->orderby("priority", "asc") - ->find_all() as $rule) { - $args = array($working_file, $output_file, unserialize($rule->args)); - call_user_func_array(array("graphics", $rule->operation), $args); - $working_file = $output_file; - } - } - - if (!empty($ops["thumb"])) { - $dims = getimagesize($item->thumb_path()); - $item->thumb_width = $dims[0]; - $item->thumb_height = $dims[1]; - $item->thumb_dirty = 0; - } - - if (!empty($ops["resize"])) { - $dims = getimagesize($item->resize_path()); - $item->resize_width = $dims[0]; - $item->resize_height = $dims[1]; - $item->resize_dirty = 0; - } - $item->save(); - } catch (Kohana_Exception $e) { - // Something went wrong rebuilding the image. Leave it dirty and move on. - // @todo we should handle this better. - Kohana::log("error", "Caught exception rebuilding image: {$item->title}\n" . - $e->getTraceAsString()); - } - } - - /** - * Resize an image. Valid options are width, height and master. Master is one of the Image - * master dimension constants. - * - * @param string $input_file - * @param string $output_file - * @param array $options - */ - static function resize($input_file, $output_file, $options) { - if (!self::$init) { - self::init_toolkit(); - } - - if (filesize($input_file) == 0) { - throw new Exception("@todo MALFORMED_INPUT_FILE"); - } - - $dims = getimagesize($input_file); - if (max($dims[0], $dims[1]) < min($options["width"], $options["height"])) { - // Image would get upscaled; do nothing - copy($input_file, $output_file); - } else { - Image::factory($input_file) - ->resize($options["width"], $options["height"], $options["master"]) - ->quality(module::get_var("core", "image_quality")) - ->save($output_file); - } - } - - /** - * Rotate an image. Valid options are degrees - * - * @param string $input_file - * @param string $output_file - * @param array $options - */ - static function rotate($input_file, $output_file, $options) { - if (!self::$init) { - self::init_toolkit(); - } - - Image::factory($input_file) - ->quality(module::get_var("core", "image_quality")) - ->rotate($options["degrees"]) - ->save($output_file); - } - - /** - * Overlay an image on top of the input file. - * - * Valid options are: file, mime_type, position, transparency_percent, padding - * - * Valid positions: northwest, north, northeast, - * west, center, east, - * southwest, south, southeast - * - * padding is in pixels - * - * @param string $input_file - * @param string $output_file - * @param array $options - */ - static function composite($input_file, $output_file, $options) { - if (!self::$init) { - self::init_toolkit(); - } - - list ($width, $height) = getimagesize($input_file); - list ($w_width, $w_height) = getimagesize($options["file"]); - - $pad = isset($options["padding"]) ? $options["padding"] : 10; - $top = $pad; - $left = $pad; - $y_center = max($height / 2 - $w_height / 2, $pad); - $x_center = max($width / 2 - $w_width / 2, $pad); - $bottom = max($height - $w_height - $pad, $pad); - $right = max($width - $w_width - $pad, $pad); - - switch ($options["position"]) { - case "northwest": $x = $left; $y = $top; break; - case "north": $x = $x_center; $y = $top; break; - case "northeast": $x = $right; $y = $top; break; - case "west": $x = $left; $y = $y_center; break; - case "center": $x = $x_center; $y = $y_center; break; - case "east": $x = $right; $y = $y_center; break; - case "southwest": $x = $left; $y = $bottom; break; - case "south": $x = $x_center; $y = $bottom; break; - case "southeast": $x = $right; $y = $bottom; break; - } - - Image::factory($input_file) - ->composite($options["file"], $x, $y, $options["transparency"]) - ->quality(module::get_var("core", "image_quality")) - ->save($output_file); - } - - /** - * Return a query result that locates all items with dirty images. - * @return Database_Result Query result - */ - static function find_dirty_images_query() { - return Database::instance()->query( - "SELECT `id` FROM {items} " . - "WHERE ((`thumb_dirty` = 1 AND (`type` <> 'album' OR `album_cover_item_id` IS NOT NULL))" . - " OR (`resize_dirty` = 1 AND `type` = 'photo')) " . - " AND `id` != 1"); - } - - /** - * Mark thumbnails and resizes as dirty. They will have to be rebuilt. - */ - static function mark_dirty($thumbs, $resizes) { - if ($thumbs || $resizes) { - $db = Database::instance(); - $fields = array(); - if ($thumbs) { - $fields["thumb_dirty"] = 1; - } - if ($resizes) { - $fields["resize_dirty"] = 1; - } - $db->update("items", $fields, true); - } - - $count = self::find_dirty_images_query()->count(); - if ($count) { - site_status::warning( - t2("One of your photos is out of date. Click here to fix it", - "%count of your photos are out of date. Click here to fix them", - $count, - array("attrs" => sprintf( - 'href="%s" class="gDialogLink"', - url::site("admin/maintenance/start/core_task::rebuild_dirty_images?csrf=__CSRF__")))), - "graphics_dirty"); - } - } - - /** - * Detect which graphics toolkits are available on this system. Return an array of key value - * pairs where the key is one of gd, imagemagick, graphicsmagick and the value is information - * about that toolkit. For GD we return the version string, and for ImageMagick and - * GraphicsMagick we return the path to the directory containing the appropriate binaries. - */ - static function detect_toolkits() { - $gd = function_exists("gd_info") ? gd_info() : array(); - $exec = function_exists("exec"); - if (!isset($gd["GD Version"])) { - $gd["GD Version"] = false; - } - return array("gd" => $gd, - "imagemagick" => $exec ? dirname(exec("which convert")) : false, - "graphicsmagick" => $exec ? dirname(exec("which gm")) : false); - } - - /** - * This needs to be run once, after the initial install, to choose a graphics toolkit. - */ - static function choose_default_toolkit() { - // Detect a graphics toolkit - $toolkits = graphics::detect_toolkits(); - foreach (array("imagemagick", "graphicsmagick", "gd") as $tk) { - if ($toolkits[$tk]) { - module::set_var("core", "graphics_toolkit", $tk); - module::set_var("core", "graphics_toolkit_path", $tk == "gd" ? "" : $toolkits[$tk]); - break; - } - } - if (!module::get_var("core", "graphics_toolkit")) { - site_status::warning( - t("Graphics toolkit missing! Please choose a toolkit", - array("url" => url::site("admin/graphics"))), - "missing_graphics_toolkit"); - } - } - - /** - * Choose which driver the Kohana Image library uses. - */ - static function init_toolkit() { - switch(module::get_var("core", "graphics_toolkit")) { - case "gd": - Kohana::config_set("image.driver", "GD"); - break; - - case "imagemagick": - Kohana::config_set("image.driver", "ImageMagick"); - Kohana::config_set( - "image.params.directory", module::get_var("core", "graphics_toolkit_path")); - break; - - case "graphicsmagick": - Kohana::config_set("image.driver", "GraphicsMagick"); - Kohana::config_set( - "image.params.directory", module::get_var("core", "graphics_toolkit_path")); - break; - } - - self::$init = 1; - } - - /** - * Verify that a specific graphics function is available with the active toolkit. - * @param string $func (eg rotate, resize) - * @return boolean - */ - static function can($func) { - if (module::get_var("core", "graphics_toolkit") == "gd" && - $func == "rotate" && - !function_exists("imagerotate")) { - return false; - } - - return true; - } -} diff --git a/core/helpers/item.php b/core/helpers/item.php deleted file mode 100644 index 7daaf1e1..00000000 --- a/core/helpers/item.php +++ /dev/null @@ -1,105 +0,0 @@ -parent(); - if ($parent->album_cover_item_id == $source->id) { - if ($parent->children_count() > 1) { - foreach ($parent->children(2) as $child) { - if ($child->id != $source->id) { - $new_cover_item = $child; - break; - } - } - item::make_album_cover($new_cover_item); - } else { - item::remove_album_cover($parent); - } - } - - $source->move_to($target); - - // If the target has no cover item, make this it. - if ($target->album_cover_item_id == null) { - item::make_album_cover($source); - } - } - - static function make_album_cover($item) { - $parent = $item->parent(); - access::required("edit", $parent); - - model_cache::clear("item", $parent->album_cover_item_id); - $parent->album_cover_item_id = $item->is_album() ? $item->album_cover_item_id : $item->id; - $parent->thumb_dirty = 1; - $parent->save(); - graphics::generate($parent); - $grand_parent = $parent->parent(); - if ($grand_parent && $grand_parent->album_cover_item_id == null) { - item::make_album_cover($parent); - } - } - - static function remove_album_cover($album) { - access::required("edit", $album); - @unlink($album->thumb_path()); - - model_cache::clear("item", $album->album_cover_item_id) ; - $album->album_cover_item_id = null; - $album->thumb_width = 0; - $album->thumb_height = 0; - $album->thumb_dirty = 1; - $album->save(); - graphics::generate($album); - } - - static function validate_no_slashes($input) { - if (strpos($input->value, "/") !== false) { - $input->add_error("no_slashes", 1); - } - } - - static function validate_no_trailing_period($input) { - if (rtrim($input->value, ".") !== $input->value) { - $input->add_error("no_trailing_period", 1); - } - } - - static function validate_no_name_conflict($input) { - $itemid = Input::instance()->post("item"); - if (is_array($itemid)) { - $itemid = $itemid[0]; - } - $item = ORM::factory("item") - ->in("id", $itemid) - ->find(); - if (Database::instance() - ->from("items") - ->where("parent_id", $item->parent_id) - ->where("id <>", $item->id) - ->where("name", $input->value) - ->count_records()) { - $input->add_error("conflict", 1); - } - } -} \ No newline at end of file diff --git a/core/helpers/l10n_client.php b/core/helpers/l10n_client.php deleted file mode 100644 index ec4c5429..00000000 --- a/core/helpers/l10n_client.php +++ /dev/null @@ -1,203 +0,0 @@ - $version, - "client_token" => self::client_token(), - "signature" => $signature, - "uid" => self::server_uid($api_key))); - if (!remote::success($response_status)) { - return false; - } - return true; - } - - static function fetch_updates() { - $request->locales = array(); - $request->messages = new stdClass(); - - $locales = locale::installed(); - foreach ($locales as $locale => $locale_data) { - $request->locales[] = $locale; - } - - // @todo Batch requests (max request size) - foreach (Database::instance() - ->select("key", "locale", "revision", "translation") - ->from("incoming_translations") - ->get() - ->as_array() as $row) { - if (!isset($request->messages->{$row->key})) { - $request->messages->{$row->key} = 1; - } - if (!empty($row->revision) && !empty($row->translation)) { - if (!is_object($request->messages->{$row->key})) { - $request->messages->{$row->key} = new stdClass(); - } - $request->messages->{$row->key}->{$row->locale} = $row->revision; - } - } - // @todo Include messages from outgoing_translations? - - $request_data = json_encode($request); - $url = self::_server_url() . "?q=translations/fetch"; - list ($response_data, $response_status) = remote::post($url, array("data" => $request_data)); - if (!remote::success($response_status)) { - throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED " . $response_status); - } - if (empty($response_data)) { - log::info("translations", "Translations fetch request resulted in an empty response"); - return; - } - - $response = json_decode($response_data); - - // Response format (JSON payload): - // [{key:, translation: , rev:, locale:}, - // {key:, ...} - // ] - $count = count($response); - log::info("translations", "Installed $count new / updated translation messages"); - - foreach ($response as $message_data) { - // @todo Better input validation - if (empty($message_data->key) || empty($message_data->translation) || - empty($message_data->locale) || empty($message_data->rev)) { - throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED: Invalid response data"); - } - $key = $message_data->key; - $locale = $message_data->locale; - $revision = $message_data->rev; - $translation = serialize(json_decode($message_data->translation)); - - // @todo Should we normalize the incoming_translations table into messages(id, key, message) - // and incoming_translations(id, translation, locale, revision)? Or just allow - // incoming_translations.message to be NULL? - $locale = $message_data->locale; - $entry = ORM::factory("incoming_translation") - ->where(array("key" => $key, "locale" => $locale)) - ->find(); - if (!$entry->loaded) { - // @todo Load a message key -> message (text) dict into memory outside of this loop - $root_entry = ORM::factory("incoming_translation") - ->where(array("key" => $key, "locale" => "root")) - ->find(); - $entry->key = $key; - $entry->message = $root_entry->message; - $entry->locale = $locale; - } - $entry->revision = $revision; - $entry->translation = $translation; - $entry->save(); - } - } - - static function submit_translations() { - // Request format (HTTP POST): - // client_token = - // uid = - // signature = md5(user_api_key($uid, $client_token) . $data . $client_token)) - // data = // JSON payload - // - // {: {message: - // translations: {: , - // : ...}}, - // : {...} - // } - - // @todo Batch requests (max request size) - // @todo include base_revision in submission / how to handle resubmissions / edit fights? - foreach (Database::instance() - ->select("key", "message", "locale", "base_revision", "translation") - ->from("outgoing_translations") - ->get() as $row) { - $key = $row->key; - if (!isset($request->{$key})) { - $request->{$key}->message = json_encode(unserialize($row->message)); - } - $request->{$key}->translations->{$row->locale} = json_encode(unserialize($row->translation)); - } - - // @todo reduce memory consumpotion, e.g. free $request - $request_data = json_encode($request); - $url = self::_server_url() . "?q=translations/submit"; - $signature = self::_sign($request_data); - - list ($response_data, $response_status) = remote::post( - $url, array("data" => $request_data, - "client_token" => self::client_token(), - "signature" => $signature, - "uid" => self::server_uid())); - - if (!remote::success($response_status)) { - throw new Exception("@todo TRANSLATIONS_SUBMISSION_FAILED " . $response_status); - } - if (empty($response_data)) { - return; - } - - $response = json_decode($response_data); - // Response format (JSON payload): - // [{key:, locale:, rev:, status:}, - // {key:, ...} - // ] - - // @todo Move messages out of outgoing into incoming, using new rev? - // @todo show which messages have been rejected / are pending? - } -} \ No newline at end of file diff --git a/core/helpers/l10n_scanner.php b/core/helpers/l10n_scanner.php deleted file mode 100644 index 80b6f01c..00000000 --- a/core/helpers/l10n_scanner.php +++ /dev/null @@ -1,154 +0,0 @@ -select("key") - ->from("incoming_translations") - ->where("locale", "root") - ->get() as $row) { - $cache[$row->key] = true; - } - } - - $key = I18n::get_message_key($message); - if (array_key_exists($key, $cache)) { - return $cache[$key]; - } - - $entry = ORM::factory("incoming_translation", array("key" => $key)); - if (!$entry->loaded) { - $entry->key = $key; - $entry->message = serialize($message); - $entry->locale = "root"; - $entry->save(); - } - } - - static function scan_php_file($file, &$cache) { - $code = file_get_contents($file); - $raw_tokens = token_get_all($code); - unset($code); - - $tokens = array(); - $func_token_list = array("t" => array(), "t2" => array()); - $token_number = 0; - // Filter out HTML / whitespace, and build a lookup for global function calls. - foreach ($raw_tokens as $token) { - if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) { - if (is_array($token)) { - if ($token[0] == T_STRING && in_array($token[1], array("t", "t2"))) { - $func_token_list[$token[1]][] = $token_number; - } - } - $tokens[] = $token; - $token_number++; - } - } - unset($raw_tokens); - - if (!empty($func_token_list["t"])) { - l10n_scanner::_parse_t_calls($tokens, $func_token_list["t"], $cache); - } - if (!empty($func_token_list["t2"])) { - l10n_scanner::_parse_plural_calls($tokens, $func_token_list["t2"], $cache); - } - } - - static function scan_info_file($file, &$cache) { - $code = file_get_contents($file); - if (preg_match("#name\s*?=\s*(.*?)\ndescription\s*?=\s*(.*)\n#", $code, $matches)) { - unset($matches[0]); - foreach ($matches as $string) { - l10n_scanner::process_message($string, $cache); - } - } - } - - private static function _parse_t_calls(&$tokens, &$call_list, &$cache) { - foreach ($call_list as $index) { - $function_name = $tokens[$index++]; - $parens = $tokens[$index++]; - $first_param = $tokens[$index++]; - $next_token = $tokens[$index]; - - if ($parens == "(") { - if (in_array($next_token, array(")", ",")) - && (is_array($first_param) && ($first_param[0] == T_CONSTANT_ENCAPSED_STRING))) { - $message = self::_escape_quoted_string($first_param[1]); - l10n_scanner::process_message($message, $cache); - } else { - // t() found, but inside is something which is not a string literal. - // @todo Call status callback with error filename/line. - } - } - } - } - - private static function _parse_plural_calls(&$tokens, &$call_list, &$cache) { - foreach ($call_list as $index) { - $function_name = $tokens[$index++]; - $parens = $tokens[$index++]; - $first_param = $tokens[$index++]; - $first_separator = $tokens[$index++]; - $second_param = $tokens[$index++]; - $next_token = $tokens[$index]; - - if ($parens == "(") { - if ($first_separator == "," && $next_token == "," - && is_array($first_param) && $first_param[0] == T_CONSTANT_ENCAPSED_STRING - && is_array($second_param) && $second_param[0] == T_CONSTANT_ENCAPSED_STRING) { - $singular = self::_escape_quoted_string($first_param[1]); - $plural = self::_escape_quoted_string($first_param[1]); - l10n_scanner::process_message(array("one" => $singular, "other" => $plural), $cache); - } else { - // t2() found, but inside is something which is not a string literal. - // @todo Call status callback with error filename/line. - } - } - } - } - - /** - * Escape quotes in a strings depending on the surrounding - * quote type used. - * - * @param $str The strings to escape - */ - private static function _escape_quoted_string($str) { - $quo = substr($str, 0, 1); - $str = substr($str, 1, -1); - if ($quo == '"') { - $str = stripcslashes($str); - } else { - $str = strtr($str, array("\\'" => "'", "\\\\" => "\\")); - } - return addcslashes($str, "\0..\37\\\""); - } -} diff --git a/core/helpers/locale.php b/core/helpers/locale.php deleted file mode 100644 index b707637f..00000000 --- a/core/helpers/locale.php +++ /dev/null @@ -1,119 +0,0 @@ -$code)) { - $installed[$code] = $available[$code]; - } - } - return $installed; - } - - static function update_installed($locales) { - // Ensure that the default is included... - $default = module::get_var("core", "default_locale"); - $locales = array_merge($locales, array($default)); - - module::set_var("core", "installed_locales", join("|", $locales)); - } - - // @todo Might want to add a localizable language name as well. - private static function _init_language_data() { - $l["af_ZA"] = "Afrikaans"; // Afrikaans - $l["ar_SA"] = "العربية"; // Arabic - $l["bg_BG"] = "Български"; // Bulgarian - $l["ca_ES"] = "Catalan"; // Catalan - $l["cs_CZ"] = "Česky"; // Czech - $l["da_DK"] = "Dansk"; // Danish - $l["de_DE"] = "Deutsch"; // German - $l["el_GR"] = "Greek"; // Greek - $l["en_GB"] = "English (UK)"; // English (UK) - $l["en_US"] = "English (US)"; // English (US) - $l["es_AR"] = "Español (AR)"; // Spanish (AR) - $l["es_ES"] = "Español"; // Spanish (ES) - $l["es_MX"] = "Español (MX)"; // Spanish (MX) - $l["et_EE"] = "Eesti"; // Estonian - $l["eu_ES"] = "Euskara"; // Basque - $l["fa_IR"] = "فارسي"; // Farsi - $l["fi_FI"] = "Suomi"; // Finnish - $l["fr_FR"] = "Français"; // French - $l["ga_IE"] = "Gaeilge"; // Irish - $l["he_IL"] = "עברית"; // Hebrew - $l["hu_HU"] = "Magyar"; // Hungarian - $l["is_IS"] = "Icelandic"; // Icelandic - $l["it_IT"] = "Italiano"; // Italian - $l["ja_JP"] = "日本語"; // Japanese - $l["ko_KR"] = "한국말"; // Korean - $l["lt_LT"] = "Lietuvių"; // Lithuanian - $l["lv_LV"] = "Latviešu"; // Latvian - $l["nl_NL"] = "Nederlands"; // Dutch - $l["no_NO"] = "Norsk bokmål"; // Norwegian - $l["pl_PL"] = "Polski"; // Polish - $l["pt_BR"] = "Português Brasileiro"; // Portuguese (BR) - $l["pt_PT"] = "Português"; // Portuguese (PT) - $l["ro_RO"] = "Română"; // Romanian - $l["ru_RU"] = "Русский"; // Russian - $l["sk_SK"] = "Slovenčina"; // Slovak - $l["sl_SI"] = "Slovenščina"; // Slovenian - $l["sr_CS"] = "Srpski"; // Serbian - $l["sv_SE"] = "Svenska"; // Swedish - $l["tr_TR"] = "Türkçe"; // Turkish - $l["uk_UA"] = "Українська"; // Ukrainian - $l["vi_VN"] = "Tiếng Việt"; // Vietnamese - $l["zh_CN"] = "简体中文"; // Chinese (CN) - $l["zh_TW"] = "繁體中文"; // Chinese (TW) - asort($l, SORT_LOCALE_STRING); - self::$locales = $l; - } - - static function display_name($locale=null) { - if (empty(self::$locales)) { - self::_init_language_data(); - } - $locale or $locale = I18n::instance()->locale(); - - return self::$locales["$locale"]; - } - - static function is_rtl($locale) { - return in_array($locale, array("he_IL", "fa_IR", "ar_SA")); - } -} \ No newline at end of file diff --git a/core/helpers/log.php b/core/helpers/log.php deleted file mode 100644 index 451f985a..00000000 --- a/core/helpers/log.php +++ /dev/null @@ -1,108 +0,0 @@ -category = $category; - $log->message = $message; - $log->severity = $severity; - $log->html = $html; - $log->url = substr(url::abs_current(true), 0, 255); - $log->referer = request::referrer(null); - $log->timestamp = time(); - $log->user_id = user::active()->id; - $log->save(); - } - - - /** - * Convert a message severity to a CSS class - * @param integer $severity - * @return string - */ - static function severity_class($severity) { - switch($severity) { - case self::SUCCESS: - return "gSuccess"; - - case self::INFO: - return "gInfo"; - - case self::WARNING: - return "gWarning"; - - case self::ERROR: - return "gError"; - } - } -} diff --git a/core/helpers/message.php b/core/helpers/message.php deleted file mode 100644 index af3b96cc..00000000 --- a/core/helpers/message.php +++ /dev/null @@ -1,108 +0,0 @@ -get("messages"); - $status[] = array($msg, $severity); - $session->set("messages", $status); - } - - /** - * Get any pending messages. There are two types of messages, transient and permanent. - * Permanent messages are used to let the admin know that there are pending administrative - * issues that need to be resolved. Transient ones are only displayed once. - * @return html text - */ - static function get() { - $buf = array(); - - $messages = Session::instance()->get_once("messages", array()); - foreach ($messages as $msg) { - $buf[] = "
  • $msg[0]
  • "; - } - if ($buf) { - return "
      " . implode("", $buf) . "
    "; - } - } - - /** - * Convert a message severity to a CSS class - * @param integer $severity - * @return string - */ - static function severity_class($severity) { - switch($severity) { - case self::SUCCESS: - return "gSuccess"; - - case self::INFO: - return "gInfo"; - - case self::WARNING: - return "gWarning"; - - case self::ERROR: - return "gError"; - } - } -} diff --git a/core/helpers/model_cache.php b/core/helpers/model_cache.php deleted file mode 100644 index 2649fdbd..00000000 --- a/core/helpers/model_cache.php +++ /dev/null @@ -1,46 +0,0 @@ -$model_name->$field_name->$id)) { - $model = ORM::factory($model_name)->where($field_name, $id)->find(); - if (!$model->loaded) { - throw new Exception("@todo MISSING_MODEL $model_name:$id"); - } - self::$cache->$model_name->$field_name->$id = $model; - } - - return self::$cache->$model_name->$field_name->$id; - } - - static function clear($model_name, $id, $field_name="id") { - if (!empty(self::$cache->$model_name->$field_name->$id)) { - unset(self::$cache->$model_name->$field_name->$id); - } - } - - static function set($model) { - self::$cache->{$model->object_name} - ->{$model->primary_key} - ->{$model->{$model->primary_key}} = $model; - } -} diff --git a/core/helpers/module.php b/core/helpers/module.php deleted file mode 100644 index a48c89ed..00000000 --- a/core/helpers/module.php +++ /dev/null @@ -1,357 +0,0 @@ -loaded) { - $module->name = $module_name; - $module->active = $module_name == "core"; // only core is active by default - } - $module->version = 1; - $module->save(); - Kohana::log("debug", "$module_name: version is now $version"); - } - - /** - * Load the corresponding Module_Model - * @param string $module_name - */ - static function get($module_name) { - // @todo can't easily use model_cache here because it throw an exception on missing models. - return ORM::factory("module", array("name" => $module_name)); - } - - /** - * Check to see if a module is installed - * @param string $module_name - */ - static function is_installed($module_name) { - return array_key_exists($module_name, self::$modules); - } - - /** - * Check to see if a module is active - * @param string $module_name - */ - static function is_active($module_name) { - return array_key_exists($module_name, self::$modules) && - self::$modules[$module_name]->active; - } - - /** - * Return the list of available modules, including uninstalled modules. - */ - static function available() { - $modules = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS); - foreach (array_merge(array("core/module.info"), glob(MODPATH . "*/module.info")) as $file) { - $module_name = basename(dirname($file)); - $modules->$module_name = new ArrayObject(parse_ini_file($file), ArrayObject::ARRAY_AS_PROPS); - $modules->$module_name->installed = self::is_installed($module_name); - $modules->$module_name->active = self::is_active($module_name); - $modules->$module_name->version = self::get_version($module_name); - $modules->$module_name->locked = false; - } - - // Lock certain modules - $modules->core->locked = true; - $modules->user->locked = true; - $modules->ksort(); - - return $modules; - } - - /** - * Return a list of all the active modules in no particular order. - */ - static function active() { - return self::$active; - } - - /** - * Install a module. This will call _installer::install(), which is responsible for - * creating database tables, setting module variables and and calling module::set_version(). - * Note that after installing, the module must be activated before it is available for use. - * @param string $module_name - */ - static function install($module_name) { - $kohana_modules = Kohana::config("core.modules"); - $kohana_modules[] = MODPATH . $module_name; - Kohana::config_set("core.modules", $kohana_modules); - - $installer_class = "{$module_name}_installer"; - if (method_exists($installer_class, "install")) { - call_user_func_array(array($installer_class, "install"), array()); - } - - // Now the module is installed but inactive, so don't leave it in the active path - array_pop($kohana_modules); - Kohana::config_set("core.modules", $kohana_modules); - - log::success( - "module", t("Installed module %module_name", array("module_name" => $module_name))); - } - - /** - * Activate an installed module. This will call _installer::activate() which should take - * any steps to make sure that the module is ready for use. This will also activate any - * existing graphics rules for this module. - * @param string $module_name - */ - static function activate($module_name) { - $kohana_modules = Kohana::config("core.modules"); - $kohana_modules[] = MODPATH . $module_name; - Kohana::config_set("core.modules", $kohana_modules); - - $installer_class = "{$module_name}_installer"; - if (method_exists($installer_class, "activate")) { - call_user_func_array(array($installer_class, "activate"), array()); - } - - $module = self::get($module_name); - if ($module->loaded) { - $module->active = true; - $module->save(); - } - - self::load_modules(); - graphics::activate_rules($module_name); - log::success( - "module", t("Activated module %module_name", array("module_name" => $module_name))); - } - - /** - * Deactivate an installed module. This will call _installer::deactivate() which - * should take any cleanup steps to make sure that the module isn't visible in any way. - * @param string $module_name - */ - static function deactivate($module_name) { - $installer_class = "{$module_name}_installer"; - if (method_exists($installer_class, "deactivate")) { - call_user_func_array(array($installer_class, "deactivate"), array()); - } - - $module = self::get($module_name); - if ($module->loaded) { - $module->active = false; - $module->save(); - } - - self::load_modules(); - graphics::deactivate_rules($module_name); - log::success( - "module", t("Deactivated module %module_name", array("module_name" => $module_name))); - } - - /** - * Uninstall a deactivated module. This will call _installer::uninstall() which should - * take whatever steps necessary to make sure that all traces of a module are gone. - * @param string $module_name - */ - static function uninstall($module_name) { - $installer_class = "{$module_name}_installer"; - if (method_exists($installer_class, "uninstall")) { - call_user_func(array($installer_class, "uninstall")); - } - - graphics::remove_rule($module_name); - $module = self::get($module_name); - if ($module->loaded) { - $module->delete(); - } - - // We could delete the module vars here too, but it's nice to leave them around - // in case the module gets reinstalled. - - self::load_modules(); - log::success( - "module", t("Uninstalled module %module_name", array("module_name" => $module_name))); - } - - /** - * Load the active modules. This is called at bootstrap time. - */ - static function load_modules() { - // Reload module list from the config file since we'll do a refresh after calling install() - $core = Kohana::config_load("core"); - $kohana_modules = $core["modules"]; - $modules = ORM::factory("module")->find_all(); - - self::$modules = array(); - self::$active = array(); - foreach ($modules as $module) { - self::$modules[$module->name] = $module; - if ($module->active) { - self::$active[] = $module; - } - if ($module->name != "core") { - $kohana_modules[] = MODPATH . $module->name; - } - } - Kohana::config_set("core.modules", $kohana_modules); - } - - /** - * Run a specific event on all active modules. - * @param string $name the event name - * @param mixed $data data to pass to each event handler - */ - static function event($name, &$data=null) { - $args = func_get_args(); - array_shift($args); - $function = str_replace(".", "_", $name); - - foreach (self::$modules as $module) { - if (!$module->active) { - continue; - } - - $class = "{$module->name}_event"; - if (method_exists($class, $function)) { - call_user_func_array(array($class, $function), $args); - } - } - } - - /** - * Get a variable from this module - * @param string $module_name - * @param string $name - * @param string $default_value - * @return the value - */ - static function get_var($module_name, $name, $default_value=null) { - // We cache all vars in core._cache so that we can load all vars at once for - // performance. - if (empty(self::$var_cache)) { - $row = Database::instance() - ->select("value") - ->from("vars") - ->where(array("module_name" => "core", "name" => "_cache")) - ->get() - ->current(); - if ($row) { - self::$var_cache = unserialize($row->value); - } else { - // core._cache doesn't exist. Create it now. - foreach (Database::instance() - ->select("module_name", "name", "value") - ->from("vars") - ->orderby("module_name", "name") - ->get() as $row) { - if ($row->module_name == "core" && $row->name == "_cache") { - // This could happen if there's a race condition - continue; - } - self::$var_cache->{$row->module_name}->{$row->name} = $row->value; - } - $cache = ORM::factory("var"); - $cache->module_name = "core"; - $cache->name = "_cache"; - $cache->value = serialize(self::$var_cache); - $cache->save(); - } - } - - if (isset(self::$var_cache->$module_name->$name)) { - return self::$var_cache->$module_name->$name; - } else { - return $default_value; - } - } - - /** - * Store a variable for this module - * @param string $module_name - * @param string $name - * @param string $value - */ - static function set_var($module_name, $name, $value) { - $var = ORM::factory("var") - ->where("module_name", $module_name) - ->where("name", $name) - ->find(); - if (!$var->loaded) { - $var->module_name = $module_name; - $var->name = $name; - } - $var->value = $value; - $var->save(); - - Database::instance()->delete("vars", array("module_name" => "core", "name" => "_cache")); - self::$var_cache = null; - } - - /** - * Increment the value of a variable for this module - * @param string $module_name - * @param string $name - * @param string $increment (optional, default is 1) - */ - static function incr_var($module_name, $name, $increment=1) { - Database::instance()->query( - "UPDATE {vars} SET `value` = `value` + $increment " . - "WHERE `module_name` = '$module_name' " . - "AND `name` = '$name'"); - - Database::instance()->delete("vars", array("module_name" => "core", "name" => "_cache")); - self::$var_cache = null; - } - - /** - * Remove a variable for this module. - * @param string $module_name - * @param string $name - */ - static function clear_var($module_name, $name) { - $var = ORM::factory("var") - ->where("module_name", $module_name) - ->where("name", $name) - ->find(); - if ($var->loaded) { - $var->delete(); - } - - Database::instance()->delete("vars", array("module_name" => "core", "name" => "_cache")); - self::$var_cache = null; - } - - /** - * Return the version of the installed module. - * @param string $module_name - */ - static function get_version($module_name) { - return self::get($module_name)->version; - } -} diff --git a/core/helpers/movie.php b/core/helpers/movie.php deleted file mode 100644 index 3293d4ac..00000000 --- a/core/helpers/movie.php +++ /dev/null @@ -1,153 +0,0 @@ -loaded || !$parent->is_album()) { - throw new Exception("@todo INVALID_PARENT"); - } - - if (!is_file($filename)) { - throw new Exception("@todo MISSING_MOVIE_FILE"); - } - - if (strpos($name, "/")) { - throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); - } - - // We don't allow trailing periods as a security measure - // ref: http://dev.kohanaphp.com/issues/684 - if (rtrim($name, ".") != $name) { - throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); - } - - $movie_info = movie::getmoviesize($filename); - - // Force an extension onto the name - $pi = pathinfo($filename); - if (empty($pi["extension"])) { - $pi["extension"] = image_type_to_extension($movie_info[2], false); - $name .= "." . $pi["extension"]; - } - - $movie = ORM::factory("item"); - $movie->type = "movie"; - $movie->title = $title; - $movie->description = $description; - $movie->name = $name; - $movie->owner_id = $owner_id ? $owner_id : user::active(); - $movie->width = $movie_info[0]; - $movie->height = $movie_info[1]; - $movie->mime_type = strtolower($pi["extension"]) == "mp4" ? "video/mp4" : "video/x-flv"; - $movie->thumb_dirty = 1; - $movie->resize_dirty = 1; - $movie->sort_column = "weight"; - $movie->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - - // Randomize the name if there's a conflict - while (ORM::Factory("item") - ->where("parent_id", $parent->id) - ->where("name", $movie->name) - ->find()->id) { - // @todo Improve this. Random numbers are not user friendly - $movie->name = rand() . "." . $pi["extension"]; - } - - // This saves the photo - $movie->add_to_parent($parent); - - // If the thumb or resize already exists then rename it - if (file_exists($movie->resize_path()) || - file_exists($movie->thumb_path())) { - $movie->name = $pi["filename"] . "-" . rand() . "." . $pi["extension"]; - $movie->save(); - } - - copy($filename, $movie->file_path()); - - module::event("item_created", $movie); - - // Build our thumbnail - graphics::generate($movie); - - // If the parent has no cover item, make this it. - if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { - item::make_album_cover($movie); - } - - return $movie; - } - - static function getmoviesize($filename) { - $ffmpeg = self::find_ffmpeg(); - if (empty($ffmpeg)) { - throw new Exception("@todo MISSING_FFMPEG"); - } - - $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($filename) . " 2>&1"; - $result = `$cmd`; - if (preg_match("/Stream.*?Video:.*?(\d+)x(\d+).*\ +([0-9\.]+) (fps|tb).*/", - $result, $regs)) { - list ($width, $height) = array($regs[1], $regs[2]); - } else { - list ($width, $height) = array(0, 0); - } - return array($width, $height); - } - - static function extract_frame($input_file, $output_file) { - $ffmpeg = self::find_ffmpeg(); - if (empty($ffmpeg)) { - throw new Exception("@todo MISSING_FFMPEG"); - } - - $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($input_file) . - " -an -ss 00:00:03 -an -r 1 -vframes 1" . - " -y -f mjpeg " . escapeshellarg($output_file); - exec($cmd); - } - - static function find_ffmpeg() { - if (!$ffmpeg_path = module::get_var("core", "ffmpeg_path")) { - if (function_exists("exec")) { - $ffmpeg_path = exec("which ffmpeg"); - if ($ffmpeg_path) { - module::set_var("core", "ffmpeg_path", $ffmpeg_path); - } - } - } - return $ffmpeg_path; - } -} diff --git a/core/helpers/photo.php b/core/helpers/photo.php deleted file mode 100644 index c1c005f5..00000000 --- a/core/helpers/photo.php +++ /dev/null @@ -1,171 +0,0 @@ -loaded || !$parent->is_album()) { - throw new Exception("@todo INVALID_PARENT"); - } - - if (!is_file($filename)) { - throw new Exception("@todo MISSING_IMAGE_FILE"); - } - - if (strpos($name, "/")) { - throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); - } - - // We don't allow trailing periods as a security measure - // ref: http://dev.kohanaphp.com/issues/684 - if (rtrim($name, ".") != $name) { - throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); - } - - $image_info = getimagesize($filename); - - // Force an extension onto the name - $pi = pathinfo($filename); - if (empty($pi["extension"])) { - $pi["extension"] = image_type_to_extension($image_info[2], false); - $name .= "." . $pi["extension"]; - } - - $photo = ORM::factory("item"); - $photo->type = "photo"; - $photo->title = $title; - $photo->description = $description; - $photo->name = $name; - $photo->owner_id = $owner_id ? $owner_id : user::active(); - $photo->width = $image_info[0]; - $photo->height = $image_info[1]; - $photo->mime_type = empty($image_info['mime']) ? "application/unknown" : $image_info['mime']; - $photo->thumb_dirty = 1; - $photo->resize_dirty = 1; - $photo->sort_column = "weight"; - $photo->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - - // Randomize the name if there's a conflict - while (ORM::Factory("item") - ->where("parent_id", $parent->id) - ->where("name", $photo->name) - ->find()->id) { - // @todo Improve this. Random numbers are not user friendly - $photo->name = rand() . "." . $pi["extension"]; - } - - // This saves the photo - $photo->add_to_parent($parent); - - /* - * If the thumb or resize already exists then rename it. We need to do this after the save - * because the resize_path and thumb_path both call relative_path which caches the - * path. Before add_to_parent the relative path will be incorrect. - */ - if (file_exists($photo->resize_path()) || - file_exists($photo->thumb_path())) { - $photo->name = $pi["filename"] . "-" . rand() . "." . $pi["extension"]; - $photo->save(); - } - - copy($filename, $photo->file_path()); - - module::event("item_created", $photo); - - // Build our thumbnail/resizes - graphics::generate($photo); - - // If the parent has no cover item, make this it. - if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { - item::make_album_cover($photo); - } - - return $photo; - } - - static function get_add_form($parent) { - $form = new Forge("albums/{$parent->id}", "", "post", array("id" => "gAddPhotoForm")); - $group = $form->group("add_photo")->label( - t("Add Photo to %album_title", array("album_title" =>$parent->title))); - $group->input("title")->label(t("Title")); - $group->textarea("description")->label(t("Description")); - $group->input("name")->label(t("Filename")); - $group->upload("file")->label(t("File"))->rules("required|allow[jpg,png,gif,flv,mp4]"); - $group->hidden("type")->value("photo"); - $group->submit("")->value(t("Upload")); - $form->add_rules_from(ORM::factory("item")); - return $form; - } - - static function get_edit_form($photo) { - $form = new Forge("photos/$photo->id", "", "post", array("id" => "gEditPhotoForm")); - $form->hidden("_method")->value("put"); - $group = $form->group("edit_photo")->label(t("Edit Photo")); - $group->input("title")->label(t("Title"))->value($photo->title); - $group->textarea("description")->label(t("Description"))->value($photo->description); - $group->input("filename")->label(t("Filename"))->value($photo->name) - ->error_messages("conflict", t("There is already a file with this name")) - ->callback("item::validate_no_slashes") - ->error_messages("no_slashes", t("The photo name can't contain a \"/\"")) - ->callback("item::validate_no_trailing_period") - ->error_messages("no_trailing_period", t("The photo name can't end in \".\"")); - - $group->submit("")->value(t("Modify")); - $form->add_rules_from(ORM::factory("item")); - return $form; - } - - /** - * Return scaled width and height. - * - * @param integer $width - * @param integer $height - * @param integer $max the target size for the largest dimension - * @param string $format the output format using %d placeholders for width and height - */ - static function img_dimensions($width, $height, $max, $format="width=\"%d\" height=\"%d\"") { - if (!$width || !$height) { - return ""; - } - - if ($width > $height) { - $new_width = $max; - $new_height = (int)$max * ($height / $width); - } else { - $new_height = $max; - $new_width = (int)$max * ($width / $height); - } - return sprintf($format, $new_width, $new_height); - } -} diff --git a/core/helpers/rest.php b/core/helpers/rest.php deleted file mode 100644 index a63b94c8..00000000 --- a/core/helpers/rest.php +++ /dev/null @@ -1,116 +0,0 @@ -post("_method", $input->get("_method", request::method())))) { - case "put": return "put"; - case "delete": return "delete"; - default: return "post"; - } - } - } - - /** - * Choose an output format based on what the client prefers to accept. - * @return string "html", "xml" or "json" - */ - static function output_format() { - // Pick a format, but let it be overridden. - $input = Input::instance(); - $fmt = $input->get( - "_format", $input->post( - "_format", request::preferred_accept( - array("xhtml", "html", "xml", "json")))); - - // Some browsers (Chrome!) prefer xhtml over html, but we'll normalize this to html for now. - if ($fmt == "xhtml") { - $fmt = "html"; - } - return $fmt; - } - - /** - * Set HTTP response code. - * @param string Use one of the status code constants defined in this class. - */ - static function http_status($status_code) { - header("HTTP/1.1 " . $status_code); - } - - /** - * Set HTTP Location header. - * @param string URL - */ - static function http_location($url) { - header("Location: " . $url); - } - - /** - * Set HTTP Content-Type header. - * @param string content type - */ - static function http_content_type($type) { - header("Content-Type: " . $type); - } -} diff --git a/core/helpers/site_status.php b/core/helpers/site_status.php deleted file mode 100644 index 6d47e565..00000000 --- a/core/helpers/site_status.php +++ /dev/null @@ -1,132 +0,0 @@ -where("key", $permanent_key) - ->find(); - if (!$message->loaded) { - $message->key = $permanent_key; - } - $message->severity = $severity; - $message->value = $msg; - $message->save(); - } - - /** - * Remove any permanent message by key. - * @param string $permanent_key - */ - static function clear($permanent_key) { - $message = ORM::factory("message")->where("key", $permanent_key)->find(); - if ($message->loaded) { - $message->delete(); - } - } - - /** - * Get any pending messages. There are two types of messages, transient and permanent. - * Permanent messages are used to let the admin know that there are pending administrative - * issues that need to be resolved. Transient ones are only displayed once. - * @return html text - */ - static function get() { - if (!user::active()->admin) { - return; - } - $buf = array(); - foreach (ORM::factory("message")->find_all() as $msg) { - $value = str_replace('__CSRF__', access::csrf_token(), $msg->value); - $buf[] = "
  • severity) . "\">$value
  • "; - } - - if ($buf) { - return "
      " . implode("", $buf) . "
    "; - } - } - - /** - * Convert a message severity to a CSS class - * @param integer $severity - * @return string - */ - static function severity_class($severity) { - switch($severity) { - case self::SUCCESS: - return "gSuccess"; - - case self::INFO: - return "gInfo"; - - case self::WARNING: - return "gWarning"; - - case self::ERROR: - return "gError"; - } - } -} diff --git a/core/helpers/task.php b/core/helpers/task.php deleted file mode 100644 index a8a004ab..00000000 --- a/core/helpers/task.php +++ /dev/null @@ -1,83 +0,0 @@ -name}_task"; - if (method_exists($class_name, "available_tasks")) { - foreach (call_user_func(array($class_name, "available_tasks")) as $task) { - $tasks[$task->callback] = $task; - } - } - } - - return $tasks; - } - - static function create($task_def, $context) { - $task = ORM::factory("task"); - $task->callback = $task_def->callback; - $task->name = $task_def->name; - $task->percent_complete = 0; - $task->status = ""; - $task->state = "started"; - $task->owner_id = user::active()->id; - $task->context = serialize($context); - $task->save(); - - return $task; - } - - static function cancel($task_id) { - $task = ORM::factory("task", $task_id); - if (!$task->loaded) { - throw new Exception("@todo MISSING_TASK"); - } - $task->done = 1; - $task->state = "cancelled"; - $task->save(); - - return $task; - } - - static function remove($task_id) { - $task = ORM::factory("task", $task_id); - if ($task->loaded) { - $task->delete(); - } - } - - static function run($task_id) { - $task = ORM::factory("task", $task_id); - if (!$task->loaded) { - throw new Exception("@todo MISSING_TASK"); - } - - $task->state = "running"; - call_user_func_array($task->callback, array(&$task)); - $task->save(); - - return $task; - } -} \ No newline at end of file diff --git a/core/helpers/theme.php b/core/helpers/theme.php deleted file mode 100644 index cbe224db..00000000 --- a/core/helpers/theme.php +++ /dev/null @@ -1,61 +0,0 @@ -"gThemeDetailsForm")); - $group = $form->group("edit_theme"); - $group->input("page_size")->label(t("Items per page"))->id("gPageSize") - ->rules("required|valid_digit") - ->value(module::get_var("core", "page_size")); - $group->input("thumb_size")->label(t("Thumbnail size (in pixels)"))->id("gThumbSize") - ->rules("required|valid_digit") - ->value(module::get_var("core", "thumb_size")); - $group->input("resize_size")->label(t("Resized image size (in pixels)"))->id("gResizeSize") - ->rules("required|valid_digit") - ->value(module::get_var("core", "resize_size")); - $group->textarea("header_text")->label(t("Header text"))->id("gHeaderText") - ->value(module::get_var("core", "header_text")); - $group->textarea("footer_text")->label(t("Footer text"))->id("gFooterText") - ->value(module::get_var("core", "footer_text")); - $group->submit("")->value(t("Save")); - return $form; - } -} - diff --git a/core/helpers/xml.php b/core/helpers/xml.php deleted file mode 100644 index e734e90c..00000000 --- a/core/helpers/xml.php +++ /dev/null @@ -1,35 +0,0 @@ -\n"; - foreach ($array as $key => $value) { - if (is_array($value)) { - $xml .= xml::to_xml($value, array_slice($element_names, 1)); - } else if (is_object($value)) { - $xml .= xml::to_xml($value->as_array(), array_slice($element_names, 1)); - } else { - $xml .= "<$key>$value\n"; - } - } - $xml .= "\n"; - return $xml; - } -} diff --git a/core/hooks/init_gallery.php b/core/hooks/init_gallery.php deleted file mode 100644 index 2c36795a..00000000 --- a/core/hooks/init_gallery.php +++ /dev/null @@ -1,44 +0,0 @@ -post("g3sid", $input->get("g3sid"))) { - $_COOKIE["g3sid"] = $g3sid; -} - -if ($user_agent = $input->post("user_agent", $input->get("user_agent"))) { - Kohana::$user_agent = $user_agent; -} diff --git a/core/images/gallery.png b/core/images/gallery.png deleted file mode 100644 index ca8e0e95..00000000 Binary files a/core/images/gallery.png and /dev/null differ diff --git a/core/images/gd.png b/core/images/gd.png deleted file mode 100644 index b341d71c..00000000 Binary files a/core/images/gd.png and /dev/null differ diff --git a/core/images/graphicsmagick.png b/core/images/graphicsmagick.png deleted file mode 100644 index 3d1d77e9..00000000 Binary files a/core/images/graphicsmagick.png and /dev/null differ diff --git a/core/images/imagemagick.jpg b/core/images/imagemagick.jpg deleted file mode 100644 index d83c4509..00000000 Binary files a/core/images/imagemagick.jpg and /dev/null differ diff --git a/core/js/albums_form_add.js b/core/js/albums_form_add.js deleted file mode 100644 index 06a364f3..00000000 --- a/core/js/albums_form_add.js +++ /dev/null @@ -1,12 +0,0 @@ -$("#gAddAlbumForm input[name=title]").change( - function() { - $("#gAddAlbumForm input[name=name]").attr( - "value", $("#gAddAlbumForm input[name=title]").attr("value"). - replace(/\s+/g, "_").replace(/\.+$/, "")); - }); -$("#gAddAlbumForm input[name=title]").keyup( - function() { - $("#gAddAlbumForm input[name=name]").attr( - "value", $("#gAddAlbumForm input[name=title]").attr("value"). - replace(/\s+/g, "_").replace(/\.+$/, "")); - }); diff --git a/core/js/fullsize.js b/core/js/fullsize.js deleted file mode 100644 index 7428adb5..00000000 --- a/core/js/fullsize.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @todo Move inline CSS out to external style sheet (theme style sheet) - */ -$(document).ready(function() { - $(".gFullSizeLink").click(function() { - var width = $(document).width(); - var height = $(document).height(); - - $("body").append('
    '); - - var image_size = _auto_fit(fullsize_detail.width, fullsize_detail.height); - - $("body").append('
    ' + - '
    '); - - $("#gFullsize").append(''); - $("#gFullsizeClose").click(function() { - $("#gFullsizeOverlay*").remove(); - $("#gFullsize").remove(); - }); - $(window).resize(function() { - $("#gFullsizeOverlay").width($(document).width()); - $("#gFullsizeOverlay").height($(document).height()); - image_size = _auto_fit(fullsize_detail.width, fullsize_detail.height); - $("#gFullsize").height(image_size.height); - $("#gFullsize").width(image_size.width); - $("#gFullsize").css("top", image_size.top); - $("#gFullsize").css("left", image_size.left); - $("#gFullSizeImage").height(image_size.height); - $("#gFullSizeImage").width(image_size.width); - }); - }); -}); - -/* - * Calculate the size of the image panel based on the size of the image and the size of the - * window. Scale the image so the entire panel fits in the view port. - */ -function _auto_fit(imageWidth, imageHeight) { - // ui-dialog gives a padding of 2 pixels - var windowWidth = $(window).width() - 10; - var windowHeight = $(window).height() - 10; - - /* If the width is greater then scale the image width first */ - if (imageWidth > windowWidth) { - var ratio = windowWidth / imageWidth; - imageWidth *= ratio; - imageHeight *= ratio; - } - /* after scaling the width, check that the height fits */ - if (imageHeight > windowHeight) { - var ratio = windowHeight / imageHeight; - imageWidth *= ratio; - imageHeight *= ratio; - } - - // handle the case where the calculation is almost zero (2.14e-14) - return { - top: ((windowHeight - imageHeight) / 2).toFixed(2), - left: ((windowWidth - imageWidth) / 2).toFixed(2), - width: imageWidth.toFixed(2), - height: imageHeight.toFixed(2) - }; -} diff --git a/core/js/l10n_client.js b/core/js/l10n_client.js deleted file mode 100644 index f43671f1..00000000 --- a/core/js/l10n_client.js +++ /dev/null @@ -1,195 +0,0 @@ -// Fork from Drupal's l10n_client module, originally written by: -// G‡bor Hojtsy http://drupal.org/user/4166 (original author) -// Young Hahn / Development Seed - http://developmentseed.org/ (friendly user interface) - -var Gallery = Gallery || { 'behaviors': {} }; - -Gallery.attachBehaviors = function(context) { - context = context || document; - // Execute all of them. - jQuery.each(Gallery.behaviors, - function() { - this(context); - }); -}; - -$(document).ready(function() { - Gallery.attachBehaviors(this); -}); - - -// Store all l10n_client related data + methods in its own object -jQuery.extend(Gallery, { - l10nClient: new (function() { - // Set "selected" string to unselected, i.e. -1 - this.selected = -1; - // Keybindings - this.keys = {'toggle':'ctrl+shift+s', 'clear': 'esc'}; // Keybindings - // Keybinding functions - this.key = function(pressed) { - switch(pressed) { - case 'toggle': - // Grab user-hilighted text & send it into the search filter - userSelection = window.getSelection ? window.getSelection() : document.getSelection ? document.getSelection() : document.selection.createRange().text; - userSelection = String(userSelection); - if(userSelection.length > 0) { - Gallery.l10nClient.filter(userSelection); - Gallery.l10nClient.toggle(1); - $('#l10n-client #gL10nSearch').focus(); - } else { - if($('#l10n-client').is('.hidden')) { - Gallery.l10nClient.toggle(1); - if(!$.browser.safari) { - $('#l10n-client #gL10nSearch').focus(); - } - } else { - Gallery.l10nClient.toggle(0); - } - } - break; - case 'clear': - this.filter(false); - break; - } - } - // Toggle the l10nclient - this.toggle = function(state) { - switch(state) { - case 1: - $('#l10n-client-string-select, #l10n-client-string-editor, #l10n-client .labels .label').show(); - $('#l10n-client').height('22em').removeClass('hidden'); - $('#l10n-client .labels .toggle').text('X'); - /* - * This CSS clashes with Gallery's CSS, probably due to - * YUI's grid / floats. - if(!$.browser.msie) { - $('body').css('border-bottom', '22em solid #fff'); - } - */ - $.cookie('Gallery_l10n_client', '1', {expires: 7, path: '/'}); - break; - case 0: - $('#l10n-client-string-select, #l10n-client-string-editor, #l10n-client .labels .label').hide(); - $('#l10n-client').height('2em').addClass('hidden'); - // TODO: Localize this message - $('#l10n-client .labels .toggle').text('Translate Text'); - /* - if(!$.browser.msie) { - $('body').css('border-bottom', '0px'); - } - */ - $.cookie('Gallery_l10n_client', '0', {expires: 7, path: '/'}); - break; - } - } - // Get a string from the DOM tree - this.getString = function(index, type) { - return l10n_client_data[index][type]; - } - // Set a string in the DOM tree - this.setString = function(index, data) { - l10n_client_data[index]['translation'] = data; - } - // Filter the the string list by a search string - this.filter = function(search) { - if(search == false || search == '') { - $('#l10n-client #l10n-search-filter-clear').focus(); - $('#l10n-client-string-select li').show(); - $('#l10n-client #gL10nSearch').val(''); - $('#l10n-client #gL10nSearch').focus(); - } else { - if(search.length > 0) { - $('#l10n-client-string-select li').hide(); - $('#l10n-client-string-select li:contains('+search+')').show(); - $('#l10n-client #gL10nSearch').val(search); - } - } - } - }) -}); - -// Attaches the localization editor behavior to all required fields. -Gallery.behaviors.l10nClient = function(context) { - - switch($.cookie('Gallery_l10n_client')) { - case '1': - Gallery.l10nClient.toggle(1); - break; - default: - Gallery.l10nClient.toggle(0); - break; - } - - // If the selection changes, copy string values to the source and target fields. - // Add class to indicate selected string in list widget. - $('#l10n-client-string-select li').click(function() { - $('#l10n-client-string-select li').removeClass('active'); - $(this).addClass('active'); - var index = $('#l10n-client-string-select li').index(this); - - $('#l10n-client-string-editor .source-text').text(Gallery.l10nClient.getString(index, 'source')); - $("#gL10nClientSaveForm input[name='l10n-message-source']").val(Gallery.l10nClient.getString(index, 'source')); - $('#gL10nClientSaveForm #l10n-edit-target').val(Gallery.l10nClient.getString(index, 'translation')); - - Gallery.l10nClient.selected = index; - }); - - // When l10n_client window is clicked, toggle based on current state. - $('#l10n-client .labels .toggle').click(function() { - if($('#l10n-client').is('.hidden')) { - Gallery.l10nClient.toggle(1); - } else { - Gallery.l10nClient.toggle(0); - } - }); - - // Register keybindings using jQuery hotkeys - // TODO: Either remove hotkeys code or add query.hotkeys.js. - if($.hotkeys) { - $.hotkeys.add(Gallery.l10nClient.keys['toggle'], function(){Gallery.l10nClient.key('toggle')}); - $.hotkeys.add(Gallery.l10nClient.keys['clear'], {target:'#l10n-client #gL10nSearch', type:'keyup'}, function(){Gallery.l10nClient.key('clear')}); - } - - // Custom listener for l10n_client livesearch - $('#l10n-client #gL10nSearch').keyup(function(key) { - Gallery.l10nClient.filter($('#l10n-client #gL10nSearch').val()); - }); - - // Clear search - $('#l10n-client #l10n-search-filter-clear').click(function() { - Gallery.l10nClient.filter(false); - return false; - }); - - // Send AJAX POST data on form submit. - $('#gL10nClientSaveForm').ajaxForm({ - dataType: "json", - success: function(data) { - // Store string in local js - Gallery.l10nClient.setString(Gallery.l10nClient.selected, $('#gL10nClientSaveForm #l10n-edit-target').val()); - - // Mark string as translated. - $('#l10n-client-string-select li').eq(Gallery.l10nClient.selected).removeClass('untranslated').removeClass('active').addClass('translated').text($('#gL10nClientSaveForm #l10n-edit-target').val()); - - // Empty input fields. - $('#l10n-client-string-editor .source-text').html(''); - $('#gL10nClientSaveForm #l10n-edit-target').val(''); - $("#gL10nClientSaveForm input[name='l10n-message-source']").val(''); - }, - error: function(xmlhttp) { - // TODO: Localize this message - alert('An HTTP error @status occured (or empty response).'.replace('@status', xmlhttp.status)); - } - }); - - - // Copy source text to translation field on button click. - $('#gL10nClientSaveForm #l10n-edit-copy').click(function() { - $('#gL10nClientSaveForm #l10n-edit-target').val($('#l10n-client-string-editor .source-text').text()); - }); - - // Clear translation field on button click. - $('#gL10nClientSaveForm #l10n-edit-clear').click(function() { - $('#gL10nClientSaveForm #l10n-edit-target').val(''); - }); -}; diff --git a/core/js/quick.js b/core/js/quick.js deleted file mode 100644 index e7f35cea..00000000 --- a/core/js/quick.js +++ /dev/null @@ -1,95 +0,0 @@ -$(document).ready(function() { - if ($("#gAlbumGrid").length) { - // @todo Add quick edit pane for album (meta, move, permissions, delete) - $(".gItem").hover(show_quick, function() {}); - } - if ($("#gPhoto").length) { - $("#gPhoto").hover(show_quick, function() {}); - } -}); - -var show_quick = function() { - var cont = $(this); - var quick = $(this).find(".gQuick"); - $("#gQuickPane").remove(); - cont.append("
    "); - var img = cont.find(".gThumbnail,.gResize"); - var pos = cont.position(); - $("#gQuickPane").css({ - "position": "absolute", - "top": pos.top, - "left": pos.left, - "text-align": "center", - "width": cont.innerWidth() + 1, - "height": "auto" - }).hide(); - cont.hover(function() {}, hide_quick); - $.get( - quick.attr("href"), - {}, - function(data, textStatus) { - $("#gQuickPane").html(data).slideDown("fast"); - $(".ui-state-default").hover( - function(){ - $(this).addClass("ui-state-hover"); - }, - function(){ - $(this).removeClass("ui-state-hover"); - } - ); - $("#gQuickPane a:not(.options)").click(function(e) { - e.preventDefault(); - if ($(this).attr("id") == "gQuickDelete" && - !confirm($(this).attr("ref"))) { - return; - } - quick_do(cont, $(this), img); - }); - $("#gQuickPane a.options").click(function(e) { - e.preventDefault(); - $("#gQuickPaneOptions").slideToggle("fast"); - }); - } - ); -}; - -var quick_do = function(cont, pane, img) { - if (pane.hasClass("ui-state-disabled")) { - return false; - } - if (pane.hasClass("gDialogLink")) { - openDialog(pane, function() { window.location.reload(); }); - } else { - img.css("opacity", "0.1"); - cont.addClass("gLoadingLarge"); - $.ajax({ - type: "GET", - url: pane.attr("href"), - dataType: "json", - success: function(data) { - img.css("opacity", "1"); - cont.removeClass("gLoadingLarge"); - if (data.src) { - img.attr("width", data.width); - img.attr("height", data.height); - img.attr("src", data.src); - if (data.height > data.width) { - img.css("margin-top", -32); - } else { - img.css("margin-top", 0); - } - } else if (data.location) { - window.location = data.location; - } else if (data.reload) { - window.location.reload(); - } - } - }); - } - return false; -}; - -var hide_quick = function() { - $("#gQuickPane").remove(); -}; - diff --git a/core/libraries/Admin_View.php b/core/libraries/Admin_View.php deleted file mode 100644 index acc3f8ec..00000000 --- a/core/libraries/Admin_View.php +++ /dev/null @@ -1,126 +0,0 @@ -theme_name = module::get_var("core", "active_admin_theme"); - if (user::active()->admin) { - $this->theme_name = Input::instance()->get("theme", $this->theme_name); - } - $this->sidebar = ""; - $this->set_global("theme", $this); - $this->set_global("user", user::active()); - } - - public function url($path, $absolute_url=false) { - $arg = "themes/{$this->theme_name}/$path"; - return $absolute_url ? url::abs_file($arg) : url::file($arg); - } - - public function display($page_name, $view_class="View") { - return new $view_class($page_name); - } - - public function admin_menu() { - $menu = Menu::factory("root"); - core_menu::admin($menu, $this); - - foreach (module::active() as $module) { - if ($module->name == "core") { - continue; - } - $class = "{$module->name}_menu"; - if (method_exists($class, "admin")) { - call_user_func_array(array($class, "admin"), array(&$menu, $this)); - } - } - - print $menu; - } - - /** - * Print out any site wide status information. - */ - public function site_status() { - return site_status::get(); - } - - /** - * Print out any messages waiting for this user. - */ - public function messages() { - return message::get(); - } - - /** - * Handle all theme functions that insert module content. - */ - public function __call($function, $args) { - switch ($function) { - case "admin_credits"; - case "admin_footer": - case "admin_header_top": - case "admin_header_bottom": - case "admin_page_bottom": - case "admin_page_top": - case "admin_head": - $blocks = array(); - foreach (module::active() as $module) { - $helper_class = "{$module->name}_theme"; - if (method_exists($helper_class, $function)) { - $blocks[] = call_user_func_array( - array($helper_class, $function), - array_merge(array($this), $args)); - } - } - - if (Session::instance()->get("debug")) { - if ($function != "admin_head") { - array_unshift( - $blocks, "
    " . - "
    $function
    "); - $blocks[] = "
    "; - } - } - - return implode("\n", $blocks); - - default: - throw new Exception("@todo UNKNOWN_THEME_FUNCTION: $function"); - } - } -} \ No newline at end of file diff --git a/core/libraries/Block.php b/core/libraries/Block.php deleted file mode 100644 index 6fe679f1..00000000 --- a/core/libraries/Block.php +++ /dev/null @@ -1,30 +0,0 @@ -__toString(); - } -} diff --git a/core/libraries/I18n.php b/core/libraries/I18n.php deleted file mode 100644 index c936be88..00000000 --- a/core/libraries/I18n.php +++ /dev/null @@ -1,410 +0,0 @@ -translate($message, $options); -} - -/** - * Translates a localizable message with plural forms. - * @param $singular String The message to be translated. E.g. "There is one album." - * @param $plural String The plural message to be translated. E.g. - * "There are %count albums." - * @param $count Number The number which is inserted for the %count placeholder and - * which is used to select the proper plural form ($singular or $plural). - * @param $options array (optional) Options array for key value pairs which are used - * for pluralization and interpolation. Special key: "locale" to override the - * currently configured locale. - * @return String The translated message string. - */ -function t2($singular, $plural, $count, $options=array()) { - return I18n::instance()->translate(array("one" => $singular, "other" => $plural), - array_merge($options, array("count" => $count))); -} - -class I18n_Core { - private static $_instance; - private $_config = array(); - private $_call_log = array(); - private $_cache = array(); - - private function __construct($config) { - $this->_config = $config; - $this->locale($config['default_locale']); - } - - public static function instance($config=null) { - if (self::$_instance == NULL || isset($config)) { - $config = isset($config) ? $config : Kohana::config('locale'); - if (empty($config['default_locale'])) { - $config['default_locale'] = module::get_var('core', 'default_locale'); - } - self::$_instance = new I18n_Core($config); - } - - return self::$_instance; - } - - public function locale($locale=null) { - if ($locale) { - $this->_config['default_locale'] = $locale; - // Attempt to set PHP's locale as well (for number formatting, collation, etc.) - // TODO: See G2 for better fallack code. - $locale_prefs = array($locale); - $locale_prefs[] = 'en_US'; - setlocale(LC_ALL, $locale_prefs); - } - return $this->_config['default_locale']; - } - - /** - * Translates a localizable message. - * @param $message String|array The message to be translated. E.g. "Hello world" - * or array("one" => "One album", "other" => "%count albums") - * @param $options array (optional) Options array for key value pairs which are used - * for pluralization and interpolation. Special keys are "count" and "locale", - * the latter to override the currently configured locale. - * @return String The translated message string. - */ - public function translate($message, $options=array()) { - $locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale']; - $count = isset($options['count']) ? $options['count'] : null; - $values = $options; - unset($values['locale']); - $this->log($message, $options); - - $entry = $this->lookup($locale, $message); - - if (null === $entry) { - // Default to the root locale. - $entry = $message; - $locale = $this->_config['root_locale']; - } - - $entry = $this->pluralize($locale, $entry, $count); - - $entry = $this->interpolate($locale, $entry, $values); - - return $entry; - } - - private function lookup($locale, $message) { - if (!isset($this->_cache[$locale])) { - $this->_cache[$locale] = array(); - // TODO: Load data from locale file instead of the DB. - foreach (Database::instance() - ->select("key", "translation") - ->from("incoming_translations") - ->where(array("locale" => $locale)) - ->get() - ->as_array() as $row) { - $this->_cache[$locale][$row->key] = unserialize($row->translation); - } - - // Override incoming with outgoing... - foreach (Database::instance() - ->select("key", "translation") - ->from("outgoing_translations") - ->where(array("locale" => $locale)) - ->get() - ->as_array() as $row) { - $this->_cache[$locale][$row->key] = unserialize($row->translation); - } - } - - $key = self::get_message_key($message); - - if (isset($this->_cache[$locale][$key])) { - return $this->_cache[$locale][$key]; - } else { - return null; - } - } - - public function has_translation($message, $options=null) { - $locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale']; - $count = empty($options['count']) ? null : $options['count']; - $values = $options; - unset($values['locale']); - $this->log($message, $options); - - $entry = $this->lookup($locale, $message); - - if (null === $entry) { - return false; - } else if (!is_array($entry)) { - return $entry !== ''; - } else { - $plural_key = self::get_plural_key($locale, $count); - return isset($entry[$plural_key]) - && $entry[$plural_key] !== null - && $entry[$plural_key] !== ''; - } - } - - public static function get_message_key($message) { - $as_string = is_array($message) ? implode('|', $message) : $message; - return md5($as_string); - } - - private function interpolate($locale, $string, $values) { - // TODO: Handle locale specific number formatting. - - // Replace x_y before replacing x. - krsort($values, SORT_STRING); - - $keys = array(); - foreach (array_keys($values) as $key) { - $keys[] = "%$key"; - } - return str_replace($keys, array_values($values), $string); - } - - private function pluralize($locale, $entry, $count) { - if (!is_array($entry)) { - return $entry; - } - - $plural_key = self::get_plural_key($locale, $count); - if (!isset($entry[$plural_key])) { - // Fallback to the default plural form. - $plural_key = 'other'; - } - - if (isset($entry[$plural_key])) { - return $entry[$plural_key]; - } else { - // Fallback to just any plural form. - list ($plural_key, $string) = each($entry); - return $string; - } - } - - private function log($message, $options) { - $key = self::get_message_key($message); - isset($this->_call_log[$key]) or $this->_call_log[$key] = array($message, $options); - } - - public function call_log() { - return $this->_call_log; - } - - private static function get_plural_key($locale, $count) { - $parts = explode('_', $locale); - $language = $parts[0]; - - // Data from CLDR 1.6 (http://unicode.org/cldr/data/common/supplemental/plurals.xml). - // Docs: http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html - switch ($language) { - case 'az': - case 'fa': - case 'hu': - case 'ja': - case 'ko': - case 'my': - case 'to': - case 'tr': - case 'vi': - case 'yo': - case 'zh': - case 'bo': - case 'dz': - case 'id': - case 'jv': - case 'ka': - case 'km': - case 'kn': - case 'ms': - case 'th': - return 'other'; - - case 'ar': - if ($count == 0) { - return 'zero'; - } else if ($count == 1) { - return 'one'; - } else if ($count == 2) { - return 'two'; - } else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 10) { - return 'few'; - } else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 99) { - return 'many'; - } else { - return 'other'; - } - - case 'pt': - case 'am': - case 'bh': - case 'fil': - case 'tl': - case 'guw': - case 'hi': - case 'ln': - case 'mg': - case 'nso': - case 'ti': - case 'wa': - if ($count == 0 || $count == 1) { - return 'one'; - } else { - return 'other'; - } - - case 'fr': - if ($count >= 0 and $count < 2) { - return 'one'; - } else { - return 'other'; - } - - case 'lv': - if ($count == 0) { - return 'zero'; - } else if ($count % 10 == 1 && $count % 100 != 11) { - return 'one'; - } else { - return 'other'; - } - - case 'ga': - case 'se': - case 'sma': - case 'smi': - case 'smj': - case 'smn': - case 'sms': - if ($count == 1) { - return 'one'; - } else if ($count == 2) { - return 'two'; - } else { - return 'other'; - } - - case 'ro': - case 'mo': - if ($count == 1) { - return 'one'; - } else if (is_int($count) && $count == 0 && ($i = $count % 100) >= 1 && $i <= 19) { - return 'few'; - } else { - return 'other'; - } - - case 'lt': - if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) { - return 'one'; - } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 9 && ($i = $count % 100) < 11 && $i > 19) { - return 'few'; - } else { - return 'other'; - } - - case 'hr': - case 'ru': - case 'sr': - case 'uk': - case 'be': - case 'bs': - case 'sh': - if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) { - return 'one'; - } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 && ($i = $count % 100) < 12 && $i > 14) { - return 'few'; - } else if (is_int($count) && ($count % 10 == 0 || (($i = $count % 10) >= 5 && $i <= 9) || (($i = $count % 100) >= 11 && $i <= 14))) { - return 'many'; - } else { - return 'other'; - } - - case 'cs': - case 'sk': - if ($count == 1) { - return 'one'; - } else if (is_int($count) && $count >= 2 && $count <= 4) { - return 'few'; - } else { - return 'other'; - } - - case 'pl': - if ($count == 1) { - return 'one'; - } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 && - ($i = $count % 100) < 12 && $i > 14 && ($i = $count % 100) < 22 && $i > 24) { - return 'few'; - } else { - return 'other'; - } - - case 'sl': - if ($count % 100 == 1) { - return 'one'; - } else if ($count % 100 == 2) { - return 'two'; - } else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 4) { - return 'few'; - } else { - return 'other'; - } - - case 'mt': - if ($count == 1) { - return 'one'; - } else if ($count == 0 || is_int($count) && ($i = $count % 100) >= 2 && $i <= 10) { - return 'few'; - } else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 19) { - return 'many'; - } else { - return 'other'; - } - - case 'mk': - if ($count % 10 == 1) { - return 'one'; - } else { - return 'other'; - } - - case 'cy': - if ($count == 1) { - return 'one'; - } else if ($count == 2) { - return 'two'; - } else if ($count == 8 || $count == 11) { - return 'many'; - } else { - return 'other'; - } - - default: // en, de, etc. - return $count == 1 ? 'one' : 'other'; - } - } -} \ No newline at end of file diff --git a/core/libraries/MY_Database.php b/core/libraries/MY_Database.php deleted file mode 100644 index c56f16e8..00000000 --- a/core/libraries/MY_Database.php +++ /dev/null @@ -1,92 +0,0 @@ -where[] = "("; - return $this; - } - - public function close_paren() { - // Search backwards for the last opening paren and resolve it - $i = count($this->where) - 1; - $this->where[$i] .= ")"; - while (--$i >= 0) { - if ($this->where[$i] == "(") { - // Remove the paren from the where clauses, and add it to the right of the operator of the - // next where clause. If removing the paren makes the next where clause the first element - // in the where list, then the operator shouldn't be there. It's there because we - // calculate whether or not we need an operator based on the number of where clauses, and - // the open paren seems like a where clause even though it isn't. - array_splice($this->where, $i, 1); - $this->where[$i] = preg_replace("/^(AND|OR) /", $i ? "\\1 (" : "(", $this->where[$i]); - return $this; - } - } - - throw new Kohana_Database_Exception('database.missing_open_paren'); - } - - /** - * Parse the query string and convert any strings of the form `\([a-zA-Z0-9_]*?)\] - * table prefix . $1 - */ - public function query($sql = '') { - if (!empty($sql)) { - $sql = $this->add_table_prefixes($sql); - } - return parent::query($sql); - } - - public function add_table_prefixes($sql) { - $prefix = $this->config["table_prefix"]; - if (strpos($sql, "SHOW TABLES") === 0) { - /* - * Don't ignore "show tables", otherwise we could have a infinite - * @todo this may have to be changed if we support more than mysql - */ - return $sql; - } else if (strpos($sql, "CREATE TABLE") === 0) { - // Creating a new table add it to the table cache. - $open_brace = strpos($sql, "{") + 1; - $close_brace = strpos($sql, "}", $open_brace); - $name = substr($sql, $open_brace, $close_brace - $open_brace); - $this->_table_names["{{$name}}"] = "{$prefix}$name"; - } - - if (!isset($this->_table_names)) { - // This should only run once on the first query - $this->_table_names =array(); - $len = strlen($prefix); - foreach($this->list_tables() as $table_name) { - if ($len > 0) { - $naked_name = strpos($table_name, $prefix) !== 0 ? - $table_name : substr($table_name, $len); - } else { - $naked_name = $table_name; - } - $this->_table_names["{{$naked_name}}"] = $table_name; - } - } - - return empty($this->_table_names) ? $sql : strtr($sql, $this->_table_names); - } -} \ No newline at end of file diff --git a/core/libraries/MY_Forge.php b/core/libraries/MY_Forge.php deleted file mode 100644 index 17d0465b..00000000 --- a/core/libraries/MY_Forge.php +++ /dev/null @@ -1,59 +0,0 @@ -hidden("csrf")->value(""); - } - /** - * Use our own template - */ - public function render($template="form.html", $custom=false) { - $this->hidden["csrf"]->value(access::csrf_token()); - return parent::render($template, $custom); - } - - /** - * Associate validation rules defined in the model with this form. - */ - public function add_rules_from($model) { - foreach ($this->inputs as $name => $input) { - if (isset($input->inputs)) { - $input->add_rules_from($model); - } - if (isset($model->rules[$name])) { - $input->rules($model->rules[$name]); - } - } - } - - /** - * Validate our CSRF value as a mandatory part of all form validation. - */ - public function validate() { - $status = parent::validate(); - access::verify_csrf(); - return $status; - } -} \ No newline at end of file diff --git a/core/libraries/MY_ORM.php b/core/libraries/MY_ORM.php deleted file mode 100644 index fb2f80a7..00000000 --- a/core/libraries/MY_ORM.php +++ /dev/null @@ -1,46 +0,0 @@ -db->open_paren(); - return $this; - } - - public function close_paren() { - $this->db->close_paren(); - return $this; - } -} - -/** - * Slide this in here for convenience. We won't ever be overloading ORM_Iterator without ORM. - */ -class ORM_Iterator extends ORM_Iterator_Core { - /** - * Cache the result row - */ - public function current() { - $row = parent::current(); - if (is_object($row)) { - model_cache::set($row); - } - return $row; - } -} \ No newline at end of file diff --git a/core/libraries/MY_Pagination.php b/core/libraries/MY_Pagination.php deleted file mode 100644 index d06a974f..00000000 --- a/core/libraries/MY_Pagination.php +++ /dev/null @@ -1,35 +0,0 @@ -auto_hide === TRUE AND $this->total_pages <= 1) { - return ""; - } - - if ($style === NULL) { - // Use default style - $style = $this->style; - } - - // Return rendered pagination view - return View::factory("pager.html", get_object_vars($this))->render(); - } -} diff --git a/core/libraries/MY_View.php b/core/libraries/MY_View.php deleted file mode 100644 index 836d1087..00000000 --- a/core/libraries/MY_View.php +++ /dev/null @@ -1,46 +0,0 @@ -set_global("csrf", access::csrf_token()); - } - - /** - * Override View_Core::render so that we trap errors stemming from bad PHP includes and show a - * visible stack trace to help developers. - * - * @see View_Core::render - */ - public function render($print=false, $renderer=false) { - try { - return parent::render($print, $renderer); - } catch (Exception $e) { - Kohana::Log('error', $e->getTraceAsString()); - Kohana::Log('debug', $e->getMessage()); - return ""; - } - } -} diff --git a/core/libraries/Menu.php b/core/libraries/Menu.php deleted file mode 100644 index d19d8b1e..00000000 --- a/core/libraries/Menu.php +++ /dev/null @@ -1,187 +0,0 @@ -id = $id; - return $this; - } - - /** - * Set the label - * @chainable - */ - public function label($label) { - $this->label = $label; - return $this; - } - - /** - * Set the url - * @chainable - */ - public function url($url) { - $this->url = $url; - return $this; - } - - /** - * Set the css id - * @chainable - */ - public function css_id($css_id) { - $this->css_id = $css_id; - return $this; - } - - /** - * Set the css class - * @chainable - */ - public function css_class($css_class) { - $this->css_class = $css_class; - return $this; - } - -} - -/** - * Menu element that provides a link to a new page. - */ -class Menu_Element_Link extends Menu_Element { - public function __toString() { - if (isset($this->css_id) && !empty($this->css_id)) { - $css_id = " id=\"$this->css_id\""; - } else { - $css_id = ""; - } - if (isset($this->css_class) && !empty($this->css_class)) { - $css_class = " $this->css_class"; - } else { - $css_class = ""; - } - return "
  • url\" " . - "title=\"$this->label\">$this->label
  • "; - } -} - -/** - * Menu element that provides a pop-up dialog - */ -class Menu_Element_Dialog extends Menu_Element { - public function __toString() { - if (isset($this->css_id) && !empty($this->css_id)) { - $css_id = " id=\"$this->css_id\""; - } else { - $css_id = ""; - } - if (isset($this->css_class) && !empty($this->css_class)) { - $css_class = " $this->css_class"; - } else { - $css_class = ""; - } - return "
  • url\" " . - "title=\"$this->label\">$this->label
  • "; - } -} - -/** - * Root menu or submenu - */ -class Menu_Core extends Menu_Element { - public $elements; - public $is_root = false; - - /** - * Return an instance of a Menu_Element - * @chainable - */ - public static function factory($type) { - switch($type) { - case "link": - return new Menu_Element_Link(); - - case "dialog": - return new Menu_Element_Dialog(); - - case "root": - $menu = new Menu(); - $menu->is_root = true; - return $menu; - - case "submenu": - return new Menu(); - - default: - throw Exception("@todo UNKNOWN_MENU_TYPE"); - } - } - - public function __construct() { - $this->elements = array(); - } - - /** - * Add a new element to this menu - */ - public function append($menu_element) { - $this->elements[$menu_element->id] = $menu_element; - return $this; - } - - /** - * Add a new element to this menu - */ - public function add_after($target_id, $new_menu_element) { - $copy = array(); - foreach ($this->elements as $id => $menu_element) { - $copy[$id] = $menu_element; - if ($id == $target_id) { - $copy[$new_menu_element->id] = $new_menu_element; - } - } - $this->elements = $copy; - return $this; - } - - /** - * Retrieve a Menu_Element by id - */ - public function get($id) { - return $this->elements[$id]; - } - - public function __toString() { - $html = $this->is_root ? "
      " : - "
    • $this->label
        "; - $html .= implode("\n", $this->elements); - $html .= $this->is_root ? "
      " : "
    "; - return $html; - } -} diff --git a/core/libraries/ORM_MPTT.php b/core/libraries/ORM_MPTT.php deleted file mode 100644 index 46280d95..00000000 --- a/core/libraries/ORM_MPTT.php +++ /dev/null @@ -1,307 +0,0 @@ -model_name = inflector::singular($this->table_name); - } - - /** - * Add this node as a child of the parent provided. - * - * @chainable - * @param integer $parent_id the id of the parent node - * @return ORM - */ - function add_to_parent($parent) { - $this->lock(); - - try { - // Make a hole in the parent for this new item - $this->db->query( - "UPDATE {{$this->table_name}} SET `left` = `left` + 2 WHERE `left` >= {$parent->right}"); - $this->db->query( - "UPDATE {{$this->table_name}} SET `right` = `right` + 2 WHERE `right` >= {$parent->right}"); - $parent->right += 2; - - // Insert this item into the hole - $this->left = $parent->right - 2; - $this->right = $parent->right - 1; - $this->parent_id = $parent->id; - $this->level = $parent->level + 1; - $this->save(); - $parent->reload(); - } catch (Exception $e) { - $this->unlock(); - throw $e; - } - - $this->unlock(); - return $this; - } - - /** - * Delete this node and all of its children. - */ - public function delete() { - $children = $this->children(); - if ($children) { - foreach ($this->children() as $item) { - // Deleting children affects the MPTT tree, so we have to reload each child before we - // delete it so that we have current left/right pointers. This is inefficient. - // @todo load each child once, not twice. - $item->reload()->delete(); - } - - // Deleting children has affected this item - $this->reload(); - } - - $this->lock(); - try { - $this->db->query( - "UPDATE {{$this->table_name}} SET `left` = `left` - 2 WHERE `left` > {$this->right}"); - $this->db->query( - "UPDATE {{$this->table_name}} SET `right` = `right` - 2 WHERE `right` > {$this->right}"); - } catch (Exception $e) { - $this->unlock(); - throw $e; - } - - $this->unlock(); - parent::delete(); - } - - /** - * Return true if the target is descendant of this item. - * @param ORM $target - * @return boolean - */ - function is_descendant($target) { - return ($this->left <= $target->left && $this->right >= $target->right); - } - - /** - * Return the parent of this node - * - * @return ORM - */ - function parent() { - if (!$this->parent_id) { - return null; - } - return model_cache::get($this->model_name, $this->parent_id); - } - - /** - * Return all the parents of this node, in order from root to this node's immediate parent. - * - * @return array ORM - */ - function parents() { - return $this - ->where("`left` <= {$this->left}") - ->where("`right` >= {$this->right}") - ->where("id <> {$this->id}") - ->orderby("left", "ASC") - ->find_all(); - } - - /** - * Return all of the children of this node, ordered by id. - * - * @chainable - * @param integer SQL limit - * @param integer SQL offset - * @param array orderby - * @return array ORM - */ - function children($limit=null, $offset=0, $orderby=null) { - $this->where("parent_id", $this->id); - if (empty($orderby)) { - $this->orderby("id", "ASC"); - } else { - $this->orderby($orderby); - } - return $this->find_all($limit, $offset); - } - - /** - * Return all of the children of this node, ordered by id. - * - * @chainable - * @param integer SQL limit - * @param integer SQL offset - * @return array ORM - */ - function children_count() { - return $this->where("parent_id", $this->id)->count_all(); - } - - /** - * Return all of the children of the specified type, ordered by id. - * - * @param integer SQL limit - * @param integer SQL offset - * @param string type to return - * @param array orderby - * @return object ORM_Iterator - */ - function descendants($limit=null, $offset=0, $type=null, $orderby=null) { - $this->where("left >", $this->left) - ->where("right <=", $this->right); - if ($type) { - $this->where("type", $type); - } - - if (empty($orderby)) { - $this->orderby("id", "ASC"); - } else { - $this->orderby($orderby); - } - - return $this->find_all($limit, $offset); - } - - /** - * Return the count of all the children of the specified type. - * - * @param string type to count - * @return integer child count - */ - function descendants_count($type=null) { - $this->where("left >", $this->left) - ->where("right <=", $this->right); - if ($type) { - $this->where("type", $type); - } - return $this->count_all(); - } - - /** - * Move this item to the specified target. - * - * @chainable - * @param Item_Model $target Target node - * @return ORM_MTPP - */ - function move_to($target) { - if ($this->left <= $target->left && - $this->right >= $target->right) { - throw new Exception("@todo INVALID_TARGET can't move item inside itself"); - } - - $number_to_move = (int)(($this->right - $this->left) / 2 + 1); - $size_of_hole = $number_to_move * 2; - $original_left = $this->left; - $original_right = $this->right; - $target_right = $target->right; - $level_delta = ($target->level + 1) - $this->level; - - $this->lock(); - try { - if ($level_delta) { - // Update the levels for the to-be-moved items - $this->db->query( - "UPDATE {{$this->table_name}} SET `level` = `level` + $level_delta" . - " WHERE `left` >= $original_left AND `right` <= $original_right"); - } - - // Make a hole in the target for the move - $target->db->query( - "UPDATE {{$this->table_name}} SET `left` = `left` + $size_of_hole" . - " WHERE `left` >= $target_right"); - $target->db->query( - "UPDATE {{$this->table_name}} SET `right` = `right` + $size_of_hole" . - " WHERE `right` >= $target_right"); - - // Change the parent. - $this->db->query( - "UPDATE {{$this->table_name}} SET `parent_id` = {$target->id}" . - " WHERE `id` = {$this->id}"); - - // If the source is to the right of the target then we just adjusted its left and right above. - $left = $original_left; - $right = $original_right; - if ($original_left > $target_right) { - $left += $size_of_hole; - $right += $size_of_hole; - } - - $new_offset = $target->right - $left; - $this->db->query( - "UPDATE {{$this->table_name}}" . - " SET `left` = `left` + $new_offset," . - " `right` = `right` + $new_offset" . - " WHERE `left` >= $left" . - " AND `right` <= $right"); - - // Close the hole in the source's parent after the move - $this->db->query( - "UPDATE {{$this->table_name}} SET `left` = `left` - $size_of_hole" . - " WHERE `left` > $right"); - $this->db->query( - "UPDATE {{$this->table_name}} SET `right` = `right` - $size_of_hole" . - " WHERE `right` > $right"); - } catch (Exception $e) { - $this->unlock(); - throw $e; - } - - $this->unlock(); - - // Lets reload to get the changes. - $this->reload(); - return $this; - } - - /** - * Lock the tree to prevent concurrent modification. - */ - protected function lock() { - $result = $this->db->query("SELECT GET_LOCK('{$this->table_name}', 1) AS l")->current(); - if (empty($result->l)) { - throw new Exception("@todo UNABLE_TO_LOCK_EXCEPTION"); - } - } - - /** - * Unlock the tree. - */ - protected function unlock() { - $this->db->query("SELECT RELEASE_LOCK('{$this->table_name}')"); - } -} diff --git a/core/libraries/Sendmail.php b/core/libraries/Sendmail.php deleted file mode 100644 index 90998457..00000000 --- a/core/libraries/Sendmail.php +++ /dev/null @@ -1,97 +0,0 @@ -headers = array(); - $config = Kohana::config("sendmail"); - foreach ($config as $key => $value) { - $this->$key($value); - } - } - - public function __get($key) { - return null; - } - - public function __call($key, $value) { - switch ($key) { - case "to": - $this->to = is_array($value[0]) ? $value[0] : array($value[0]); - break; - case "header": - if (count($value) != 2) { - throw new Exception("@todo INVALID_HEADER_PARAMETERS"); - } - $this->headers[$value[0]] = $value[1]; - break; - case "from": - $this->headers["From"] = $value[0]; - break; - case "reply_to": - $this->headers["Reply-To"] = $value[0]; - break; - default: - $this->$key = $value[0]; - } - return $this; - } - - public function send() { - if (empty($this->to)) { - throw new Exception("@todo TO_IS_REQUIRED_FOR_MAIL"); - } - $to = implode(", ", $this->to); - $headers = array(); - foreach ($this->headers as $key => $value) { - $key = ucfirst($key); - $headers[] = "$key: $value"; - } - - // The docs say headers should be separated by \r\n, but occasionaly that doesn't work and you - // need to use a single \n. This can be set in config/sendmail.php - $headers = implode($this->header_separator, $headers); - $message = wordwrap($this->message, $this->line_length, "\n"); - if (!$this->mail($to, $this->subject, $message, $headers)) { - Kohana::log("error", wordwrap("Sending mail failed:\nTo: $to\n $this->subject\n" . - "Headers: $headers\n $this->message")); - throw new Exception("@todo SEND_MAIL_FAILED"); - } - return $this; - } - - public function mail($to, $subject, $message, $headers) { - return mail($to, $subject, $message, $headers); - } -} diff --git a/core/libraries/Task_Definition.php b/core/libraries/Task_Definition.php deleted file mode 100644 index 8d9c5922..00000000 --- a/core/libraries/Task_Definition.php +++ /dev/null @@ -1,50 +0,0 @@ -callback = $callback; - return $this; - } - - function description($description) { - $this->description = $description; - return $this; - } - - function name($name) { - $this->name = $name; - return $this; - } - - function severity($severity) { - $this->severity = $severity; - return $this; - } -} diff --git a/core/libraries/Theme_View.php b/core/libraries/Theme_View.php deleted file mode 100644 index b5b97666..00000000 --- a/core/libraries/Theme_View.php +++ /dev/null @@ -1,221 +0,0 @@ -theme_name = module::get_var("core", "active_site_theme"); - if (user::active()->admin) { - $this->theme_name = Input::instance()->get("theme", $this->theme_name); - } - $this->item = null; - $this->tag = null; - $this->set_global("theme", $this); - $this->set_global("user", user::active()); - $this->set_global("page_type", $page_type); - if ($page_type == "album") { - $this->set_global("thumb_proportion", $this->thumb_proportion()); - } - - $maintenance_mode = Kohana::config("core.maintenance_mode", false, false); - if ($maintenance_mode) { - message::warning(t("This site is currently in maintenance mode")); - } - } - - /** - * Proportion of the current thumb_size's to default - * @return int - */ - public function thumb_proportion() { - // @TODO change the 200 to a theme supplied value when and if we come up with an - // API to allow the theme to set defaults. - return module::get_var("core", "thumb_size", 200) / 200; - } - - public function url($path, $absolute_url=false) { - $arg = "themes/{$this->theme_name}/$path"; - return $absolute_url ? url::abs_file($arg) : url::file($arg); - } - - public function item() { - return $this->item; - } - - public function tag() { - return $this->tag; - } - - public function page_type() { - return $this->page_type; - } - - public function display($page_name, $view_class="View") { - return new $view_class($page_name); - } - - public function site_menu() { - $menu = Menu::factory("root"); - if ($this->page_type != "login") { - core_menu::site($menu, $this); - - foreach (module::active() as $module) { - if ($module->name == "core") { - continue; - } - $class = "{$module->name}_menu"; - if (method_exists($class, "site")) { - call_user_func_array(array($class, "site"), array(&$menu, $this)); - } - } - } - - print $menu; - } - - public function album_menu() { - $menu = Menu::factory("root"); - core_menu::album($menu, $this); - - foreach (module::active() as $module) { - if ($module->name == "core") { - continue; - } - $class = "{$module->name}_menu"; - if (method_exists($class, "album")) { - call_user_func_array(array($class, "album"), array(&$menu, $this)); - } - } - - print $menu; - } - - public function photo_menu() { - $menu = Menu::factory("root"); - core_menu::photo($menu, $this); - - foreach (module::active() as $module) { - if ($module->name == "core") { - continue; - } - $class = "{$module->name}_menu"; - if (method_exists($class, "photo")) { - call_user_func_array(array($class, "photo"), array(&$menu, $this)); - } - } - - print $menu; - } - - public function pager() { - if ($this->children_count) { - $this->pagination = new Pagination(); - $this->pagination->initialize( - array('query_string' => 'page', - 'total_items' => $this->children_count, - 'items_per_page' => $this->page_size, - 'style' => 'classic')); - return $this->pagination->render(); - } - } - - /** - * Print out any site wide status information. - */ - public function site_status() { - return site_status::get(); - } - - /** - * Print out any messages waiting for this user. - */ - public function messages() { - return message::get(); - } - - /** - * Handle all theme functions that insert module content. - */ - public function __call($function, $args) { - switch ($function) { - case "album_blocks": - case "album_bottom": - case "album_top": - case "credits"; - case "dynamic_bottom": - case "dynamic_top": - case "footer": - case "head": - case "header_bottom": - case "header_top": - case "page_bottom": - case "page_top": - case "photo_blocks": - case "photo_bottom": - case "photo_top": - case "resize_bottom": - case "resize_top": - case "sidebar_blocks": - case "sidebar_bottom": - case "sidebar_top": - case "thumb_bottom": - case "thumb_info": - case "thumb_top": - $blocks = array(); - foreach (module::active() as $module) { - $helper_class = "{$module->name}_theme"; - if (method_exists($helper_class, $function)) { - $blocks[] = call_user_func_array( - array($helper_class, $function), - array_merge(array($this), $args)); - } - } - if (Session::instance()->get("debug")) { - if ($function != "head") { - array_unshift( - $blocks, "
    " . - "
    $function
    "); - $blocks[] = "
    "; - } - } - return implode("\n", $blocks); - - default: - throw new Exception("@todo UNKNOWN_THEME_FUNCTION: $function"); - } - } -} \ No newline at end of file diff --git a/core/models/access_cache.php b/core/models/access_cache.php deleted file mode 100644 index 10d05df7..00000000 --- a/core/models/access_cache.php +++ /dev/null @@ -1,21 +0,0 @@ - "required|length[0,255]", - "title" => "required|length[0,255]", - "description" => "length[0,65535]" - ); - - /** - * Add a set of restrictions to any following queries to restrict access only to items - * viewable by the active user. - * @chainable - */ - public function viewable() { - if (is_null($this->view_restrictions)) { - if (user::active()->admin) { - $this->view_restrictions = array(); - } else { - foreach (user::group_ids() as $id) { - // Separate the first restriction from the rest to make it easier for us to formulate - // our where clause below - if (empty($this->view_restrictions)) { - $this->view_restrictions[0] = "view_$id"; - } else { - $this->view_restrictions[1]["view_$id"] = access::ALLOW; - } - } - } - } - switch (count($this->view_restrictions)) { - case 0: - break; - - case 1: - $this->where($this->view_restrictions[0], access::ALLOW); - break; - - default: - $this->open_paren(); - $this->where($this->view_restrictions[0], access::ALLOW); - $this->orwhere($this->view_restrictions[1]); - $this->close_paren(); - break; - } - - return $this; - } - - /** - * Is this item an album? - * @return true if it's an album - */ - public function is_album() { - return $this->type == 'album'; - } - - /** - * Is this item a photo? - * @return true if it's a photo - */ - public function is_photo() { - return $this->type == 'photo'; - } - - /** - * Is this item a movie? - * @return true if it's a movie - */ - public function is_movie() { - return $this->type == 'movie'; - } - - public function delete() { - module::event("item_before_delete", $this); - - $parent = $this->parent(); - if ($parent->album_cover_item_id == $this->id) { - item::remove_album_cover($parent); - } - - $path = $this->file_path(); - $resize_path = $this->resize_path(); - $thumb_path = $this->thumb_path(); - - parent::delete(); - if (is_dir($path)) { - @dir::unlink($path); - @dir::unlink(dirname($resize_path)); - @dir::unlink(dirname($thumb_path)); - } else { - @unlink($path); - @unlink($resize_path); - @unlink($thumb_path); - } - } - - /** - * Move this item to the specified target. - * @chainable - * @param Item_Model $target Target item (must be an album - * @return ORM_MTPP - */ - function move_to($target) { - if (!$target->is_album()) { - throw new Exception("@todo INVALID_MOVE_TYPE $target->type"); - } - - if ($this->id == 1) { - throw new Exception("@todo INVALID_SOURCE root album"); - } - - $original_path = $this->file_path(); - $original_resize_path = $this->resize_path(); - $original_thumb_path = $this->thumb_path(); - - parent::move_to($target, true); - $this->relative_path_cache = null; - - rename($original_path, $this->file_path()); - if ($this->is_album()) { - @rename(dirname($original_resize_path), dirname($this->resize_path())); - @rename(dirname($original_thumb_path), dirname($this->thumb_path())); - Database::instance() - ->update("items", - array("relative_path_cache" => null), - array("left >" => $this->left, "right <" => $this->right)); - } else { - @rename($original_resize_path, $this->resize_path()); - @rename($original_thumb_path, $this->thumb_path()); - } - - return $this; - } - - /** - * Rename the underlying file for this item to a new name. Move all the files. This requires a - * save. - * - * @chainable - */ - public function rename($new_name) { - if ($new_name == $this->name) { - return; - } - - if (strpos($new_name, "/")) { - throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); - } - - $old_relative_path = $this->relative_path(); - $new_relative_path = dirname($old_relative_path) . "/" . $new_name; - @rename(VARPATH . "albums/$old_relative_path", VARPATH . "albums/$new_relative_path"); - @rename(VARPATH . "resizes/$old_relative_path", VARPATH . "resizes/$new_relative_path"); - @rename(VARPATH . "thumbs/$old_relative_path", VARPATH . "thumbs/$new_relative_path"); - $this->name = $new_name; - - if ($this->is_album()) { - Database::instance() - ->update("items", - array("relative_path_cache" => null), - array("left >" => $this->left, "right <" => $this->right)); - } - - return $this; - } - - /** - * album: url::site("albums/2") - * photo: url::site("photos/3") - * - * @param string $query the query string (eg "show=3") - */ - public function url($query=array(), $full_uri=false) { - $url = ($full_uri ? url::abs_site("{$this->type}s/$this->id") - : url::site("{$this->type}s/$this->id")); - if ($query) { - $url .= "?$query"; - } - return $url; - } - - /** - * album: /var/albums/album1/album2 - * photo: /var/albums/album1/album2/photo.jpg - */ - public function file_path() { - return VARPATH . "albums/" . $this->relative_path(); - } - - /** - * album: http://example.com/gallery3/var/resizes/album1/ - * photo: http://example.com/gallery3/var/albums/album1/photo.jpg - */ - public function file_url($full_uri=false) { - return $full_uri ? - url::abs_file("var/albums/" . $this->relative_path()) : - url::file("var/albums/" . $this->relative_path()); - } - - /** - * album: /var/resizes/album1/.thumb.jpg - * photo: /var/albums/album1/photo.thumb.jpg - */ - public function thumb_path() { - $base = VARPATH . "thumbs/" . $this->relative_path(); - if ($this->is_photo()) { - return $base; - } else if ($this->is_album()) { - return $base . "/.album.jpg"; - } else if ($this->is_movie()) { - // Replace the extension with jpg - return preg_replace("/...$/", "jpg", $base); - } - } - - /** - * Return true if there is a thumbnail for this item. - */ - public function has_thumb() { - return $this->thumb_width && $this->thumb_height; - } - - /** - * album: http://example.com/gallery3/var/resizes/album1/.thumb.jpg - * photo: http://example.com/gallery3/var/albums/album1/photo.thumb.jpg - */ - public function thumb_url($full_uri=false) { - $cache_buster = "?m=" . $this->updated; - $base = ($full_uri ? - url::abs_file("var/thumbs/" . $this->relative_path()) : - url::file("var/thumbs/" . $this->relative_path())); - if ($this->is_photo()) { - return $base . $cache_buster; - } else if ($this->is_album()) { - return $base . "/.album.jpg" . $cache_buster; - } else if ($this->is_movie()) { - // Replace the extension with jpg - $base = preg_replace("/...$/", "jpg", $base); - return $base . $cache_buster; - } - } - - /** - * album: /var/resizes/album1/.resize.jpg - * photo: /var/albums/album1/photo.resize.jpg - */ - public function resize_path() { - return VARPATH . "resizes/" . $this->relative_path() . - ($this->is_album() ? "/.album.jpg" : ""); - } - - /** - * album: http://example.com/gallery3/var/resizes/album1/.resize.jpg - * photo: http://example.com/gallery3/var/albums/album1/photo.resize.jpg - */ - public function resize_url($full_uri=false) { - return ($full_uri ? - url::abs_file("var/resizes/" . $this->relative_path()) : - url::file("var/resizes/" . $this->relative_path())) . - ($this->is_album() ? "/.album.jpg" : ""); - } - - /** - * Return the relative path to this item's file. - * @return string - */ - public function relative_path() { - if (!isset($this->relative_path_cache)) { - $paths = array(); - foreach (Database::instance() - ->select("name") - ->from("items") - ->where("left <=", $this->left) - ->where("right >=", $this->right) - ->where("id <>", 1) - ->orderby("left", "ASC") - ->get() as $row) { - $paths[] = $row->name; - } - $this->relative_path_cache = implode($paths, "/"); - $this->save(); - } - return $this->relative_path_cache; - } - - /** - * @see ORM::__get() - */ - public function __get($column) { - if ($column == "owner") { - // This relationship depends on an outside module, which may not be present so handle - // failures gracefully. - try { - return model_cache::get("user", $this->owner_id); - } catch (Exception $e) { - return null; - } - } else { - return parent::__get($column); - } - } - - /** - * @see ORM::__set() - */ - public function __set($column, $value) { - if ($column == "name") { - // Clear the relative path as it is no longer valid. - $this->relative_path_cache = null; - } - parent::__set($column, $value); - } - - /** - * @see ORM::save() - */ - public function save() { - if (!empty($this->changed) && $this->changed != array("view_count" => "view_count")) { - $this->updated = time(); - if (!$this->loaded) { - $this->created = $this->updated; - $r = ORM::factory("item")->select("MAX(weight) as max_weight")->find(); - $this->weight = $r->max_weight + 1; - } - } - return parent::save(); - } - - /** - * Return the Item_Model representing the cover for this album. - * @return Item_Model or null if there's no cover - */ - public function album_cover() { - if (!$this->is_album()) { - return null; - } - - if (empty($this->album_cover_item_id)) { - return null; - } - - try { - return model_cache::get("item", $this->album_cover_item_id); - } catch (Exception $e) { - // It's possible (unlikely) that the item was deleted, if so keep going. - return null; - } - } - - /** - * Find the position of the given child id in this album. The resulting value is 1-indexed, so - * the first child in the album is at position 1. - */ - public function get_position($child_id) { - $result = Database::instance()->query(" - SELECT COUNT(*) AS position FROM {items} - WHERE parent_id = {$this->parent_id} - AND {$this->sort_column} <= (SELECT {$this->sort_column} - FROM {items} WHERE id = $child_id) - ORDER BY {$this->sort_column} {$this->sort_order}"); - - return $result->current()->position; - } - - /** - * Return an tag for the thumbnail. - * @param array $extra_attrs Extra attributes to add to the img tag - * @param int (optional) $max Maximum size of the thumbnail (default: null) - * @param boolean (optional) $center_vertically Center vertically (default: false) - * @return string - */ - public function thumb_tag($extra_attrs=array(), $max=null, $center_vertically=false) { - list ($height, $width) = $this->scale_dimensions($max); - if ($center_vertically && $max) { - // The constant is divide by 2 to calculate the file and 10 to convert to em - $margin_top = ($max - $height) / 20; - $extra_attrs["style"] = "margin-top: {$margin_top}em"; - $extra_attrs["title"] = $this->title; - } - $attrs = array_merge($extra_attrs, - array( - "src" => $this->thumb_url(), - "alt" => $this->title, - "width" => $width, - "height" => $height) - ); - // html::image forces an absolute url which we don't want - return ""; - } - - /** - * Calculate the largest width/height that fits inside the given maximum, while preserving the - * aspect ratio. - * @param int $max Maximum size of the largest dimension - * @return array - */ - public function scale_dimensions($max) { - $width = $this->thumb_width; - $height = $this->thumb_height; - - if ($height) { - if (isset($max)) { - if ($width > $height) { - $height = (int)($max * ($height / $width)); - $width = $max; - } else { - $width = (int)($max * ($width / $height)); - $height = $max; - } - } - } else { - // Missing thumbnail, can happen on albums with no photos yet. - // @todo we should enforce a placeholder for those albums. - $width = 0; - $height = 0; - } - return array($height, $width); - } - - /** - * Return an tag for the resize. - * @param array $extra_attrs Extra attributes to add to the img tag - * @return string - */ - public function resize_tag($extra_attrs) { - $attrs = array_merge($extra_attrs, - array("src" => $this->resize_url(), - "alt" => $this->title, - "width" => $this->resize_width, - "height" => $this->resize_height) - ); - // html::image forces an absolute url which we don't want - return ""; - } - - /** - * Return a flowplayer "; - } - - /** - * Return all of the children of this node, ordered by the defined sort order. - * - * @chainable - * @param integer SQL limit - * @param integer SQL offset - * @return array ORM - */ - function children($limit=null, $offset=0) { - return parent::children($limit, $offset, array($this->sort_column => $this->sort_order)); - } - - /** - * Return all of the children of the specified type, ordered by the defined sort order. - * @param integer SQL limit - * @param integer SQL offset - * @param string type to return - * @return object ORM_Iterator - */ - function descendants($limit=null, $offset=0, $type=null) { - return parent::descendants($limit, $offset, $type, - array($this->sort_column => $this->sort_order)); - } -} diff --git a/core/models/log.php b/core/models/log.php deleted file mode 100644 index 6734afb8..00000000 --- a/core/models/log.php +++ /dev/null @@ -1,22 +0,0 @@ -context); - if (array_key_exists($key, $context)) { - return $context[$key]; - } else { - return $default; - } - } - - public function set($key, $value) { - $context = unserialize($this->context); - $context[$key] = $value; - $this->context = serialize($context); - } - - public function save() { - if (!empty($this->changed)) { - $this->updated = time(); - } - return parent::save(); - } - - public function owner() { - return user::lookup($this->owner_id); - } -} \ No newline at end of file diff --git a/core/models/theme.php b/core/models/theme.php deleted file mode 100644 index f479fd5a..00000000 --- a/core/models/theme.php +++ /dev/null @@ -1,21 +0,0 @@ -where("name", "access_test")->find(); - if ($group->loaded) { - $group->delete(); - } - } catch (Exception $e) { } - - try { - access::delete_permission("access_test"); - } catch (Exception $e) { } - - try { - $user = ORM::factory("user")->where("name", "access_test")->find(); - if ($user->loaded) { - $user->delete(); - } - } catch (Exception $e) { } - } - - public function setup() { - user::set_active(user::guest()); - } - - public function groups_and_permissions_are_bound_to_columns_test() { - access::register_permission("access_test", "Access Test"); - $group = group::create("access_test"); - - // We have a new column for this perm / group combo - $fields = Database::instance()->list_fields("access_caches"); - $this->assert_true(array_key_exists("access_test_{$group->id}", $fields)); - - access::delete_permission("access_test"); - $group->delete(); - - // Now the column has gone away - $fields = Database::instance()->list_fields("access_caches"); - $this->assert_false(array_key_exists("access_test_{$group->id}", $fields)); - } - - public function adding_and_removing_items_adds_ands_removes_rows_test() { - $root = ORM::factory("item", 1); - $item = album::create($root, rand(), "test album"); - - // New rows exist - $this->assert_true(ORM::factory("access_cache")->where("item_id", $item->id)->find()->loaded); - $this->assert_true(ORM::factory("access_intent")->where("item_id", $item->id)->find()->loaded); - - // Delete the item - $item->delete(); - - // Rows are gone - $this->assert_false(ORM::factory("access_cache")->where("item_id", $item->id)->find()->loaded); - $this->assert_false(ORM::factory("access_intent")->where("item_id", $item->id)->find()->loaded); - } - - public function new_photos_inherit_parent_permissions_test() { - $root = ORM::factory("item", 1); - - $album = album::create($root, rand(), "test album"); - access::allow(group::everybody(), "view", $album); - - $photo = ORM::factory("item"); - $photo->type = "photo"; - $photo->add_to_parent($album); - access::add_item($photo); - - $this->assert_true($photo->__get("view_" . group::everybody()->id)); - } - - public function can_allow_deny_and_reset_intent_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); - $intent = ORM::factory("access_intent")->where("item_id", $album)->find(); - - // Allow - access::allow(group::everybody(), "view", $album); - $this->assert_same(access::ALLOW, $intent->reload()->view_1); - - // Deny - access::deny(group::everybody(), "view", $album); - $this->assert_same( - access::DENY, - ORM::factory("access_intent")->where("item_id", $album)->find()->view_1); - - // Allow again. If the initial value was allow, then the first Allow clause above may not - // have actually changed any values. - access::allow(group::everybody(), "view", $album); - $this->assert_same( - access::ALLOW, - ORM::factory("access_intent")->where("item_id", $album)->find()->view_1); - - access::reset(group::everybody(), "view", $album); - $this->assert_same( - null, - ORM::factory("access_intent")->where("item_id", $album)->find()->view_1); - } - - public function cant_reset_root_item_test() { - try { - access::reset(group::everybody(), "view", ORM::factory("item", 1)); - } catch (Exception $e) { - return; - } - $this->assert_true(false, "Should not be able to reset root intent"); - } - - public function can_view_item_test() { - $root = ORM::factory("item", 1); - access::allow(group::everybody(), "view", $root); - $this->assert_true(access::group_can(group::everybody(), "view", $root)); - } - - public function can_always_fails_on_unloaded_items_test() { - $root = ORM::factory("item", 1); - access::allow(group::everybody(), "view", $root); - $this->assert_true(access::group_can(group::everybody(), "view", $root)); - - $bogus = ORM::factory("item", -1); - $this->assert_false(access::group_can(group::everybody(), "view", $bogus)); - } - - public function cant_view_child_of_hidden_parent_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); - - $root->reload(); - access::deny(group::everybody(), "view", $root); - access::reset(group::everybody(), "view", $album); - - $album->reload(); - $this->assert_false(access::group_can(group::everybody(), "view", $album)); - } - - public function view_permissions_propagate_down_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); - - access::allow(group::everybody(), "view", $root); - access::reset(group::everybody(), "view", $album); - $album->reload(); - $this->assert_true(access::group_can(group::everybody(), "view", $album)); - } - - public function can_toggle_view_permissions_propagate_down_test() { - $root = ORM::factory("item", 1); - $album1 = album::create($root, rand(), "test album"); - $album2 = album::create($album1, rand(), "test album"); - $album3 = album::create($album2, rand(), "test album"); - $album4 = album::create($album3, rand(), "test album"); - - $album1->reload(); - $album2->reload(); - $album3->reload(); - $album4->reload(); - - access::allow(group::everybody(), "view", $root); - access::deny(group::everybody(), "view", $album1); - access::reset(group::everybody(), "view", $album2); - access::reset(group::everybody(), "view", $album3); - access::reset(group::everybody(), "view", $album4); - - $album4->reload(); - $this->assert_false(access::group_can(group::everybody(), "view", $album4)); - - access::allow(group::everybody(), "view", $album1); - $album4->reload(); - $this->assert_true(access::group_can(group::everybody(), "view", $album4)); - } - - public function revoked_view_permissions_cant_be_allowed_lower_down_test() { - $root = ORM::factory("item", 1); - $album1 = album::create($root, rand(), "test album"); - $album2 = album::create($album1, rand(), "test album"); - - $root->reload(); - access::deny(group::everybody(), "view", $root); - access::allow(group::everybody(), "view", $album2); - - $album1->reload(); - $this->assert_false(access::group_can(group::everybody(), "view", $album1)); - - $album2->reload(); - $this->assert_false(access::group_can(group::everybody(), "view", $album2)); - } - - public function can_edit_item_test() { - $root = ORM::factory("item", 1); - access::allow(group::everybody(), "edit", $root); - $this->assert_true(access::group_can(group::everybody(), "edit", $root)); - } - - public function non_view_permissions_propagate_down_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); - - access::allow(group::everybody(), "edit", $root); - access::reset(group::everybody(), "edit", $album); - $this->assert_true(access::group_can(group::everybody(), "edit", $album)); - } - - public function non_view_permissions_can_be_revoked_lower_down_test() { - $root = ORM::factory("item", 1); - $outer = album::create($root, rand(), "test album"); - $outer_photo = ORM::factory("item"); - $outer_photo->type = "photo"; - $outer_photo->add_to_parent($outer); - access::add_item($outer_photo); - - $inner = album::create($outer, rand(), "test album"); - $inner_photo = ORM::factory("item"); - $inner_photo->type = "photo"; - $inner_photo->add_to_parent($inner); - access::add_item($inner_photo); - - $outer->reload(); - $inner->reload(); - - access::allow(group::everybody(), "edit", $root); - access::deny(group::everybody(), "edit", $outer); - access::allow(group::everybody(), "edit", $inner); - - // Outer album is not editable, inner one is. - $this->assert_false(access::group_can(group::everybody(), "edit", $outer_photo)); - $this->assert_true(access::group_can(group::everybody(), "edit", $inner_photo)); - } - - public function i_can_edit_test() { - // Create a new user that belongs to no groups - $user = user::create("access_test", "Access Test", ""); - foreach ($user->groups as $group) { - $user->remove($group); - } - $user->save(); - user::set_active($user); - - // This user can't edit anything - $root = ORM::factory("item", 1); - $this->assert_false(access::can("edit", $root)); - - // Now add them to a group that has edit permission - $group = group::create("access_test"); - $group->add($user); - $group->save(); - access::allow($group, "edit", $root); - - $user = ORM::factory("user", $user->id); // reload() does not flush related columns - user::set_active($user); - - // And verify that the user can edit. - $this->assert_true(access::can("edit", $root)); - } - - public function everybody_view_permission_maintains_htaccess_files_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); - - $this->assert_false(file_exists($album->file_path() . "/.htaccess")); - - access::deny(group::everybody(), "view", $album); - $this->assert_true(file_exists($album->file_path() . "/.htaccess")); - - access::allow(group::everybody(), "view", $album); - $this->assert_false(file_exists($album->file_path() . "/.htaccess")); - - access::deny(group::everybody(), "view", $album); - $this->assert_true(file_exists($album->file_path() . "/.htaccess")); - - access::reset(group::everybody(), "view", $album); - $this->assert_false(file_exists($album->file_path() . "/.htaccess")); - } - - public function everybody_view_full_permission_maintains_htaccess_files_test() { - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), "test album"); - - $this->assert_false(file_exists($album->file_path() . "/.htaccess")); - $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); - $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); - - access::deny(group::everybody(), "view_full", $album); - $this->assert_true(file_exists($album->file_path() . "/.htaccess")); - $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); - $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); - - access::allow(group::everybody(), "view_full", $album); - $this->assert_false(file_exists($album->file_path() . "/.htaccess")); - $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); - $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); - - access::deny(group::everybody(), "view_full", $album); - $this->assert_true(file_exists($album->file_path() . "/.htaccess")); - $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); - $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); - - access::reset(group::everybody(), "view_full", $album); - $this->assert_false(file_exists($album->file_path() . "/.htaccess")); - $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); - $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); - } -} diff --git a/core/tests/Album_Helper_Test.php b/core/tests/Album_Helper_Test.php deleted file mode 100644 index 80afa8d1..00000000 --- a/core/tests/Album_Helper_Test.php +++ /dev/null @@ -1,87 +0,0 @@ -assert_equal(VARPATH . "albums/$rand", $album->file_path()); - $this->assert_equal(VARPATH . "thumbs/$rand/.album.jpg", $album->thumb_path()); - $this->assert_true(is_dir(VARPATH . "thumbs/$rand"), "missing thumb dir"); - - // It's unclear that a resize makes sense for an album. But we have one. - $this->assert_equal(VARPATH . "resizes/$rand/.album.jpg", $album->resize_path()); - $this->assert_true(is_dir(VARPATH . "resizes/$rand"), "missing resizes dir"); - - $this->assert_equal(1, $album->parent_id); // MPTT tests will cover other hierarchy checks - $this->assert_equal($rand, $album->name); - $this->assert_equal($rand, $album->title); - $this->assert_equal($rand, $album->description); - } - - public function create_conflicting_album_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album1 = album::create($root, $rand, $rand, $rand); - $album2 = album::create($root, $rand, $rand, $rand); - $this->assert_true($album1->name != $album2->name); - } - - public function thumb_url_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $this->assert_equal("http://./var/thumbs/$rand/.album.jpg", $album->thumb_url()); - } - - public function resize_url_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $this->assert_equal("http://./var/resizes/$rand/.album.jpg", $album->resize_url()); - } - - public function create_album_shouldnt_allow_names_with_slash_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $album = album::create($root, $rand . "/", $rand, $rand); - } catch (Exception $e) { - // pass - return; - } - - $this->assert_true(false, "Shouldn't create an album with / in the name"); - } - - public function create_album_silently_trims_trailing_periods_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $album = album::create($root, $rand . "..", $rand, $rand); - } catch (Exception $e) { - $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); - return; - } - - $this->assert_true(false, "Shouldn't create an album with trailing . in the name"); - } -} diff --git a/core/tests/Albums_Controller_Test.php b/core/tests/Albums_Controller_Test.php deleted file mode 100644 index ef1fac77..00000000 --- a/core/tests/Albums_Controller_Test.php +++ /dev/null @@ -1,76 +0,0 @@ -_post = $_POST; - } - - public function teardown() { - $_POST = $this->_post; - } - - public function change_album_test() { - $controller = new Albums_Controller(); - $root = ORM::factory("item", 1); - $album = album::create($root, "test", "test", "test"); - $orig_name = $album->name; - - $_POST["dirname"] = "test"; - $_POST["name"] = "new name"; - $_POST["title"] = "new title"; - $_POST["description"] = "new description"; - $_POST["column"] = "weight"; - $_POST["direction"] = "ASC"; - $_POST["csrf"] = access::csrf_token(); - $_POST["_method"] = "put"; - access::allow(group::everybody(), "edit", $root); - - ob_start(); - $controller->_update($album); - $results = ob_get_contents(); - ob_end_clean(); - - $this->assert_equal( - json_encode(array("result" => "success", "location" => "http://./index.php/test")), - $results); - $this->assert_equal("new title", $album->title); - $this->assert_equal("new description", $album->description); - - // We don't change the name, yet. - $this->assert_equal($orig_name, $album->name); - } - - public function change_album_no_csrf_fails_test() { - $controller = new Albums_Controller(); - $root = ORM::factory("item", 1); - $album = album::create($root, "test", "test", "test"); - $_POST["name"] = "new name"; - $_POST["title"] = "new title"; - $_POST["description"] = "new description"; - access::allow(group::everybody(), "edit", $root); - - try { - $controller->_update($album); - $this->assert_true(false, "This should fail"); - } catch (Exception $e) { - // pass - } - } -} diff --git a/core/tests/Core_Installer_Test.php b/core/tests/Core_Installer_Test.php deleted file mode 100644 index f7036286..00000000 --- a/core/tests/Core_Installer_Test.php +++ /dev/null @@ -1,50 +0,0 @@ -assert_true(file_exists(VARPATH . "albums")); - $this->assert_true(file_exists(VARPATH . "resizes")); - } - - public function install_registers_core_module_test() { - $core = ORM::factory("module")->where("name", "core")->find(); - $this->assert_equal("core", $core->name); - - // This is probably too volatile to keep for long - $this->assert_equal(1, $core->version); - } - - public function install_creates_root_item_test() { - $max_right = ORM::factory("item") - ->select("MAX(`right`) AS `right`") - ->find()->right; - $root = ORM::factory('item')->find(1); - $this->assert_equal("Gallery", $root->title); - $this->assert_equal(1, $root->left); - $this->assert_equal($max_right, $root->right); - $this->assert_equal(null, $root->parent_id); - $this->assert_equal(1, $root->level); - } -} diff --git a/core/tests/Database_Test.php b/core/tests/Database_Test.php deleted file mode 100644 index bd3d2f53..00000000 --- a/core/tests/Database_Test.php +++ /dev/null @@ -1,134 +0,0 @@ -where("a", 1) - ->where("b", 2) - ->compile(); - $sql = str_replace("\n", " ", $sql); - $this->assert_same("SELECT * WHERE `a` = 1 AND `b` = 2", $sql); - } - - function compound_where_test() { - $sql = Database::instance() - ->where("outer1", 1) - ->open_paren() - ->where("inner1", 1) - ->orwhere("inner2", 2) - ->close_paren() - ->where("outer2", 2) - ->compile(); - $sql = str_replace("\n", " ", $sql); - $this->assert_same( - "SELECT * WHERE `outer1` = 1 AND (`inner1` = 1 OR `inner2` = 2) AND `outer2` = 2", - $sql); - } - - function group_first_test() { - $sql = Database::instance() - ->open_paren() - ->where("inner1", 1) - ->orwhere("inner2", 2) - ->close_paren() - ->where("outer1", 1) - ->where("outer2", 2) - ->compile(); - $sql = str_replace("\n", " ", $sql); - $this->assert_same( - "SELECT * WHERE (`inner1` = 1 OR `inner2` = 2) AND `outer1` = 1 AND `outer2` = 2", - $sql); - } - - function where_array_test() { - $sql = Database::instance() - ->where("outer1", 1) - ->open_paren() - ->where("inner1", 1) - ->orwhere(array("inner2" => 2, "inner3" => 3)) - ->close_paren() - ->compile(); - $sql = str_replace("\n", " ", $sql); - $this->assert_same( - "SELECT * WHERE `outer1` = 1 AND (`inner1` = 1 OR `inner2` = 2 OR `inner3` = 3)", - $sql); - } - - function notlike_test() { - $sql = Database::instance() - ->where("outer1", 1) - ->open_paren() - ->ornotlike("inner1", 1) - ->close_paren() - ->compile(); - $sql = str_replace("\n", " ", $sql); - $this->assert_same( - "SELECT * WHERE `outer1` = 1 OR ( `inner1` NOT LIKE '%1%')", - $sql); - } - - function prefix_replacement_test() { - $db = Database_For_Test::instance(); - $converted = $db->add_table_prefixes("CREATE TABLE IF NOT EXISTS {test_tables} ( - `id` int(9) NOT NULL auto_increment, - `name` varchar(32) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY(`name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8"); - $expected = "CREATE TABLE IF NOT EXISTS g3test_test_tables ( - `id` int(9) NOT NULL auto_increment, - `name` varchar(32) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY(`name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8"; - $this->assert_same($expected, $converted); - - $sql = "UPDATE {test_tables} SET `name` = '{test string}' " . - "WHERE `item_id` IN " . - " (SELECT `id` FROM {items} " . - " WHERE `left` >= 1 " . - " AND `right` <= 6)"; - $sql = $db->add_table_prefixes($sql); - - $expected = "UPDATE g3test_test_tables SET `name` = '{test string}' " . - "WHERE `item_id` IN " . - " (SELECT `id` FROM g3test_items " . - " WHERE `left` >= 1 " . - " AND `right` <= 6)"; - - $this->assert_same($expected, $sql); - } - - public function setup() { - } - - public function teardown() { - } - -} - -class Database_For_Test extends Database { - static function instance() { - $db = new Database_For_Test(); - $db->_table_names["{items}"] = "g3test_items"; - $db->config["table_prefix"] = "g3test_"; - return $db; - } -} diff --git a/core/tests/Dir_Helper_Test.php b/core/tests/Dir_Helper_Test.php deleted file mode 100644 index 46bb871c..00000000 --- a/core/tests/Dir_Helper_Test.php +++ /dev/null @@ -1,32 +0,0 @@ -assert_boolean(!file_exists($filename), "File not deleted"); - $this->assert_boolean(!file_exists($dirname), "Directory not deleted"); - } -} diff --git a/core/tests/DrawForm_Test.php b/core/tests/DrawForm_Test.php deleted file mode 100644 index 2c5aaba4..00000000 --- a/core/tests/DrawForm_Test.php +++ /dev/null @@ -1,84 +0,0 @@ - "gTestGroupForm")); - $form->input("title")->label(t("Title")); - $form->textarea("description")->label(t("Text Area")); - $form->submit("")->value(t("Submit")); - $rendered = $form->__toString(); - - $expected = "
    \n" . - "\n" . - "
      \n" . - "
    • \n" . - " \n" . - " \n" . - "
    • \n" . - "
    • \n" . - " \n" . - " \n" . - "
    • \n" . - "
    • \n" . - " \n" . - "
    • \n" . - "
    \n" . - "
    \n"; - $this->assert_same($expected, $rendered); - } - - function group_test() { - $form = new Forge("test/controller", "", "post", array("id" => "gTestGroupForm")); - $group = $form->group("test_group")->label(t("Test Group")); - $group->input("title")->label(t("Title")); - $group->textarea("description")->label(t("Text Area")); - $group->submit("")->value(t("Submit")); - $rendered = $form->__toString(); - - $expected = "
    \n" . - "\n" . - "
    \n" . - " Test Group\n" . - "
      \n" . - "
    • \n" . - " \n" . - " \n" . - "
    • \n" . - "
    • \n" . - " \n" . - " \n" . - "
    • \n" . - "
    • \n" . - " \n" . - "
    • \n" . - "
    \n" . - "
    \n" . - "
    \n"; - $this->assert_same($expected, $rendered); - } - -} - diff --git a/core/tests/File_Structure_Test.php b/core/tests/File_Structure_Test.php deleted file mode 100644 index 1caa82ba..00000000 --- a/core/tests/File_Structure_Test.php +++ /dev/null @@ -1,235 +0,0 @@ -getPathname())) { - $this->assert_false( - preg_match('/\?\>\s*$/', file_get_contents($file)), - "{$file->getPathname()} ends in ?>"); - } - } - } - - public function view_files_correct_suffix_test() { - $dir = new GalleryCodeFilterIterator( - new RecursiveIteratorIterator(new RecursiveDirectoryIterator(DOCROOT))); - foreach ($dir as $file) { - if (strpos($file, "views")) { - $this->assert_true( - preg_match("#/views/.*?(\.html|mrss|txt)\.php$#", $file->getPathname()), - "{$file->getPathname()} should end in .{html,mrss,txt}.php"); - } - } - } - - public function no_windows_line_endings_test() { - $dir = new GalleryCodeFilterIterator( - new RecursiveIteratorIterator(new RecursiveDirectoryIterator(DOCROOT))); - foreach ($dir as $file) { - if (preg_match("/\.(php|css|html|js)$/", $file)) { - foreach (file($file) as $line) { - $this->assert_true(substr($line, -2) != "\r\n", "$file has windows style line endings"); - } - } - } - } - - private function _check_view_preamble($path, &$errors) { - // The preamble for views is a single line that prevents direct script access - if (strpos($path, SYSPATH) === 0) { - // Kohana preamble - $expected = "\n"; - } else { - // Gallery preamble - // @todo use the same preamble for both! - $expected = "\n"; - } - - $fp = fopen($path, "r"); - $actual = fgets($fp); - fclose($fp); - - if ($expected != $actual) { - $errors[] = "$path:1\n expected:\n\t$expected\n actual:\n\t$actual"; - } - } - - private function _check_php_preamble($path, &$errors) { - if (strpos($path, SYSPATH) === 0 || - strpos($path, MODPATH . "unit_test") === 0) { - // Kohana: we only care about the first line - $fp = fopen($path, "r"); - $actual = array(fgets($fp)); - fclose($fp); - $expected = array("_get_preamble($path); - $expected = array( - "getPathname(); - switch ($path) { - case DOCROOT . "installer/database_config.php": - case DOCROOT . "installer/init_var.php": - // Special case views - $this->_check_view_preamble($path, $errors); - break; - - case DOCROOT . "index.php": - case DOCROOT . "installer/index.php": - // Front controllers - break; - - case DOCROOT . "index.local.php": - // Special case optional file, not part of the codebase - break; - - default: - if (strpos($path, DOCROOT . "var/logs") === 0) { - continue; - } else if (preg_match("/views/", $path)) { - $this->_check_view_preamble($path, $errors); - } else { - $this->_check_php_preamble($path, $errors); - } - } - } - - if ($errors) { - $this->assert_false(true, "Preamble errors:\n" . join("\n", $errors)); - } - } - - public function no_tabs_in_our_code_test() { - $dir = new PhpCodeFilterIterator( - new GalleryCodeFilterIterator( - new RecursiveIteratorIterator( - new RecursiveDirectoryIterator(DOCROOT)))); - foreach ($dir as $file) { - $this->assert_false( - preg_match('/\t/', file_get_contents($file)), - "{$file->getPathname()} has tabs in it"); - } - } - - private function _get_preamble($file) { - $lines = file($file); - $copy = array(); - for ($i = 0; $i < count($lines); $i++) { - $copy[] = rtrim($lines[$i]); - if (!strncmp($lines[$i], ' */', 3)) { - return $copy; - } - } - return $copy; - } - - public function helpers_are_static_test() { - $dir = new PhpCodeFilterIterator( - new GalleryCodeFilterIterator( - new RecursiveIteratorIterator( - new RecursiveDirectoryIterator(DOCROOT)))); - foreach ($dir as $file) { - if (basename(dirname($file)) == "helpers") { - foreach (file($file) as $line) { - $this->assert_true( - !preg_match("/\sfunction\s.*\(/", $line) || - preg_match("/^\s*(private static function _|static function)/", $line), - "should be \"static function foo\" or \"private static function _foo\":\n" . - "$file\n$line\n"); - } - } - } - } -} - -class PhpCodeFilterIterator extends FilterIterator { - public function accept() { - $path_name = $this->getInnerIterator()->getPathName(); - return (substr($path_name, -4) == ".php" && - !(strpos($path_name, VARPATH) === 0)); - } -} - -class GalleryCodeFilterIterator extends FilterIterator { - public function accept() { - // Skip anything that we didn"t write - $path_name = $this->getInnerIterator()->getPathName(); - return !( - strpos($path_name, ".svn") || - strpos($path_name, "core/views/kohana_profiler.php") !== false || - strpos($path_name, DOCROOT . "test") !== false || - strpos($path_name, DOCROOT . "var") !== false || - strpos($path_name, MODPATH . "forge") !== false || - strpos($path_name, APPPATH . "views/kohana_error_page.php") !== false || - strpos($path_name, MODPATH . "gallery_unit_test/views/kohana_error_page.php") !== false || - strpos($path_name, MODPATH . "gallery_unit_test/views/kohana_unit_test_cli.php") !== false || - strpos($path_name, MODPATH . "unit_test") !== false || - strpos($path_name, MODPATH . "exif/lib") !== false || - strpos($path_name, MODPATH . "user/lib/PasswordHash") !== false || - strpos($path_name, DOCROOT . "lib/swfupload") !== false || - strpos($path_name, SYSPATH) !== false || - substr($path_name, -1, 1) == "~"); - } -} diff --git a/core/tests/I18n_Test.php b/core/tests/I18n_Test.php deleted file mode 100644 index 9010606a..00000000 --- a/core/tests/I18n_Test.php +++ /dev/null @@ -1,108 +0,0 @@ - 'en', - 'default_locale' => 'te_ST', - 'locale_dir' => VARPATH . 'locale/'); - $this->i18n = I18n::instance($config); - - ORM::factory("incoming_translation") - ->where("locale", "te_ST") - ->delete_all(); - - $messages_te_ST = array( - array('Hello world', 'Hallo Welt'), - array(array('one' => 'One item has been added', - 'other' => '%count elements have been added'), - array('one' => 'Ein Element wurde hinzugefuegt.', - 'other' => '%count Elemente wurden hinzugefuegt.')), - array('Hello %name, how are you today?', 'Hallo %name, wie geht es Dir heute?')); - - foreach ($messages_te_ST as $data) { - list ($message, $translation) = $data; - $entry = ORM::factory("incoming_translation"); - $entry->key = I18n::get_message_key($message); - $entry->message = serialize($message); - $entry->translation = serialize($translation); - $entry->locale = 'te_ST'; - $entry->revision = null; - $entry->save(); - } - } - - public function get_locale_test() { - $locale = $this->i18n->locale(); - $this->assert_equal("te_ST", $locale); - } - - public function set_locale_test() { - $this->i18n->locale("de_DE"); - $locale = $this->i18n->locale(); - $this->assert_equal("de_DE", $locale); - } - - public function translate_simple_test() { - $result = $this->i18n->translate('Hello world'); - $this->assert_equal('Hallo Welt', $result); - } - - public function translate_simple_root_fallback_test() { - $result = $this->i18n->translate('Hello world zzz'); - $this->assert_equal('Hello world zzz', $result); - } - - public function translate_plural_other_test() { - $result = $this->i18n->translate(array('one' => 'One item has been added', - 'other' => '%count elements have been added'), - array('count' => 5)); - $this->assert_equal('5 Elemente wurden hinzugefuegt.', $result); - } - - public function translate_plural_one_test() { - $result = $this->i18n->translate(array('one' => 'One item has been added', - 'other' => '%count elements have been added'), - array('count' => 1)); - $this->assert_equal('Ein Element wurde hinzugefuegt.', $result); - } - - public function translate_interpolate_test() { - $result = $this->i18n->translate('Hello %name, how are you today?', array('name' => 'John')); - $this->assert_equal('Hallo John, wie geht es Dir heute?', $result); - } - - public function translate_interpolate_missing_value_test() { - $result = $this->i18n->translate('Hello %name, how are you today?', array('foo' => 'bar')); - $this->assert_equal('Hallo %name, wie geht es Dir heute?', $result); - } - - public function translate_plural_zero_test() { - // te_ST has the same plural rules as en and de. - // For count 0, plural form "other" should be used. - $result = $this->i18n->translate(array('one' => 'One item has been added', - 'other' => '%count elements have been added'), - array('count' => 0)); - $this->assert_equal('0 Elemente wurden hinzugefuegt.', $result); - } -} \ No newline at end of file diff --git a/core/tests/Item_Model_Test.php b/core/tests/Item_Model_Test.php deleted file mode 100644 index 615b8997..00000000 --- a/core/tests/Item_Model_Test.php +++ /dev/null @@ -1,143 +0,0 @@ -assert_true(!empty($item->created)); - $this->assert_true(!empty($item->updated)); - } - - private function create_random_item() { - $item = ORM::factory("item"); - /* Set all required fields (values are irrelevant) */ - $item->name = rand(); - $item->type = "photo"; - return $item->add_to_parent(ORM::factory("item", 1)); - } - - public function updating_doesnt_change_created_date_test() { - $item = self::create_random_item(); - - // Force the creation date to something well known - $db = Database::instance(); - $db->update("items", array("created" => 0, "updated" => 0), array("id" => $item->id)); - $item->reload(); - $item->title = "foo"; // force a change - $item->save(); - - $this->assert_true(empty($item->created)); - $this->assert_true(!empty($item->updated)); - } - - public function updating_view_count_only_doesnt_change_updated_date_test() { - $item = self::create_random_item(); - $item->reload(); - $this->assert_same(0, $item->view_count); - - // Force the updated date to something well known - $db = Database::instance(); - $db->update("items", array("updated" => 0), array("id" => $item->id)); - $item->reload(); - $item->view_count++; - $item->save(); - - $this->assert_same(1, $item->view_count); - $this->assert_true(empty($item->updated)); - } - - public function move_photo_test() { - // Create a test photo - $item = self::create_random_item(); - - file_put_contents($item->thumb_path(), "thumb"); - file_put_contents($item->resize_path(), "resize"); - file_put_contents($item->file_path(), "file"); - - $original_name = $item->name; - $new_name = rand(); - - // Now rename it - $item->rename($new_name)->save(); - - // Expected: the name changed, the name is now baked into all paths, and all files were moved. - $this->assert_equal($new_name, $item->name); - $this->assert_equal($new_name, basename($item->file_path())); - $this->assert_equal($new_name, basename($item->thumb_path())); - $this->assert_equal($new_name, basename($item->resize_path())); - $this->assert_equal("thumb", file_get_contents($item->thumb_path())); - $this->assert_equal("resize", file_get_contents($item->resize_path())); - $this->assert_equal("file", file_get_contents($item->file_path())); - } - - public function move_album_test() { - // Create an album with a photo in it - $root = ORM::factory("item", 1); - $album = album::create($root, rand(), rand(), rand()); - $photo = ORM::factory("item"); - $photo->name = rand(); - $photo->type = "photo"; - $photo->add_to_parent($album); - - file_put_contents($photo->thumb_path(), "thumb"); - file_put_contents($photo->resize_path(), "resize"); - file_put_contents($photo->file_path(), "file"); - - $original_album_name = $album->name; - $original_photo_name = $photo->name; - $new_album_name = rand(); - - // Now rename the album - $album->rename($new_album_name)->save(); - $photo->reload(); - - // Expected: - // * the album name changed. - // * the album dirs are all moved - // * the photo's paths are all inside the albums paths - // * the photo files are all still intact and accessible - $this->assert_equal($new_album_name, $album->name); - $this->assert_equal($new_album_name, basename($album->file_path())); - $this->assert_equal($new_album_name, basename(dirname($album->thumb_path()))); - $this->assert_equal($new_album_name, basename(dirname($album->resize_path()))); - - $this->assert_same(0, strpos($photo->file_path(), $album->file_path())); - $this->assert_same(0, strpos($photo->thumb_path(), dirname($album->thumb_path()))); - $this->assert_same(0, strpos($photo->resize_path(), dirname($album->resize_path()))); - - $this->assert_equal("thumb", file_get_contents($photo->thumb_path())); - $this->assert_equal("resize", file_get_contents($photo->resize_path())); - $this->assert_equal("file", file_get_contents($photo->file_path())); - } - - public function item_rename_wont_accept_slash_test() { - // Create a test photo - $item = self::create_random_item(); - - $new_name = rand() . "/"; - - try { - $item->rename($new_name)->save(); - } catch (Exception $e) { - // pass - return; - } - $this->assert_false(true, "Item_Model::rename should not accept / characters"); - } -} diff --git a/core/tests/Menu_Test.php b/core/tests/Menu_Test.php deleted file mode 100644 index c91aee0b..00000000 --- a/core/tests/Menu_Test.php +++ /dev/null @@ -1,32 +0,0 @@ -append(Menu::factory("link")->id("element_1")) - ->append(Menu::factory("dialog")->id("element_2")) - ->append(Menu::factory("submenu")->id("element_3") - ->append(Menu::factory("link")->id("element_3_1"))); - - $this->assert_equal("element_2", $menu->get("element_2")->id); - $this->assert_equal("element_3_1", $menu->get("element_3")->get("element_3_1")->id); - } -} \ No newline at end of file diff --git a/core/tests/Movie_Helper_Test.php b/core/tests/Movie_Helper_Test.php deleted file mode 100644 index b92ef3f8..00000000 --- a/core/tests/Movie_Helper_Test.php +++ /dev/null @@ -1,46 +0,0 @@ -assert_true(false, "Shouldn't create a movie with / in the name"); - } - - public function create_movie_shouldnt_allow_names_with_trailing_periods_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $movie = movie::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg.", $rand, $rand); - } catch (Exception $e) { - $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); - return; - } - - $this->assert_true(false, "Shouldn't create a movie with trailing . in the name"); - } -} diff --git a/core/tests/ORM_MPTT_Test.php b/core/tests/ORM_MPTT_Test.php deleted file mode 100644 index 200c8a74..00000000 --- a/core/tests/ORM_MPTT_Test.php +++ /dev/null @@ -1,221 +0,0 @@ -type = "album"; - $album->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - $album->sort_column = "weight"; - $album->sort_order = "ASC"; - $album->add_to_parent($root); - - $this->assert_equal($album->parent()->right - 2, $album->left); - $this->assert_equal($album->parent()->right - 1, $album->right); - $this->assert_equal($album->parent()->level + 1, $album->level); - $this->assert_equal($album->parent()->id, $album->parent_id); - } - - public function add_hierarchy_test() { - $root = ORM::factory("item", 1); - $album1 = self::create_item_and_add_to_parent($root); - $album1_1 = self::create_item_and_add_to_parent($album1); - $album1_2 = self::create_item_and_add_to_parent($album1); - $album1_1_1 = self::create_item_and_add_to_parent($album1_1); - $album1_1_2 = self::create_item_and_add_to_parent($album1_1); - - $album1->reload(); - $this->assert_equal(9, $album1->right - $album1->left); - - $album1_1->reload(); - $this->assert_equal(5, $album1_1->right - $album1_1->left); - } - - public function delete_hierarchy_test() { - $root = ORM::factory("item", 1); - $album1 = self::create_item_and_add_to_parent($root); - $album1_1 = self::create_item_and_add_to_parent($album1); - $album1_2 = self::create_item_and_add_to_parent($album1); - $album1_1_1 = self::create_item_and_add_to_parent($album1_1); - $album1_1_2 = self::create_item_and_add_to_parent($album1_1); - - $album1_1->delete(); - $album1->reload(); - - // Now album1 contains only album1_2 - $this->assert_equal(3, $album1->right - $album1->left); - } - - public function move_to_test() { - $root = ORM::factory("item", 1); - $album1 = album::create($root, "move_to_test_1", "move_to_test_1"); - $album1_1 = album::create($album1, "move_to_test_1_1", "move_to_test_1_1"); - $album1_2 = album::create($album1, "move_to_test_1_2", "move_to_test_1_2"); - $album1_1_1 = album::create($album1_1, "move_to_test_1_1_1", "move_to_test_1_1_1"); - $album1_1_2 = album::create($album1_1, "move_to_test_1_1_2", "move_to_test_1_1_2"); - - $album1_2->reload(); - $album1_1_1->reload(); - - $album1_1_1->move_to($album1_2); - - $album1_1->reload(); - $album1_2->reload(); - - $this->assert_equal(3, $album1_1->right - $album1_1->left); - $this->assert_equal(3, $album1_2->right - $album1_2->left); - - $this->assert_equal( - array($album1_1_2->id => "move_to_test_1_1_2"), - $album1_1->children()->select_list()); - - $this->assert_equal( - array($album1_1_1->id => "move_to_test_1_1_1"), - $album1_2->children()->select_list()); - } - - public function parent_test() { - $root = ORM::factory("item", 1); - $album = self::create_item_and_add_to_parent($root); - - $parent = ORM::factory("item", 1); - $this->assert_equal($parent->id, $album->parent()->id); - } - - public function parents_test() { - $root = ORM::factory("item", 1); - $outer = self::create_item_and_add_to_parent($root); - $inner = self::create_item_and_add_to_parent($outer); - - $parent_ids = array(); - foreach ($inner->parents() as $parent) { - $parent_ids[] = $parent->id; - } - $this->assert_equal(array(1, $outer->id), $parent_ids); - } - - public function children_test() { - $root = ORM::factory("item", 1); - $outer = self::create_item_and_add_to_parent($root); - $inner1 = self::create_item_and_add_to_parent($outer); - $inner2 = self::create_item_and_add_to_parent($outer); - - $child_ids = array(); - foreach ($outer->children() as $child) { - $child_ids[] = $child->id; - } - $this->assert_equal(array($inner1->id, $inner2->id), $child_ids); - } - - public function children_limit_test() { - $root = ORM::factory("item", 1); - $outer = self::create_item_and_add_to_parent($root); - $inner1 = self::create_item_and_add_to_parent($outer); - $inner2 = self::create_item_and_add_to_parent($outer); - - $this->assert_equal(array($inner2->id => $inner2->name), - $outer->children(1, 1)->select_list('id')); - } - - public function children_count_test() { - $root = ORM::factory("item", 1); - $outer = self::create_item_and_add_to_parent($root); - $inner1 = self::create_item_and_add_to_parent($outer); - $inner2 = self::create_item_and_add_to_parent($outer); - - $this->assert_equal(2, $outer->children_count()); - } - - public function descendant_test() { - $root = ORM::factory("item", 1); - - $parent = ORM::factory("item"); - $parent->type = "album"; - $parent->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - $parent->sort_column = "weight"; - $parent->sort_order = "ASC"; - $parent->add_to_parent($root); - - $photo = ORM::factory("item"); - $photo->type = "photo"; - $photo->add_to_parent($parent); - - $album1 = ORM::factory("item"); - $album1->type = "album"; - $album1->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); - $album1->sort_column = "weight"; - $album1->sort_order = "ASC"; - $album1->add_to_parent($parent); - - $photo1 = ORM::factory("item"); - $photo1->type = "photo"; - $photo1->add_to_parent($album1); - - $parent->reload(); - - $this->assert_equal(3, $parent->descendants()->count()); - $this->assert_equal(2, $parent->descendants(null, 0, "photo")->count()); - $this->assert_equal(1, $parent->descendants(null, 0, "album")->count()); - } - - public function descendant_limit_test() { - $root = ORM::factory("item", 1); - - $parent = self::create_item_and_add_to_parent($root); - $album1 = self::create_item_and_add_to_parent($parent); - $album2 = self::create_item_and_add_to_parent($parent); - $album3 = self::create_item_and_add_to_parent($parent); - - $parent->reload(); - $this->assert_equal(2, $parent->descendants(2)->count()); - } - - public function descendant_count_test() { - $root = ORM::factory("item", 1); - - $parent = ORM::factory("item"); - $parent->type = "album"; - $parent->add_to_parent($root); - - $photo = ORM::factory("item"); - $photo->type = "photo"; - $photo->add_to_parent($parent); - - $album1 = ORM::factory("item"); - $album1->type = "album"; - $album1->add_to_parent($parent); - - $photo1 = ORM::factory("item"); - $photo1->type = "photo"; - $photo1->add_to_parent($album1); - - $parent->reload(); - - $this->assert_equal(3, $parent->descendants_count()); - $this->assert_equal(2, $parent->descendants_count("photo")); - $this->assert_equal(1, $parent->descendants_count("album")); - } -} diff --git a/core/tests/Photo_Helper_Test.php b/core/tests/Photo_Helper_Test.php deleted file mode 100644 index deb11bb9..00000000 --- a/core/tests/Photo_Helper_Test.php +++ /dev/null @@ -1,109 +0,0 @@ -assert_equal(VARPATH . "albums/$rand.jpg", $photo->file_path()); - $this->assert_equal(VARPATH . "thumbs/{$rand}.jpg", $photo->thumb_path()); - $this->assert_equal(VARPATH . "resizes/{$rand}.jpg", $photo->resize_path()); - - $this->assert_true(is_file($photo->file_path()), "missing: {$photo->file_path()}"); - $this->assert_true(is_file($photo->resize_path()), "missing: {$photo->resize_path()}"); - $this->assert_true(is_file($photo->thumb_path()), "missing: {$photo->thumb_path()}"); - - $this->assert_equal($root->id, $photo->parent_id); // MPTT tests cover other hierarchy checks - $this->assert_equal("$rand.jpg", $photo->name); - $this->assert_equal($rand, $photo->title); - $this->assert_equal($rand, $photo->description); - $this->assert_equal("image/jpeg", $photo->mime_type); - $this->assert_equal($image_info[0], $photo->width); - $this->assert_equal($image_info[1], $photo->height); - - $this->assert_equal($photo->parent()->right - 2, $photo->left); - $this->assert_equal($photo->parent()->right - 1, $photo->right); - } - - public function create_conflicting_photo_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $photo1 = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); - $photo2 = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); - $this->assert_true($photo1->name != $photo2->name); - } - - public function create_photo_with_no_extension_test() { - $root = ORM::factory("item", 1); - try { - photo::create($root, "/tmp", "name", "title", "description"); - $this->assert_false("should fail with an exception"); - } catch (Exception $e) { - // pass - } - } - - public function thumb_url_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); - $this->assert_equal("http://./var/thumbs/{$rand}.jpg", $photo->thumb_url()); - } - - public function resize_url_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - $album = album::create($root, $rand, $rand, $rand); - $photo = photo::create($album, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); - - $this->assert_equal("http://./var/resizes/{$rand}/{$rand}.jpg", $photo->resize_url()); - } - - public function create_photo_shouldnt_allow_names_with_slash_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand/.jpg", $rand, $rand); - } catch (Exception $e) { - // pass - return; - } - - $this->assert_true(false, "Shouldn't create a photo with / in the name"); - } - - public function create_photo_silently_trims_trailing_periods_test() { - $rand = rand(); - $root = ORM::factory("item", 1); - try { - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg.", $rand, $rand); - } catch (Exception $e) { - $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); - return; - } - - $this->assert_true(false, "Shouldn't create a photo with trailing . in the name"); - } -} diff --git a/core/tests/Photos_Controller_Test.php b/core/tests/Photos_Controller_Test.php deleted file mode 100644 index 71319315..00000000 --- a/core/tests/Photos_Controller_Test.php +++ /dev/null @@ -1,74 +0,0 @@ -_post = $_POST; - } - - public function teardown() { - $_POST = $this->_post; - } - - public function change_photo_test() { - $controller = new Photos_Controller(); - $root = ORM::factory("item", 1); - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "test.jpeg", "test", "test"); - $orig_name = $photo->name; - - $_POST["filename"] = "test.jpeg"; - $_POST["name"] = "new name"; - $_POST["title"] = "new title"; - $_POST["description"] = "new description"; - $_POST["csrf"] = access::csrf_token(); - access::allow(group::everybody(), "edit", $root); - - ob_start(); - $controller->_update($photo); - $results = ob_get_contents(); - ob_end_clean(); - - $this->assert_equal( - json_encode(array("result" => "success", - "location" => "http://./index.php/test.jpeg")), - $results); - $this->assert_equal("new title", $photo->title); - $this->assert_equal("new description", $photo->description); - - // We don't change the name, yet. - $this->assert_equal($orig_name, $photo->name); - } - - public function change_photo_no_csrf_fails_test() { - $controller = new Photos_Controller(); - $root = ORM::factory("item", 1); - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "test", "test", "test"); - $_POST["name"] = "new name"; - $_POST["title"] = "new title"; - $_POST["description"] = "new description"; - access::allow(group::everybody(), "edit", $root); - - try { - $controller->_update($photo); - $this->assert_true(false, "This should fail"); - } catch (Exception $e) { - // pass - } - } -} diff --git a/core/tests/REST_Controller_Test.php b/core/tests/REST_Controller_Test.php deleted file mode 100644 index 8fb04d86..00000000 --- a/core/tests/REST_Controller_Test.php +++ /dev/null @@ -1,197 +0,0 @@ -_post = $_POST; - $this->mock_controller = new Mock_RESTful_Controller("mock"); - $this->mock_not_loaded_controller = new Mock_RESTful_Controller("mock_not_loaded"); - $_POST = array(); - } - - public function teardown() { - $_POST = $this->_post; - } - - public function dispatch_index_test() { - $_SERVER["REQUEST_METHOD"] = "GET"; - $_POST["_method"] = ""; - $this->mock_controller->__call("index", ""); - $this->assert_equal("index", $this->mock_controller->method_called); - } - - public function dispatch_show_test() { - $_SERVER["REQUEST_METHOD"] = "GET"; - $_POST["_method"] = ""; - $this->mock_controller->__call("3", ""); - $this->assert_equal("show", $this->mock_controller->method_called); - $this->assert_equal("Mock_Model", get_class($this->mock_controller->resource)); - } - - public function dispatch_update_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["_method"] = "PUT"; - $_POST["csrf"] = access::csrf_token(); - $this->mock_controller->__call("3", ""); - $this->assert_equal("update", $this->mock_controller->method_called); - $this->assert_equal("Mock_Model", get_class($this->mock_controller->resource)); - } - - public function dispatch_update_fails_without_csrf_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["_method"] = "PUT"; - try { - $this->mock_controller->__call("3", ""); - $this->assert_false(true, "this should fail with a forbidden exception"); - } catch (Exception $e) { - // pass - } - } - - public function dispatch_delete_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["_method"] = "DELETE"; - $_POST["csrf"] = access::csrf_token(); - $this->mock_controller->__call("3", ""); - $this->assert_equal("delete", $this->mock_controller->method_called); - $this->assert_equal("Mock_Model", get_class($this->mock_controller->resource)); - } - - public function dispatch_delete_fails_without_csrf_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["_method"] = "DELETE"; - try { - $this->mock_controller->__call("3", ""); - $this->assert_false(true, "this should fail with a forbidden exception"); - } catch (Exception $e) { - // pass - } - } - - public function dispatch_404_test() { - /* The dispatcher should throw a 404 if the resource isn't loaded and the method isn't POST. */ - $methods = array( - array("GET", ""), - array("POST", "PUT"), - array("POST", "DELETE")); - - foreach ($methods as $method) { - $_SERVER["REQUEST_METHOD"] = $method[0]; - $_POST["_method"] = $method[1]; - $exception_caught = false; - try { - $this->mock_not_loaded_controller->__call(rand(), ""); - } catch (Kohana_404_Exception $e) { - $exception_caught = true; - } - $this->assert_true($exception_caught, "$method[0], $method[1]"); - } - } - - public function dispatch_create_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["_method"] = ""; - $_POST["csrf"] = access::csrf_token(); - $this->mock_not_loaded_controller->__call("", ""); - $this->assert_equal("create", $this->mock_not_loaded_controller->method_called); - $this->assert_equal( - "Mock_Not_Loaded_Model", get_class($this->mock_not_loaded_controller->resource)); - } - - public function dispatch_create_fails_without_csrf_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["_method"] = ""; - try { - $this->mock_not_loaded_controller->__call("", ""); - $this->assert_false(true, "this should fail with a forbidden exception"); - } catch (Exception $e) { - // pass - } - } - - public function dispatch_form_test_add() { - $this->mock_controller->form_add("args"); - $this->assert_equal("form_add", $this->mock_controller->method_called); - $this->assert_equal("args", $this->mock_controller->resource); - } - - public function dispatch_form_test_edit() { - $this->mock_controller->form_edit("1"); - $this->assert_equal("form_edit", $this->mock_controller->method_called); - $this->assert_equal("Mock_Model", get_class($this->mock_controller->resource)); - } - - public function routes_test() { - $this->assert_equal("mock/form_add/args", router::routed_uri("form/add/mock/args")); - $this->assert_equal("mock/form_edit/args", router::routed_uri("form/edit/mock/args")); - $this->assert_equal(null, router::routed_uri("rest/args")); - } -} - -class Mock_RESTful_Controller extends REST_Controller { - public $method_called; - public $resource; - - public function __construct($type) { - $this->resource_type = $type; - parent::__construct(); - } - - public function _index() { - $this->method_called = "index"; - } - - public function _create($resource) { - $this->method_called = "create"; - $this->resource = $resource; - } - - public function _show($resource) { - $this->method_called = "show"; - $this->resource = $resource; - } - - public function _update($resource) { - $this->method_called = "update"; - $this->resource = $resource; - } - - public function _delete($resource) { - $this->method_called = "delete"; - $this->resource = $resource; - } - - public function _form_add($args) { - $this->method_called = "form_add"; - $this->resource = $args; - } - - public function _form_edit($resource) { - $this->method_called = "form_edit"; - $this->resource = $resource; - } -} - -class Mock_Model { - public $loaded = true; -} - -class Mock_Not_Loaded_Model { - public $loaded = false; -} diff --git a/core/tests/REST_Helper_Test.php b/core/tests/REST_Helper_Test.php deleted file mode 100644 index 1bfc63ab..00000000 --- a/core/tests/REST_Helper_Test.php +++ /dev/null @@ -1,45 +0,0 @@ -_post = $_POST; - } - - public function teardown() { - $_POST = $this->_post; - } - - public function request_method_test() { - foreach (array("GET", "POST") as $method) { - foreach (array("", "PUT", "DELETE") as $tunnel) { - if ($method == "GET") { - $expected = "GET"; - } else { - $expected = $tunnel == "" ? $method : $tunnel; - } - $_SERVER["REQUEST_METHOD"] = $method; - $_POST["_method"] = $tunnel; - - $this->assert_equal(strtolower(rest::request_method()), strtolower($expected), - "Request method: {$method}, tunneled: {$tunnel}"); - } - } - } -} diff --git a/core/tests/Sendmail_Test.php b/core/tests/Sendmail_Test.php deleted file mode 100644 index 64c1fff0..00000000 --- a/core/tests/Sendmail_Test.php +++ /dev/null @@ -1,115 +0,0 @@ -to("receiver@someemail.com") - /* - * @todo figure out why this test fails so badly, when the following - * line is not supplied. It doesn't seem to be set by setup method - * as you would expect. - */ - ->from("from@gallery3.com") - ->subject("Test Email Unit test") - ->message("The mail message body") - ->send() - ->send_text; - - $this->assert_equal($expected, $result); - } - - public function sendmail_reply_to_test() { - $expected = "To: receiver@someemail.com\r\n" . - "From: from@gallery3.com\n" . - "Reply-To: reply-to@gallery3.com\r\n" . - "Subject: Test Email Unit test\r\n\r\n" . - "The mail message body"; - $result = Sendmail_For_Test::factory() - ->to("receiver@someemail.com") - ->subject("Test Email Unit test") - ->reply_to("reply-to@gallery3.com") - ->message("The mail message body") - ->send() - ->send_text; - $this->assert_equal($expected, $result); - } - - public function sendmail_html_message_test() { - $expected = "To: receiver@someemail.com\r\n" . - "From: from@gallery3.com\n" . - "Reply-To: public@gallery3.com\n" . - "MIME-Version: 1.0\n" . - "Content-type: text/html; charset=iso-8859-1\r\n" . - "Subject: Test Email Unit test\r\n\r\n" . - "

    This is an html msg

    "; - $result = Sendmail_For_Test::factory() - ->to("receiver@someemail.com") - ->subject("Test Email Unit test") - ->header("MIME-Version", "1.0") - ->header("Content-type", "text/html; charset=iso-8859-1") - ->message("

    This is an html msg

    ") - ->send() - ->send_text; - $this->assert_equal($expected, $result); - } - - public function sendmail_wrapped_message_test() { - $expected = "To: receiver@someemail.com\r\n" . - "From: from@gallery3.com\n" . - "Reply-To: public@gallery3.com\r\n" . - "Subject: Test Email Unit test\r\n\r\n" . - "This is a long message that needs to go\n" . - "over forty characters If we get lucky we\n" . - "might make it long enought to wrap a\n" . - "couple of times."; - $result = Sendmail_For_Test::factory() - ->to("receiver@someemail.com") - ->subject("Test Email Unit test") - ->line_length(40) - ->message("This is a long message that needs to go over forty characters " . - "If we get lucky we might make it long enought to wrap a couple " . - "of times.") - ->send() - ->send_text; - $this->assert_equal($expected, $result); - } -} - -class Sendmail_For_Test extends Sendmail { - static function factory() { - return new Sendmail_For_Test(); - } - - public function mail($to, $subject, $message, $headers) { - $this->send_text = "To: $to\r\n{$headers}\r\nSubject: $this->subject\r\n\r\n$message"; - return true; - } -} \ No newline at end of file diff --git a/core/tests/Var_Test.php b/core/tests/Var_Test.php deleted file mode 100644 index 82370631..00000000 --- a/core/tests/Var_Test.php +++ /dev/null @@ -1,49 +0,0 @@ -assert_equal("original value", module::get_var("core", "Parameter")); - - module::set_var("core", "Parameter", "updated value"); - $this->assert_equal("updated value", module::get_var("core", "Parameter")); - } - - public function clear_parameter_test() { - module::set_var("core", "Parameter", "original value"); - $this->assert_equal("original value", module::get_var("core", "Parameter")); - - module::clear_var("core", "Parameter"); - $this->assert_equal(null, module::get_var("core", "Parameter")); - } - - public function incr_parameter_test() { - module::set_var("core", "Parameter", "original value"); - module::incr_var("core", "Parameter"); - $this->assert_equal("1", module::get_var("core", "Parameter")); - - module::set_var("core", "Parameter", "2"); - module::incr_var("core", "Parameter", "9"); - $this->assert_equal("11", module::get_var("core", "Parameter")); - - module::incr_var("core", "NonExistent", "9"); - $this->assert_equal(null, module::get_var("core", "NonExistent")); - } -} \ No newline at end of file diff --git a/core/tests/images/DSC_0003.jpg b/core/tests/images/DSC_0003.jpg deleted file mode 100644 index 5780d9d8..00000000 Binary files a/core/tests/images/DSC_0003.jpg and /dev/null differ diff --git a/core/tests/images/DSC_0005.jpg b/core/tests/images/DSC_0005.jpg deleted file mode 100644 index 4d2b53a9..00000000 Binary files a/core/tests/images/DSC_0005.jpg and /dev/null differ diff --git a/core/tests/images/DSC_0017.jpg b/core/tests/images/DSC_0017.jpg deleted file mode 100644 index b7f7bb90..00000000 Binary files a/core/tests/images/DSC_0017.jpg and /dev/null differ diff --git a/core/tests/images/DSC_0019.jpg b/core/tests/images/DSC_0019.jpg deleted file mode 100644 index 0ce25aa4..00000000 Binary files a/core/tests/images/DSC_0019.jpg and /dev/null differ diff --git a/core/tests/images/DSC_0067.jpg b/core/tests/images/DSC_0067.jpg deleted file mode 100644 index 84f134cb..00000000 Binary files a/core/tests/images/DSC_0067.jpg and /dev/null differ diff --git a/core/tests/images/DSC_0072.jpg b/core/tests/images/DSC_0072.jpg deleted file mode 100644 index dfad82b0..00000000 Binary files a/core/tests/images/DSC_0072.jpg and /dev/null differ diff --git a/core/tests/images/P4050088.jpg b/core/tests/images/P4050088.jpg deleted file mode 100644 index 62f4749d..00000000 Binary files a/core/tests/images/P4050088.jpg and /dev/null differ diff --git a/core/tests/selenium/Add_Album.html b/core/tests/selenium/Add_Album.html deleted file mode 100644 index ccd4d0b7..00000000 --- a/core/tests/selenium/Add_Album.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - -AddAlbum - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AddAlbum
    open/index.php/albums/1
    clicklink=Add album
    typenameseleniumtest
    typetitleSelenium Test Album
    typedescriptionTest
    click//button[@type='button']
    assertTextPresentSelenium Test Album
    - - diff --git a/core/tests/selenium/Add_Comment.html b/core/tests/selenium/Add_Comment.html deleted file mode 100644 index b4b96ed2..00000000 --- a/core/tests/selenium/Add_Comment.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - -Add comment - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Add comment
    open/index.php/albums/1
    clickAndWaitgPhotoId-2
    typegAuthorTest
    typegEmailtest@gmail.com
    typegTextThis is a selenium test comment.
    click//button[@type='submit']
    assertTextPresentThis is a selenium test comment.
    - - diff --git a/core/tests/selenium/Add_Item.html b/core/tests/selenium/Add_Item.html deleted file mode 100644 index 741dff65..00000000 --- a/core/tests/selenium/Add_Item.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - -AddItem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AddItem
    open/index.php/albums/1
    clicklink=Add an item
    typenameseleniumitem.jpg
    typetitleSelenium Item
    typedescriptionTest item
    typefile/Users/ckieffer/Sites/gallery3.0/core/tests/images/DSC_0003.jpg
    click//button[@type='button']
    clicklink=X
    assertTextPresentSelenium Item
    - - diff --git a/core/tests/selenium/Login.html b/core/tests/selenium/Login.html deleted file mode 100644 index 5e17a3c7..00000000 --- a/core/tests/selenium/Login.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - -Login - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Login
    open/index.php/albums/1
    clickgLoginLink
    typegNameadmin
    typegPasswordadmin
    clickAndWait//button[@type='button']
    clickAndWaitgUserProfileLink
    - - diff --git a/core/tests/test.jpg b/core/tests/test.jpg deleted file mode 100644 index 1f3525e5..00000000 Binary files a/core/tests/test.jpg and /dev/null differ diff --git a/core/views/admin_advanced_settings.html.php b/core/views/admin_advanced_settings.html.php deleted file mode 100644 index 1f3825bd..00000000 --- a/core/views/admin_advanced_settings.html.php +++ /dev/null @@ -1,34 +0,0 @@ - -
    -

    -

    - -

    -
      -
    • - ") ?> -
    • -
    - - - - - - - - - module_name == "core" && $var->name == "_cache") continue ?> - - - - - - -
    module_name ?> name ?> - module_name/$var->name") ?>" - class="gDialogLink" - title=" $var->name, "module_name" => $var->module_name)) ?>"> - value ?> - -
    -
    diff --git a/core/views/admin_block_log_entries.html.php b/core/views/admin_block_log_entries.html.php deleted file mode 100644 index db6313e1..00000000 --- a/core/views/admin_block_log_entries.html.php +++ /dev/null @@ -1,11 +0,0 @@ - - diff --git a/core/views/admin_block_news.html.php b/core/views/admin_block_news.html.php deleted file mode 100644 index cb276ae5..00000000 --- a/core/views/admin_block_news.html.php +++ /dev/null @@ -1,11 +0,0 @@ - -
      - -
    • - "> -

      - -

      -
    • - -
    diff --git a/core/views/admin_block_photo_stream.html.php b/core/views/admin_block_photo_stream.html.php deleted file mode 100644 index e8a4d933..00000000 --- a/core/views/admin_block_photo_stream.html.php +++ /dev/null @@ -1,14 +0,0 @@ - - -

    - -

    diff --git a/core/views/admin_block_platform.html.php b/core/views/admin_block_platform.html.php deleted file mode 100644 index 6b79f047..00000000 --- a/core/views/admin_block_platform.html.php +++ /dev/null @@ -1,18 +0,0 @@ - -
      -
    • - PHP_OS)) ?> -
    • -
    • - function_exists("apache_get_version") ? apache_get_version() : t("Unknown"))) ?> -
    • -
    • - phpversion())) ?> -
    • -
    • - Database::instance()->query("SELECT version() as v")->current()->v)) ?> -
    • -
    • - $load_average)) ?> -
    • -
    diff --git a/core/views/admin_block_stats.html.php b/core/views/admin_block_stats.html.php deleted file mode 100644 index 2d975073..00000000 --- a/core/views/admin_block_stats.html.php +++ /dev/null @@ -1,12 +0,0 @@ - -
      -
    • - module::get_var("core", "version"))) ?> -
    • -
    • - $album_count)) ?> -
    • -
    • - $photo_count)) ?> -
    • -
    diff --git a/core/views/admin_block_welcome.html.php b/core/views/admin_block_welcome.html.php deleted file mode 100644 index 488fa908..00000000 --- a/core/views/admin_block_welcome.html.php +++ /dev/null @@ -1,20 +0,0 @@ - -

    - -

    -
      -
    • - graphics and language settings.", - array("graphics_url" => url::site("admin/graphics"), - "language_url" => url::site("admin/languages"))) ?> -
    • -
    • - choose a theme, or customize the way it looks.", - array("theme_url" => url::site("admin/theme"), - "theme_details_url" => url::site("admin/theme_details"))) ?> -
    • -
    • - install modules to add cool features!", - array("modules_url" => url::site("admin/modules"))) ?> -
    • -
    diff --git a/core/views/admin_dashboard.html.php b/core/views/admin_dashboard.html.php deleted file mode 100644 index c266d7e1..00000000 --- a/core/views/admin_dashboard.html.php +++ /dev/null @@ -1,38 +0,0 @@ - - -
    - -
    diff --git a/core/views/admin_graphics.html.php b/core/views/admin_graphics.html.php deleted file mode 100644 index 08374471..00000000 --- a/core/views/admin_graphics.html.php +++ /dev/null @@ -1,28 +0,0 @@ - - -
    -

    -

    - -

    - -

    - - -
    -

    - -
    -
    - diff --git a/core/views/admin_graphics_gd.html.php b/core/views/admin_graphics_gd.html.php deleted file mode 100644 index cae68b74..00000000 --- a/core/views/admin_graphics_gd.html.php +++ /dev/null @@ -1,29 +0,0 @@ - -
    gd["GD Version"] ? " gInstalledToolkit" : " gUnavailable" ?>"> - " alt="" /> -

    -

    - GD website for more information.", - array("url" => "http://www.boutell.com/gd")) ?> -

    - gd["GD Version"] && function_exists('imagerotate')): ?> -

    - $tk->gd["GD Version"])) ?> -

    -

    - -

    - gd["GD Version"]): ?> -

    - $tk->gd["GD Version"])) ?> -

    -

    - -

    - -

    - -

    - -
    diff --git a/core/views/admin_graphics_graphicsmagick.html.php b/core/views/admin_graphics_graphicsmagick.html.php deleted file mode 100644 index 720a9459..00000000 --- a/core/views/admin_graphics_graphicsmagick.html.php +++ /dev/null @@ -1,21 +0,0 @@ - -
    graphicsmagick ? " gInstalledToolkit" : " gUnavailable" ?>"> -

    - " alt="" /> -

    - GraphicsMagick website for more information.", - array("url" => "http://www.graphicsmagick.org")) ?> -

    - graphicsmagick): ?> -

    - $tk->graphicsmagick)) ?> -

    -

    - -

    - -

    - -

    - -
    diff --git a/core/views/admin_graphics_imagemagick.html.php b/core/views/admin_graphics_imagemagick.html.php deleted file mode 100644 index c7468eed..00000000 --- a/core/views/admin_graphics_imagemagick.html.php +++ /dev/null @@ -1,21 +0,0 @@ - -
    imagemagick ? " gInstalledToolkit" : " gUnavailable" ?>"> -

    - " alt="" /> -

    - ImageMagick website for more information.", - array("url" => "http://www.imagemagick.org")) ?> -

    - imagemagick): ?> -

    - $tk->imagemagick)) ?> -

    -

    - -

    - -

    - -

    - -
    diff --git a/core/views/admin_graphics_none.html.php b/core/views/admin_graphics_none.html.php deleted file mode 100644 index 5306a70d..00000000 --- a/core/views/admin_graphics_none.html.php +++ /dev/null @@ -1,7 +0,0 @@ - -
    -

    -

    - -

    -
    diff --git a/core/views/admin_languages.html.php b/core/views/admin_languages.html.php deleted file mode 100644 index 2b43f1b4..00000000 --- a/core/views/admin_languages.html.php +++ /dev/null @@ -1,15 +0,0 @@ - -
    -

    - - - -

    - " - class="gDialogLink"> - - - -

    - -
    diff --git a/core/views/admin_maintenance.html.php b/core/views/admin_maintenance.html.php deleted file mode 100644 index bc060a7b..00000000 --- a/core/views/admin_maintenance.html.php +++ /dev/null @@ -1,181 +0,0 @@ - -
    -

    -

    - -

    - -
    -

    - - - - - - - - - - - - - -
    - - - - - -
    - name ?> - - description ?> - - callback?csrf=$csrf") ?>" - class="gDialogLink"> - - -
    -
    - - count()): ?> -
    -

    - " - class="gButtonLink ui-icon-left ui-state-default ui-corner-all right"> - - - - - - - - - - - - - "> - - - - - - - - -
    - - - - - - - - - - - -
    - updated) ?> - - name ?> - - done): ?> - state == "cancelled"): ?> - - - - state == "stalled"): ?> - - - $task->percent_complete)) ?> - - - status ?> - - owner()->name ?> - - state == "stalled"): ?> - id?csrf=$csrf") ?>"> - - - - id?csrf=$csrf") ?>"> - - -
    -
    - - - count()): ?> -
    - " - class="gButtonLink ui-icon-left ui-state-default ui-corner-all right"> - - -

    - - - - - - - - - - - "> - - - - - - - - -
    - - - - - - - - - - - -
    - updated) ?> - - name ?> - - state == "success"): ?> - - state == "error"): ?> - - state == "cancelled"): ?> - - - - status ?> - - owner()->name ?> - - done): ?> - id?csrf=$csrf") ?>"> - - - - id?csrf=$csrf") ?>"> - - - id?csrf=$csrf") ?>"> - - - -
    -
    - -
    diff --git a/core/views/admin_maintenance_task.html.php b/core/views/admin_maintenance_task.html.php deleted file mode 100644 index 1ee02311..00000000 --- a/core/views/admin_maintenance_task.html.php +++ /dev/null @@ -1,32 +0,0 @@ - - -
    -
    -
    -
    - - -
    -
    diff --git a/core/views/admin_modules.html.php b/core/views/admin_modules.html.php deleted file mode 100644 index 3fddd6cd..00000000 --- a/core/views/admin_modules.html.php +++ /dev/null @@ -1,32 +0,0 @@ - -
    -

    -

    - -

    - -
    "> - - - - - - - - - - $module_info): ?> - "> - $module_name); ?> - locked) $data["disabled"] = 1; ?> - - - - - - - -
    name) ?> version ?> description) ?>
    - "/> -
    -
    diff --git a/core/views/admin_theme_details.html.php b/core/views/admin_theme_details.html.php deleted file mode 100644 index eb450b16..00000000 --- a/core/views/admin_theme_details.html.php +++ /dev/null @@ -1,6 +0,0 @@ - -
    -

    - - -
    diff --git a/core/views/admin_themes.html.php b/core/views/admin_themes.html.php deleted file mode 100644 index f85bce70..00000000 --- a/core/views/admin_themes.html.php +++ /dev/null @@ -1,89 +0,0 @@ - - - -

    -

    - -

    - -
    -

    -
    - " - alt="name ?>" /> -

    name ?>

    -

    - description ?> -

    -
    - -

    - -
    - -
    -

    -
    - " - alt="name ?>" /> -

    name ?>

    -

    - description ?> -

    -
    - -

    - -
    \ No newline at end of file diff --git a/core/views/admin_themes_preview.html.php b/core/views/admin_themes_preview.html.php deleted file mode 100644 index a7aea172..00000000 --- a/core/views/admin_themes_preview.html.php +++ /dev/null @@ -1,7 +0,0 @@ - -

    - "> - %theme_name", array("theme_name" => $info->name)) ?> - -

    - diff --git a/core/views/after_install.html.php b/core/views/after_install.html.php deleted file mode 100644 index aa26858a..00000000 --- a/core/views/after_install.html.php +++ /dev/null @@ -1,29 +0,0 @@ - -

    - -

    - -

    - -

    - -

    - %user_name account. The very first thing you should do is to change your password to something that you'll remember.", array("user_name" => $user->name)) ?> -

    - -

    - id}") ?>" - title="" - id="gAfterInstallChangePasswordLink" class="gButtonLink ui-state-default ui-corners-all"> - -

    - -

    - Gallery website has news and information about Gallery Project and community.", array("url" => "http://gallery.menalto.com")) ?> -

    - -

    - documentation site or you can ask for help in the forums!", array("codex_url" => "http://codex.gallery2.org/Main_Page", "forum_url" => "http://gallery.menalto.com/forum")) ?> - diff --git a/core/views/after_install_loader.html.php b/core/views/after_install_loader.html.php deleted file mode 100644 index baf91eed..00000000 --- a/core/views/after_install_loader.html.php +++ /dev/null @@ -1,7 +0,0 @@ - -" - href=""/> - diff --git a/core/views/form.html.php b/core/views/form.html.php deleted file mode 100644 index ec2a56a9..00000000 --- a/core/views/form.html.php +++ /dev/null @@ -1,75 +0,0 @@ - -"; -} -if ($title) { - print $title; -} - -if (!function_exists("DrawForm")) { - function DrawForm($inputs, $level=1) { - $error_messages = array(); - $prefix = str_repeat(" ", $level); - $haveGroup = false; - // On the first level, make sure we have a group if not add the

      tag now - if ($level == 1) { - foreach ($inputs as $input) { - $haveGroup |= $input->type == 'group'; - } - if (!$haveGroup) { - print "$prefix
        \n"; - } - } - - foreach ($inputs as $input) { - if ($input->type == 'group') { - print "$prefix
        \n"; - print "$prefix {$input->label}\n"; - print "$prefix
          \n"; - - DrawForm($input->inputs, $level + 2); - print "$prefix
        \n"; - - // Since hidden fields can only have name and value attributes lets just render it now - $hidden_prefix = "$prefix "; - foreach ($input->hidden as $hidden) { - print "$prefix {$hidden->render()}\n"; - } - print "$prefix
        \n"; - } else { - if ($input->error_messages()) { - print "$prefix
      • \n"; - } else { - print "$prefix
      • \n"; - } - - if ($input->label()) { - print "$prefix {$input->label()}\n"; - } - print "$prefix {$input->render()}\n"; - if ($input->message()) { - print "$prefix

        {$input->message()}

        \n"; - } - if ($input->error_messages()) { - foreach ($input->error_messages() as $error_message) { - print "$prefix

        \n"; - print "$prefix $error_message\n"; - print "$prefix

        \n"; - } - } - print "$prefix
      • \n"; - } - } - if ($level == 1 && !$haveGroup) { - print "$prefix
      \n"; - } - } -} -DrawForm($inputs); - -print($close); -?> diff --git a/core/views/kohana_error_page.php b/core/views/kohana_error_page.php deleted file mode 100644 index d9bf9698..00000000 --- a/core/views/kohana_error_page.php +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - <?= t("Something went wrong!") ?> - - - - admin ?> -
      -

      - -

      -

      - -

      - -

      - -

      - -
      - -
      -

      - -

      - - - - - - - diff --git a/core/views/kohana_profiler.php b/core/views/kohana_profiler.php deleted file mode 100644 index c7534349..00000000 --- a/core/views/kohana_profiler.php +++ /dev/null @@ -1,35 +0,0 @@ - - - -
      - - render(); ?> - -

      s

      -
      diff --git a/core/views/l10n_client.html.php b/core/views/l10n_client.html.php deleted file mode 100644 index 8f4092c7..00000000 --- a/core/views/l10n_client.html.php +++ /dev/null @@ -1,31 +0,0 @@ - - diff --git a/core/views/maintenance.html.php b/core/views/maintenance.html.php deleted file mode 100644 index f80b6e7a..00000000 --- a/core/views/maintenance.html.php +++ /dev/null @@ -1,50 +0,0 @@ - - - - - <?= t("Gallery - Maintenance Mode") ?> - - - - -

      - -

      -

      - -

      - - - - - diff --git a/core/views/move_browse.html.php b/core/views/move_browse.html.php deleted file mode 100644 index 4f69c0e9..00000000 --- a/core/views/move_browse.html.php +++ /dev/null @@ -1,47 +0,0 @@ - - -

      - type == "photo"): ?> - - type == "movie"): ?> - - type == "album"): ?> - - -

      -
      -
        -
      • - -
      • -
      -
      id") ?>"> - - - " disabled="disabled"/> -
      -
      diff --git a/core/views/move_tree.html.php b/core/views/move_tree.html.php deleted file mode 100644 index a3a4bc8f..00000000 --- a/core/views/move_tree.html.php +++ /dev/null @@ -1,19 +0,0 @@ - -thumb_tag(array(), 25); ?> -is_descendant($parent)): ?> - title ?> - - title ?> - - diff --git a/core/views/permissions_browse.html.php b/core/views/permissions_browse.html.php deleted file mode 100644 index afd87c2b..00000000 --- a/core/views/permissions_browse.html.php +++ /dev/null @@ -1,56 +0,0 @@ - - -
      - -
        -
      • - AllowOverride FileInfo Options to fix this.", array("url" => "http://httpd.apache.org/docs/2.0/mod/core.html#allowoverride")) ?> -
      • -
      - - - -
      diff --git a/core/views/permissions_form.html.php b/core/views/permissions_form.html.php deleted file mode 100644 index 3dbd0d98..00000000 --- a/core/views/permissions_form.html.php +++ /dev/null @@ -1,94 +0,0 @@ - -
      - -
      - - - - - - - - - - - - - - - name, $item) ?> - name, $item) ?> - name, $item) ?> - - - - - - - - - - - - - - - - - - - - - -
      name ?>
      display_name) ?> - <?= t('denied icon') ?> - - <?= t('locked icon') ?> - - - - <?= t('passive allowed icon') ?> - - - <?= t('inactive denied icon') ?> - - - - <?= t('inactive allowed icon') ?> - - - <?= t('passive denied icon') ?> - - - - <?= t('inactive allowed icon') ?> - - id == 1): ?> - <?= t('denied icon') ?> - - - <?= t('denied icon') ?> - - - - id == 1): ?> - <?= t('allowed icon') ?> - - - <?= t('allowed icon') ?> - - - - <?= t('inactive denied icon') ?> - -
      -
      -
      diff --git a/core/views/quick_pane.html.php b/core/views/quick_pane.html.php deleted file mode 100644 index 95de972b..00000000 --- a/core/views/quick_pane.html.php +++ /dev/null @@ -1,108 +0,0 @@ - -type == "photo"): ?> - -type == "movie"): ?> - -type == "album"): ?> - - -id?page_type=$page_type") ?>" - title=""> - - - - - -is_photo() && graphics::can("rotate")): ?> -id/ccw?csrf=$csrf&page_type=$page_type") ?>" - title=""> - - - - - -id/cw?csrf=$csrf&page_type=$page_type") ?>" - title=""> - - - - - - - - -type == "photo"): ?> - -type == "movie"): ?> - -type == "album"): ?> - - -id") ?>" - title=""> - - - - - - - -parent())): ?> -type == "photo"): ?> - -type == "movie"): ?> - -type == "album"): ?> -album_cover_item_id)): ?> -album_cover_item_id) ? " ui-state-disabled" : "" ?> - - - -id?csrf=$csrf&page_type=$page_type") ?>" - title=""> - - - - - -type == "photo"): ?> - - -type == "movie"): ?> - - -type == "album"): ?> - - - -id?csrf=$csrf&page_type=$page_type") ?>" ref="" id="gQuickDelete" title=""> - - - - - - -is_album()): ?> -"> - - - - - - - diff --git a/core/views/scaffold.html.php b/core/views/scaffold.html.php deleted file mode 100644 index 765464b5..00000000 --- a/core/views/scaffold.html.php +++ /dev/null @@ -1,169 +0,0 @@ - - - - Gallery3 Scaffold - - - -
      -
      - "/> -
      -
      -

      Gallery3 Scaffold

      -

      - This is - a scaffold: - a temporary structure built to support the developers as - they create the real product. As we flesh out Gallery 3, - we'll make it possible for you to peer inside and see the - application taking shape. Eventually, this page will go - away and you'll start in the application itself. In the - meantime, here are some useful links to get you started. -

      - - 0): ?> -
      -

      - - ( albums, photos, comments, tags) -

      -
      - - -
      -
      - Generate Test Data -

      - add: [ - - - - ] photos and albums -

      -

      - add: [ - - - - ] albums only -

      -

      - add: [ - - - - ] comments -

      -

      - add: [ - - - - ] tags -

      -
      -
      - Packaging - ">Make Package -
      -
      -
      -
      - - diff --git a/core/views/simple_uploader.html.php b/core/views/simple_uploader.html.php deleted file mode 100644 index b6725c31..00000000 --- a/core/views/simple_uploader.html.php +++ /dev/null @@ -1,249 +0,0 @@ - - - - - -
      "> -
      - $item->title)) ?> -
      -
      - -
      - -
        -
      • - suhosin.session.encrypt setting from Suhosin. You must disable this setting to upload photos.", - array("encrypt_url" => "http://www.hardened-php.net/suhosin/configuration.html#suhosin.session.encrypt", - "suhosin_url" => "http://www.hardened-php.net/suhosin/")) ?> -
      • -
      - - -

      - -

      -
        - parents() as $parent): ?> -
      • title ?>
      • - -
      • title ?>
      • -
      - -

      -
      -
      -
      - -
      - - - - -
      - - - - diff --git a/index.php b/index.php index ae94c19f..02bd3d3d 100644 --- a/index.php +++ b/index.php @@ -41,7 +41,7 @@ define('KOHANA', 'index.php'); is_link(basename(__FILE__)) and chdir(dirname(realpath(__FILE__))); // Define application and system paths -define('APPPATH', strtr(realpath('core') . '/', DIRECTORY_SEPARATOR, '/')); +define('APPPATH', strtr(realpath('application') . '/', DIRECTORY_SEPARATOR, '/')); define('MODPATH', strtr(realpath('modules') . '/', DIRECTORY_SEPARATOR, '/')); define('THEMEPATH', strtr(realpath('themes') . '/', DIRECTORY_SEPARATOR, '/')); define('SYSPATH', strtr(realpath('kohana') . '/', DIRECTORY_SEPARATOR, '/')); diff --git a/modules/gallery/config/cookie.php b/modules/gallery/config/cookie.php new file mode 100644 index 00000000..692ef548 --- /dev/null +++ b/modules/gallery/config/cookie.php @@ -0,0 +1,49 @@ + email address that appears as the from address + * line-length => word wrap length (PHP documentations suggest no larger tha 70 characters + * reply-to => what goes into the reply to header + */ +$config["from"] = "admin@gallery3.com"; +$config["line_length"] = 70; +$config["reply_to"] = "public@gallery3.com"; +$config["header_separator"] = "\n"; diff --git a/modules/gallery/config/session.php b/modules/gallery/config/session.php new file mode 100644 index 00000000..990fa31f --- /dev/null +++ b/modules/gallery/config/session.php @@ -0,0 +1,66 @@ +admin)) { + throw new Exception("@todo UNAUTHORIZED", 401); + } + parent::__construct(); + } + + public function __call($controller_name, $args) { + if (request::method() == "post") { + access::verify_csrf(); + } + + if ($controller_name == "index") { + $controller_name = "dashboard"; + } + $controller_name = "Admin_{$controller_name}_Controller"; + if ($args) { + $method = array_shift($args); + } else { + $method = "index"; + } + + if (!method_exists($controller_name, $method)) { + return kohana::show_404(); + } + + call_user_func_array(array(new $controller_name, $method), $args); + } +} + diff --git a/modules/gallery/controllers/admin_advanced_settings.php b/modules/gallery/controllers/admin_advanced_settings.php new file mode 100644 index 00000000..79bc1183 --- /dev/null +++ b/modules/gallery/controllers/admin_advanced_settings.php @@ -0,0 +1,53 @@ +content = new View("admin_advanced_settings.html"); + $view->content->vars = ORM::factory("var") + ->orderby("module_name", "name") + ->find_all(); + print $view; + } + + public function edit($module_name, $var_name) { + $value = module::get_var($module_name, $var_name); + $form = new Forge("admin/advanced_settings/save/$module_name/$var_name", "", "post"); + $group = $form->group("edit_var")->label( + t("Edit %var (%module_name)", + array("module_name" => $module_name, "var" => $var_name))); + $group->input("module_name")->label(t("Module"))->value($module_name)->disabled(1); + $group->input("var_name")->label(t("Setting"))->value($var_name)->disabled(1); + $group->textarea("value")->label(t("Value"))->value($value); + $group->submit("")->value(t("Save")); + print $form; + } + + public function save($module_name, $var_name) { + access::verify_csrf(); + + module::set_var($module_name, $var_name, Input::instance()->post("value")); + message::success( + t("Saved value for %var (%module_name)", + array("var" => $var_name, "module_name" => $module_name))); + + print json_encode(array("result" => "success")); + } +} diff --git a/modules/gallery/controllers/admin_dashboard.php b/modules/gallery/controllers/admin_dashboard.php new file mode 100644 index 00000000..d2d2f79b --- /dev/null +++ b/modules/gallery/controllers/admin_dashboard.php @@ -0,0 +1,93 @@ +content = new View("admin_dashboard.html"); + $view->content->blocks = block_manager::get_html("dashboard_center"); + $view->sidebar = "
      " . + block_manager::get_html("dashboard_sidebar") . + "
      "; + print $view; + } + + public function add_block() { + $form = core_block::get_add_block_form(); + if ($form->validate()) { + list ($module_name, $id) = explode(":", $form->add_block->id->value); + $available = block_manager::get_available(); + + if ($form->add_block->center->value) { + block_manager::add("dashboard_center", $module_name, $id); + message::success( + t("Added %title block to the dashboard center", + array("title" => $available["$module_name:$id"]))); + } else { + block_manager::add("dashboard_sidebar", $module_name, $id); + message::success( + t("Added %title to the dashboard sidebar", + array("title" => $available["$module_name:$id"]))); + } + } + url::redirect("admin/dashboard"); + } + + public function remove_block($id) { + access::verify_csrf(); + $blocks_center = block_manager::get_active("dashboard_center"); + $blocks_sidebar = block_manager::get_active("dashboard_sidebar"); + + if (array_key_exists($id, $blocks_sidebar)) { + $deleted = $blocks_sidebar[$id]; + block_manager::remove("dashboard_sidebar", $id); + } else if (array_key_exists($id, $blocks_center)) { + $deleted = $blocks_center[$id]; + block_manager::remove("dashboard_center", $id); + } + + if (!empty($deleted)) { + $available = block_manager::get_available(); + $title = $available[join(":", $deleted)]; + message::success(t("Removed %title block", array("title" => $title))); + } + + url::redirect("admin"); + } + + public function reorder() { + access::verify_csrf(); + $active_set = array(); + foreach (array("dashboard_sidebar", "dashboard_center") as $location) { + foreach (block_manager::get_active($location) as $id => $info) { + $active_set[$id] = $info; + } + } + + foreach (array("dashboard_sidebar", "dashboard_center") as $location) { + $new_blocks = array(); + foreach ($this->input->get($location, array()) as $id) { + $new_blocks[$id] = $active_set[$id]; + } + block_manager::set_active($location, $new_blocks); + } + + $this->_force_block_adder(); + } +} diff --git a/modules/gallery/controllers/admin_graphics.php b/modules/gallery/controllers/admin_graphics.php new file mode 100644 index 00000000..0b3014f0 --- /dev/null +++ b/modules/gallery/controllers/admin_graphics.php @@ -0,0 +1,63 @@ +content = new View("admin_graphics.html"); + $view->content->available = ""; + + $tk = new ArrayObject(graphics::detect_toolkits(), ArrayObject::ARRAY_AS_PROPS); + $active = module::get_var("core", "graphics_toolkit", "none"); + foreach (array("gd", "imagemagick", "graphicsmagick", "none") as $id) { + if ($id == $active) { + $view->content->active = new View("admin_graphics_$id.html"); + $view->content->active->tk = $tk; + $view->content->active->is_active = true; + } else if ($id != "none") { + $v = new View("admin_graphics_$id.html"); + $v->tk = $tk; + $v->is_active = false; + $view->content->available .= $v; + } + } + + print $view; + } + + public function choose($toolkit) { + access::verify_csrf(); + if ($toolkit != module::get_var("core", "graphics_toolkit")) { + module::set_var("core", "graphics_toolkit", $toolkit); + + $toolkit_info = graphics::detect_toolkits(); + if ($toolkit == "graphicsmagick" || $toolkit == "imagemagick") { + module::set_var("core", "graphics_toolkit_path", $toolkit_info[$toolkit]); + } + + site_status::clear("missing_graphics_toolkit"); + message::success(t("Updated Graphics Toolkit")); + log::success("graphics", t("Changed graphics toolkit to: %toolkit", + array("toolkit" => $toolkit))); + } + + url::redirect("admin/graphics"); + } +} + diff --git a/modules/gallery/controllers/admin_languages.php b/modules/gallery/controllers/admin_languages.php new file mode 100644 index 00000000..37d335a3 --- /dev/null +++ b/modules/gallery/controllers/admin_languages.php @@ -0,0 +1,136 @@ +content = new View("admin_languages.html"); + $v->content->settings_form = $this->_languages_form(); + if (empty($share_translations_form)) { + $share_translations_form = $this->_share_translations_form(); + } + $v->content->share_translations_form = $share_translations_form; + $this->_outgoing_translations_count(); + print $v; + } + + public function save() { + $form = $this->_languages_form(); + if ($form->validate()) { + module::set_var("core", "default_locale", $form->choose_language->locale->value); + locale::update_installed($form->choose_language->installed_locales->value); + message::success(t("Settings saved")); + } + url::redirect("admin/languages"); + } + + public function share() { + $form = $this->_share_translations_form(); + if (!$form->validate()) { + // Show the page with form errors + return $this->index($form); + } + + if ($form->sharing->share) { + l10n_client::submit_translations(); + message::success(t("Translations submitted")); + } else { + return $this->_save_api_key($form); + } + url::redirect("admin/languages"); + } + + private function _save_api_key($form) { + $new_key = $form->sharing->api_key->value; + if ($new_key && !l10n_client::validate_api_key($new_key)) { + $form->sharing->api_key->add_error("invalid", 1); + $valid = false; + } else { + $valid = true; + } + + if ($valid) { + $old_key = l10n_client::api_key(); + l10n_client::api_key($new_key); + if ($old_key && !$new_key) { + message::success(t("Your API key has been cleared.")); + } else if ($old_key && $new_key && $old_key != $new_key) { + message::success(t("Your API key has been changed.")); + } else if (!$old_key && $new_key) { + message::success(t("Your API key has been saved.")); + } + + log::success(t("core"), t("l10n_client API key changed.")); + url::redirect("admin/languages"); + } else { + // Show the page with form errors + $this->index($form); + } + } + + private function _languages_form() { + $all_locales = locale::available(); + $installed_locales = locale::installed(); + $form = new Forge("admin/languages/save", "", "post", array("id" => "gLanguageSettingsForm")); + $group = $form->group("choose_language") + ->label(t("Language settings")); + $group->dropdown("locale") + ->options($installed_locales) + ->selected(module::get_var("core", "default_locale")) + ->label(t("Default language")) + ->rules('required'); + + $installation_options = array(); + foreach ($all_locales as $code => $display_name) { + $installation_options[$code] = array($display_name, isset($installed_locales->$code)); + } + $group->checklist("installed_locales") + ->label(t("Installed Languages")) + ->options($installation_options) + ->rules("required"); + $group->submit("save")->value(t("Save settings")); + return $form; + } + + private function _outgoing_translations_count() { + return ORM::factory("outgoing_translation")->count_all(); + } + + private function _share_translations_form() { + $form = new Forge("admin/languages/share", "", "post", array("id" => "gShareTranslationsForm")); + $group = $form->group("sharing") + ->label(t("Sharing you own translations with the Gallery community is easy. Please do!")); + $api_key = l10n_client::api_key(); + $server_link = l10n_client::server_api_key_url(); + $group->input("api_key") + ->label(empty($api_key) + ? t("This is a unique key that will allow you to send translations to the remote server. To get your API key go to %server-link.", + array("server-link" => html::anchor($server_link))) + : t("API Key")) + ->value($api_key) + ->error_messages("invalid", t("The API key you provided is invalid.")); + $group->submit("save")->value(t("Save settings")); + if ($api_key && $this->_outgoing_translations_count()) { + // TODO: UI improvement: hide API key / save button when API key is set. + $group->submit("share")->value(t("Submit translations")); + } + return $form; + } +} + diff --git a/modules/gallery/controllers/admin_maintenance.php b/modules/gallery/controllers/admin_maintenance.php new file mode 100644 index 00000000..c169de75 --- /dev/null +++ b/modules/gallery/controllers/admin_maintenance.php @@ -0,0 +1,181 @@ +query( + "UPDATE {tasks} SET `state` = 'stalled' " . + "WHERE done = 0 " . + "AND state <> 'stalled' " . + "AND unix_timestamp(now()) - updated > 15"); + $stalled_count = $query->count(); + if ($stalled_count) { + log::warning("tasks", + t2("One task is stalled", + "%count tasks are stalled", + $stalled_count), + t('view', + array("url" => url::site("admin/maintenance")))); + } + + $view = new Admin_View("admin.html"); + $view->content = new View("admin_maintenance.html"); + $view->content->task_definitions = task::get_definitions(); + $view->content->running_tasks = ORM::factory("task") + ->where("done", 0)->orderby("updated", "DESC")->find_all(); + $view->content->finished_tasks = ORM::factory("task") + ->where("done", 1)->orderby("updated", "DESC")->find_all(); + print $view; + } + + /** + * Start a new task + * @param string $task_callback + */ + public function start($task_callback) { + access::verify_csrf(); + + $tasks = task::get_definitions(); + $task = task::create($tasks[$task_callback], array()); + $view = new View("admin_maintenance_task.html"); + $view->task = $task; + + log::info("tasks", t("Task %task_name started (task id %task_id)", + array("task_name" => $task->name, "task_id" => $task->id)), + html::anchor(url::site("admin/maintenance"), t("maintenance"))); + print $view; + } + + /** + * Resume a stalled task + * @param string $task_id + */ + public function resume($task_id) { + access::verify_csrf(); + + $task = ORM::factory("task", $task_id); + if (!$task->loaded) { + throw new Exception("@todo MISSING_TASK"); + } + $view = new View("admin_maintenance_task.html"); + $view->task = $task; + + log::info("tasks", t("Task %task_name resumed (task id %task_id)", + array("task_name" => $task->name, "task_id" => $task->id)), + html::anchor(url::site("admin/maintenance"), t("maintenance"))); + print $view; + } + + /** + * Cancel a task. + * @param string $task_id + */ + public function cancel($task_id) { + access::verify_csrf(); + + task::cancel($task_id); + + message::success(t("Task cancelled")); + url::redirect("admin/maintenance"); + } + + public function cancel_running_tasks() { + access::verify_csrf(); + Database::instance()->update( + "tasks", + array("done" => 1, "state" => "cancelled"), + array("done" => 0)); + message::success(t("All running tasks cancelled")); + url::redirect("admin/maintenance"); + } + + /** + * Remove a task. + * @param string $task_id + */ + public function remove($task_id) { + access::verify_csrf(); + + task::remove($task_id); + + message::success(t("Task removed")); + url::redirect("admin/maintenance"); + } + + public function remove_finished_tasks() { + access::verify_csrf(); + Database::instance()->delete("tasks", array("done" => 1)); + message::success(t("All finished tasks removed")); + url::redirect("admin/maintenance"); + } + + /** + * Run a task. This will trigger the task to do a small amount of work, then it will report + * back with status on the task. + * @param string $task_id + */ + public function run($task_id) { + access::verify_csrf(); + + try { + $task = task::run($task_id); + } catch (Exception $e) { + Kohana::log( + "error", + sprintf( + "%s in %s at line %s:\n%s", $e->getMessage(), $e->getFile(), + $e->getLine(), $e->getTraceAsString())); + throw $e; + } + + if ($task->done) { + switch ($task->state) { + case "success": + log::success("tasks", t("Task %task_name completed (task id %task_id)", + array("task_name" => $task->name, "task_id" => $task->id)), + html::anchor(url::site("admin/maintenance"), t("maintenance"))); + message::success(t("Task completed successfully")); + break; + + case "error": + log::error("tasks", t("Task %task_name failed (task id %task_id)", + array("task_name" => $task->name, "task_id" => $task->id)), + html::anchor(url::site("admin/maintenance"), t("maintenance"))); + message::success(t("Task failed")); + break; + } + print json_encode(array("result" => "success", + "task" => array( + "percent_complete" => $task->percent_complete, + "status" => $task->status, + "done" => $task->done), + "location" => url::site("admin/maintenance"))); + + } else { + print json_encode(array("result" => "in_progress", + "task" => array( + "percent_complete" => $task->percent_complete, + "status" => $task->status, + "done" => $task->done))); + } + } +} diff --git a/modules/gallery/controllers/admin_modules.php b/modules/gallery/controllers/admin_modules.php new file mode 100644 index 00000000..f7dd909d --- /dev/null +++ b/modules/gallery/controllers/admin_modules.php @@ -0,0 +1,65 @@ +content = new View("admin_modules.html"); + $view->content->available = module::available(); + print $view; + } + + public function save() { + access::verify_csrf(); + + $changes->activate = array(); + $changes->deactivate = array(); + $activated_names = array(); + $deactivated_names = array(); + foreach (module::available() as $module_name => $info) { + if ($info->locked) { + continue; + } + + $desired = $this->input->post($module_name) == 1; + if ($info->active && !$desired && module::is_active($module_name)) { + $changes->deactivate[] = $module_name; + $deactivated_names[] = $info->name; + module::deactivate($module_name); + } else if (!$info->active && $desired && !module::is_active($module_name)) { + $changes->activate[] = $module_name; + $activated_names[] = $info->name; + module::install($module_name); + module::activate($module_name); + } + } + + module::event("module_change", $changes); + + // @todo this type of collation is questionable from a i18n perspective + if ($activated_names) { + message::success(t("Activated: %names", array("names" => join(", ", $activated_names)))); + } + if ($deactivated_names) { + message::success(t("Deactivated: %names", array("names" => join(", ", $deactivated_names)))); + } + url::redirect("admin/modules"); + } +} + diff --git a/modules/gallery/controllers/admin_theme_details.php b/modules/gallery/controllers/admin_theme_details.php new file mode 100644 index 00000000..542ec31c --- /dev/null +++ b/modules/gallery/controllers/admin_theme_details.php @@ -0,0 +1,67 @@ +content = new View("admin_theme_details.html"); + $view->content->form = theme::get_edit_form_admin(); + print $view; + } + + public function save() { + $form = theme::get_edit_form_admin(); + if ($form->validate()) { + module::set_var("core", "page_size", $form->edit_theme->page_size->value); + + $thumb_size = $form->edit_theme->thumb_size->value; + $thumb_dirty = false; + if (module::get_var("core", "thumb_size") != $thumb_size) { + graphics::remove_rule("core", "thumb", "resize"); + graphics::add_rule( + "core", "thumb", "resize", + array("width" => $thumb_size, "height" => $thumb_size, "master" => Image::AUTO), + 100); + module::set_var("core", "thumb_size", $thumb_size); + } + + $resize_size = $form->edit_theme->resize_size->value; + $resize_dirty = false; + if (module::get_var("core", "resize_size") != $resize_size) { + graphics::remove_rule("core", "resize", "resize"); + graphics::add_rule( + "core", "resize", "resize", + array("width" => $resize_size, "height" => $resize_size, "master" => Image::AUTO), + 100); + module::set_var("core", "resize_size", $resize_size); + } + + module::set_var("core", "header_text", $form->edit_theme->header_text->value); + module::set_var("core", "footer_text", $form->edit_theme->footer_text->value); + + message::success(t("Updated theme details")); + url::redirect("admin/theme_details"); + } else { + $view = new Admin_View("admin.html"); + $view->content = $form; + print $view; + } + } +} + diff --git a/modules/gallery/controllers/admin_themes.php b/modules/gallery/controllers/admin_themes.php new file mode 100644 index 00000000..05c134d1 --- /dev/null +++ b/modules/gallery/controllers/admin_themes.php @@ -0,0 +1,79 @@ +content = new View("admin_themes.html"); + $view->content->admin = module::get_var("core", "active_admin_theme"); + $view->content->site = module::get_var("core", "active_site_theme"); + $view->content->themes = $this->_get_themes(); + print $view; + } + + private function _get_themes() { + $themes = array(); + foreach (scandir(THEMEPATH) as $theme_name) { + if ($theme_name[0] == ".") { + continue; + } + + $file = THEMEPATH . "$theme_name/theme.info"; + $theme_info = new ArrayObject(parse_ini_file($file), ArrayObject::ARRAY_AS_PROPS); + $themes[$theme_name] = $theme_info; + } + return $themes; + } + + public function preview($type, $theme_name) { + $view = new View("admin_themes_preview.html"); + $theme_name = preg_replace("/[^\w]/", "", $theme_name); + $view->info = new ArrayObject( + parse_ini_file(THEMEPATH . "$theme_name/theme.info"), ArrayObject::ARRAY_AS_PROPS); + $view->theme_name = $theme_name; + $view->type = $type; + if ($type == "admin") { + $view->url = url::site("admin?theme=$theme_name"); + } else { + $view->url = url::site("albums/1?theme=$theme_name"); + } + print $view; + } + + public function choose($type, $theme_name) { + access::verify_csrf(); + + $theme_name = preg_replace("/[^\w]/", "", $theme_name); + $info = new ArrayObject( + parse_ini_file(THEMEPATH . "$theme_name/theme.info"), ArrayObject::ARRAY_AS_PROPS); + + if ($type == "admin" && $info->admin) { + module::set_var("core", "active_admin_theme", $theme_name); + message::success(t("Successfully changed your admin theme to %theme_name", + array("theme_name" => $info->name))); + } else if ($type == "site" && $info->site) { + module::set_var("core", "active_site_theme", $theme_name); + message::success(t("Successfully changed your Gallery theme to %theme_name", + array("theme_name" => $info->name))); + } + + url::redirect("admin/themes"); + } +} + diff --git a/modules/gallery/controllers/after_install.php b/modules/gallery/controllers/after_install.php new file mode 100644 index 00000000..f066afe4 --- /dev/null +++ b/modules/gallery/controllers/after_install.php @@ -0,0 +1,30 @@ +admin) { + url::redirect("albums/1"); + } + + $v = new View("after_install.html"); + $v->user = user::active(); + print $v; + } +} diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php new file mode 100644 index 00000000..5b4d5979 --- /dev/null +++ b/modules/gallery/controllers/albums.php @@ -0,0 +1,229 @@ +id != 1) { + access::forbidden(); + } else { + print new Theme_View("login_page.html", "album"); + return; + } + } + + $page_size = module::get_var("core", "page_size", 9); + $show = $this->input->get("show"); + + if ($show) { + $index = $album->get_position($show); + $page = ceil($index / $page_size); + if ($page == 1) { + url::redirect("albums/$album->id"); + } else { + url::redirect("albums/$album->id?page=$page"); + } + } + + $page = $this->input->get("page", "1"); + $children_count = $album->viewable()->children_count(); + $offset = ($page - 1) * $page_size; + $max_pages = max(ceil($children_count / $page_size), 1); + + // Make sure that the page references a valid offset + if ($page < 1) { + url::redirect("albums/$album->id"); + } else if ($page > $max_pages) { + url::redirect("albums/$album->id?page=$max_pages"); + } + + $template = new Theme_View("page.html", "album"); + $template->set_global("page_size", $page_size); + $template->set_global("item", $album); + $template->set_global("children", $album->viewable()->children($page_size, $offset)); + $template->set_global("children_count", $children_count); + $template->set_global("parents", $album->parents()); + $template->content = new View("album.html"); + + // We can't use math in ORM or the query builder, so do this by hand. It's important + // that we do this with math, otherwise concurrent accesses will damage accuracy. + Database::instance()->query( + "UPDATE {items} SET `view_count` = `view_count` + 1 WHERE `id` = $album->id"); + + print $template; + } + + /** + * @see REST_Controller::_create($resource) + */ + public function _create($album) { + access::required("add", $album); + + switch ($this->input->post("type")) { + case "album": + return $this->_create_album($album); + + case "photo": + return $this->_create_photo($album); + + default: + access::forbidden(); + } + } + + private function _create_album($album) { + access::required("add", $album); + + $form = album::get_add_form($album); + if ($form->validate()) { + $new_album = album::create( + $album, + $this->input->post("name"), + $this->input->post("title", $this->input->post("name")), + $this->input->post("description"), + user::active()->id); + + log::success("content", "Created an album", + html::anchor("albums/$new_album->id", "view album")); + message::success(t("Created album %album_title", array("album_title" => $new_album->title))); + + print json_encode( + array("result" => "success", + "location" => url::site("albums/$new_album->id"), + "resource" => url::site("albums/$new_album->id"))); + } else { + print json_encode( + array("result" => "error", + "form" => $form->__toString() . html::script("core/js/albums_form_add.js"))); + } + } + + private function _create_photo($album) { + access::required("add", $album); + + // If we set the content type as JSON, it triggers saving the result as + // a document in the browser (well, in Chrome at least). + // @todo figure out why and fix this. + $form = photo::get_add_form($album); + if ($form->validate()) { + $photo = photo::create( + $album, + $this->input->post("file"), + $_FILES["file"]["name"], + $this->input->post("title", $this->input->post("name")), + $this->input->post("description"), + user::active()->id); + + log::success("content", "Added a photo", html::anchor("photos/$photo->id", "view photo")); + message::success(t("Added photo %photo_title", array("photo_title" => $photo->title))); + + print json_encode( + array("result" => "success", + "resource" => url::site("photos/$photo->id"), + "location" => url::site("photos/$photo->id"))); + } else { + print json_encode( + array("result" => "error", + "form" => $form->__toString())); + } + } + + /** + * @see REST_Controller::_update($resource) + */ + public function _update($album) { + access::required("edit", $album); + + $form = album::get_edit_form($album); + if ($valid = $form->validate()) { + // Make sure that there's not a conflict + if (Database::instance() + ->from("items") + ->where("parent_id", $album->parent_id) + ->where("id <>", $album->id) + ->where("name", $form->edit_album->dirname->value) + ->count_records()) { + $form->edit_album->dirname->add_error("conflict", 1); + $valid = false; + } + } + + // @todo + // @todo we need to make sure that filename / dirname components can't contain a / + // @todo + + if ($valid) { + $orig = clone $album; + $album->title = $form->edit_album->title->value; + $album->description = $form->edit_album->description->value; + $album->sort_column = $form->edit_album->sort_order->column->value; + $album->sort_order = $form->edit_album->sort_order->direction->value; + $album->rename($form->edit_album->dirname->value); + $album->save(); + + module::event("item_updated", $orig, $album); + + log::success("content", "Updated album", "id\">view"); + message::success(t("Saved album %album_title", array("album_title" => $album->title))); + + print json_encode( + array("result" => "success", + "location" => url::site("albums/$album->id"))); + } else { + print json_encode( + array("result" => "error", + "form" => $form->__toString())); + } + } + + /** + * @see REST_Controller::_form_add($parameters) + */ + public function _form_add($album_id) { + $album = ORM::factory("item", $album_id); + access::required("add", $album); + + switch ($this->input->get("type")) { + case "album": + print album::get_add_form($album) . + html::script("core/js/albums_form_add.js"); + break; + + case "photo": + print photo::get_add_form($album); + break; + + default: + kohana::show_404(); + } + } + + /** + * @see REST_Controller::_form_add($parameters) + */ + public function _form_edit($album) { + access::required("edit", $album); + + print album::get_edit_form($album); + } +} diff --git a/modules/gallery/controllers/file_proxy.php b/modules/gallery/controllers/file_proxy.php new file mode 100644 index 00000000..f3c5f109 --- /dev/null +++ b/modules/gallery/controllers/file_proxy.php @@ -0,0 +1,120 @@ +input->server("REQUEST_URI"); + $request_uri = preg_replace("/\?.*/", "", $request_uri); + + // var_uri: http://example.com/gallery3/var/ + $var_uri = url::file("var/"); + + // Make sure that the request is for a file inside var + $offset = strpos($request_uri, $var_uri); + if ($offset === false) { + kohana::show_404(); + } + + $file = substr($request_uri, strlen($var_uri)); + + // Make sure that we don't leave the var dir + if (strpos($file, "..") !== false) { + kohana::show_404(); + } + + // We only handle var/resizes and var/albums + $paths = explode("/", $file); + $type = $paths[0]; + if ($type != "resizes" && $type != "albums" && $type != "thumbs") { + kohana::show_404(); + } + + // If the last element is .album.jpg, pop that off since it's not a real item + if ($paths[count($paths)-1] == ".album.jpg") { + array_pop($paths); + } + if ($paths[count($paths)-1] == "") { + array_pop($paths); + } + + // Find all items that match the level and name, then iterate over those to find a match. + // In most cases we'll get it in one. Note that for the level calculation, we just count the + // size of $paths. $paths includes the type ("thumbs", etc) but it doesn't include the root, + // so it's a wash. + $count = count($paths); + $compare_file = VARPATH . $file; + $item = null; + foreach (ORM::factory("item") + ->where("name", $paths[$count - 1]) + ->where("level", $count) + ->find_all() as $match) { + if ($type == "albums") { + $match_file = $match->file_path(); + } else if ($type == "resizes") { + $match_file = $match->resize_path(); + } else { + $match_file = $match->thumb_path(); + } + if ($match_file == $compare_file) { + $item = $match; + break; + } + } + + if (!$item) { + kohana::show_404(); + } + + // Make sure we have access to the item + if (!access::can("view", $item)) { + kohana::show_404(); + } + + // Make sure we have view_full access to the original + if ($type == "albums" && !access::can("view_full", $item)) { + kohana::show_404(); + } + + // Don't try to load a directory + if ($type == "albums" && $item->is_album()) { + kohana::show_404(); + } + + if (!file_exists($match_file)) { + kohana::show_404(); + } + + // Dump out the image + header("Content-Type: $item->mime_type"); + Kohana::close_buffers(false); + $fd = fopen($match_file, "rb"); + fpassthru($fd); + fclose($fd); + } +} diff --git a/modules/gallery/controllers/items.php b/modules/gallery/controllers/items.php new file mode 100644 index 00000000..13891726 --- /dev/null +++ b/modules/gallery/controllers/items.php @@ -0,0 +1,30 @@ +url(array(), true)); + } +} diff --git a/modules/gallery/controllers/l10n_client.php b/modules/gallery/controllers/l10n_client.php new file mode 100644 index 00000000..17520051 --- /dev/null +++ b/modules/gallery/controllers/l10n_client.php @@ -0,0 +1,128 @@ +admin or access::forbidden(); + + $input = Input::instance(); + $message = $input->post("l10n-message-source"); + $translation = $input->post("l10n-edit-target"); + $key = I18n::get_message_key($message); + $locale = I18n::instance()->locale(); + + $entry = ORM::factory("outgoing_translation") + ->where(array("key" => $key, + "locale" => $locale)) + ->find(); + + if (!$entry->loaded) { + $entry->key = $key; + $entry->locale = $locale; + $entry->message = serialize($message); + $entry->base_revision = null; + } + + $entry->translation = serialize($translation); + + $entry_from_incoming = ORM::factory("incoming_translation") + ->where(array("key" => $key, + "locale" => $locale)) + ->find(); + + if (!$entry_from_incoming->loaded) { + $entry->base_revision = $entry_from_incoming->revision; + } + + $entry->save(); + + print json_encode(new stdClass()); + } + + public function toggle_l10n_mode() { + access::verify_csrf(); + + $session = Session::instance(); + $session->set("l10n_mode", + !$session->get("l10n_mode", false)); + + url::redirect("albums/1"); + } + + private static function _l10n_client_form() { + $form = new Forge("l10n_client/save", "", "post", array("id" => "gL10nClientSaveForm")); + $group = $form->group("l10n_message"); + $group->hidden("l10n-message-source")->value(""); + $group->textarea("l10n-edit-target"); + $group->submit("l10n-edit-save")->value(t("Save translation")); + // TODO(andy_st): Avoiding multiple submit buttons for now (hassle with jQuery form plugin). + // $group->submit("l10n-edit-copy")->value(t("Copy source")); + // $group->submit("l10n-edit-clear")->value(t("Clear")); + + return $form; + } + + private static function _l10n_client_search_form() { + $form = new Forge("l10n_client/search", "", "post", array("id" => "gL10nSearchForm")); + $group = $form->group("l10n_search"); + $group->input("l10n-search")->id("gL10nSearch"); + $group->submit("l10n-search-filter-clear")->value(t("X")); + + return $form; + } + + public static function l10n_form() { + $calls = I18n::instance()->call_log(); + + if ($calls) { + $string_list = array(); + foreach ($calls as $call) { + list ($message, $options) = $call; + // Note: Don't interpolate placeholders for the actual translation input field. + // TODO: Use $options to generate a preview. + if (is_array($message)) { + // TODO: Handle plural forms. + // Translate each message. If it has a plural form, get + // the current locale's plural rules and all plural translations. + continue; + } + $source = $message; + $translation = ''; + $options_for_raw_translation = array(); + if (isset($options['count'])) { + $options_for_raw_translation['count'] = $options['count']; + } + if (I18n::instance()->has_translation($message, $options_for_raw_translation)) { + $translation = I18n::instance()->translate($message, $options_for_raw_translation); + } + $string_list[] = array('source' => $source, + 'translation' => $translation); + } + + $v = new View('l10n_client.html'); + $v->string_list = $string_list; + $v->l10n_form = self::_l10n_client_form(); + $v->l10n_search_form = self::_l10n_client_search_form(); + return $v; + } + + return ''; + } +} diff --git a/modules/gallery/controllers/maintenance.php b/modules/gallery/controllers/maintenance.php new file mode 100644 index 00000000..b5f39bed --- /dev/null +++ b/modules/gallery/controllers/maintenance.php @@ -0,0 +1,24 @@ +source = $source; + $view->tree = $this->_get_tree_html($source, ORM::factory("item", 1)); + print $view; + } + + public function save($source_id) { + access::verify_csrf(); + $source = ORM::factory("item", $source_id); + $target = ORM::factory("item", $this->input->post("target_id")); + + item::move($source, $target); + + print json_encode( + array("result" => "success", + "location" => url::site("albums/{$target->id}"))); + } + + public function show_sub_tree($source_id, $target_id) { + $source = ORM::factory("item", $source_id); + $target = ORM::factory("item", $target_id); + access::required("edit", $source); + access::required("view", $target); + + print $this->_get_tree_html($source, $target); + } + + private function _get_tree_html($source, $target) { + $view = new View("move_tree.html"); + $view->source = $source; + $view->parent = $target; + $view->children = ORM::factory("item") + ->viewable() + ->where("type", "album") + ->where("parent_id", $target->id) + ->find_all(); + return $view; + } + +} diff --git a/modules/gallery/controllers/movies.php b/modules/gallery/controllers/movies.php new file mode 100644 index 00000000..55bbb0e5 --- /dev/null +++ b/modules/gallery/controllers/movies.php @@ -0,0 +1,114 @@ +viewable() + ->where("parent_id", $photo->parent_id) + ->where("id >", $photo->id) + ->orderby("id", "ASC") + ->find(); + $previous_item = ORM::factory("item") + ->viewable() + ->where("parent_id", $photo->parent_id) + ->where("id <", $photo->id) + ->orderby("id", "DESC") + ->find(); + $position = ORM::factory("item") + ->viewable() + ->where("parent_id", $photo->parent_id) + ->where("id <=", $photo->id) + ->count_all(); + + $template = new Theme_View("page.html", "photo"); + $template->set_global("item", $photo); + $template->set_global("children", array()); + $template->set_global("children_count", $photo->children_count()); + $template->set_global("parents", $photo->parents()); + $template->set_global("next_item", $next_item->loaded ? $next_item : null); + $template->set_global("previous_item", $previous_item->loaded ? $previous_item : null); + $template->set_global("sibling_count", $photo->parent()->children_count()); + $template->set_global("position", $position); + + $template->content = new View("movie.html"); + + $photo->view_count++; + $photo->save(); + + print $template; + } + + /** + * @see REST_Controller::_update($resource) + */ + public function _update($photo) { + access::required("edit", $photo); + + $form = photo::get_edit_form($photo); + if ($valid = $form->validate()) { + // Make sure that there's not a conflict + if (Database::instance() + ->from("items") + ->where("parent_id", $photo->parent_id) + ->where("id <>", $photo->id) + ->where("name", $form->edit_photo->filename->value) + ->count_records()) { + $form->edit_photo->filename->add_error("conflict", 1); + $valid = false; + } + } + + if ($valid) { + $orig = clone $photo; + $photo->title = $form->edit_photo->title->value; + $photo->description = $form->edit_photo->description->value; + $photo->rename($form->edit_photo->filename->value); + $photo->save(); + + module::event("item_updated", $orig, $photo); + + log::success("content", "Updated photo", "id\">view"); + message::success(t("Saved photo %photo_title", array("photo_title" => $photo->title))); + + print json_encode( + array("result" => "success", + "location" => url::site("photos/$photo->id"))); + } else { + print json_encode( + array("result" => "error", + "form" => $form->__toString())); + } + } + + /** + * @see REST_Controller::_form_edit($resource) + */ + public function _form_edit($photo) { + access::required("edit", $photo); + print photo::get_edit_form($photo); + } +} diff --git a/modules/gallery/controllers/permissions.php b/modules/gallery/controllers/permissions.php new file mode 100644 index 00000000..b0cee303 --- /dev/null +++ b/modules/gallery/controllers/permissions.php @@ -0,0 +1,80 @@ +is_album()) { + access::forbidden(); + } + + $view = new View("permissions_browse.html"); + $view->htaccess_works = access::htaccess_works(); + $view->item = $item; + $view->parents = $item->parents(); + $view->form = $this->_get_form($item); + + print $view; + } + + function form($id) { + $item = ORM::factory("item", $id); + access::required("edit", $item); + + if (!$item->is_album()) { + access::forbidden(); + } + + print $this->_get_form($item); + } + + function change($command, $group_id, $perm_id, $item_id) { + access::verify_csrf(); + $group = ORM::factory("group", $group_id); + $perm = ORM::factory("permission", $perm_id); + $item = ORM::factory("item", $item_id); + access::required("edit", $item); + + if ($group->loaded && $perm->loaded && $item->loaded) { + switch($command) { + case "allow": + access::allow($group, $perm->name, $item); + break; + + case "deny": + access::deny($group, $perm->name, $item); + break; + + case "reset": + access::reset($group, $perm->name, $item); + break; + } + } + } + + function _get_form($item) { + $view = new View("permissions_form.html"); + $view->item = $item; + $view->groups = ORM::factory("group")->find_all(); + $view->permissions = ORM::factory("permission")->find_all(); + return $view; + } +} diff --git a/modules/gallery/controllers/photos.php b/modules/gallery/controllers/photos.php new file mode 100644 index 00000000..5d4040cf --- /dev/null +++ b/modules/gallery/controllers/photos.php @@ -0,0 +1,116 @@ +viewable() + ->where("parent_id", $photo->parent_id) + ->where("id >", $photo->id) + ->orderby("id", "ASC") + ->find(); + $previous_item = ORM::factory("item") + ->viewable() + ->where("parent_id", $photo->parent_id) + ->where("id <", $photo->id) + ->orderby("id", "DESC") + ->find(); + $position = ORM::factory("item") + ->viewable() + ->where("parent_id", $photo->parent_id) + ->where("id <=", $photo->id) + ->count_all(); + + $template = new Theme_View("page.html", "photo"); + $template->set_global("item", $photo); + $template->set_global("children", array()); + $template->set_global("children_count", $photo->children_count()); + $template->set_global("parents", $photo->parents()); + $template->set_global("next_item", $next_item->loaded ? $next_item : null); + $template->set_global("previous_item", $previous_item->loaded ? $previous_item : null); + $template->set_global("sibling_count", $photo->parent()->children_count()); + $template->set_global("position", $position); + + $template->content = new View("photo.html"); + + $photo->view_count++; + $photo->save(); + + print $template; + } + + /** + * @see REST_Controller::_update($resource) + */ + public function _update($photo) { + access::required("edit", $photo); + + $form = photo::get_edit_form($photo); + if ($valid = $form->validate()) { + if ($form->edit_photo->filename->value != $photo->name) { + // Make sure that there's not a conflict + if (Database::instance() + ->from("items") + ->where("parent_id", $photo->parent_id) + ->where("id <>", $photo->id) + ->where("name", $form->edit_photo->filename->value) + ->count_records()) { + $form->edit_photo->filename->add_error("conflict", 1); + $valid = false; + } + } + } + + if ($valid) { + $orig = clone $photo; + $photo->title = $form->edit_photo->title->value; + $photo->description = $form->edit_photo->description->value; + $photo->rename($form->edit_photo->filename->value); + $photo->save(); + + module::event("item_updated", $orig, $photo); + + log::success("content", "Updated photo", "id\">view"); + message::success(t("Saved photo %photo_title", array("photo_title" => $photo->title))); + + print json_encode( + array("result" => "success", + "location" => url::site("photos/$photo->id"))); + } else { + print json_encode( + array("result" => "error", + "form" => $form->__toString())); + } + } + + /** + * @see REST_Controller::_form_edit($resource) + */ + public function _form_edit($photo) { + access::required("edit", $photo); + print photo::get_edit_form($photo); + } +} diff --git a/modules/gallery/controllers/quick.php b/modules/gallery/controllers/quick.php new file mode 100644 index 00000000..643dce30 --- /dev/null +++ b/modules/gallery/controllers/quick.php @@ -0,0 +1,122 @@ +loaded) { + return ""; + } + + $view = new View("quick_pane.html"); + $view->item = $item; + $view->page_type = Input::instance()->get("page_type"); + print $view; + } + + public function rotate($id, $dir) { + access::verify_csrf(); + $item = ORM::factory("item", $id); + if (!$item->loaded) { + return ""; + } + + $degrees = 0; + switch($dir) { + case "ccw": + $degrees = -90; + break; + + case "cw": + $degrees = 90; + break; + } + + if ($degrees) { + graphics::rotate($item->file_path(), $item->file_path(), array("degrees" => $degrees)); + + list($item->width, $item->height) = getimagesize($item->file_path()); + $item->resize_dirty= 1; + $item->thumb_dirty= 1; + $item->save(); + + graphics::generate($item); + + $parent = $item->parent(); + if ($parent->album_cover_item_id == $item->id) { + copy($item->thumb_path(), $parent->thumb_path()); + $parent->thumb_width = $item->thumb_width; + $parent->thumb_height = $item->thumb_height; + $parent->save(); + } + } + + if (Input::instance()->get("page_type") == "album") { + print json_encode( + array("src" => $item->thumb_url() . "?rnd=" . rand(), + "width" => $item->thumb_width, + "height" => $item->thumb_height)); + } else { + print json_encode( + array("src" => $item->resize_url() . "?rnd=" . rand(), + "width" => $item->resize_width, + "height" => $item->resize_height)); + } + } + + public function make_album_cover($id) { + access::verify_csrf(); + item::make_album_cover(ORM::factory("item", $id)); + + print json_encode(array("result" => "success")); + } + + public function delete($id) { + access::verify_csrf(); + $item = ORM::factory("item", $id); + access::required("edit", $item); + + if ($item->is_album()) { + $msg = t("Deleted album %title", array("title" => $item->title)); + } else { + $msg = t("Deleted photo %title", array("title" => $item->title)); + } + + $item->delete(); + message::success($msg); + + if (Input::instance()->get("page_type") == "album") { + print json_encode(array("result" => "success", "reload" => 1)); + } else { + print json_encode(array("result" => "success", + "location" => url::site("albums/$parent->id"))); + } + } + + public function form_edit($id) { + $item = ORM::factory("item", $id); + access::required("edit", $item); + if ($item->is_album()) { + $form = album::get_edit_form($item); + } else { + $form = photo::get_edit_form($item); + } + print $form; + } +} diff --git a/modules/gallery/controllers/rest.php b/modules/gallery/controllers/rest.php new file mode 100644 index 00000000..11a6bbac --- /dev/null +++ b/modules/gallery/controllers/rest.php @@ -0,0 +1,183 @@ +resource_type == null) { + throw new Exception("@todo ERROR_MISSING_RESOURCE_TYPE"); + } + parent::__construct(); + } + + /** + * Handle dispatching for all REST controllers. + */ + public function __call($function, $args) { + // If no parameter was provided after the controller name (eg "/albums") then $function will + // be set to "index". Otherwise, $function is the first parameter, and $args are all + // subsequent parameters. + $request_method = rest::request_method(); + if ($function == "index" && $request_method == "get") { + return $this->_index(); + } + + $resource = ORM::factory($this->resource_type, (int)$function); + if (!$resource->loaded && $request_method != "post") { + return Kohana::show_404(); + } + + if ($request_method != "get") { + access::verify_csrf(); + } + + switch ($request_method) { + case "get": + return $this->_show($resource); + + case "put": + return $this->_update($resource); + + case "delete": + return $this->_delete($resource); + + case "post": + return $this->_create($resource); + } + } + + /* We're editing an existing item, load it from the database. */ + public function form_edit($resource_id) { + if ($this->resource_type == null) { + throw new Exception("@todo ERROR_MISSING_RESOURCE_TYPE"); + } + + // @todo this needs security checks + $resource = ORM::factory($this->resource_type, $resource_id); + if (!$resource->loaded) { + return Kohana::show_404(); + } + + return $this->_form_edit($resource); + } + + /* We're adding a new item, pass along any additional parameters. */ + public function form_add($parameters) { + return $this->_form_add($parameters); + } + + /** + * Perform a GET request on the controller root + * (e.g. http://www.example.com/gallery3/comments) + */ + public function _index() { + throw new Exception("@todo _create NOT IMPLEMENTED"); + } + + /** + * Perform a POST request on this resource + * @param ORM $resource the instance of this resource type + */ + public function _create($resource) { + throw new Exception("@todo _create NOT IMPLEMENTED"); + } + + /** + * Perform a GET request on this resource + * @param ORM $resource the instance of this resource type + */ + public function _show($resource) { + throw new Exception("@todo _show NOT IMPLEMENTED"); + } + + /** + * Perform a PUT request on this resource + * @param ORM $resource the instance of this resource type + */ + public function _update($resource) { + throw new Exception("@todo _update NOT IMPLEMENTED"); + } + + /** + * Perform a DELETE request on this resource + * @param ORM $resource the instance of this resource type + */ + public function _delete($resource) { + throw new Exception("@todo _delete NOT IMPLEMENTED"); + } + + /** + * Present a form for adding a new resource + * @param string part of the URI after the controller name + */ + public function _form_add($parameter) { + throw new Exception("@todo _form_add NOT IMPLEMENTED"); + } + + /** + * Present a form for editing an existing resource + * @param ORM $resource the resource container for instances of this resource type + */ + public function _form_edit($resource) { + throw new Exception("@todo _form_edit NOT IMPLEMENTED"); + } +} diff --git a/modules/gallery/controllers/scaffold.php b/modules/gallery/controllers/scaffold.php new file mode 100644 index 00000000..f0063725 --- /dev/null +++ b/modules/gallery/controllers/scaffold.php @@ -0,0 +1,437 @@ +template->album_count = ORM::factory("item")->where("type", "album")->count_all(); + $this->template->photo_count = ORM::factory("item")->where("type", "photo")->count_all(); + $this->template->album_tree = $this->_load_album_tree(); + $this->template->add_photo_html = $this->_get_add_photo_html(); + } catch (Exception $e) { + $this->template->album_count = 0; + $this->template->photo_count = 0; + $this->template->deepest_photo = null; + $this->template->album_tree = array(); + $this->template->add_photo_html = ""; + } + + $this->_load_comment_info(); + $this->_load_tag_info(); + + restore_error_handler(); + + if (!empty($session) && $session->get("profiler", false)) { + $profiler = new Profiler(); + $profiler->render(); + } + } + + + function add_photos() { + $path = trim($this->input->post("path")); + $parent_id = (int)$this->input->post("parent_id"); + $parent = ORM::factory("item", $parent_id); + if (!$parent->loaded) { + throw new Exception("@todo BAD_ALBUM"); + } + + batch::start(); + cookie::set("add_photos_path", $path); + $photo_count = 0; + foreach (glob("$path/*.[Jj][Pp][Gg]") as $file) { + set_time_limit(30); + photo::create($parent, $file, basename($file), basename($file)); + $photo_count++; + } + batch::stop(); + + if ($photo_count > 0) { + log::success("content", "(scaffold) Added $photo_count photos", + html::anchor("albums/$parent_id", "View album")); + } + + url::redirect("scaffold"); + } + + function add_albums_and_photos($count, $desired_type=null) { + srand(time()); + $parents = ORM::factory("item")->where("type", "album")->find_all()->as_array(); + $owner_id = user::active()->id; + + $test_images = glob(APPPATH . "tests/images/*.[Jj][Pp][Gg]"); + + batch::start(); + $album_count = $photo_count = 0; + for ($i = 0; $i < $count; $i++) { + set_time_limit(30); + + $parent = $parents[array_rand($parents)]; + $parent->reload(); + $type = $desired_type; + if (!$type) { + $type = rand(0, 10) ? "photo" : "album"; + } + if ($type == "album") { + $thumb_size = module::get_var("core", "thumb_size"); + $parents[] = album::create( + $parent, "rnd_" . rand(), "Rnd $i", "random album $i", $owner_id) + ->save(); + $album_count++; + } else { + $photo_index = rand(0, count($test_images) - 1); + photo::create($parent, $test_images[$photo_index], basename($test_images[$photo_index]), + "rnd_" . rand(), "sample thumb", $owner_id); + $photo_count++; + } + } + batch::stop(); + + if ($photo_count > 0) { + log::success("content", "(scaffold) Added $photo_count photos"); + } + + if ($album_count > 0) { + log::success("content", "(scaffold) Added $album_count albums"); + } + url::redirect("scaffold"); + } + + function random_phrase($count) { + static $words; + if (empty($words)) { + $sample_text = "Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium + laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi + architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas + sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione + voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, + amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt, ut + labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis + nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi + consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam + nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla + pariatur? At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis + praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi + sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt + mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et + expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque + nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas + assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis + debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et + molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut + reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores + repellat."; + $words = preg_split('/\s+/', $sample_text); + } + + $chosen = array(); + for ($i = 0; $i < $count; $i++) { + $chosen[] = $words[array_rand($words)]; + } + + return implode(' ', $chosen); + } + + function add_comments($count) { + srand(time()); + $photos = ORM::factory("item")->where("type", "photo")->find_all()->as_array(); + $users = ORM::factory("user")->find_all()->as_array(); + + if (empty($photos)) { + url::redirect("scaffold"); + } + + if (module::is_active("akismet")) { + akismet::$test_mode = 1; + } + for ($i = 0; $i < $count; $i++) { + $photo = $photos[array_rand($photos)]; + $author = $users[array_rand($users)]; + $guest_name = ucfirst($this->random_phrase(rand(1, 3))); + $guest_email = sprintf("%s@%s.com", $this->random_phrase(1), $this->random_phrase(1)); + $guest_url = sprintf("http://www.%s.com", $this->random_phrase(1)); + comment::create($photo, $author, $this->random_phrase(rand(8, 500)), + $guest_name, $guest_email, $guest_url); + } + + url::redirect("scaffold"); + } + + function add_tags($count) { + $items = ORM::factory("item")->find_all()->as_array(); + + if (!empty($items)) { + $tags = $this->_generateTags($count); + + while ($count-- > 0) { + $tag_name = $tags[array_rand($tags)]; + $item = $items[array_rand($items)]; + + tag::add($item, $tag_name); + } + } + + url::redirect("scaffold"); + } + + private function _generateTags($number){ + // Words from lorem2.com + $words = explode( + " ", + "Lorem ipsum dolor sit amet consectetuer adipiscing elit Donec odio Quisque volutpat " . + "mattis eros Nullam malesuada erat ut turpis Suspendisse urna nibh viverra non " . + "semper suscipit posuere a pede Donec nec justo eget felis facilisis " . + "fermentum Aliquam porttitor mauris sit amet orci Aenean dignissim pellentesque " . + "felis Morbi in sem quis dui placerat ornare Pellentesque odio nisi euismod in " . + "pharetra a ultricies in diam Sed arcu Cras consequat Praesent dapibus neque " . + "id cursus faucibus tortor neque egestas augue eu vulputate magna eros eu " . + "erat Aliquam erat volutpat Nam dui mi tincidunt quis accumsan porttitor " . + "facilisis luctus metus Phasellus ultrices nulla quis nibh Quisque a " . + "lectus Donec consectetuer ligula vulputate sem tristique cursus Nam nulla quam " . + "gravida non commodo a sodales sit amet nisi Pellentesque fermentum " . + "dolor Aliquam quam lectus facilisis auctor ultrices ut elementum vulputate " . + "nunc Sed adipiscing ornare risus Morbi est est blandit sit amet sagittis vel " . + "euismod vel velit Pellentesque egestas sem Suspendisse commodo ullamcorper " . + "magna"); + + while ($number--) { + $results[] = $words[array_rand($words, 1)]; + } + return $results; + } + + function _error_handler($x) { + } + + private function _load_comment_info() { + if (class_exists("Comment_Model")) { + $this->template->comment_count = ORM::factory("comment")->count_all(); + } else { + $this->template->comment_count = 0; + } + } + + private function _load_tag_info() { + if (class_exists("Tag_Model")) { + $this->template->tag_count = ORM::factory("tag")->count_all(); + $this->template->most_tagged = Database::instance() + ->select("item_id AS id", "COUNT(tag_id) AS count") + ->from("items_tags") + ->groupby("item_id") + ->orderby("count", "DESC") + ->limit(1) + ->get() + ->current(); + } else { + $this->template->tag_count = 0; + $this->template->most_tagged = 0; + } + } + + function install($module_name, $redirect=true) { + $to_install = array(); + if ($module_name == "*") { + foreach (module::available() as $module_name => $info) { + if (empty($info->installed)) { + $to_install[] = $module_name; + } + } + } else { + $to_install[] = $module_name; + } + + foreach ($to_install as $module_name) { + if ($module_name != "core") { + require_once(DOCROOT . "modules/${module_name}/helpers/${module_name}_installer.php"); + } + module::install($module_name); + } + + if ($redirect) { + url::redirect("scaffold"); + } + } + + + public function package() { + $this->auto_render = false; + $db = Database::instance(); + + // Drop all tables + foreach ($db->list_tables() as $table) { + $db->query("DROP TABLE IF EXISTS `$table`"); + } + + // Clean out data + dir::unlink(VARPATH . "uploads"); + dir::unlink(VARPATH . "albums"); + dir::unlink(VARPATH . "resizes"); + dir::unlink(VARPATH . "thumbs"); + dir::unlink(VARPATH . "modules"); + dir::unlink(VARPATH . "tmp"); + + $db->clear_cache(); + module::$modules = array(); + module::$active = array(); + + // Use a known random seed so that subsequent packaging runs will reuse the same random + // numbers, keeping our install.sql file more stable. + srand(0); + + try { + core_installer::install(true); + module::load_modules(); + + foreach (array("user", "comment", "organize", "info", "rss", + "search", "slideshow", "tag") as $module_name) { + module::install($module_name); + module::activate($module_name); + } + } catch (Exception $e) { + Kohana::log("error", $e->getTraceAsString()); + print $e->getTrace(); + throw $e; + } + + url::redirect("scaffold/dump_database"); + } + + public function dump_database() { + $this->auto_render = false; + + // We now have a clean install with just the packages that we want. Make sure that the + // database is clean too. + $db = Database::instance(); + $db->query("TRUNCATE {sessions}"); + $db->query("TRUNCATE {logs}"); + $db->query("DELETE FROM {vars} WHERE `module_name` = 'core' AND `name` = '_cache'"); + $db->update("users", array("password" => ""), array("id" => 1)); + $db->update("users", array("password" => ""), array("id" => 2)); + + $dbconfig = Kohana::config('database.default'); + $conn = $dbconfig["connection"]; + $pass = $conn["pass"] ? "-p{$conn['pass']}" : ""; + $sql_file = DOCROOT . "installer/install.sql"; + if (!is_writable($sql_file)) { + print "$sql_file is not writeable"; + return; + } + $command = "mysqldump --compact --add-drop-table -h{$conn['host']} " . + "-u{$conn['user']} $pass {$conn['database']} > $sql_file"; + exec($command, $output, $status); + if ($status) { + print "
      ";
      +      print "$command\n";
      +      print "Failed to dump database\n";
      +      print implode("\n", $output);
      +      return;
      +    }
      +
      +    // Post-process the sql file
      +    $buf = "";
      +    $root_timestamp = ORM::factory("item", 1)->created;
      +    foreach (file($sql_file) as $line) {
      +      // Prefix tables
      +      $line = preg_replace(
      +        "/(CREATE TABLE|IF EXISTS|INSERT INTO) `{$dbconfig['table_prefix']}(\w+)`/", "\\1 {\\2}",
      +        $line);
      +
      +      // Normalize dates
      +      $line = preg_replace("/,$root_timestamp,/", ",UNIX_TIMESTAMP(),", $line);
      +      $buf .= $line;
      +    }
      +    $fd = fopen($sql_file, "wb");
      +    fwrite($fd, $buf);
      +    fclose($fd);
      +
      +    url::redirect("scaffold/dump_var");
      +  }
      +
      +  public function dump_var() {
      +    $this->auto_render = false;
      +
      +    $objects = new RecursiveIteratorIterator(
      +      new RecursiveDirectoryIterator(VARPATH),
      +      RecursiveIteratorIterator::SELF_FIRST);
      +
      +    $var_file = DOCROOT . "installer/init_var.php";
      +    if (!is_writable($var_file)) {
      +      print "$var_file is not writeable";
      +      return;
      +    }
      +
      +    $paths = array();
      +    foreach($objects as $name => $file){
      +      if ($file->getBasename() == "database.php") {
      +        continue;
      +      } else if (basename($file->getPath()) == "logs") {
      +        continue;
      +      }
      +
      +      if ($file->isDir()) {
      +        $paths[] = "VARPATH . \"" . substr($name, strlen(VARPATH)) . "\"";
      +      } else {
      +        // @todo: serialize non-directories
      +        print "Unknown file: $name";
      +        return;
      +      }
      +    }
      +    // Sort the paths so that the var file is stable
      +    sort($paths);
      +
      +    $fd = fopen($var_file, "w");
      +    fwrite($fd, "\n");
      +    fwrite($fd, "where("type", "album")->find_all() as $album) {
      +      if ($album->parent_id) {
      +        $tree[$album->parent_id]->children[] = $album->id;
      +      }
      +      $tree[$album->id]->album = $album;
      +      $tree[$album->id]->children = array();
      +    }
      +
      +    return $tree;
      +  }
      +
      +  public function form($arg1, $arg2) {
      +    if ($arg1 == "add" && $arg2 == "photos") {
      +      print $this->_get_add_photo_html();
      +    }
      +    $this->auto_render = false;
      +  }
      +
      +  public function _get_add_photo_html($parent_id=1) {
      +    $parent = ORM::factory("item", $parent_id);
      +    return photo::get_add_form($parent);
      +  }
      +}
      diff --git a/modules/gallery/controllers/simple_uploader.php b/modules/gallery/controllers/simple_uploader.php
      new file mode 100644
      index 00000000..bdf9582f
      --- /dev/null
      +++ b/modules/gallery/controllers/simple_uploader.php
      @@ -0,0 +1,86 @@
      +item = $item;
      +    print $v;
      +  }
      +
      +  public function start() {
      +    batch::start();
      +  }
      +
      +  public function add_photo($id) {
      +    $album = ORM::factory("item", $id);
      +    access::required("add", $album);
      +    access::verify_csrf();
      +
      +    $file_validation = new Validation($_FILES);
      +    $file_validation->add_rules("Filedata", "upload::valid", "upload::type[gif,jpg,png,flv,mp4]");
      +    if ($file_validation->validate()) {
      +
      +      // SimpleUploader.swf does not yet call /start directly, so simulate it here for now.
      +      if (!batch::in_progress()) {
      +        batch::start();
      +      }
      +
      +      $temp_filename = upload::save("Filedata");
      +      try {
      +        $name = substr(basename($temp_filename), 10);  // Skip unique identifier Kohana adds
      +        $title = $this->convert_filename_to_title($name);
      +        $path_info = pathinfo($temp_filename);
      +        if (array_key_exists("extension", $path_info) &&
      +            in_array(strtolower($path_info["extension"]), array("flv", "mp4"))) {
      +          $movie = movie::create($album, $temp_filename, $name, $title);
      +          log::success("content", t("Added a movie"),
      +                       html::anchor("movies/$movie->id", t("view movie")));
      +        } else {
      +          $photo = photo::create($album, $temp_filename, $name, $title);
      +          log::success("content", t("Added a photo"),
      +                       html::anchor("photos/$photo->id", t("view photo")));
      +        }
      +      } catch (Exception $e) {
      +        unlink($temp_filename);
      +        throw $e;
      +      }
      +      unlink($temp_filename);
      +    }
      +    print "File Received";
      +  }
      +
      +  /**
      +   * We should move this into a helper somewhere.. but where is appropriate?
      +   */
      +  private function convert_filename_to_title($filename) {
      +    $title = strtr($filename, "_", " ");
      +    $title = preg_replace("/\..*?$/", "", $title);
      +    $title = preg_replace("/ +/", " ", $title);
      +    return $title;
      +  }
      +
      +  public function finish() {
      +    batch::stop();
      +    print json_encode(array("result" => "success"));
      +  }
      +}
      diff --git a/modules/gallery/css/debug.css b/modules/gallery/css/debug.css
      new file mode 100644
      index 00000000..fe5665ad
      --- /dev/null
      +++ b/modules/gallery/css/debug.css
      @@ -0,0 +1,28 @@
      +.gAnnotatedThemeBlock {
      +  border: 1px solid #C00;
      +  clear: both;
      +  margin: 1em;
      +  padding: 1em;
      +  position: relative;
      +}
      +
      +.gAnnotatedThemeBlock_album_top {
      +  float: right;
      +}
      +
      +.gAnnotatedThemeBlock_header_bottom {
      +  float: right;
      +}
      +
      +.gAnnotatedThemeBlock div.title {
      +  background: #C00;
      +  border: 1px solid black;
      +  color: white;
      +  font-size: 110%;
      +  padding: 4px;
      +  position: absolute;
      +  right: -1em;
      +  top: -1em;
      +  text-align: left;
      +  -moz-border-radius: 5%;
      +}
      diff --git a/modules/gallery/css/l10n_client.css b/modules/gallery/css/l10n_client.css
      new file mode 100644
      index 00000000..8973715f
      --- /dev/null
      +++ b/modules/gallery/css/l10n_client.css
      @@ -0,0 +1,185 @@
      +// TODO(andy_st): Add original copyright notice from Drupal l10_client.
      +// TODO(andy_st): Add G3 copyright notice.
      +// TODO(andy_st): clean up formatting to match our other CSS files.
      +
      +/* $Id: l10n_client.css,v 1.6 2008/09/09 10:48:20 goba Exp $ */
      +
      +/* width percentages add to 99% rather than 100% to prevent float
      +overflows from occurring in an unnamed browser that can't decide
      +how it wants to round. */
      +
      +/* l10n_client container */
      +#l10n-client {
      +  text-align:left;
      +  z-index:99;
      +  line-height:1em;
      +  color:#000; background:#fff;
      +  position:fixed;
      +  width:100%; height: 2em;
      +  bottom:0px; left:0px;
      +  overflow:hidden;}
      +
      +  * html #l10n-client {
      +    position:static;}
      +
      +#l10n-client-string-select .string-list,
      +#l10n-client-string-editor .source,
      +#l10n-client-string-editor .editor {
      +  height:20em;}
      +
      +#l10n-client .labels {
      +  overflow:hidden;
      +  position:relative;
      +  height:2em;
      +  color:#fff;
      +  background:#37a;}
      +
      +  #l10n-client .labels .label {
      +    display:none;}
      +
      +  /* Panel toggle button (span) */
      +  #l10n-client .labels .toggle {
      +    cursor:pointer;
      +    display:block;
      +    position:absolute; right:0em;
      +    padding: 0em .75em; height:2em; line-height:2em;
      +    text-transform:uppercase;
      +    text-align:center; background:#000;}
      +
      +  /* Panel labels */
      +  #l10n-client h2 {
      +    border-left:1px solid #fff;
      +    height:1em; line-height:1em;
      +    padding: .5em; margin:0px;
      +    font-size:1em;
      +    text-transform:uppercase;}
      +
      +    #l10n-client .strings h2 {
      +      border:0px;}
      +
      +  /* 25 + 37 + 37 = 99 */
      +  #l10n-client .strings {
      +    width:25%; float:left;}
      +
      +  #l10n-client .source {
      +    width:37%; float:left;}
      +
      +  #l10n-client .translation {
      +    width:37%; float:left;}
      +
      +/* Translatable string list */
      +#l10n-client-string-select {
      +  display:none;
      +  float:left;
      +  width:25%;}
      +
      +  #l10n-client .string-list {
      +    height:17em;
      +    overflow:auto;
      +    list-style:none; list-style-image:none;
      +    margin:0em; padding:0em;}
      +
      +  #l10n-client .string-list li {
      +    font-size:.9em;
      +    line-height:1.5em;
      +    cursor:default;
      +    background:transparent;
      +    list-style:none; list-style-image:none;
      +    border-bottom:1px solid #ddd;
      +    padding:.25em .5em;
      +    margin:0em;}
      +
      +  /* Green for translated */
      +  #l10n-client .string-list li.translated {
      +    border-bottom-color:#9c3;
      +    background:#cf6; color:#360;}
      +
      +    #l10n-client .string-list li.translated:hover {
      +      background: #df8;}
      +
      +    #l10n-client .string-list li.translated:active {
      +      background: #9c3;}
      +
      +  #l10n-client .string-list li.hidden {
      +    display:none;}
      +
      +  /* Gray + Blue hover for untranslated */
      +  #l10n-client .string-list li.untranslated {}
      +
      +    #l10n-client .string-list li.untranslated:hover {
      +      background: #ace;}
      +
      +    #l10n-client .string-list li.untranslated:active {
      +      background: #8ac;}
      +
      +  /* Selected string is indicated by bold text */
      +  #l10n-client .string-list li.active {
      +    font-weight:bold;}
      +
      +  #l10n-client #gL10nSearchForm {
      +    background:#eee;
      +    text-align:center;
      +    height:2em; line-height:2em;
      +    margin:0em; padding:.5em .5em;
      +  }
      +
      +  #l10n-client #gL10nSearchForm .form-item,
      +  #l10n-client #gL10nSearchForm input.form-text,
      +  #l10n-client #gL10nSearchForm #search-filter-go,
      +  #l10n-client #gL10nSearchForm #search-filter-clear {
      +    display:inline;
      +    vertical-align:middle;
      +  }
      +
      +    #l10n-client #gL10nSearchForm .form-item {
      +      margin:0em;
      +      padding:0em;
      +    }
      +
      +    #l10n-client #gL10nSearchForm input.form-text {
      +      width:80%;
      +    }
      +
      +    #l10n-client #gL10nSearchForm #search-filter-clear {
      +      width:10%;
      +      margin:0em;
      +    }
      +
      +
      +#l10n-client-string-editor {
      +  display:none;
      +  float:left;
      +  width:74%;}
      +
      +  #l10n-client-string-editor .source {
      +    overflow:hidden;
      +    width:50%; float:left;}
      +
      +    #l10n-client-string-editor .source .source-text {
      +      line-height:1.5em;
      +      background:#eee;
      +      height:16em; margin:1em; padding:1em;
      +      overflow:auto;}
      +
      +  #l10n-client-string-editor .translation {
      +    overflow:hidden;
      +    width:49%; float:right;}
      +
      +#gL10nClientSaveForm {
      +  padding:0em;}
      +
      +  #gL10nClientSaveForm .form-textarea {
      +    height:13em;
      +    font-size:1em; line-height:1.25em;
      +    width:95%;}
      +
      +  #gL10nClientSaveForm .form-submit {
      +    margin-top: 0em;}
      +
      +
      +#l10n-client form ul,
      +#l10n-client form li,
      +#l10n-client form input[type=submit],
      +#l10n-client form input[type=text] {
      +  display: inline ! important ;
      +}
      diff --git a/modules/gallery/css/quick.css b/modules/gallery/css/quick.css
      new file mode 100644
      index 00000000..02f9953e
      --- /dev/null
      +++ b/modules/gallery/css/quick.css
      @@ -0,0 +1,40 @@
      +.gItem:hover {
      +  background-color: #cfdeff;
      +}
      +
      +.gQuick {
      +  border: none !important;
      +  margin: 0 !important;
      +  padding: 0 !important;
      +}
      +
      +#gQuickPane {
      +  background: #000;
      +  border-bottom: 1px solid #ccc;
      +  opacity: 0.9;
      +}
      +
      +#gQuickPane a {
      +  cursor: pointer;
      +  float: left;
      +  margin: 4px;
      +}
      +
      +#gQuickPaneOptions {
      +  background: #000;
      +  float: left;
      +  width: 100%;
      +}
      +
      +#gQuickPaneOptions li a {
      +  display: block;
      +  float: none;
      +  width: auto;
      +  margin: 0;
      +  padding: .5em .5em .5em .8em;
      +  text-align: left;
      +}
      +
      +#gQuickPaneOptions li a:hover {
      +  background-color: #4d4d4d;
      +}
      diff --git a/modules/gallery/helpers/MY_remote.php b/modules/gallery/helpers/MY_remote.php
      new file mode 100644
      index 00000000..4abf5bf1
      --- /dev/null
      +++ b/modules/gallery/helpers/MY_remote.php
      @@ -0,0 +1,163 @@
      + $value) {
      +      if (!empty($post_data_raw)) {
      +        $post_data_raw .= '&';
      +      }
      +      $post_data_raw .= urlencode($key) . '=' . urlencode($value);
      +    }
      +    
      +    $extra_headers['Content-Type'] = 'application/x-www-form-urlencoded';
      +    $extra_headers['Content-Length'] = strlen($post_data_raw);
      +    
      +    return $post_data_raw;
      +  }
      +
      +  /**
      +   * A single request, without following redirects
      +   *
      +   * @todo: Handle redirects? If so, only for GET (i.e. not for POST), and use G2's WebHelper_simple::_parseLocation logic.
      +   */
      +  static function do_request($url, $method='GET', $headers=array(), $body='') {
      +    /* Convert illegal characters */
      +    $url = str_replace(' ', '%20', $url);
      +    
      +    $url_components = self::_parse_url_for_fsockopen($url);
      +    $handle = fsockopen(
      +      $url_components['fsockhost'], $url_components['port'], $errno, $errstr, 5);
      +    if (empty($handle)) {
      +      // log "Error $errno: '$errstr' requesting $url";
      +      return array(null, null, null);
      +    }
      +    
      +    $header_lines = array('Host: ' . $url_components['host']);
      +    foreach ($headers as $key => $value) {
      +      $header_lines[] = $key . ': ' . $value;
      +    }
      +    
      +    $success = fwrite($handle, sprintf("%s %s HTTP/1.0\r\n%s\r\n\r\n%s",
      +                                       $method,
      +                                       $url_components['uri'],
      +                                       implode("\r\n", $header_lines),
      +                                       $body));
      +    if (!$success) {
      +      // Zero bytes written or false was returned
      +      // log "fwrite failed in requestWebPage($url)" . ($success === false ? ' - false' : ''
      +      return array(null, null, null);
      +    }
      +    fflush($handle);
      +    
      +    /*
      +     * Read the status line.  fgets stops after newlines.  The first line is the protocol
      +     * version followed by a numeric status code and its associated textual phrase.
      +     */
      +    $response_status = trim(fgets($handle, 4096));
      +    if (empty($response_status)) {
      +      // 'Empty http response code, maybe timeout'
      +      return array(null, null, null);
      +    }
      +    
      +    /* Read the headers */
      +    $response_headers = array();
      +    while (!feof($handle)) {
      +      $line = trim(fgets($handle, 4096));
      +      if (empty($line)) {
      +        break;
      +      }
      +      
      +      /* Normalize the line endings */
      +      $line = str_replace("\r", '', $line);
      +      
      +      list ($key, $value) = explode(':', $line, 2);
      +      if (isset($response_headers[$key])) {
      +        if (!is_array($response_headers[$key])) {
      +          $response_headers[$key] = array($response_headers[$key]);
      +        }
      +        $response_headers[$key][] = trim($value);
      +      } else {
      +        $response_headers[$key] = trim($value);
      +      }
      +    }
      +    
      +    /* Read the body */
      +    $response_body = '';
      +    while (!feof($handle)) {
      +      $response_body .= fread($handle, 4096);
      +    }
      +    fclose($handle);
      +
      +    return array($response_status, $response_headers, $response_body);
      +  }
      +
      +  /**
      +   * Prepare for fsockopen call.
      +   * @param string $url
      +   * @return array url components
      +   * @access private
      +   */
      +  private static function _parse_url_for_fsockopen($url) {
      +    $url_components = parse_url($url);
      +    if (strtolower($url_components['scheme']) == 'https') {
      +      $url_components['fsockhost'] = 'ssl://' . $url_components['host'];
      +      $default_port = 443;
      +    } else {
      +      $url_components['fsockhost'] = $url_components['host'];
      +      $default_port = 80;
      +    }
      +    if (empty($url_components['port'])) {
      +      $url_components['port'] = $default_port;
      +    }
      +    if (empty($url_components['path'])) {
      +      $url_components['path'] = '/';
      +    }
      +    $uri = $url_components['path']
      +      . (empty($url_components['query']) ? '' : '?' . $url_components['query']);
      +    /* Unescape ampersands, since if the url comes from form input it will be escaped */
      +    $url_components['uri'] = str_replace('&', '&', $uri);
      +    return $url_components;
      +  }
      +}
      +
      diff --git a/modules/gallery/helpers/MY_url.php b/modules/gallery/helpers/MY_url.php
      new file mode 100644
      index 00000000..81dcbe1e
      --- /dev/null
      +++ b/modules/gallery/helpers/MY_url.php
      @@ -0,0 +1,81 @@
      +relative_path();
      +    }
      +    return parent::site($uri . $query, $protocol);
      +  }
      +
      +  static function parse_url() {
      +    if (Router::$controller) {
      +      return;
      +    }
      +
      +    $count = count(Router::$segments);
      +    foreach (ORM::factory("item")
      +             ->where("name", html_entity_decode(Router::$segments[$count - 1], ENT_QUOTES))
      +             ->where("level", $count + 1)
      +             ->find_all() as $match) {
      +      if ($match->relative_path() == html_entity_decode(Router::$current_uri, ENT_QUOTES)) {
      +        $item = $match;
      +      }
      +    }
      +
      +    if (!empty($item)) {
      +      Router::$controller = "{$item->type}s";
      +      Router::$controller_path = APPPATH . "controllers/{$item->type}s.php";
      +      Router::$method = $item->id;
      +    }
      +  }
      +
      +  /**
      +   * Just like url::file() except that it returns an absolute URI
      +   */
      +  static function abs_file($path) {
      +    return url::base(
      +      false, (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') ? 'http' : 'https') . $path;
      +  }
      +
      +  /**
      +   * Just like url::site() except that it returns an absolute URI and
      +   * doesn't take a protocol parameter.
      +   */
      +  static function abs_site($path) {
      +    return url::site(
      +      $path, (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') ? 'http' : 'https');
      +  }
      +
      +  /**
      +   * Just like url::current except that it returns an absolute URI
      +   */
      +  static function abs_current($qs=false) {
      +    return self::abs_site(url::current($qs));
      +  }
      +}
      diff --git a/modules/gallery/helpers/access.php b/modules/gallery/helpers/access.php
      new file mode 100644
      index 00000000..64ce91fa
      --- /dev/null
      +++ b/modules/gallery/helpers/access.php
      @@ -0,0 +1,628 @@
      + tuples.  It would be inefficient to check
      + *   these tuples every time we want to do a lookup, so we use these intents to create an entire
      + *   table of permissions for easy lookup in the Access_Cache_Model.  There's a 1:1 mapping
      + *   between Item_Model and Access_Cache_Model entries.
      + *
      + * o For efficiency, we create columns in Access_Intent_Model and Access_Cache_Model for each of
      + *   the possible Group_Model and Permission_Model combinations.  This may lead to performance
      + *   issues for very large Gallery installs, but for small to medium sized ones (5-10 groups, 5-10
      + *   permissions) it's especially efficient because there's a single field value for each
      + *   group/permission/item combination.
      + *
      + * o For efficiency, we store the cache columns for view permissions directly in the Item_Model.
      + *   This means that we can filter items by group/permission combination without doing any table
      + *   joins making for an especially efficient permission check at the expense of having to
      + *   maintain extra columns for each item.
      + *
      + * o If at any time the Access_Cache_Model becomes invalid, we can rebuild the entire table from
      + *   the Access_Intent_Model
      + */
      +class access_Core {
      +  const DENY      = 0;
      +  const ALLOW     = 1;
      +  const UNKNOWN   = 2;
      +
      +  /**
      +   * Does the active user have this permission on this item?
      +   *
      +   * @param  string     $perm_name
      +   * @param  Item_Model $item
      +   * @return boolean
      +   */
      +  static function can($perm_name, $item) {
      +    if (!$item->loaded) {
      +      return false;
      +    }
      +
      +    if (user::active()->admin) {
      +      return true;
      +    }
      +
      +    $resource = $perm_name == "view" ?
      +      $item : model_cache::get("access_cache", $item->id, "item_id");
      +    foreach (user::group_ids() as $id) {
      +      if ($resource->__get("{$perm_name}_$id") === self::ALLOW) {
      +        return true;
      +      }
      +    }
      +    return false;
      +  }
      +
      +  /**
      +   * If the active user does not have this permission, failed with an access::forbidden().
      +   *
      +   * @param  string     $perm_name
      +   * @param  Item_Model $item
      +   * @return boolean
      +   */
      +  static function required($perm_name, $item) {
      +    if (!self::can($perm_name, $item)) {
      +      self::forbidden();
      +    }
      +  }
      +
      +  /**
      +   * Does this group have this permission on this item?
      +   *
      +   * @param  Group_Model $group
      +   * @param  string      $perm_name
      +   * @param  Item_Model  $item
      +   * @return boolean
      +   */
      +  static function group_can($group, $perm_name, $item) {
      +    $resource = $perm_name == "view" ?
      +      $item : model_cache::get("access_cache", $item->id, "item_id");
      +    return $resource->__get("{$perm_name}_{$group->id}") === self::ALLOW;
      +  }
      +
      +  /**
      +   * Return this group's intent for this permission on this item.
      +   *
      +   * @param  Group_Model $group
      +   * @param  string      $perm_name
      +   * @param  Item_Model  $item
      +   * @return integer     access::ALLOW, access::DENY or null for no intent
      +   */
      +  static function group_intent($group, $perm_name, $item) {
      +    $intent = model_cache::get("access_intent", $item->id, "item_id");
      +    return $intent->__get("{$perm_name}_{$group->id}");
      +  }
      +
      +  /**
      +   * Is the permission on this item locked by a parent?  If so return the nearest parent that
      +   * locks it.
      +   *
      +   * @param  Group_Model $group
      +   * @param  string      $perm_name
      +   * @param  Item_Model  $item
      +   * @return ORM_Model   item that locks this one
      +   */
      +  static function locked_by($group, $perm_name, $item) {
      +    if ($perm_name != "view") {
      +      return null;
      +    }
      +
      +    // For view permissions, if any parent is self::DENY, then those parents lock this one.
      +    // Return
      +    $lock = ORM::factory("item")
      +      ->where("`left` <= $item->left")
      +      ->where("`right` >= $item->right")
      +      ->where("items.id <> $item->id")
      +      ->join("access_intents", "items.id", "access_intents.item_id")
      +      ->where("access_intents.view_$group->id", 0)
      +      ->orderby("level", "DESC")
      +      ->limit(1)
      +      ->find();
      +
      +    if ($lock->loaded) {
      +      return $lock;
      +    } else {
      +      return null;
      +    }
      +  }
      +
      +  /**
      +   * Terminate immediately with an HTTP 503 Forbidden response.
      +   */
      +  static function forbidden() {
      +    throw new Exception("@todo FORBIDDEN", 503);
      +  }
      +
      +  /**
      +   * Internal method to set a permission
      +   *
      +   * @param  Group_Model $group
      +   * @param  string      $perm_name
      +   * @param  Item_Model  $item
      +   * @param  boolean     $value
      +   */
      +  private static function _set(Group_Model $group, $perm_name, $album, $value) {
      +    if (get_class($group) != "Group_Model") {
      +      throw new Exception("@todo PERMISSIONS_ONLY_WORK_ON_GROUPS");
      +    }
      +    if (!$album->loaded) {
      +      throw new Exception("@todo INVALID_ALBUM $album->id");
      +    }
      +    if (!$album->is_album()) {
      +      throw new Exception("@todo INVALID_ALBUM_TYPE not an album");
      +    }
      +    $access = model_cache::get("access_intent", $album->id, "item_id");
      +    $access->__set("{$perm_name}_{$group->id}", $value);
      +    $access->save();
      +
      +    if ($perm_name == "view") {
      +      self::_update_access_view_cache($group, $album);
      +    } else {
      +      self::_update_access_non_view_cache($group, $perm_name, $album);
      +    }
      +
      +    self::_update_htaccess_files($album, $group, $perm_name, $value);
      +  }
      +
      +  /**
      +   * Allow a group to have a permission on an item.
      +   *
      +   * @param  Group_Model $group
      +   * @param  string  $perm_name
      +   * @param  Item_Model $item
      +   */
      +  static function allow($group, $perm_name, $item) {
      +    self::_set($group, $perm_name, $item, self::ALLOW);
      +  }
      +
      +  /**
      +   * Deny a group the given permission on an item.
      +   *
      +   * @param  Group_Model $group
      +   * @param  string  $perm_name
      +   * @param  Item_Model $item
      +   */
      +  static function deny($group, $perm_name, $item) {
      +    self::_set($group, $perm_name, $item, self::DENY);
      +  }
      +
      +  /**
      +   * Unset the given permission for this item and use inherited values
      +   *
      +   * @param  Group_Model $group
      +   * @param  string  $perm_name
      +   * @param  Item_Model $item
      +   */
      +  static function reset($group, $perm_name, $item) {
      +    if ($item->id == 1) {
      +      throw new Exception("@todo CANT_RESET_ROOT_PERMISSION");
      +    }
      +    self::_set($group, $perm_name, $item, null);
      +  }
      +
      +  /**
      +   * Register a permission so that modules can use it.
      +   *
      +   * @param  string $name           The internal name for for this permission
      +   * @param  string $display_name   The internationalized version of the displayable name
      +   * @return void
      +  */
      +  static function register_permission($name, $display_name) {
      +    $permission = ORM::factory("permission", $name);
      +    if ($permission->loaded) {
      +      throw new Exception("@todo PERMISSION_ALREADY_EXISTS $name");
      +    }
      +    $permission->name = $name;
      +    $permission->display_name = $display_name;
      +    $permission->save();
      +
      +    foreach (self::_get_all_groups() as $group) {
      +      self::_add_columns($name, $group);
      +    }
      +  }
      +
      +  /**
      +   * Delete a permission.
      +   *
      +   * @param  string $perm_name
      +   * @return void
      +   */
      +  static function delete_permission($name) {
      +    foreach (self::_get_all_groups() as $group) {
      +      self::_drop_columns($name, $group);
      +    }
      +    $permission = ORM::factory("permission")->where("name", $name)->find();
      +    if ($permission->loaded) {
      +      $permission->delete();
      +    }
      +  }
      +
      +  /**
      +   * Add the appropriate columns for a new group
      +   *
      +   * @param Group_Model $group
      +   * @return void
      +   */
      +  static function add_group($group) {
      +    foreach (ORM::factory("permission")->find_all() as $perm) {
      +      self::_add_columns($perm->name, $group);
      +    }
      +  }
      +
      +  /**
      +   * Remove a group's permission columns (usually when it's deleted)
      +   *
      +   * @param Group_Model $group
      +   * @return void
      +   */
      +  static function delete_group($group) {
      +    foreach (ORM::factory("permission")->find_all() as $perm) {
      +      self::_drop_columns($perm->name, $group);
      +    }
      +  }
      +
      +  /**
      +   * Add new access rows when a new item is added.
      +   *
      +   * @param Item_Model $item
      +   * @return void
      +   */
      +  static function add_item($item) {
      +    $access_intent = ORM::factory("access_intent", $item->id);
      +    if ($access_intent->loaded) {
      +      throw new Exception("@todo ITEM_ALREADY_ADDED $item->id");
      +    }
      +    $access_intent = ORM::factory("access_intent");
      +    $access_intent->item_id = $item->id;
      +    $access_intent->save();
      +
      +    // Create a new access cache entry and copy the parents values.
      +    $access_cache = ORM::factory("access_cache");
      +    $access_cache->item_id = $item->id;
      +    if ($item->id != 1) {
      +      $parent_access_cache =
      +        ORM::factory("access_cache")->where("item_id", $item->parent()->id)->find();
      +      foreach (self::_get_all_groups() as $group) {
      +        foreach (ORM::factory("permission")->find_all() as $perm) {
      +          $field = "{$perm->name}_{$group->id}";
      +          if ($perm->name == "view") {
      +            $item->$field = $item->parent()->$field;
      +          } else {
      +            $access_cache->$field = $parent_access_cache->$field;
      +          }
      +        }
      +      }
      +    }
      +    $item->save();
      +    $access_cache->save();
      +  }
      +
      +  /**
      +   * Delete appropriate access rows when an item is deleted.
      +   *
      +   * @param Item_Model $item
      +   * @return void
      +   */
      +  static function delete_item($item) {
      +    ORM::factory("access_intent")->where("item_id", $item->id)->find()->delete();
      +    ORM::factory("access_cache")->where("item_id", $item->id)->find()->delete();
      +  }
      +
      +  /**
      +   * Verify our Cross Site Request Forgery token is valid, else throw an exception.
      +   */
      +  static function verify_csrf() {
      +    $input = Input::instance();
      +    if ($input->post("csrf", $input->get("csrf", null)) !== Session::instance()->get("csrf")) {
      +      self::forbidden();
      +    }
      +  }
      +
      +  /**
      +   * Get the Cross Site Request Forgery token for this session.
      +   * @return string
      +   */
      +  static function csrf_token() {
      +    $session = Session::instance();
      +    $csrf = $session->get("csrf");
      +    if (empty($csrf)) {
      +      $csrf = md5(rand());
      +      $session->set("csrf", $csrf);
      +    }
      +    return $csrf;
      +  }
      +
      +  /**
      +   * Generate an  element containing the Cross Site Request Forgery token for this session.
      +   * @return string
      +   */
      +  static function csrf_form_field() {
      +    return "";
      +  }
      +
      +  /**
      +   * Internal method to get all available groups.
      +   *
      +   * @return ORM_Iterator
      +   */
      +  private static function _get_all_groups() {
      +    // When we build the core package, it's possible that the user module is not installed yet.
      +    // This is ok at packaging time, so work around it.
      +    if (module::is_active("user")) {
      +      return ORM::factory("group")->find_all();
      +    } else {
      +      return array();
      +    }
      +  }
      +
      +  /**
      +   * Internal method to  remove Permission/Group columns
      +   *
      +   * @param  Group_Model $group
      +   * @param  string      $perm_name
      +   * @return void
      +   */
      +  private static function _drop_columns($perm_name, $group) {
      +    $db = Database::instance();
      +    $field = "{$perm_name}_{$group->id}";
      +    $cache_table = $perm_name == "view" ? "items" : "access_caches";
      +    $db->query("ALTER TABLE {{$cache_table}} DROP `$field`");
      +    $db->query("ALTER TABLE {access_intents} DROP `$field`");
      +    ORM::factory("access_intent")->clear_cache();
      +  }
      +
      +  /**
      +   * Internal method to add Permission/Group columns
      +   *
      +   * @param  Group_Model $group
      +   * @param  string  $perm_name
      +   * @return void
      +   */
      +  private static function _add_columns($perm_name, $group) {
      +    $db = Database::instance();
      +    $field = "{$perm_name}_{$group->id}";
      +    $cache_table = $perm_name == "view" ? "items" : "access_caches";
      +    $db->query("ALTER TABLE {{$cache_table}} ADD `$field` SMALLINT NOT NULL DEFAULT 0");
      +    $db->query("ALTER TABLE {access_intents} ADD `$field` BOOLEAN DEFAULT NULL");
      +    $db->update("access_intents", array($field => 0), array("item_id" => 1));
      +    ORM::factory("access_intent")->clear_cache();
      +  }
      +
      +  /**
      +   * Update the Access_Cache model based on information from the Access_Intent model for view
      +   * permissions only.
      +   *
      +   * @todo: use database locking
      +   *
      +   * @param  Group_Model $group
      +   * @param  Item_Model $item
      +   * @return void
      +   */
      +  private static function _update_access_view_cache($group, $item) {
      +    $access = ORM::factory("access_intent")->where("item_id", $item->id)->find();
      +
      +    $db = Database::instance();
      +    $field = "view_{$group->id}";
      +
      +    // With view permissions, deny values in the parent can override allow values in the child,
      +    // so start from the bottom of the tree and work upwards overlaying negative on top of
      +    // positive.
      +    //
      +    // If the item's intent is ALLOW or DEFAULT, it's possible that some ancestor has specified
      +    // DENY and this ALLOW cannot be obeyed.  So in that case, back up the tree and find any
      +    // non-DEFAULT and non-ALLOW parent and propagate from there.  If we can't find a matching
      +    // item, then its safe to propagate from here.
      +    if ($access->$field !== self::DENY) {
      +      $tmp_item = ORM::factory("item")
      +        ->where("left <", $item->left)
      +        ->where("right >", $item->right)
      +        ->join("access_intents", "access_intents.item_id", "items.id")
      +        ->where("access_intents.$field", self::DENY)
      +        ->orderby("left", "DESC")
      +        ->limit(1)
      +        ->find();
      +      if ($tmp_item->loaded) {
      +        $item = $tmp_item;
      +      }
      +    }
      +
      +    // We will have a problem if we're trying to change a DENY to an ALLOW because the
      +    // access_caches table will already contain DENY values and we won't be able to overwrite
      +    // them according the rule above.  So mark every permission below this level as UNKNOWN so
      +    // that we can tell which permissions have been changed, and which ones need to be updated.
      +    $db->update("items", array($field => self::UNKNOWN),
      +                array("left >=" => $item->left, "right <=" => $item->right));
      +
      +    $query = ORM::factory("access_intent")
      +      ->select(array("access_intents.$field", "items.left", "items.right", "items.id"))
      +      ->join("items", "items.id", "access_intents.item_id")
      +      ->where("left >=", $item->left)
      +      ->where("right <=", $item->right)
      +      ->where("type", "album")
      +      ->where("access_intents.$field IS NOT", null)
      +      ->orderby("level", "DESC")
      +      ->find_all();
      +    foreach ($query as $row) {
      +      if ($row->$field == self::ALLOW) {
      +        // Propagate ALLOW for any row that is still UNKNOWN.
      +        $db->update("items", array($field => $row->$field),
      +          array($field => self::UNKNOWN, "left >=" => $row->left, "right <=" => $row->right));
      +      } else if ($row->$field == self::DENY) {
      +        // DENY overwrites everything below it
      +        $db->update("items", array($field => $row->$field),
      +                    array("left >=" => $row->left, "right <=" => $row->right));
      +      }
      +    }
      +
      +    // Finally, if our intent is DEFAULT at this point it means that we were unable to find a
      +    // DENY parent in the hierarchy to propagate from.  So we'll still have a UNKNOWN values in
      +    // the hierarchy, and all of those are safe to change to ALLOW.
      +    $db->update("items", array($field => self::ALLOW),
      +                array($field => self::UNKNOWN, "left >=" => $item->left, "right <=" => $item->right));
      +  }
      +
      +  /**
      +   * Update the Access_Cache model based on information from the Access_Intent model for non-view
      +   * permissions.
      +   *
      +   * @todo: use database locking
      +   *
      +   * @param  Group_Model $group
      +   * @param  string  $perm_name
      +   * @param  Item_Model $item
      +   * @return void
      +   */
      +  private static function _update_access_non_view_cache($group, $perm_name, $item) {
      +    $access = ORM::factory("access_intent")->where("item_id", $item->id)->find();
      +
      +    $db = Database::instance();
      +    $field = "{$perm_name}_{$group->id}";
      +
      +    // If the item's intent is DEFAULT, then we need to back up the chain to find the nearest
      +    // parent with an intent and propagate from there.
      +    //
      +    // @todo To optimize this, we wouldn't need to propagate from the parent, we could just
      +    //       propagate from here with the parent's intent.
      +    if ($access->$field === null) {
      +      $tmp_item = ORM::factory("item")
      +        ->join("access_intents", "items.id", "access_intents.item_id")
      +        ->where("left <", $item->left)
      +        ->where("right >", $item->right)
      +        ->where("$field IS NOT", null)
      +        ->orderby("left", "DESC")
      +        ->limit(1)
      +        ->find();
      +      if ($tmp_item->loaded) {
      +        $item = $tmp_item;
      +      }
      +    }
      +
      +    // With non-view permissions, each level can override any permissions that came above it
      +    // so start at the top and work downwards, overlaying permissions as we go.
      +    $query = ORM::factory("access_intent")
      +      ->select(array("access_intents.$field", "items.left", "items.right"))
      +      ->join("items", "items.id", "access_intents.item_id")
      +      ->where("left >=", $item->left)
      +      ->where("right <=", $item->right)
      +      ->where("$field IS NOT", null)
      +      ->orderby("level", "ASC")
      +      ->find_all();
      +    foreach  ($query as $row) {
      +      $db->query(
      +        "UPDATE {access_caches} SET `$field` = {$row->$field} " .
      +        "WHERE `item_id` IN " .
      +        "  (SELECT `id` FROM {items} " .
      +        "  WHERE `left` >= $row->left " .
      +        "  AND `right` <= $row->right)");
      +    }
      +  }
      +
      +  /**
      +   * Maintain .htacccess files to prevent direct access to albums, resizes and thumbnails when we
      +   * apply the view and view_full permissions to guest users.
      +   */
      +  private static function _update_htaccess_files($album, $group, $perm_name, $value) {
      +    if ($group->id != 1 || !($perm_name == "view" || $perm_name == "view_full")) {
      +      return;
      +    }
      +
      +    $dirs = array($album->file_path());
      +    if ($perm_name == "view") {
      +      $dirs[] = dirname($album->resize_path());
      +      $dirs[] = dirname($album->thumb_path());
      +    }
      +
      +    $base_url = url::site("file_proxy");
      +    foreach ($dirs as $dir) {
      +      if ($value === self::DENY) {
      +        $fp = fopen("$dir/.htaccess", "w+");
      +        fwrite($fp, "\n");
      +        fwrite($fp, "  RewriteEngine On\n");
      +        fwrite($fp, "  RewriteRule (.*) $base_url/\$1 [L]\n");
      +        fwrite($fp, "\n");
      +        fwrite($fp, "\n");
      +        fwrite($fp, "  Order Deny,Allow\n");
      +        fwrite($fp, "  Deny from All\n");
      +        fwrite($fp, "\n");
      +        fclose($fp);
      +      } else {
      +        @unlink($dir . "/.htaccess");
      +      }
      +    }
      +  }
      +
      +  static function private_key() {
      +    return module::get_var("core", "private_key");
      +  }
      +
      +  /**
      +   * Verify that our htaccess based permission system actually works.  Create a temporary
      +   * directory containing an .htaccess file that uses mod_rewrite to redirect /verify to
      +   * /success.  Then request that url.  If we retrieve it successfully, then our redirects are
      +   * working and our permission system works.
      +   */
      +  static function htaccess_works() {
      +    $success_url = url::file("var/tmp/security_test/success");
      +
      +    @mkdir(VARPATH . "tmp/security_test");
      +    if ($fp = @fopen(VARPATH . "tmp/security_test/.htaccess", "w+")) {
      +      fwrite($fp, "RewriteEngine On\n");
      +      fwrite($fp, "RewriteRule verify $success_url [L]\n");
      +      fclose($fp);
      +    }
      +
      +    if ($fp = @fopen(VARPATH . "tmp/security_test/success", "w+")) {
      +      fwrite($fp, "success");
      +      fclose($fp);
      +    }
      +
      +    list ($response) = remote::do_request(url::abs_file("var/tmp/security_test/verify"));
      +    $works = $response == "HTTP/1.1 200 OK";
      +    @dir::unlink(VARPATH . "tmp/security_test");
      +
      +    return $works;
      +  }
      +}
      diff --git a/modules/gallery/helpers/album.php b/modules/gallery/helpers/album.php
      new file mode 100644
      index 00000000..362b93d0
      --- /dev/null
      +++ b/modules/gallery/helpers/album.php
      @@ -0,0 +1,132 @@
      +loaded || !$parent->is_album()) {
      +      throw new Exception("@todo INVALID_PARENT");
      +    }
      +
      +    if (strpos($name, "/")) {
      +      throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH");
      +    }
      +
      +    // We don't allow trailing periods as a security measure
      +    // ref: http://dev.kohanaphp.com/issues/684
      +    if (rtrim($name, ".") != $name) {
      +      throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD");
      +    }
      +
      +    $album = ORM::factory("item");
      +    $album->type = "album";
      +    $album->title = $title;
      +    $album->description = $description;
      +    $album->name = $name;
      +    $album->owner_id = $owner_id;
      +    $album->thumb_dirty = 1;
      +    $album->resize_dirty = 1;
      +    $album->rand_key = ((float)mt_rand()) / (float)mt_getrandmax();
      +    $album->sort_column = "weight";
      +    $album->sort_order = "ASC";
      +
      +    while (ORM::factory("item")
      +           ->where("parent_id", $parent->id)
      +           ->where("name", $album->name)
      +           ->find()->id) {
      +      $album->name = "{$name}-" . rand();
      +    }
      +
      +    $album = $album->add_to_parent($parent);
      +    mkdir($album->file_path());
      +    mkdir(dirname($album->thumb_path()));
      +    mkdir(dirname($album->resize_path()));
      +
      +    module::event("item_created", $album);
      +
      +    return $album;
      +  }
      +
      +  static function get_add_form($parent) {
      +    $form = new Forge("albums/{$parent->id}", "", "post", array("id" => "gAddAlbumForm"));
      +    $group = $form->group("add_album")
      +      ->label(t("Add an album to %album_title", array("album_title" => $parent->title)));
      +    $group->input("title")->label(t("Title"));
      +    $group->textarea("description")->label(t("Description"));
      +    $group->input("name")->label(t("Directory Name"))
      +      ->callback("item::validate_no_slashes")
      +      ->error_messages("no_slashes", t("The directory name can't contain the \"/\" character"));
      +    $group->hidden("type")->value("album");
      +    $group->submit("")->value(t("Create"));
      +    $form->add_rules_from(ORM::factory("item"));
      +    return $form;
      +  }
      +
      +  static function get_edit_form($parent) {
      +    $form = new Forge("albums/{$parent->id}", "", "post", array("id" => "gEditAlbumForm"));
      +    $form->hidden("_method")->value("put");
      +    $group = $form->group("edit_album")->label(t("Edit Album"));
      +
      +    $group->input("title")->label(t("Title"))->value($parent->title);
      +    $group->textarea("description")->label(t("Description"))->value($parent->description);
      +    if ($parent->id != 1) {
      +      $group->input("dirname")->label(t("Directory Name"))->value($parent->name)
      +        ->callback("item::validate_no_slashes")
      +        ->error_messages("no_slashes", t("The directory name can't contain a \"/\""))
      +        ->callback("item::validate_no_trailing_period")
      +        ->error_messages("no_trailing_period", t("The directory name can't end in \".\""));
      +    }
      +
      +    $sort_order = $group->group("sort_order", array("id" => "gAlbumSortOrder"))
      +      ->label(t("Sort Order"));
      +
      +    $sort_order->dropdown("column", array("id" => "gAlbumSortColumn"))
      +      ->label(t("Sort by"))
      +      ->options(array("weight" => t("Default"),
      +                      "captured" => t("Capture Date"),
      +                      "created" => t("Creation Date"),
      +                      "title" => t("Title"),
      +                      "updated" => t("Updated Date"),
      +                      "view_count" => t("Number of views"),
      +                      "rand_key" => t("Random")))
      +      ->selected($parent->sort_column);
      +    $sort_order->dropdown("direction", array("id" => "gAlbumSortDirection"))
      +      ->label(t("Order"))
      +      ->options(array("ASC" => t("Ascending"),
      +                      "DESC" => t("Descending")))
      +      ->selected($parent->sort_order);
      +    $group->hidden("type")->value("album");
      +    $group->submit("")->value(t("Modify"));
      +    $form->add_rules_from(ORM::factory("item"));
      +    return $form;
      +  }
      +}
      diff --git a/modules/gallery/helpers/batch.php b/modules/gallery/helpers/batch.php
      new file mode 100644
      index 00000000..0faa3369
      --- /dev/null
      +++ b/modules/gallery/helpers/batch.php
      @@ -0,0 +1,40 @@
      +set("batch_level", $session->get("batch_level", 0) + 1);
      +  }
      +
      +  static function stop() {
      +    $session = Session::instance();
      +    $batch_level = $session->get("batch_level", 0) - 1;
      +    if ($batch_level > 0) {
      +      $session->set("batch_level", $batch_level);
      +    } else {
      +      $session->delete("batch_level");
      +      module::event("batch_complete");
      +    }
      +  }
      +
      +  static function in_progress() {
      +    return Session::instance()->get("batch_level", 0) > 0;
      +  }
      +}
      diff --git a/modules/gallery/helpers/block_manager.php b/modules/gallery/helpers/block_manager.php
      new file mode 100644
      index 00000000..022626e5
      --- /dev/null
      +++ b/modules/gallery/helpers/block_manager.php
      @@ -0,0 +1,67 @@
      +name}_block";
      +      if (method_exists($class_name, "get_list")) {
      +        foreach (call_user_func(array($class_name, "get_list")) as $id => $title) {
      +          $blocks["{$module->name}:$id"] = $title;
      +        }
      +      }
      +    }
      +    return $blocks;
      +  }
      +
      +  static function get_html($location) {
      +    $active = self::get_active($location);
      +    $result = "";
      +    foreach ($active as $id => $desc) {
      +      if (method_exists("$desc[0]_block", "get")) {
      +        $block = call_user_func(array("$desc[0]_block", "get"), $desc[1]);
      +        $block->id = $id;
      +        $result .= $block;
      +      }
      +    }
      +    return $result;
      +  }
      +}
      diff --git a/modules/gallery/helpers/core.php b/modules/gallery/helpers/core.php
      new file mode 100644
      index 00000000..63f51f86
      --- /dev/null
      +++ b/modules/gallery/helpers/core.php
      @@ -0,0 +1,52 @@
      +admin) {
      +      Router::$controller = "maintenance";
      +      Router::$controller_path = APPPATH . "controllers/maintenance.php";
      +      Router::$method = "index";
      +    }
      +  }
      +
      +  /**
      +   * This function is called when the Gallery is fully initialized.  We relay it to modules as the
      +   * "gallery_ready" event.  Any module that wants to perform an action at the start of every
      +   * request should implement the _event::gallery_ready() handler.
      +   */
      +  static function ready() {
      +    module::event("gallery_ready");
      +  }
      +
      +  /**
      +   * This function is called right before the Kohana framework shuts down.  We relay it to modules
      +   * as the "gallery_shutdown" event.  Any module that wants to perform an action at the start of
      +   * every request should implement the _event::gallery_shutdown() handler.
      +   */
      +  static function shutdown() {
      +    module::event("gallery_shutdown");
      +  }
      +}
      \ No newline at end of file
      diff --git a/modules/gallery/helpers/core_block.php b/modules/gallery/helpers/core_block.php
      new file mode 100644
      index 00000000..0e2e9c54
      --- /dev/null
      +++ b/modules/gallery/helpers/core_block.php
      @@ -0,0 +1,100 @@
      + t("Welcome to Gallery 3!"),
      +      "photo_stream" => t("Photo Stream"),
      +      "log_entries" => t("Log Entries"),
      +      "stats" => t("Gallery Stats"),
      +      "platform_info" => t("Platform Information"),
      +      "project_news" => t("Gallery Project News"));
      +  }
      +
      +  static function get($block_id) {
      +    $block = new Block();
      +    switch($block_id) {
      +    case "welcome":
      +      $block->css_id = "gWelcome";
      +      $block->title = t("Welcome to Gallery3");
      +      $block->content = new View("admin_block_welcome.html");
      +      break;
      +
      +    case "photo_stream":
      +      $block->css_id = "gPhotoStream";
      +      $block->title = t("Photo Stream");
      +      $block->content = new View("admin_block_photo_stream.html");
      +      $block->content->photos =
      +        ORM::factory("item")->where("type", "photo")->orderby("created", "DESC")->find_all(10);
      +      break;
      +
      +    case "log_entries":
      +      $block->css_id = "gLogEntries";
      +      $block->title = t("Log Entries");
      +      $block->content = new View("admin_block_log_entries.html");
      +      $block->content->entries = ORM::factory("log")->orderby("timestamp", "DESC")->find_all(5);
      +        break;
      +
      +    case "stats":
      +      $block->css_id = "gStats";
      +      $block->title = t("Gallery Stats");
      +      $block->content = new View("admin_block_stats.html");
      +      $block->content->album_count = ORM::factory("item")->where("type", "album")->count_all();
      +      $block->content->photo_count = ORM::factory("item")->where("type", "photo")->count_all();
      +      break;
      +
      +    case "platform_info":
      +      $block->css_id = "gPlatform";
      +      $block->title = t("Platform Information");
      +      $block->content = new View("admin_block_platform.html");
      +      if (is_readable("/proc/loadavg")) {
      +        $block->content->load_average =
      +          join(" ", array_slice(split(" ", array_shift(file("/proc/loadavg"))), 0, 3));
      +      } else {
      +        $block->content->load_average = t("Unavailable");
      +      }
      +      break;
      +
      +    case "project_news":
      +      $block->css_id = "gProjectNews";
      +      $block->title = t("Gallery Project News");
      +      $block->content = new View("admin_block_news.html");
      +      $block->content->feed = feed::parse("http://gallery.menalto.com/node/feed", 3);
      +      break;
      +
      +    case "block_adder":
      +      $block->css_id = "gBlockAdder";
      +      $block->title = t("Dashboard Content");
      +      $block->content = self::get_add_block_form();
      +    }
      +
      +    return $block;
      +  }
      +
      +  static function get_add_block_form() {
      +    $form = new Forge("admin/dashboard/add_block", "", "post",
      +                      array("id" => "gAddDashboardBlockForm"));
      +    $group = $form->group("add_block")->label(t("Add Block"));
      +    $group->dropdown("id")->label("Available Blocks")->options(block_manager::get_available());
      +    $group->submit("center")->value(t("Add to center"));
      +    $group->submit("sidebar")->value(t("Add to sidebar"));
      +    return $form;
      +  }
      +}
      \ No newline at end of file
      diff --git a/modules/gallery/helpers/core_event.php b/modules/gallery/helpers/core_event.php
      new file mode 100644
      index 00000000..bbb53cc9
      --- /dev/null
      +++ b/modules/gallery/helpers/core_event.php
      @@ -0,0 +1,46 @@
      +admin && module::get_var("core", "choose_default_tookit", null)) {
      +      graphics::choose_default_toolkit();
      +      module::clear_var("core", "choose_default_tookit");
      +    }
      +  }
      +}
      diff --git a/modules/gallery/helpers/core_installer.php b/modules/gallery/helpers/core_installer.php
      new file mode 100644
      index 00000000..cffcbedb
      --- /dev/null
      +++ b/modules/gallery/helpers/core_installer.php
      @@ -0,0 +1,278 @@
      +query("CREATE TABLE {access_caches} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `item_id` int(9),
      +                   PRIMARY KEY (`id`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {access_intents} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `item_id` int(9),
      +                   PRIMARY KEY (`id`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {graphics_rules} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `active` BOOLEAN default 0,
      +                   `args` varchar(255) default NULL,
      +                   `module_name` varchar(64) NOT NULL,
      +                   `operation` varchar(64) NOT NULL,
      +                   `priority` int(9) NOT NULL,
      +                   `target`  varchar(32) NOT NULL,
      +                   PRIMARY KEY (`id`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {items} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `album_cover_item_id` int(9) default NULL,
      +                   `captured` int(9) default NULL,
      +                   `created` int(9) default NULL,
      +                   `description` varchar(2048) default NULL,
      +                   `height` int(9) default NULL,
      +                   `left` int(9) NOT NULL,
      +                   `level` int(9) NOT NULL,
      +                   `mime_type` varchar(64) default NULL,
      +                   `name` varchar(255) default NULL,
      +                   `owner_id` int(9) default NULL,
      +                   `parent_id` int(9) NOT NULL,
      +                   `rand_key` float default NULL,
      +                   `relative_path_cache` varchar(255) default NULL,
      +                   `resize_dirty` boolean default 1,
      +                   `resize_height` int(9) default NULL,
      +                   `resize_width` int(9) default NULL,
      +                   `right` int(9) NOT NULL,
      +                   `sort_column` varchar(64) default NULL,
      +                   `sort_order` char(4) default 'ASC',
      +                   `thumb_dirty` boolean default 1,
      +                   `thumb_height` int(9) default NULL,
      +                   `thumb_width` int(9) default NULL,
      +                   `title` varchar(255) default NULL,
      +                   `type` varchar(32) NOT NULL,
      +                   `updated` int(9) default NULL,
      +                   `view_count` int(9) default 0,
      +                   `weight` int(9) NOT NULL default 0,
      +                   `width` int(9) default NULL,
      +                   PRIMARY KEY (`id`),
      +                   KEY `parent_id` (`parent_id`),
      +                   KEY `type` (`type`),
      +                   KEY `random` (`rand_key`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {logs} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `category` varchar(64) default NULL,
      +                   `html` varchar(255) default NULL,
      +                   `message` text default NULL,
      +                   `referer` varchar(255) default NULL,
      +                   `severity` int(9) default 0,
      +                   `timestamp` int(9) default 0,
      +                   `url` varchar(255) default NULL,
      +                   `user_id` int(9) default 0,
      +                   PRIMARY KEY (`id`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {messages} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `key` varchar(255) default NULL,
      +                   `severity` varchar(32) default NULL,
      +                   `value` varchar(255) default NULL,
      +                   PRIMARY KEY (`id`),
      +                   UNIQUE KEY(`key`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {modules} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `active` BOOLEAN default 0,
      +                   `name` varchar(64) default NULL,
      +                   `version` int(9) default NULL,
      +                   PRIMARY KEY (`id`),
      +                   UNIQUE KEY(`name`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {themes} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `name` varchar(64) default NULL,
      +                   `version` int(9) default NULL,
      +                   PRIMARY KEY (`id`),
      +                   UNIQUE KEY(`name`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {permissions} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `display_name` varchar(64) default NULL,
      +                   `name` varchar(64) default NULL,
      +                   PRIMARY KEY (`id`),
      +                   UNIQUE KEY(`name`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {incoming_translations} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `key` char(32) NOT NULL,
      +                   `locale` char(10) NOT NULL,
      +                   `message` text NOT NULL,
      +                   `revision` int(9) DEFAULT NULL,
      +                   `translation` text,
      +                   PRIMARY KEY (`id`),
      +                   UNIQUE KEY(`key`, `locale`),
      +                   KEY `locale_key` (`locale`, `key`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {outgoing_translations} (
      +                   `id` int(9) NOT NULL auto_increment,
      +                   `base_revision` int(9) DEFAULT NULL,
      +                   `key` char(32) NOT NULL,
      +                   `locale` char(10) NOT NULL,
      +                   `message` text NOT NULL,
      +                   `translation` text,
      +                   PRIMARY KEY (`id`),
      +                   UNIQUE KEY(`key`, `locale`),
      +                   KEY `locale_key` (`locale`, `key`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {sessions} (
      +                  `session_id` varchar(127) NOT NULL,
      +                  `data` text NOT NULL,
      +                  `last_activity` int(10) UNSIGNED NOT NULL,
      +                  PRIMARY KEY (`session_id`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {tasks} (
      +                  `id` int(9) NOT NULL auto_increment,
      +                  `callback` varchar(128) default NULL,
      +                  `context` text NOT NULL,
      +                  `done` boolean default 0,
      +                  `name` varchar(128) default NULL,
      +                  `owner_id` int(9) default NULL,
      +                  `percent_complete` int(9) default 0,
      +                  `state` varchar(32) default NULL,
      +                  `status` varchar(255) default NULL,
      +                  `updated` int(9) default NULL,
      +                  PRIMARY KEY (`id`),
      +                  KEY (`owner_id`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      $db->query("CREATE TABLE {vars} (
      +                  `id` int(9) NOT NULL auto_increment,
      +                  `module_name` varchar(64) NOT NULL,
      +                  `name` varchar(64) NOT NULL,
      +                  `value` text,
      +                  PRIMARY KEY (`id`),
      +                  UNIQUE KEY(`module_name`, `name`))
      +                 ENGINE=InnoDB DEFAULT CHARSET=utf8;");
      +
      +      foreach (array("albums", "logs", "modules", "resizes", "thumbs", "tmp", "uploads") as $dir) {
      +        @mkdir(VARPATH . $dir);
      +      }
      +
      +      access::register_permission("view", "View");
      +      access::register_permission("view_full", "View Full Size");
      +      access::register_permission("edit", "Edit");
      +      access::register_permission("add", "Add");
      +
      +      $root = ORM::factory("item");
      +      $root->type = "album";
      +      $root->title = "Gallery";
      +      $root->description = "";
      +      $root->left = 1;
      +      $root->right = 2;
      +      $root->parent_id = 0;
      +      $root->level = 1;
      +      $root->thumb_dirty = 1;
      +      $root->resize_dirty = 1;
      +      $root->sort_column = "weight";
      +      $root->sort_order = "ASC";
      +      $root->save();
      +      access::add_item($root);
      +
      +      module::set_var("core", "active_site_theme", "default");
      +      module::set_var("core", "active_admin_theme", "admin_default");
      +      module::set_var("core", "page_size", 9);
      +      module::set_var("core", "thumb_size", 200);
      +      module::set_var("core", "resize_size", 640);
      +      module::set_var("core", "default_locale", "en_US");
      +      module::set_var("core", "image_quality", 75);
      +
      +      // Add rules for generating our thumbnails and resizes
      +      graphics::add_rule(
      +        "core", "thumb", "resize",
      +        array("width" => 200, "height" => 200, "master" => Image::AUTO),
      +        100);
      +      graphics::add_rule(
      +        "core", "resize", "resize",
      +        array("width" => 640, "height" => 480, "master" => Image::AUTO),
      +        100);
      +
      +      // Instantiate default themes (site and admin)
      +      foreach (array("default", "admin_default") as $theme_name) {
      +        $theme_info = new ArrayObject(parse_ini_file(THEMEPATH . $theme_name . "/theme.info"),
      +                                      ArrayObject::ARRAY_AS_PROPS);
      +        $theme = ORM::factory("theme");
      +        $theme->name = $theme_name;
      +        $theme->version = $theme_info->version;
      +        $theme->save();
      +      }
      +
      +      block_manager::add("dashboard_sidebar", "core", "block_adder");
      +      block_manager::add("dashboard_sidebar", "core", "stats");
      +      block_manager::add("dashboard_sidebar", "core", "platform_info");
      +      block_manager::add("dashboard_sidebar", "core", "project_news");
      +      block_manager::add("dashboard_center", "core", "welcome");
      +      block_manager::add("dashboard_center", "core", "photo_stream");
      +      block_manager::add("dashboard_center", "core", "log_entries");
      +
      +      module::set_version("core", 1);
      +      module::set_var("core", "version", "3.0 pre-beta svn");
      +      module::set_var("core", "choose_default_tookit", 1);
      +    }
      +  }
      +
      +  static function uninstall() {
      +    $db = Database::instance();
      +    $db->query("DROP TABLE IF EXISTS {access_caches}");
      +    $db->query("DROP TABLE IF EXISTS {access_intents}");
      +    $db->query("DROP TABLE IF EXISTS {graphics_rules}");
      +    $db->query("DROP TABLE IF EXISTS {items}");
      +    $db->query("DROP TABLE IF EXISTS {logs}");
      +    $db->query("DROP TABLE IF EXISTS {messages}");
      +    $db->query("DROP TABLE IF EXISTS {modules}");
      +    $db->query("DROP TABLE IF EXISTS {themes}");
      +    $db->query("DROP TABLE IF EXISTS {incoming_translations}");
      +    $db->query("DROP TABLE IF EXISTS {outgoing_translations}");
      +    $db->query("DROP TABLE IF EXISTS {permissions}");
      +    $db->query("DROP TABLE IF EXISTS {sessions}");
      +    $db->query("DROP TABLE IF EXISTS {tasks}");
      +    $db->query("DROP TABLE IF EXISTS {vars}");
      +    foreach (array("albums", "resizes", "thumbs", "uploads",
      +                   "modules", "logs", "database.php") as $entry) {
      +      system("/bin/rm -rf " . VARPATH . $entry);
      +    }
      +  }
      +}
      diff --git a/modules/gallery/helpers/core_menu.php b/modules/gallery/helpers/core_menu.php
      new file mode 100644
      index 00000000..eb208560
      --- /dev/null
      +++ b/modules/gallery/helpers/core_menu.php
      @@ -0,0 +1,162 @@
      +admin) {
      +      $menu->append($scaffold_menu = Menu::factory("submenu")
      +                    ->id("scaffold")
      +                    ->label("Scaffold"));
      +      $scaffold_menu->append(Menu::factory("link")
      +                             ->id("scaffold_home")
      +                             ->label("Dashboard")
      +                             ->url(url::site("scaffold")));
      +    }
      +
      +    $menu->append(Menu::factory("link")
      +                  ->id("home")
      +                  ->label(t("Home"))
      +                  ->url(url::site("albums/1")));
      +
      +    $item = $theme->item();
      +
      +    if (user::active()->admin || ($item && access::can("edit", $item))) {
      +      $menu->append($options_menu = Menu::factory("submenu")
      +                    ->id("options_menu")
      +                    ->label(t("Options")));
      +
      +      if ($item && access::can("edit", $item)) {
      +        $options_menu
      +          ->append(Menu::factory("dialog")
      +                   ->id("edit_item")
      +                   ->label($item->is_album() ? t("Edit album") : t("Edit photo"))
      +                   ->url(url::site("form/edit/{$item->type}s/$item->id")));
      +
      +        // @todo Move album options menu to the album quick edit pane
      +        // @todo Create resized item quick edit pane menu
      +        if ($item->is_album()) {
      +          $options_menu
      +            ->append(Menu::factory("dialog")
      +                     ->id("add_item")
      +                     ->label(t("Add a photo"))
      +                     ->url(url::site("simple_uploader/app/$item->id")))
      +            ->append(Menu::factory("dialog")
      +                     ->id("add_album")
      +                     ->label(t("Add an album"))
      +                     ->url(url::site("form/add/albums/$item->id?type=album")))
      +            ->append(Menu::factory("dialog")
      +                     ->id("edit_permissions")
      +                     ->label(t("Edit permissions"))
      +                     ->url(url::site("permissions/browse/$item->id")));
      +        }
      +      }
      +    }
      +
      +    if (user::active()->admin) {
      +      $menu->append($admin_menu = Menu::factory("submenu")
      +                    ->id("admin_menu")
      +                    ->label(t("Admin")));
      +      self::admin($admin_menu, $theme);
      +      foreach (module::active() as $module) {
      +        if ($module->name == "core") {
      +          continue;
      +        }
      +        $class = "{$module->name}_menu";
      +        if (method_exists($class, "admin")) {
      +          call_user_func_array(array($class, "admin"), array(&$admin_menu, $theme));
      +        }
      +      }
      +    }
      +  }
      +
      +  static function album($menu, $theme) {
      +  }
      +
      +  static function photo($menu, $theme) {
      +    if (access::can("view_full", $theme->item())) {
      +      $menu
      +        ->append(Menu::factory("link")
      +               ->id("fullsize")
      +               ->label(t("View full size"))
      +               ->url("#")
      +               ->css_class("gFullSizeLink"));
      +    }
      +    $menu
      +      ->append(Menu::factory("link")
      +               ->id("album")
      +               ->label(t("Return to album"))
      +               ->url($theme->item()->parent()->url("show={$theme->item->id}"))
      +               ->css_id("gAlbumLink"));
      +  }
      +
      +  static function admin($menu, $theme) {
      +    $menu
      +      ->append(Menu::factory("link")
      +               ->id("dashboard")
      +               ->label(t("Dashboard"))
      +               ->url(url::site("admin")))
      +      ->append(Menu::factory("submenu")
      +               ->id("settings_menu")
      +               ->label(t("Settings"))
      +               ->append(Menu::factory("link")
      +                        ->id("graphics_toolkits")
      +                        ->label(t("Graphics"))
      +                        ->url(url::site("admin/graphics")))
      +               ->append(Menu::factory("link")
      +                        ->id("languages")
      +                        ->label(t("Languages"))
      +                        ->url(url::site("admin/languages")))
      +               ->append(Menu::factory("link")
      +                        ->id("l10n_mode")
      +                        ->label(Session::instance()->get("l10n_mode", false)
      +                                ? t("Stop translating") : t("Start translating"))
      +                        ->url(url::site("l10n_client/toggle_l10n_mode?csrf=" .
      +                                        access::csrf_token())))
      +               ->append(Menu::factory("link")
      +                        ->id("advanced")
      +                        ->label("Advanced")
      +                        ->url(url::site("admin/advanced_settings"))))
      +      ->append(Menu::factory("link")
      +               ->id("modules")
      +               ->label(t("Modules"))
      +               ->url(url::site("admin/modules")))
      +      ->append(Menu::factory("submenu")
      +               ->id("content_menu")
      +               ->label(t("Content")))
      +      ->append(Menu::factory("submenu")
      +               ->id("appearance_menu")
      +               ->label(t("Appearance"))
      +               ->append(Menu::factory("link")
      +                        ->id("themes")
      +                        ->label(t("Theme Choice"))
      +                        ->url(url::site("admin/themes")))
      +               ->append(Menu::factory("link")
      +                        ->id("theme_details")
      +                        ->label(t("Theme Options"))
      +                        ->url(url::site("admin/theme_details"))))
      +      ->append(Menu::factory("link")
      +               ->id("maintenance")
      +               ->label(t("Maintenance"))
      +               ->url(url::site("admin/maintenance")))
      +      ->append(Menu::factory("submenu")
      +               ->id("statistics_menu")
      +               ->label(t("Statistics"))
      +               ->url("#"));
      +  }
      +}
      diff --git a/modules/gallery/helpers/core_search.php b/modules/gallery/helpers/core_search.php
      new file mode 100644
      index 00000000..9957a493
      --- /dev/null
      +++ b/modules/gallery/helpers/core_search.php
      @@ -0,0 +1,24 @@
      +description, $item->name, $item->title));
      +  }
      +}
      diff --git a/modules/gallery/helpers/core_task.php b/modules/gallery/helpers/core_task.php
      new file mode 100644
      index 00000000..e078192c
      --- /dev/null
      +++ b/modules/gallery/helpers/core_task.php
      @@ -0,0 +1,166 @@
      +count();
      +    $tasks = array();
      +    $tasks[] = Task_Definition::factory()
      +                 ->callback("core_task::rebuild_dirty_images")
      +                 ->name(t("Rebuild Images"))
      +                 ->description($dirty_count ?
      +                               t2("You have one out of date photo",
      +                                  "You have %count out of date photos",
      +                                  $dirty_count)
      +                               : t("All your photos are up to date"))
      +      ->severity($dirty_count ? log::WARNING : log::SUCCESS);
      +
      +    $tasks[] = Task_Definition::factory()
      +                 ->callback("core_task::update_l10n")
      +                 ->name(t("Update translations"))
      +                 ->description(t("Download new and updated translated strings"))
      +      ->severity(log::SUCCESS);
      +
      +    return $tasks;
      +  }
      +
      +  /**
      +   * Task that rebuilds all dirty images.
      +   * @param Task_Model the task
      +   */
      +  static function rebuild_dirty_images($task) {
      +    $result = graphics::find_dirty_images_query();
      +    $remaining = $result->count();
      +    $completed = $task->get("completed", 0);
      +
      +    $i = 0;
      +    foreach ($result as $row) {
      +      $item = ORM::factory("item", $row->id);
      +      if ($item->loaded) {
      +        graphics::generate($item);
      +      }
      +
      +      $completed++;
      +      $remaining--;
      +
      +      if (++$i == 2) {
      +        break;
      +      }
      +    }
      +
      +    $task->status = t2("Updated: 1 image. Total: %total_count.",
      +                       "Updated: %count images. Total: %total_count.",
      +                       $completed,
      +                       array("total_count" => ($remaining + $completed)));
      +
      +    if ($completed + $remaining > 0) {
      +      $task->percent_complete = (int)(100 * $completed / ($completed + $remaining));
      +    } else {
      +      $task->percent_complete = 100;
      +    }
      +
      +    $task->set("completed", $completed);
      +    if ($remaining == 0) {
      +      $task->done = true;
      +      $task->state = "success";
      +      site_status::clear("graphics_dirty");
      +    }
      +  }
      +
      +  static function update_l10n(&$task) {
      +    $start = microtime(true);
      +    $dirs = $task->get("dirs");
      +    $files = $task->get("files");
      +    $cache = $task->get("cache", array());
      +    $i = 0;
      +
      +    switch ($task->get("mode", "init")) {
      +    case "init":  // 0%
      +      $dirs = array("core", "modules", "themes", "installer");
      +      $files = array();
      +      $task->set("mode", "find_files");
      +      $task->status = t("Finding files");
      +      break;
      +
      +    case "find_files":  // 0% - 10%
      +      while (($dir = array_pop($dirs)) && microtime(true) - $start < 0.5) {
      +        if (basename($dir) == "tests") {
      +          continue;
      +        }
      +
      +        foreach (glob(DOCROOT . "$dir/*") as $path) {
      +          $relative_path = str_replace(DOCROOT, "", $path);
      +          if (is_dir($path)) {
      +            $dirs[] = $relative_path;
      +          } else {
      +            $files[] = $relative_path;
      +          }
      +        }
      +      }
      +
      +      $task->status = t2("Finding files: found 1 file",
      +                         "Finding files: found %count files", count($files));
      +
      +      if (!$dirs) {
      +        $task->set("mode", "scan_files");
      +        $task->set("total_files", count($files));
      +        $task->status = t("Scanning files");
      +        $task->percent_complete = 10;
      +      }
      +      break;
      +
      +    case "scan_files": // 10% - 90%
      +      while (($file = array_pop($files)) && microtime(true) - $start < 0.5) {
      +        $file = DOCROOT . $file;
      +        switch (pathinfo($file, PATHINFO_EXTENSION)) {
      +        case "php":
      +          l10n_scanner::scan_php_file($file, $cache);
      +          break;
      +
      +        case "info":
      +          l10n_scanner::scan_info_file($file, $cache);
      +          break;
      +        }
      +      }
      +
      +      $total_files = $task->get("total_files");
      +      $task->status = t2("Scanning files: scanned 1 file",
      +                         "Scanning files: scanned %count files", $total_files - count($files));
      +
      +      $task->percent_complete = 10 + 80 * ($total_files - count($files)) / $total_files;
      +      if (empty($files)) {
      +        $task->set("mode", "fetch_updates");
      +        $task->status = t("Fetching updates");
      +        $task->percent_complete = 90;
      +      }
      +      break;
      +
      +    case "fetch_updates":  // 90% - 100%
      +      l10n_client::fetch_updates();
      +      $task->done = true;
      +      $task->state = "success";
      +      $task->status = t("Translations installed/updated");
      +      $task->percent_complete = 100;
      +    }
      +
      +    $task->set("files", $files);
      +    $task->set("dirs", $dirs);
      +    $task->set("cache", $cache);
      +  }
      +}
      \ No newline at end of file
      diff --git a/modules/gallery/helpers/core_theme.php b/modules/gallery/helpers/core_theme.php
      new file mode 100644
      index 00000000..28f544a1
      --- /dev/null
      +++ b/modules/gallery/helpers/core_theme.php
      @@ -0,0 +1,137 @@
      +get("debug")) {
      +      $buf .= "";
      +    }
      +    if (($theme->page_type == "album" || $theme->page_type == "photo")
      +        && access::can("edit", $theme->item())) {
      +      $buf .= "";
      +      $buf .= html::script("core/js/quick.js");
      +    }
      +    if ($theme->page_type == "photo" && access::can("view_full", $theme->item())) {
      +      $buf .= "";
      +      $buf .= html::script("core/js/fullsize.js");
      +    }
      +
      +    if ($session->get("l10n_mode", false)) {
      +      $buf .= "";
      +      $buf .= html::script("lib/jquery.cookie.js");
      +      $buf .= html::script("core/js/l10n_client.js");
      +    }
      +
      +    return $buf;
      +  }
      +
      +  static function resize_top($theme, $item) {
      +    if (access::can("edit", $item)) {
      +      $edit_link = url::site("quick/pane/$item->id?page_type=photo");
      +      return "
      "; + } + } + + static function resize_bottom($theme, $item) { + if (access::can("edit", $item)) { + return "
      "; + } + } + + static function thumb_top($theme, $child) { + if (access::can("edit", $child)) { + $edit_link = url::site("quick/pane/$child->id?page_type=album"); + return "
      "; + } + } + + static function thumb_bottom($theme, $child) { + if (access::can("edit", $child)) { + return "
      "; + } + } + + static function admin_head($theme) { + $session = Session::instance(); + $buf = ""; + if ($session->get("debug")) { + $buf .= ""; + } + + if ($session->get("l10n_mode", false)) { + $buf .= ""; + $buf .= html::script("lib/jquery.cookie.js"); + $buf .= html::script("core/js/l10n_client.js"); + } + + return $buf; + } + + static function page_bottom($theme) { + $session = Session::instance(); + if ($session->get("profiler", false)) { + $profiler = new Profiler(); + $profiler->render(); + } + if ($session->get("l10n_mode", false)) { + return L10n_Client_Controller::l10n_form(); + } + + if ($session->get("after_install")) { + $session->delete("after_install"); + return new View("after_install_loader.html"); + } + } + + static function admin_page_bottom($theme) { + $session = Session::instance(); + if ($session->get("profiler", false)) { + $profiler = new Profiler(); + $profiler->render(); + } + if ($session->get("l10n_mode", false)) { + return L10n_Client_Controller::l10n_form(); + } + } + + static function credits() { + return "
    • " . + t("Powered by Gallery %version", + array("url" => "http://gallery.menalto.com", + "version" => module::get_var("core", "version"))) . + "
    • "; + } + + static function admin_credits() { + return core_theme::credits(); + } +} \ No newline at end of file diff --git a/modules/gallery/helpers/dir.php b/modules/gallery/helpers/dir.php new file mode 100644 index 00000000..1717bdd9 --- /dev/null +++ b/modules/gallery/helpers/dir.php @@ -0,0 +1,40 @@ +isDot()) { + unset($resource); + continue; + } else if ($resource->isFile()) { + unlink($resource->getPathName()); + } else if ($resource->isDir()) { + dir::unlink($resource->getRealPath()); + } + unset($resource); + } + return @rmdir($path); + } + return false; + } + + +} diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php new file mode 100644 index 00000000..805a95c0 --- /dev/null +++ b/modules/gallery/helpers/graphics.php @@ -0,0 +1,387 @@ + 200, "height" => 200, "master" => Image::AUTO), 100); + * + * Specifies that "core" is adding a rule to resize thumbnails down to a max of 200px on + * the longest side. The core module adds default rules at a priority of 100. You can set + * higher and lower priorities to perform operations before or after this fires. + * + * @param string $module_name the module that added the rule + * @param string $target the target for this operation ("thumb" or "resize") + * @param string $operation the name of the operation + * @param array $args arguments to the operation + * @param integer $priority the priority for this rule (lower priorities are run first) + */ + static function add_rule($module_name, $target, $operation, $args, $priority) { + $rule = ORM::factory("graphics_rule"); + $rule->module_name = $module_name; + $rule->target = $target; + $rule->operation = $operation; + $rule->priority = $priority; + $rule->args = serialize($args); + $rule->active = true; + $rule->save(); + + self::mark_dirty($target == "thumb", $target == "resize"); + } + + /** + * Remove any matching graphics rules + * @param string $module_name the module that added the rule + * @param string $target the target for this operation ("thumb" or "resize") + * @param string $operation the name of the operation + */ + static function remove_rule($module_name, $target, $operation) { + ORM::factory("graphics_rule") + ->where("module_name", $module_name) + ->where("target", $target) + ->where("operation", $operation) + ->delete_all(); + + self::mark_dirty($target == "thumb", $target == "resize"); + } + + /** + * Remove all rules for this module + * @param string $module_name + */ + static function remove_rules($module_name) { + $status = Database::instance()->delete("graphics_rules", array("module_name" => $module_name)); + if (count($status)) { + self::mark_dirty(true, true); + } + } + + /** + * Activate the rules for this module, typically done when the module itself is deactivated. + * Note that this does not mark images as dirty so that if you deactivate and reactivate a + * module it won't cause all of your images to suddenly require a rebuild. + */ + static function activate_rules($module_name) { + Database::instance() + ->update("graphics_rules",array("active" => true), array("module_name" => $module_name)); + } + + /** + * Deactivate the rules for this module, typically done when the module itself is deactivated. + * Note that this does not mark images as dirty so that if you deactivate and reactivate a + * module it won't cause all of your images to suddenly require a rebuild. + */ + static function deactivate_rules($module_name) { + Database::instance() + ->update("graphics_rules",array("active" => false), array("module_name" => $module_name)); + } + + /** + * Rebuild the thumb and resize for the given item. + * @param Item_Model $item + */ + static function generate($item) { + if ($item->is_album()) { + if (!$cover = $item->album_cover()) { + return; + } + $input_file = $cover->file_path(); + $input_item = $cover; + } else { + $input_file = $item->file_path(); + $input_item = $item; + } + + if ($item->thumb_dirty) { + $ops["thumb"] = $item->thumb_path(); + } + if ($item->resize_dirty && !$item->is_album() && !$item->is_movie()) { + $ops["resize"] = $item->resize_path(); + } + + if (empty($ops)) { + return; + } + + try { + foreach ($ops as $target => $output_file) { + if ($input_item->is_movie()) { + // Convert the movie to a JPG first + $output_file = preg_replace("/...$/", "jpg", $output_file); + movie::extract_frame($input_file, $output_file); + $working_file = $output_file; + } else { + $working_file = $input_file; + } + + foreach (ORM::factory("graphics_rule") + ->where("target", $target) + ->where("active", true) + ->orderby("priority", "asc") + ->find_all() as $rule) { + $args = array($working_file, $output_file, unserialize($rule->args)); + call_user_func_array(array("graphics", $rule->operation), $args); + $working_file = $output_file; + } + } + + if (!empty($ops["thumb"])) { + $dims = getimagesize($item->thumb_path()); + $item->thumb_width = $dims[0]; + $item->thumb_height = $dims[1]; + $item->thumb_dirty = 0; + } + + if (!empty($ops["resize"])) { + $dims = getimagesize($item->resize_path()); + $item->resize_width = $dims[0]; + $item->resize_height = $dims[1]; + $item->resize_dirty = 0; + } + $item->save(); + } catch (Kohana_Exception $e) { + // Something went wrong rebuilding the image. Leave it dirty and move on. + // @todo we should handle this better. + Kohana::log("error", "Caught exception rebuilding image: {$item->title}\n" . + $e->getTraceAsString()); + } + } + + /** + * Resize an image. Valid options are width, height and master. Master is one of the Image + * master dimension constants. + * + * @param string $input_file + * @param string $output_file + * @param array $options + */ + static function resize($input_file, $output_file, $options) { + if (!self::$init) { + self::init_toolkit(); + } + + if (filesize($input_file) == 0) { + throw new Exception("@todo MALFORMED_INPUT_FILE"); + } + + $dims = getimagesize($input_file); + if (max($dims[0], $dims[1]) < min($options["width"], $options["height"])) { + // Image would get upscaled; do nothing + copy($input_file, $output_file); + } else { + Image::factory($input_file) + ->resize($options["width"], $options["height"], $options["master"]) + ->quality(module::get_var("core", "image_quality")) + ->save($output_file); + } + } + + /** + * Rotate an image. Valid options are degrees + * + * @param string $input_file + * @param string $output_file + * @param array $options + */ + static function rotate($input_file, $output_file, $options) { + if (!self::$init) { + self::init_toolkit(); + } + + Image::factory($input_file) + ->quality(module::get_var("core", "image_quality")) + ->rotate($options["degrees"]) + ->save($output_file); + } + + /** + * Overlay an image on top of the input file. + * + * Valid options are: file, mime_type, position, transparency_percent, padding + * + * Valid positions: northwest, north, northeast, + * west, center, east, + * southwest, south, southeast + * + * padding is in pixels + * + * @param string $input_file + * @param string $output_file + * @param array $options + */ + static function composite($input_file, $output_file, $options) { + if (!self::$init) { + self::init_toolkit(); + } + + list ($width, $height) = getimagesize($input_file); + list ($w_width, $w_height) = getimagesize($options["file"]); + + $pad = isset($options["padding"]) ? $options["padding"] : 10; + $top = $pad; + $left = $pad; + $y_center = max($height / 2 - $w_height / 2, $pad); + $x_center = max($width / 2 - $w_width / 2, $pad); + $bottom = max($height - $w_height - $pad, $pad); + $right = max($width - $w_width - $pad, $pad); + + switch ($options["position"]) { + case "northwest": $x = $left; $y = $top; break; + case "north": $x = $x_center; $y = $top; break; + case "northeast": $x = $right; $y = $top; break; + case "west": $x = $left; $y = $y_center; break; + case "center": $x = $x_center; $y = $y_center; break; + case "east": $x = $right; $y = $y_center; break; + case "southwest": $x = $left; $y = $bottom; break; + case "south": $x = $x_center; $y = $bottom; break; + case "southeast": $x = $right; $y = $bottom; break; + } + + Image::factory($input_file) + ->composite($options["file"], $x, $y, $options["transparency"]) + ->quality(module::get_var("core", "image_quality")) + ->save($output_file); + } + + /** + * Return a query result that locates all items with dirty images. + * @return Database_Result Query result + */ + static function find_dirty_images_query() { + return Database::instance()->query( + "SELECT `id` FROM {items} " . + "WHERE ((`thumb_dirty` = 1 AND (`type` <> 'album' OR `album_cover_item_id` IS NOT NULL))" . + " OR (`resize_dirty` = 1 AND `type` = 'photo')) " . + " AND `id` != 1"); + } + + /** + * Mark thumbnails and resizes as dirty. They will have to be rebuilt. + */ + static function mark_dirty($thumbs, $resizes) { + if ($thumbs || $resizes) { + $db = Database::instance(); + $fields = array(); + if ($thumbs) { + $fields["thumb_dirty"] = 1; + } + if ($resizes) { + $fields["resize_dirty"] = 1; + } + $db->update("items", $fields, true); + } + + $count = self::find_dirty_images_query()->count(); + if ($count) { + site_status::warning( + t2("One of your photos is out of date. Click here to fix it", + "%count of your photos are out of date. Click here to fix them", + $count, + array("attrs" => sprintf( + 'href="%s" class="gDialogLink"', + url::site("admin/maintenance/start/core_task::rebuild_dirty_images?csrf=__CSRF__")))), + "graphics_dirty"); + } + } + + /** + * Detect which graphics toolkits are available on this system. Return an array of key value + * pairs where the key is one of gd, imagemagick, graphicsmagick and the value is information + * about that toolkit. For GD we return the version string, and for ImageMagick and + * GraphicsMagick we return the path to the directory containing the appropriate binaries. + */ + static function detect_toolkits() { + $gd = function_exists("gd_info") ? gd_info() : array(); + $exec = function_exists("exec"); + if (!isset($gd["GD Version"])) { + $gd["GD Version"] = false; + } + return array("gd" => $gd, + "imagemagick" => $exec ? dirname(exec("which convert")) : false, + "graphicsmagick" => $exec ? dirname(exec("which gm")) : false); + } + + /** + * This needs to be run once, after the initial install, to choose a graphics toolkit. + */ + static function choose_default_toolkit() { + // Detect a graphics toolkit + $toolkits = graphics::detect_toolkits(); + foreach (array("imagemagick", "graphicsmagick", "gd") as $tk) { + if ($toolkits[$tk]) { + module::set_var("core", "graphics_toolkit", $tk); + module::set_var("core", "graphics_toolkit_path", $tk == "gd" ? "" : $toolkits[$tk]); + break; + } + } + if (!module::get_var("core", "graphics_toolkit")) { + site_status::warning( + t("Graphics toolkit missing! Please choose a toolkit", + array("url" => url::site("admin/graphics"))), + "missing_graphics_toolkit"); + } + } + + /** + * Choose which driver the Kohana Image library uses. + */ + static function init_toolkit() { + switch(module::get_var("core", "graphics_toolkit")) { + case "gd": + Kohana::config_set("image.driver", "GD"); + break; + + case "imagemagick": + Kohana::config_set("image.driver", "ImageMagick"); + Kohana::config_set( + "image.params.directory", module::get_var("core", "graphics_toolkit_path")); + break; + + case "graphicsmagick": + Kohana::config_set("image.driver", "GraphicsMagick"); + Kohana::config_set( + "image.params.directory", module::get_var("core", "graphics_toolkit_path")); + break; + } + + self::$init = 1; + } + + /** + * Verify that a specific graphics function is available with the active toolkit. + * @param string $func (eg rotate, resize) + * @return boolean + */ + static function can($func) { + if (module::get_var("core", "graphics_toolkit") == "gd" && + $func == "rotate" && + !function_exists("imagerotate")) { + return false; + } + + return true; + } +} diff --git a/modules/gallery/helpers/item.php b/modules/gallery/helpers/item.php new file mode 100644 index 00000000..7daaf1e1 --- /dev/null +++ b/modules/gallery/helpers/item.php @@ -0,0 +1,105 @@ +parent(); + if ($parent->album_cover_item_id == $source->id) { + if ($parent->children_count() > 1) { + foreach ($parent->children(2) as $child) { + if ($child->id != $source->id) { + $new_cover_item = $child; + break; + } + } + item::make_album_cover($new_cover_item); + } else { + item::remove_album_cover($parent); + } + } + + $source->move_to($target); + + // If the target has no cover item, make this it. + if ($target->album_cover_item_id == null) { + item::make_album_cover($source); + } + } + + static function make_album_cover($item) { + $parent = $item->parent(); + access::required("edit", $parent); + + model_cache::clear("item", $parent->album_cover_item_id); + $parent->album_cover_item_id = $item->is_album() ? $item->album_cover_item_id : $item->id; + $parent->thumb_dirty = 1; + $parent->save(); + graphics::generate($parent); + $grand_parent = $parent->parent(); + if ($grand_parent && $grand_parent->album_cover_item_id == null) { + item::make_album_cover($parent); + } + } + + static function remove_album_cover($album) { + access::required("edit", $album); + @unlink($album->thumb_path()); + + model_cache::clear("item", $album->album_cover_item_id) ; + $album->album_cover_item_id = null; + $album->thumb_width = 0; + $album->thumb_height = 0; + $album->thumb_dirty = 1; + $album->save(); + graphics::generate($album); + } + + static function validate_no_slashes($input) { + if (strpos($input->value, "/") !== false) { + $input->add_error("no_slashes", 1); + } + } + + static function validate_no_trailing_period($input) { + if (rtrim($input->value, ".") !== $input->value) { + $input->add_error("no_trailing_period", 1); + } + } + + static function validate_no_name_conflict($input) { + $itemid = Input::instance()->post("item"); + if (is_array($itemid)) { + $itemid = $itemid[0]; + } + $item = ORM::factory("item") + ->in("id", $itemid) + ->find(); + if (Database::instance() + ->from("items") + ->where("parent_id", $item->parent_id) + ->where("id <>", $item->id) + ->where("name", $input->value) + ->count_records()) { + $input->add_error("conflict", 1); + } + } +} \ No newline at end of file diff --git a/modules/gallery/helpers/l10n_client.php b/modules/gallery/helpers/l10n_client.php new file mode 100644 index 00000000..ec4c5429 --- /dev/null +++ b/modules/gallery/helpers/l10n_client.php @@ -0,0 +1,203 @@ + $version, + "client_token" => self::client_token(), + "signature" => $signature, + "uid" => self::server_uid($api_key))); + if (!remote::success($response_status)) { + return false; + } + return true; + } + + static function fetch_updates() { + $request->locales = array(); + $request->messages = new stdClass(); + + $locales = locale::installed(); + foreach ($locales as $locale => $locale_data) { + $request->locales[] = $locale; + } + + // @todo Batch requests (max request size) + foreach (Database::instance() + ->select("key", "locale", "revision", "translation") + ->from("incoming_translations") + ->get() + ->as_array() as $row) { + if (!isset($request->messages->{$row->key})) { + $request->messages->{$row->key} = 1; + } + if (!empty($row->revision) && !empty($row->translation)) { + if (!is_object($request->messages->{$row->key})) { + $request->messages->{$row->key} = new stdClass(); + } + $request->messages->{$row->key}->{$row->locale} = $row->revision; + } + } + // @todo Include messages from outgoing_translations? + + $request_data = json_encode($request); + $url = self::_server_url() . "?q=translations/fetch"; + list ($response_data, $response_status) = remote::post($url, array("data" => $request_data)); + if (!remote::success($response_status)) { + throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED " . $response_status); + } + if (empty($response_data)) { + log::info("translations", "Translations fetch request resulted in an empty response"); + return; + } + + $response = json_decode($response_data); + + // Response format (JSON payload): + // [{key:, translation: , rev:, locale:}, + // {key:, ...} + // ] + $count = count($response); + log::info("translations", "Installed $count new / updated translation messages"); + + foreach ($response as $message_data) { + // @todo Better input validation + if (empty($message_data->key) || empty($message_data->translation) || + empty($message_data->locale) || empty($message_data->rev)) { + throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED: Invalid response data"); + } + $key = $message_data->key; + $locale = $message_data->locale; + $revision = $message_data->rev; + $translation = serialize(json_decode($message_data->translation)); + + // @todo Should we normalize the incoming_translations table into messages(id, key, message) + // and incoming_translations(id, translation, locale, revision)? Or just allow + // incoming_translations.message to be NULL? + $locale = $message_data->locale; + $entry = ORM::factory("incoming_translation") + ->where(array("key" => $key, "locale" => $locale)) + ->find(); + if (!$entry->loaded) { + // @todo Load a message key -> message (text) dict into memory outside of this loop + $root_entry = ORM::factory("incoming_translation") + ->where(array("key" => $key, "locale" => "root")) + ->find(); + $entry->key = $key; + $entry->message = $root_entry->message; + $entry->locale = $locale; + } + $entry->revision = $revision; + $entry->translation = $translation; + $entry->save(); + } + } + + static function submit_translations() { + // Request format (HTTP POST): + // client_token = + // uid = + // signature = md5(user_api_key($uid, $client_token) . $data . $client_token)) + // data = // JSON payload + // + // {: {message: + // translations: {: , + // : ...}}, + // : {...} + // } + + // @todo Batch requests (max request size) + // @todo include base_revision in submission / how to handle resubmissions / edit fights? + foreach (Database::instance() + ->select("key", "message", "locale", "base_revision", "translation") + ->from("outgoing_translations") + ->get() as $row) { + $key = $row->key; + if (!isset($request->{$key})) { + $request->{$key}->message = json_encode(unserialize($row->message)); + } + $request->{$key}->translations->{$row->locale} = json_encode(unserialize($row->translation)); + } + + // @todo reduce memory consumpotion, e.g. free $request + $request_data = json_encode($request); + $url = self::_server_url() . "?q=translations/submit"; + $signature = self::_sign($request_data); + + list ($response_data, $response_status) = remote::post( + $url, array("data" => $request_data, + "client_token" => self::client_token(), + "signature" => $signature, + "uid" => self::server_uid())); + + if (!remote::success($response_status)) { + throw new Exception("@todo TRANSLATIONS_SUBMISSION_FAILED " . $response_status); + } + if (empty($response_data)) { + return; + } + + $response = json_decode($response_data); + // Response format (JSON payload): + // [{key:, locale:, rev:, status:}, + // {key:, ...} + // ] + + // @todo Move messages out of outgoing into incoming, using new rev? + // @todo show which messages have been rejected / are pending? + } +} \ No newline at end of file diff --git a/modules/gallery/helpers/l10n_scanner.php b/modules/gallery/helpers/l10n_scanner.php new file mode 100644 index 00000000..80b6f01c --- /dev/null +++ b/modules/gallery/helpers/l10n_scanner.php @@ -0,0 +1,154 @@ +select("key") + ->from("incoming_translations") + ->where("locale", "root") + ->get() as $row) { + $cache[$row->key] = true; + } + } + + $key = I18n::get_message_key($message); + if (array_key_exists($key, $cache)) { + return $cache[$key]; + } + + $entry = ORM::factory("incoming_translation", array("key" => $key)); + if (!$entry->loaded) { + $entry->key = $key; + $entry->message = serialize($message); + $entry->locale = "root"; + $entry->save(); + } + } + + static function scan_php_file($file, &$cache) { + $code = file_get_contents($file); + $raw_tokens = token_get_all($code); + unset($code); + + $tokens = array(); + $func_token_list = array("t" => array(), "t2" => array()); + $token_number = 0; + // Filter out HTML / whitespace, and build a lookup for global function calls. + foreach ($raw_tokens as $token) { + if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) { + if (is_array($token)) { + if ($token[0] == T_STRING && in_array($token[1], array("t", "t2"))) { + $func_token_list[$token[1]][] = $token_number; + } + } + $tokens[] = $token; + $token_number++; + } + } + unset($raw_tokens); + + if (!empty($func_token_list["t"])) { + l10n_scanner::_parse_t_calls($tokens, $func_token_list["t"], $cache); + } + if (!empty($func_token_list["t2"])) { + l10n_scanner::_parse_plural_calls($tokens, $func_token_list["t2"], $cache); + } + } + + static function scan_info_file($file, &$cache) { + $code = file_get_contents($file); + if (preg_match("#name\s*?=\s*(.*?)\ndescription\s*?=\s*(.*)\n#", $code, $matches)) { + unset($matches[0]); + foreach ($matches as $string) { + l10n_scanner::process_message($string, $cache); + } + } + } + + private static function _parse_t_calls(&$tokens, &$call_list, &$cache) { + foreach ($call_list as $index) { + $function_name = $tokens[$index++]; + $parens = $tokens[$index++]; + $first_param = $tokens[$index++]; + $next_token = $tokens[$index]; + + if ($parens == "(") { + if (in_array($next_token, array(")", ",")) + && (is_array($first_param) && ($first_param[0] == T_CONSTANT_ENCAPSED_STRING))) { + $message = self::_escape_quoted_string($first_param[1]); + l10n_scanner::process_message($message, $cache); + } else { + // t() found, but inside is something which is not a string literal. + // @todo Call status callback with error filename/line. + } + } + } + } + + private static function _parse_plural_calls(&$tokens, &$call_list, &$cache) { + foreach ($call_list as $index) { + $function_name = $tokens[$index++]; + $parens = $tokens[$index++]; + $first_param = $tokens[$index++]; + $first_separator = $tokens[$index++]; + $second_param = $tokens[$index++]; + $next_token = $tokens[$index]; + + if ($parens == "(") { + if ($first_separator == "," && $next_token == "," + && is_array($first_param) && $first_param[0] == T_CONSTANT_ENCAPSED_STRING + && is_array($second_param) && $second_param[0] == T_CONSTANT_ENCAPSED_STRING) { + $singular = self::_escape_quoted_string($first_param[1]); + $plural = self::_escape_quoted_string($first_param[1]); + l10n_scanner::process_message(array("one" => $singular, "other" => $plural), $cache); + } else { + // t2() found, but inside is something which is not a string literal. + // @todo Call status callback with error filename/line. + } + } + } + } + + /** + * Escape quotes in a strings depending on the surrounding + * quote type used. + * + * @param $str The strings to escape + */ + private static function _escape_quoted_string($str) { + $quo = substr($str, 0, 1); + $str = substr($str, 1, -1); + if ($quo == '"') { + $str = stripcslashes($str); + } else { + $str = strtr($str, array("\\'" => "'", "\\\\" => "\\")); + } + return addcslashes($str, "\0..\37\\\""); + } +} diff --git a/modules/gallery/helpers/locale.php b/modules/gallery/helpers/locale.php new file mode 100644 index 00000000..b707637f --- /dev/null +++ b/modules/gallery/helpers/locale.php @@ -0,0 +1,119 @@ +$code)) { + $installed[$code] = $available[$code]; + } + } + return $installed; + } + + static function update_installed($locales) { + // Ensure that the default is included... + $default = module::get_var("core", "default_locale"); + $locales = array_merge($locales, array($default)); + + module::set_var("core", "installed_locales", join("|", $locales)); + } + + // @todo Might want to add a localizable language name as well. + private static function _init_language_data() { + $l["af_ZA"] = "Afrikaans"; // Afrikaans + $l["ar_SA"] = "العربية"; // Arabic + $l["bg_BG"] = "Български"; // Bulgarian + $l["ca_ES"] = "Catalan"; // Catalan + $l["cs_CZ"] = "Česky"; // Czech + $l["da_DK"] = "Dansk"; // Danish + $l["de_DE"] = "Deutsch"; // German + $l["el_GR"] = "Greek"; // Greek + $l["en_GB"] = "English (UK)"; // English (UK) + $l["en_US"] = "English (US)"; // English (US) + $l["es_AR"] = "Español (AR)"; // Spanish (AR) + $l["es_ES"] = "Español"; // Spanish (ES) + $l["es_MX"] = "Español (MX)"; // Spanish (MX) + $l["et_EE"] = "Eesti"; // Estonian + $l["eu_ES"] = "Euskara"; // Basque + $l["fa_IR"] = "فارسي"; // Farsi + $l["fi_FI"] = "Suomi"; // Finnish + $l["fr_FR"] = "Français"; // French + $l["ga_IE"] = "Gaeilge"; // Irish + $l["he_IL"] = "עברית"; // Hebrew + $l["hu_HU"] = "Magyar"; // Hungarian + $l["is_IS"] = "Icelandic"; // Icelandic + $l["it_IT"] = "Italiano"; // Italian + $l["ja_JP"] = "日本語"; // Japanese + $l["ko_KR"] = "한국말"; // Korean + $l["lt_LT"] = "Lietuvių"; // Lithuanian + $l["lv_LV"] = "Latviešu"; // Latvian + $l["nl_NL"] = "Nederlands"; // Dutch + $l["no_NO"] = "Norsk bokmål"; // Norwegian + $l["pl_PL"] = "Polski"; // Polish + $l["pt_BR"] = "Português Brasileiro"; // Portuguese (BR) + $l["pt_PT"] = "Português"; // Portuguese (PT) + $l["ro_RO"] = "Română"; // Romanian + $l["ru_RU"] = "Русский"; // Russian + $l["sk_SK"] = "Slovenčina"; // Slovak + $l["sl_SI"] = "Slovenščina"; // Slovenian + $l["sr_CS"] = "Srpski"; // Serbian + $l["sv_SE"] = "Svenska"; // Swedish + $l["tr_TR"] = "Türkçe"; // Turkish + $l["uk_UA"] = "Українська"; // Ukrainian + $l["vi_VN"] = "Tiếng Việt"; // Vietnamese + $l["zh_CN"] = "简体中文"; // Chinese (CN) + $l["zh_TW"] = "繁體中文"; // Chinese (TW) + asort($l, SORT_LOCALE_STRING); + self::$locales = $l; + } + + static function display_name($locale=null) { + if (empty(self::$locales)) { + self::_init_language_data(); + } + $locale or $locale = I18n::instance()->locale(); + + return self::$locales["$locale"]; + } + + static function is_rtl($locale) { + return in_array($locale, array("he_IL", "fa_IR", "ar_SA")); + } +} \ No newline at end of file diff --git a/modules/gallery/helpers/log.php b/modules/gallery/helpers/log.php new file mode 100644 index 00000000..451f985a --- /dev/null +++ b/modules/gallery/helpers/log.php @@ -0,0 +1,108 @@ +category = $category; + $log->message = $message; + $log->severity = $severity; + $log->html = $html; + $log->url = substr(url::abs_current(true), 0, 255); + $log->referer = request::referrer(null); + $log->timestamp = time(); + $log->user_id = user::active()->id; + $log->save(); + } + + + /** + * Convert a message severity to a CSS class + * @param integer $severity + * @return string + */ + static function severity_class($severity) { + switch($severity) { + case self::SUCCESS: + return "gSuccess"; + + case self::INFO: + return "gInfo"; + + case self::WARNING: + return "gWarning"; + + case self::ERROR: + return "gError"; + } + } +} diff --git a/modules/gallery/helpers/message.php b/modules/gallery/helpers/message.php new file mode 100644 index 00000000..af3b96cc --- /dev/null +++ b/modules/gallery/helpers/message.php @@ -0,0 +1,108 @@ +get("messages"); + $status[] = array($msg, $severity); + $session->set("messages", $status); + } + + /** + * Get any pending messages. There are two types of messages, transient and permanent. + * Permanent messages are used to let the admin know that there are pending administrative + * issues that need to be resolved. Transient ones are only displayed once. + * @return html text + */ + static function get() { + $buf = array(); + + $messages = Session::instance()->get_once("messages", array()); + foreach ($messages as $msg) { + $buf[] = "
    • $msg[0]
    • "; + } + if ($buf) { + return "
        " . implode("", $buf) . "
      "; + } + } + + /** + * Convert a message severity to a CSS class + * @param integer $severity + * @return string + */ + static function severity_class($severity) { + switch($severity) { + case self::SUCCESS: + return "gSuccess"; + + case self::INFO: + return "gInfo"; + + case self::WARNING: + return "gWarning"; + + case self::ERROR: + return "gError"; + } + } +} diff --git a/modules/gallery/helpers/model_cache.php b/modules/gallery/helpers/model_cache.php new file mode 100644 index 00000000..2649fdbd --- /dev/null +++ b/modules/gallery/helpers/model_cache.php @@ -0,0 +1,46 @@ +$model_name->$field_name->$id)) { + $model = ORM::factory($model_name)->where($field_name, $id)->find(); + if (!$model->loaded) { + throw new Exception("@todo MISSING_MODEL $model_name:$id"); + } + self::$cache->$model_name->$field_name->$id = $model; + } + + return self::$cache->$model_name->$field_name->$id; + } + + static function clear($model_name, $id, $field_name="id") { + if (!empty(self::$cache->$model_name->$field_name->$id)) { + unset(self::$cache->$model_name->$field_name->$id); + } + } + + static function set($model) { + self::$cache->{$model->object_name} + ->{$model->primary_key} + ->{$model->{$model->primary_key}} = $model; + } +} diff --git a/modules/gallery/helpers/module.php b/modules/gallery/helpers/module.php new file mode 100644 index 00000000..a48c89ed --- /dev/null +++ b/modules/gallery/helpers/module.php @@ -0,0 +1,357 @@ +loaded) { + $module->name = $module_name; + $module->active = $module_name == "core"; // only core is active by default + } + $module->version = 1; + $module->save(); + Kohana::log("debug", "$module_name: version is now $version"); + } + + /** + * Load the corresponding Module_Model + * @param string $module_name + */ + static function get($module_name) { + // @todo can't easily use model_cache here because it throw an exception on missing models. + return ORM::factory("module", array("name" => $module_name)); + } + + /** + * Check to see if a module is installed + * @param string $module_name + */ + static function is_installed($module_name) { + return array_key_exists($module_name, self::$modules); + } + + /** + * Check to see if a module is active + * @param string $module_name + */ + static function is_active($module_name) { + return array_key_exists($module_name, self::$modules) && + self::$modules[$module_name]->active; + } + + /** + * Return the list of available modules, including uninstalled modules. + */ + static function available() { + $modules = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS); + foreach (array_merge(array("core/module.info"), glob(MODPATH . "*/module.info")) as $file) { + $module_name = basename(dirname($file)); + $modules->$module_name = new ArrayObject(parse_ini_file($file), ArrayObject::ARRAY_AS_PROPS); + $modules->$module_name->installed = self::is_installed($module_name); + $modules->$module_name->active = self::is_active($module_name); + $modules->$module_name->version = self::get_version($module_name); + $modules->$module_name->locked = false; + } + + // Lock certain modules + $modules->core->locked = true; + $modules->user->locked = true; + $modules->ksort(); + + return $modules; + } + + /** + * Return a list of all the active modules in no particular order. + */ + static function active() { + return self::$active; + } + + /** + * Install a module. This will call _installer::install(), which is responsible for + * creating database tables, setting module variables and and calling module::set_version(). + * Note that after installing, the module must be activated before it is available for use. + * @param string $module_name + */ + static function install($module_name) { + $kohana_modules = Kohana::config("core.modules"); + $kohana_modules[] = MODPATH . $module_name; + Kohana::config_set("core.modules", $kohana_modules); + + $installer_class = "{$module_name}_installer"; + if (method_exists($installer_class, "install")) { + call_user_func_array(array($installer_class, "install"), array()); + } + + // Now the module is installed but inactive, so don't leave it in the active path + array_pop($kohana_modules); + Kohana::config_set("core.modules", $kohana_modules); + + log::success( + "module", t("Installed module %module_name", array("module_name" => $module_name))); + } + + /** + * Activate an installed module. This will call _installer::activate() which should take + * any steps to make sure that the module is ready for use. This will also activate any + * existing graphics rules for this module. + * @param string $module_name + */ + static function activate($module_name) { + $kohana_modules = Kohana::config("core.modules"); + $kohana_modules[] = MODPATH . $module_name; + Kohana::config_set("core.modules", $kohana_modules); + + $installer_class = "{$module_name}_installer"; + if (method_exists($installer_class, "activate")) { + call_user_func_array(array($installer_class, "activate"), array()); + } + + $module = self::get($module_name); + if ($module->loaded) { + $module->active = true; + $module->save(); + } + + self::load_modules(); + graphics::activate_rules($module_name); + log::success( + "module", t("Activated module %module_name", array("module_name" => $module_name))); + } + + /** + * Deactivate an installed module. This will call _installer::deactivate() which + * should take any cleanup steps to make sure that the module isn't visible in any way. + * @param string $module_name + */ + static function deactivate($module_name) { + $installer_class = "{$module_name}_installer"; + if (method_exists($installer_class, "deactivate")) { + call_user_func_array(array($installer_class, "deactivate"), array()); + } + + $module = self::get($module_name); + if ($module->loaded) { + $module->active = false; + $module->save(); + } + + self::load_modules(); + graphics::deactivate_rules($module_name); + log::success( + "module", t("Deactivated module %module_name", array("module_name" => $module_name))); + } + + /** + * Uninstall a deactivated module. This will call _installer::uninstall() which should + * take whatever steps necessary to make sure that all traces of a module are gone. + * @param string $module_name + */ + static function uninstall($module_name) { + $installer_class = "{$module_name}_installer"; + if (method_exists($installer_class, "uninstall")) { + call_user_func(array($installer_class, "uninstall")); + } + + graphics::remove_rule($module_name); + $module = self::get($module_name); + if ($module->loaded) { + $module->delete(); + } + + // We could delete the module vars here too, but it's nice to leave them around + // in case the module gets reinstalled. + + self::load_modules(); + log::success( + "module", t("Uninstalled module %module_name", array("module_name" => $module_name))); + } + + /** + * Load the active modules. This is called at bootstrap time. + */ + static function load_modules() { + // Reload module list from the config file since we'll do a refresh after calling install() + $core = Kohana::config_load("core"); + $kohana_modules = $core["modules"]; + $modules = ORM::factory("module")->find_all(); + + self::$modules = array(); + self::$active = array(); + foreach ($modules as $module) { + self::$modules[$module->name] = $module; + if ($module->active) { + self::$active[] = $module; + } + if ($module->name != "core") { + $kohana_modules[] = MODPATH . $module->name; + } + } + Kohana::config_set("core.modules", $kohana_modules); + } + + /** + * Run a specific event on all active modules. + * @param string $name the event name + * @param mixed $data data to pass to each event handler + */ + static function event($name, &$data=null) { + $args = func_get_args(); + array_shift($args); + $function = str_replace(".", "_", $name); + + foreach (self::$modules as $module) { + if (!$module->active) { + continue; + } + + $class = "{$module->name}_event"; + if (method_exists($class, $function)) { + call_user_func_array(array($class, $function), $args); + } + } + } + + /** + * Get a variable from this module + * @param string $module_name + * @param string $name + * @param string $default_value + * @return the value + */ + static function get_var($module_name, $name, $default_value=null) { + // We cache all vars in core._cache so that we can load all vars at once for + // performance. + if (empty(self::$var_cache)) { + $row = Database::instance() + ->select("value") + ->from("vars") + ->where(array("module_name" => "core", "name" => "_cache")) + ->get() + ->current(); + if ($row) { + self::$var_cache = unserialize($row->value); + } else { + // core._cache doesn't exist. Create it now. + foreach (Database::instance() + ->select("module_name", "name", "value") + ->from("vars") + ->orderby("module_name", "name") + ->get() as $row) { + if ($row->module_name == "core" && $row->name == "_cache") { + // This could happen if there's a race condition + continue; + } + self::$var_cache->{$row->module_name}->{$row->name} = $row->value; + } + $cache = ORM::factory("var"); + $cache->module_name = "core"; + $cache->name = "_cache"; + $cache->value = serialize(self::$var_cache); + $cache->save(); + } + } + + if (isset(self::$var_cache->$module_name->$name)) { + return self::$var_cache->$module_name->$name; + } else { + return $default_value; + } + } + + /** + * Store a variable for this module + * @param string $module_name + * @param string $name + * @param string $value + */ + static function set_var($module_name, $name, $value) { + $var = ORM::factory("var") + ->where("module_name", $module_name) + ->where("name", $name) + ->find(); + if (!$var->loaded) { + $var->module_name = $module_name; + $var->name = $name; + } + $var->value = $value; + $var->save(); + + Database::instance()->delete("vars", array("module_name" => "core", "name" => "_cache")); + self::$var_cache = null; + } + + /** + * Increment the value of a variable for this module + * @param string $module_name + * @param string $name + * @param string $increment (optional, default is 1) + */ + static function incr_var($module_name, $name, $increment=1) { + Database::instance()->query( + "UPDATE {vars} SET `value` = `value` + $increment " . + "WHERE `module_name` = '$module_name' " . + "AND `name` = '$name'"); + + Database::instance()->delete("vars", array("module_name" => "core", "name" => "_cache")); + self::$var_cache = null; + } + + /** + * Remove a variable for this module. + * @param string $module_name + * @param string $name + */ + static function clear_var($module_name, $name) { + $var = ORM::factory("var") + ->where("module_name", $module_name) + ->where("name", $name) + ->find(); + if ($var->loaded) { + $var->delete(); + } + + Database::instance()->delete("vars", array("module_name" => "core", "name" => "_cache")); + self::$var_cache = null; + } + + /** + * Return the version of the installed module. + * @param string $module_name + */ + static function get_version($module_name) { + return self::get($module_name)->version; + } +} diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php new file mode 100644 index 00000000..3293d4ac --- /dev/null +++ b/modules/gallery/helpers/movie.php @@ -0,0 +1,153 @@ +loaded || !$parent->is_album()) { + throw new Exception("@todo INVALID_PARENT"); + } + + if (!is_file($filename)) { + throw new Exception("@todo MISSING_MOVIE_FILE"); + } + + if (strpos($name, "/")) { + throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); + } + + // We don't allow trailing periods as a security measure + // ref: http://dev.kohanaphp.com/issues/684 + if (rtrim($name, ".") != $name) { + throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); + } + + $movie_info = movie::getmoviesize($filename); + + // Force an extension onto the name + $pi = pathinfo($filename); + if (empty($pi["extension"])) { + $pi["extension"] = image_type_to_extension($movie_info[2], false); + $name .= "." . $pi["extension"]; + } + + $movie = ORM::factory("item"); + $movie->type = "movie"; + $movie->title = $title; + $movie->description = $description; + $movie->name = $name; + $movie->owner_id = $owner_id ? $owner_id : user::active(); + $movie->width = $movie_info[0]; + $movie->height = $movie_info[1]; + $movie->mime_type = strtolower($pi["extension"]) == "mp4" ? "video/mp4" : "video/x-flv"; + $movie->thumb_dirty = 1; + $movie->resize_dirty = 1; + $movie->sort_column = "weight"; + $movie->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); + + // Randomize the name if there's a conflict + while (ORM::Factory("item") + ->where("parent_id", $parent->id) + ->where("name", $movie->name) + ->find()->id) { + // @todo Improve this. Random numbers are not user friendly + $movie->name = rand() . "." . $pi["extension"]; + } + + // This saves the photo + $movie->add_to_parent($parent); + + // If the thumb or resize already exists then rename it + if (file_exists($movie->resize_path()) || + file_exists($movie->thumb_path())) { + $movie->name = $pi["filename"] . "-" . rand() . "." . $pi["extension"]; + $movie->save(); + } + + copy($filename, $movie->file_path()); + + module::event("item_created", $movie); + + // Build our thumbnail + graphics::generate($movie); + + // If the parent has no cover item, make this it. + if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { + item::make_album_cover($movie); + } + + return $movie; + } + + static function getmoviesize($filename) { + $ffmpeg = self::find_ffmpeg(); + if (empty($ffmpeg)) { + throw new Exception("@todo MISSING_FFMPEG"); + } + + $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($filename) . " 2>&1"; + $result = `$cmd`; + if (preg_match("/Stream.*?Video:.*?(\d+)x(\d+).*\ +([0-9\.]+) (fps|tb).*/", + $result, $regs)) { + list ($width, $height) = array($regs[1], $regs[2]); + } else { + list ($width, $height) = array(0, 0); + } + return array($width, $height); + } + + static function extract_frame($input_file, $output_file) { + $ffmpeg = self::find_ffmpeg(); + if (empty($ffmpeg)) { + throw new Exception("@todo MISSING_FFMPEG"); + } + + $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($input_file) . + " -an -ss 00:00:03 -an -r 1 -vframes 1" . + " -y -f mjpeg " . escapeshellarg($output_file); + exec($cmd); + } + + static function find_ffmpeg() { + if (!$ffmpeg_path = module::get_var("core", "ffmpeg_path")) { + if (function_exists("exec")) { + $ffmpeg_path = exec("which ffmpeg"); + if ($ffmpeg_path) { + module::set_var("core", "ffmpeg_path", $ffmpeg_path); + } + } + } + return $ffmpeg_path; + } +} diff --git a/modules/gallery/helpers/photo.php b/modules/gallery/helpers/photo.php new file mode 100644 index 00000000..c1c005f5 --- /dev/null +++ b/modules/gallery/helpers/photo.php @@ -0,0 +1,171 @@ +loaded || !$parent->is_album()) { + throw new Exception("@todo INVALID_PARENT"); + } + + if (!is_file($filename)) { + throw new Exception("@todo MISSING_IMAGE_FILE"); + } + + if (strpos($name, "/")) { + throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); + } + + // We don't allow trailing periods as a security measure + // ref: http://dev.kohanaphp.com/issues/684 + if (rtrim($name, ".") != $name) { + throw new Exception("@todo NAME_CANNOT_END_IN_PERIOD"); + } + + $image_info = getimagesize($filename); + + // Force an extension onto the name + $pi = pathinfo($filename); + if (empty($pi["extension"])) { + $pi["extension"] = image_type_to_extension($image_info[2], false); + $name .= "." . $pi["extension"]; + } + + $photo = ORM::factory("item"); + $photo->type = "photo"; + $photo->title = $title; + $photo->description = $description; + $photo->name = $name; + $photo->owner_id = $owner_id ? $owner_id : user::active(); + $photo->width = $image_info[0]; + $photo->height = $image_info[1]; + $photo->mime_type = empty($image_info['mime']) ? "application/unknown" : $image_info['mime']; + $photo->thumb_dirty = 1; + $photo->resize_dirty = 1; + $photo->sort_column = "weight"; + $photo->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); + + // Randomize the name if there's a conflict + while (ORM::Factory("item") + ->where("parent_id", $parent->id) + ->where("name", $photo->name) + ->find()->id) { + // @todo Improve this. Random numbers are not user friendly + $photo->name = rand() . "." . $pi["extension"]; + } + + // This saves the photo + $photo->add_to_parent($parent); + + /* + * If the thumb or resize already exists then rename it. We need to do this after the save + * because the resize_path and thumb_path both call relative_path which caches the + * path. Before add_to_parent the relative path will be incorrect. + */ + if (file_exists($photo->resize_path()) || + file_exists($photo->thumb_path())) { + $photo->name = $pi["filename"] . "-" . rand() . "." . $pi["extension"]; + $photo->save(); + } + + copy($filename, $photo->file_path()); + + module::event("item_created", $photo); + + // Build our thumbnail/resizes + graphics::generate($photo); + + // If the parent has no cover item, make this it. + if (access::can("edit", $parent) && $parent->album_cover_item_id == null) { + item::make_album_cover($photo); + } + + return $photo; + } + + static function get_add_form($parent) { + $form = new Forge("albums/{$parent->id}", "", "post", array("id" => "gAddPhotoForm")); + $group = $form->group("add_photo")->label( + t("Add Photo to %album_title", array("album_title" =>$parent->title))); + $group->input("title")->label(t("Title")); + $group->textarea("description")->label(t("Description")); + $group->input("name")->label(t("Filename")); + $group->upload("file")->label(t("File"))->rules("required|allow[jpg,png,gif,flv,mp4]"); + $group->hidden("type")->value("photo"); + $group->submit("")->value(t("Upload")); + $form->add_rules_from(ORM::factory("item")); + return $form; + } + + static function get_edit_form($photo) { + $form = new Forge("photos/$photo->id", "", "post", array("id" => "gEditPhotoForm")); + $form->hidden("_method")->value("put"); + $group = $form->group("edit_photo")->label(t("Edit Photo")); + $group->input("title")->label(t("Title"))->value($photo->title); + $group->textarea("description")->label(t("Description"))->value($photo->description); + $group->input("filename")->label(t("Filename"))->value($photo->name) + ->error_messages("conflict", t("There is already a file with this name")) + ->callback("item::validate_no_slashes") + ->error_messages("no_slashes", t("The photo name can't contain a \"/\"")) + ->callback("item::validate_no_trailing_period") + ->error_messages("no_trailing_period", t("The photo name can't end in \".\"")); + + $group->submit("")->value(t("Modify")); + $form->add_rules_from(ORM::factory("item")); + return $form; + } + + /** + * Return scaled width and height. + * + * @param integer $width + * @param integer $height + * @param integer $max the target size for the largest dimension + * @param string $format the output format using %d placeholders for width and height + */ + static function img_dimensions($width, $height, $max, $format="width=\"%d\" height=\"%d\"") { + if (!$width || !$height) { + return ""; + } + + if ($width > $height) { + $new_width = $max; + $new_height = (int)$max * ($height / $width); + } else { + $new_height = $max; + $new_width = (int)$max * ($width / $height); + } + return sprintf($format, $new_width, $new_height); + } +} diff --git a/modules/gallery/helpers/rest.php b/modules/gallery/helpers/rest.php new file mode 100644 index 00000000..a63b94c8 --- /dev/null +++ b/modules/gallery/helpers/rest.php @@ -0,0 +1,116 @@ +post("_method", $input->get("_method", request::method())))) { + case "put": return "put"; + case "delete": return "delete"; + default: return "post"; + } + } + } + + /** + * Choose an output format based on what the client prefers to accept. + * @return string "html", "xml" or "json" + */ + static function output_format() { + // Pick a format, but let it be overridden. + $input = Input::instance(); + $fmt = $input->get( + "_format", $input->post( + "_format", request::preferred_accept( + array("xhtml", "html", "xml", "json")))); + + // Some browsers (Chrome!) prefer xhtml over html, but we'll normalize this to html for now. + if ($fmt == "xhtml") { + $fmt = "html"; + } + return $fmt; + } + + /** + * Set HTTP response code. + * @param string Use one of the status code constants defined in this class. + */ + static function http_status($status_code) { + header("HTTP/1.1 " . $status_code); + } + + /** + * Set HTTP Location header. + * @param string URL + */ + static function http_location($url) { + header("Location: " . $url); + } + + /** + * Set HTTP Content-Type header. + * @param string content type + */ + static function http_content_type($type) { + header("Content-Type: " . $type); + } +} diff --git a/modules/gallery/helpers/site_status.php b/modules/gallery/helpers/site_status.php new file mode 100644 index 00000000..6d47e565 --- /dev/null +++ b/modules/gallery/helpers/site_status.php @@ -0,0 +1,132 @@ +where("key", $permanent_key) + ->find(); + if (!$message->loaded) { + $message->key = $permanent_key; + } + $message->severity = $severity; + $message->value = $msg; + $message->save(); + } + + /** + * Remove any permanent message by key. + * @param string $permanent_key + */ + static function clear($permanent_key) { + $message = ORM::factory("message")->where("key", $permanent_key)->find(); + if ($message->loaded) { + $message->delete(); + } + } + + /** + * Get any pending messages. There are two types of messages, transient and permanent. + * Permanent messages are used to let the admin know that there are pending administrative + * issues that need to be resolved. Transient ones are only displayed once. + * @return html text + */ + static function get() { + if (!user::active()->admin) { + return; + } + $buf = array(); + foreach (ORM::factory("message")->find_all() as $msg) { + $value = str_replace('__CSRF__', access::csrf_token(), $msg->value); + $buf[] = "
    • severity) . "\">$value
    • "; + } + + if ($buf) { + return "
        " . implode("", $buf) . "
      "; + } + } + + /** + * Convert a message severity to a CSS class + * @param integer $severity + * @return string + */ + static function severity_class($severity) { + switch($severity) { + case self::SUCCESS: + return "gSuccess"; + + case self::INFO: + return "gInfo"; + + case self::WARNING: + return "gWarning"; + + case self::ERROR: + return "gError"; + } + } +} diff --git a/modules/gallery/helpers/task.php b/modules/gallery/helpers/task.php new file mode 100644 index 00000000..a8a004ab --- /dev/null +++ b/modules/gallery/helpers/task.php @@ -0,0 +1,83 @@ +name}_task"; + if (method_exists($class_name, "available_tasks")) { + foreach (call_user_func(array($class_name, "available_tasks")) as $task) { + $tasks[$task->callback] = $task; + } + } + } + + return $tasks; + } + + static function create($task_def, $context) { + $task = ORM::factory("task"); + $task->callback = $task_def->callback; + $task->name = $task_def->name; + $task->percent_complete = 0; + $task->status = ""; + $task->state = "started"; + $task->owner_id = user::active()->id; + $task->context = serialize($context); + $task->save(); + + return $task; + } + + static function cancel($task_id) { + $task = ORM::factory("task", $task_id); + if (!$task->loaded) { + throw new Exception("@todo MISSING_TASK"); + } + $task->done = 1; + $task->state = "cancelled"; + $task->save(); + + return $task; + } + + static function remove($task_id) { + $task = ORM::factory("task", $task_id); + if ($task->loaded) { + $task->delete(); + } + } + + static function run($task_id) { + $task = ORM::factory("task", $task_id); + if (!$task->loaded) { + throw new Exception("@todo MISSING_TASK"); + } + + $task->state = "running"; + call_user_func_array($task->callback, array(&$task)); + $task->save(); + + return $task; + } +} \ No newline at end of file diff --git a/modules/gallery/helpers/theme.php b/modules/gallery/helpers/theme.php new file mode 100644 index 00000000..cbe224db --- /dev/null +++ b/modules/gallery/helpers/theme.php @@ -0,0 +1,61 @@ +"gThemeDetailsForm")); + $group = $form->group("edit_theme"); + $group->input("page_size")->label(t("Items per page"))->id("gPageSize") + ->rules("required|valid_digit") + ->value(module::get_var("core", "page_size")); + $group->input("thumb_size")->label(t("Thumbnail size (in pixels)"))->id("gThumbSize") + ->rules("required|valid_digit") + ->value(module::get_var("core", "thumb_size")); + $group->input("resize_size")->label(t("Resized image size (in pixels)"))->id("gResizeSize") + ->rules("required|valid_digit") + ->value(module::get_var("core", "resize_size")); + $group->textarea("header_text")->label(t("Header text"))->id("gHeaderText") + ->value(module::get_var("core", "header_text")); + $group->textarea("footer_text")->label(t("Footer text"))->id("gFooterText") + ->value(module::get_var("core", "footer_text")); + $group->submit("")->value(t("Save")); + return $form; + } +} + diff --git a/modules/gallery/helpers/xml.php b/modules/gallery/helpers/xml.php new file mode 100644 index 00000000..e734e90c --- /dev/null +++ b/modules/gallery/helpers/xml.php @@ -0,0 +1,35 @@ +\n"; + foreach ($array as $key => $value) { + if (is_array($value)) { + $xml .= xml::to_xml($value, array_slice($element_names, 1)); + } else if (is_object($value)) { + $xml .= xml::to_xml($value->as_array(), array_slice($element_names, 1)); + } else { + $xml .= "<$key>$value\n"; + } + } + $xml .= "\n"; + return $xml; + } +} diff --git a/modules/gallery/hooks/init_gallery.php b/modules/gallery/hooks/init_gallery.php new file mode 100644 index 00000000..2c36795a --- /dev/null +++ b/modules/gallery/hooks/init_gallery.php @@ -0,0 +1,44 @@ +post("g3sid", $input->get("g3sid"))) { + $_COOKIE["g3sid"] = $g3sid; +} + +if ($user_agent = $input->post("user_agent", $input->get("user_agent"))) { + Kohana::$user_agent = $user_agent; +} diff --git a/modules/gallery/images/gallery.png b/modules/gallery/images/gallery.png new file mode 100644 index 00000000..ca8e0e95 Binary files /dev/null and b/modules/gallery/images/gallery.png differ diff --git a/modules/gallery/images/gd.png b/modules/gallery/images/gd.png new file mode 100644 index 00000000..b341d71c Binary files /dev/null and b/modules/gallery/images/gd.png differ diff --git a/modules/gallery/images/graphicsmagick.png b/modules/gallery/images/graphicsmagick.png new file mode 100644 index 00000000..3d1d77e9 Binary files /dev/null and b/modules/gallery/images/graphicsmagick.png differ diff --git a/modules/gallery/images/imagemagick.jpg b/modules/gallery/images/imagemagick.jpg new file mode 100644 index 00000000..d83c4509 Binary files /dev/null and b/modules/gallery/images/imagemagick.jpg differ diff --git a/modules/gallery/js/albums_form_add.js b/modules/gallery/js/albums_form_add.js new file mode 100644 index 00000000..06a364f3 --- /dev/null +++ b/modules/gallery/js/albums_form_add.js @@ -0,0 +1,12 @@ +$("#gAddAlbumForm input[name=title]").change( + function() { + $("#gAddAlbumForm input[name=name]").attr( + "value", $("#gAddAlbumForm input[name=title]").attr("value"). + replace(/\s+/g, "_").replace(/\.+$/, "")); + }); +$("#gAddAlbumForm input[name=title]").keyup( + function() { + $("#gAddAlbumForm input[name=name]").attr( + "value", $("#gAddAlbumForm input[name=title]").attr("value"). + replace(/\s+/g, "_").replace(/\.+$/, "")); + }); diff --git a/modules/gallery/js/fullsize.js b/modules/gallery/js/fullsize.js new file mode 100644 index 00000000..7428adb5 --- /dev/null +++ b/modules/gallery/js/fullsize.js @@ -0,0 +1,78 @@ +/** + * @todo Move inline CSS out to external style sheet (theme style sheet) + */ +$(document).ready(function() { + $(".gFullSizeLink").click(function() { + var width = $(document).width(); + var height = $(document).height(); + + $("body").append('
      '); + + var image_size = _auto_fit(fullsize_detail.width, fullsize_detail.height); + + $("body").append('
      ' + + '
      '); + + $("#gFullsize").append(''); + $("#gFullsizeClose").click(function() { + $("#gFullsizeOverlay*").remove(); + $("#gFullsize").remove(); + }); + $(window).resize(function() { + $("#gFullsizeOverlay").width($(document).width()); + $("#gFullsizeOverlay").height($(document).height()); + image_size = _auto_fit(fullsize_detail.width, fullsize_detail.height); + $("#gFullsize").height(image_size.height); + $("#gFullsize").width(image_size.width); + $("#gFullsize").css("top", image_size.top); + $("#gFullsize").css("left", image_size.left); + $("#gFullSizeImage").height(image_size.height); + $("#gFullSizeImage").width(image_size.width); + }); + }); +}); + +/* + * Calculate the size of the image panel based on the size of the image and the size of the + * window. Scale the image so the entire panel fits in the view port. + */ +function _auto_fit(imageWidth, imageHeight) { + // ui-dialog gives a padding of 2 pixels + var windowWidth = $(window).width() - 10; + var windowHeight = $(window).height() - 10; + + /* If the width is greater then scale the image width first */ + if (imageWidth > windowWidth) { + var ratio = windowWidth / imageWidth; + imageWidth *= ratio; + imageHeight *= ratio; + } + /* after scaling the width, check that the height fits */ + if (imageHeight > windowHeight) { + var ratio = windowHeight / imageHeight; + imageWidth *= ratio; + imageHeight *= ratio; + } + + // handle the case where the calculation is almost zero (2.14e-14) + return { + top: ((windowHeight - imageHeight) / 2).toFixed(2), + left: ((windowWidth - imageWidth) / 2).toFixed(2), + width: imageWidth.toFixed(2), + height: imageHeight.toFixed(2) + }; +} diff --git a/modules/gallery/js/l10n_client.js b/modules/gallery/js/l10n_client.js new file mode 100644 index 00000000..f43671f1 --- /dev/null +++ b/modules/gallery/js/l10n_client.js @@ -0,0 +1,195 @@ +// Fork from Drupal's l10n_client module, originally written by: +// G‡bor Hojtsy http://drupal.org/user/4166 (original author) +// Young Hahn / Development Seed - http://developmentseed.org/ (friendly user interface) + +var Gallery = Gallery || { 'behaviors': {} }; + +Gallery.attachBehaviors = function(context) { + context = context || document; + // Execute all of them. + jQuery.each(Gallery.behaviors, + function() { + this(context); + }); +}; + +$(document).ready(function() { + Gallery.attachBehaviors(this); +}); + + +// Store all l10n_client related data + methods in its own object +jQuery.extend(Gallery, { + l10nClient: new (function() { + // Set "selected" string to unselected, i.e. -1 + this.selected = -1; + // Keybindings + this.keys = {'toggle':'ctrl+shift+s', 'clear': 'esc'}; // Keybindings + // Keybinding functions + this.key = function(pressed) { + switch(pressed) { + case 'toggle': + // Grab user-hilighted text & send it into the search filter + userSelection = window.getSelection ? window.getSelection() : document.getSelection ? document.getSelection() : document.selection.createRange().text; + userSelection = String(userSelection); + if(userSelection.length > 0) { + Gallery.l10nClient.filter(userSelection); + Gallery.l10nClient.toggle(1); + $('#l10n-client #gL10nSearch').focus(); + } else { + if($('#l10n-client').is('.hidden')) { + Gallery.l10nClient.toggle(1); + if(!$.browser.safari) { + $('#l10n-client #gL10nSearch').focus(); + } + } else { + Gallery.l10nClient.toggle(0); + } + } + break; + case 'clear': + this.filter(false); + break; + } + } + // Toggle the l10nclient + this.toggle = function(state) { + switch(state) { + case 1: + $('#l10n-client-string-select, #l10n-client-string-editor, #l10n-client .labels .label').show(); + $('#l10n-client').height('22em').removeClass('hidden'); + $('#l10n-client .labels .toggle').text('X'); + /* + * This CSS clashes with Gallery's CSS, probably due to + * YUI's grid / floats. + if(!$.browser.msie) { + $('body').css('border-bottom', '22em solid #fff'); + } + */ + $.cookie('Gallery_l10n_client', '1', {expires: 7, path: '/'}); + break; + case 0: + $('#l10n-client-string-select, #l10n-client-string-editor, #l10n-client .labels .label').hide(); + $('#l10n-client').height('2em').addClass('hidden'); + // TODO: Localize this message + $('#l10n-client .labels .toggle').text('Translate Text'); + /* + if(!$.browser.msie) { + $('body').css('border-bottom', '0px'); + } + */ + $.cookie('Gallery_l10n_client', '0', {expires: 7, path: '/'}); + break; + } + } + // Get a string from the DOM tree + this.getString = function(index, type) { + return l10n_client_data[index][type]; + } + // Set a string in the DOM tree + this.setString = function(index, data) { + l10n_client_data[index]['translation'] = data; + } + // Filter the the string list by a search string + this.filter = function(search) { + if(search == false || search == '') { + $('#l10n-client #l10n-search-filter-clear').focus(); + $('#l10n-client-string-select li').show(); + $('#l10n-client #gL10nSearch').val(''); + $('#l10n-client #gL10nSearch').focus(); + } else { + if(search.length > 0) { + $('#l10n-client-string-select li').hide(); + $('#l10n-client-string-select li:contains('+search+')').show(); + $('#l10n-client #gL10nSearch').val(search); + } + } + } + }) +}); + +// Attaches the localization editor behavior to all required fields. +Gallery.behaviors.l10nClient = function(context) { + + switch($.cookie('Gallery_l10n_client')) { + case '1': + Gallery.l10nClient.toggle(1); + break; + default: + Gallery.l10nClient.toggle(0); + break; + } + + // If the selection changes, copy string values to the source and target fields. + // Add class to indicate selected string in list widget. + $('#l10n-client-string-select li').click(function() { + $('#l10n-client-string-select li').removeClass('active'); + $(this).addClass('active'); + var index = $('#l10n-client-string-select li').index(this); + + $('#l10n-client-string-editor .source-text').text(Gallery.l10nClient.getString(index, 'source')); + $("#gL10nClientSaveForm input[name='l10n-message-source']").val(Gallery.l10nClient.getString(index, 'source')); + $('#gL10nClientSaveForm #l10n-edit-target').val(Gallery.l10nClient.getString(index, 'translation')); + + Gallery.l10nClient.selected = index; + }); + + // When l10n_client window is clicked, toggle based on current state. + $('#l10n-client .labels .toggle').click(function() { + if($('#l10n-client').is('.hidden')) { + Gallery.l10nClient.toggle(1); + } else { + Gallery.l10nClient.toggle(0); + } + }); + + // Register keybindings using jQuery hotkeys + // TODO: Either remove hotkeys code or add query.hotkeys.js. + if($.hotkeys) { + $.hotkeys.add(Gallery.l10nClient.keys['toggle'], function(){Gallery.l10nClient.key('toggle')}); + $.hotkeys.add(Gallery.l10nClient.keys['clear'], {target:'#l10n-client #gL10nSearch', type:'keyup'}, function(){Gallery.l10nClient.key('clear')}); + } + + // Custom listener for l10n_client livesearch + $('#l10n-client #gL10nSearch').keyup(function(key) { + Gallery.l10nClient.filter($('#l10n-client #gL10nSearch').val()); + }); + + // Clear search + $('#l10n-client #l10n-search-filter-clear').click(function() { + Gallery.l10nClient.filter(false); + return false; + }); + + // Send AJAX POST data on form submit. + $('#gL10nClientSaveForm').ajaxForm({ + dataType: "json", + success: function(data) { + // Store string in local js + Gallery.l10nClient.setString(Gallery.l10nClient.selected, $('#gL10nClientSaveForm #l10n-edit-target').val()); + + // Mark string as translated. + $('#l10n-client-string-select li').eq(Gallery.l10nClient.selected).removeClass('untranslated').removeClass('active').addClass('translated').text($('#gL10nClientSaveForm #l10n-edit-target').val()); + + // Empty input fields. + $('#l10n-client-string-editor .source-text').html(''); + $('#gL10nClientSaveForm #l10n-edit-target').val(''); + $("#gL10nClientSaveForm input[name='l10n-message-source']").val(''); + }, + error: function(xmlhttp) { + // TODO: Localize this message + alert('An HTTP error @status occured (or empty response).'.replace('@status', xmlhttp.status)); + } + }); + + + // Copy source text to translation field on button click. + $('#gL10nClientSaveForm #l10n-edit-copy').click(function() { + $('#gL10nClientSaveForm #l10n-edit-target').val($('#l10n-client-string-editor .source-text').text()); + }); + + // Clear translation field on button click. + $('#gL10nClientSaveForm #l10n-edit-clear').click(function() { + $('#gL10nClientSaveForm #l10n-edit-target').val(''); + }); +}; diff --git a/modules/gallery/js/quick.js b/modules/gallery/js/quick.js new file mode 100644 index 00000000..e7f35cea --- /dev/null +++ b/modules/gallery/js/quick.js @@ -0,0 +1,95 @@ +$(document).ready(function() { + if ($("#gAlbumGrid").length) { + // @todo Add quick edit pane for album (meta, move, permissions, delete) + $(".gItem").hover(show_quick, function() {}); + } + if ($("#gPhoto").length) { + $("#gPhoto").hover(show_quick, function() {}); + } +}); + +var show_quick = function() { + var cont = $(this); + var quick = $(this).find(".gQuick"); + $("#gQuickPane").remove(); + cont.append("
      "); + var img = cont.find(".gThumbnail,.gResize"); + var pos = cont.position(); + $("#gQuickPane").css({ + "position": "absolute", + "top": pos.top, + "left": pos.left, + "text-align": "center", + "width": cont.innerWidth() + 1, + "height": "auto" + }).hide(); + cont.hover(function() {}, hide_quick); + $.get( + quick.attr("href"), + {}, + function(data, textStatus) { + $("#gQuickPane").html(data).slideDown("fast"); + $(".ui-state-default").hover( + function(){ + $(this).addClass("ui-state-hover"); + }, + function(){ + $(this).removeClass("ui-state-hover"); + } + ); + $("#gQuickPane a:not(.options)").click(function(e) { + e.preventDefault(); + if ($(this).attr("id") == "gQuickDelete" && + !confirm($(this).attr("ref"))) { + return; + } + quick_do(cont, $(this), img); + }); + $("#gQuickPane a.options").click(function(e) { + e.preventDefault(); + $("#gQuickPaneOptions").slideToggle("fast"); + }); + } + ); +}; + +var quick_do = function(cont, pane, img) { + if (pane.hasClass("ui-state-disabled")) { + return false; + } + if (pane.hasClass("gDialogLink")) { + openDialog(pane, function() { window.location.reload(); }); + } else { + img.css("opacity", "0.1"); + cont.addClass("gLoadingLarge"); + $.ajax({ + type: "GET", + url: pane.attr("href"), + dataType: "json", + success: function(data) { + img.css("opacity", "1"); + cont.removeClass("gLoadingLarge"); + if (data.src) { + img.attr("width", data.width); + img.attr("height", data.height); + img.attr("src", data.src); + if (data.height > data.width) { + img.css("margin-top", -32); + } else { + img.css("margin-top", 0); + } + } else if (data.location) { + window.location = data.location; + } else if (data.reload) { + window.location.reload(); + } + } + }); + } + return false; +}; + +var hide_quick = function() { + $("#gQuickPane").remove(); +}; + diff --git a/modules/gallery/libraries/Admin_View.php b/modules/gallery/libraries/Admin_View.php new file mode 100644 index 00000000..acc3f8ec --- /dev/null +++ b/modules/gallery/libraries/Admin_View.php @@ -0,0 +1,126 @@ +theme_name = module::get_var("core", "active_admin_theme"); + if (user::active()->admin) { + $this->theme_name = Input::instance()->get("theme", $this->theme_name); + } + $this->sidebar = ""; + $this->set_global("theme", $this); + $this->set_global("user", user::active()); + } + + public function url($path, $absolute_url=false) { + $arg = "themes/{$this->theme_name}/$path"; + return $absolute_url ? url::abs_file($arg) : url::file($arg); + } + + public function display($page_name, $view_class="View") { + return new $view_class($page_name); + } + + public function admin_menu() { + $menu = Menu::factory("root"); + core_menu::admin($menu, $this); + + foreach (module::active() as $module) { + if ($module->name == "core") { + continue; + } + $class = "{$module->name}_menu"; + if (method_exists($class, "admin")) { + call_user_func_array(array($class, "admin"), array(&$menu, $this)); + } + } + + print $menu; + } + + /** + * Print out any site wide status information. + */ + public function site_status() { + return site_status::get(); + } + + /** + * Print out any messages waiting for this user. + */ + public function messages() { + return message::get(); + } + + /** + * Handle all theme functions that insert module content. + */ + public function __call($function, $args) { + switch ($function) { + case "admin_credits"; + case "admin_footer": + case "admin_header_top": + case "admin_header_bottom": + case "admin_page_bottom": + case "admin_page_top": + case "admin_head": + $blocks = array(); + foreach (module::active() as $module) { + $helper_class = "{$module->name}_theme"; + if (method_exists($helper_class, $function)) { + $blocks[] = call_user_func_array( + array($helper_class, $function), + array_merge(array($this), $args)); + } + } + + if (Session::instance()->get("debug")) { + if ($function != "admin_head") { + array_unshift( + $blocks, "
      " . + "
      $function
      "); + $blocks[] = "
      "; + } + } + + return implode("\n", $blocks); + + default: + throw new Exception("@todo UNKNOWN_THEME_FUNCTION: $function"); + } + } +} \ No newline at end of file diff --git a/modules/gallery/libraries/Block.php b/modules/gallery/libraries/Block.php new file mode 100644 index 00000000..6fe679f1 --- /dev/null +++ b/modules/gallery/libraries/Block.php @@ -0,0 +1,30 @@ +__toString(); + } +} diff --git a/modules/gallery/libraries/I18n.php b/modules/gallery/libraries/I18n.php new file mode 100644 index 00000000..c936be88 --- /dev/null +++ b/modules/gallery/libraries/I18n.php @@ -0,0 +1,410 @@ +translate($message, $options); +} + +/** + * Translates a localizable message with plural forms. + * @param $singular String The message to be translated. E.g. "There is one album." + * @param $plural String The plural message to be translated. E.g. + * "There are %count albums." + * @param $count Number The number which is inserted for the %count placeholder and + * which is used to select the proper plural form ($singular or $plural). + * @param $options array (optional) Options array for key value pairs which are used + * for pluralization and interpolation. Special key: "locale" to override the + * currently configured locale. + * @return String The translated message string. + */ +function t2($singular, $plural, $count, $options=array()) { + return I18n::instance()->translate(array("one" => $singular, "other" => $plural), + array_merge($options, array("count" => $count))); +} + +class I18n_Core { + private static $_instance; + private $_config = array(); + private $_call_log = array(); + private $_cache = array(); + + private function __construct($config) { + $this->_config = $config; + $this->locale($config['default_locale']); + } + + public static function instance($config=null) { + if (self::$_instance == NULL || isset($config)) { + $config = isset($config) ? $config : Kohana::config('locale'); + if (empty($config['default_locale'])) { + $config['default_locale'] = module::get_var('core', 'default_locale'); + } + self::$_instance = new I18n_Core($config); + } + + return self::$_instance; + } + + public function locale($locale=null) { + if ($locale) { + $this->_config['default_locale'] = $locale; + // Attempt to set PHP's locale as well (for number formatting, collation, etc.) + // TODO: See G2 for better fallack code. + $locale_prefs = array($locale); + $locale_prefs[] = 'en_US'; + setlocale(LC_ALL, $locale_prefs); + } + return $this->_config['default_locale']; + } + + /** + * Translates a localizable message. + * @param $message String|array The message to be translated. E.g. "Hello world" + * or array("one" => "One album", "other" => "%count albums") + * @param $options array (optional) Options array for key value pairs which are used + * for pluralization and interpolation. Special keys are "count" and "locale", + * the latter to override the currently configured locale. + * @return String The translated message string. + */ + public function translate($message, $options=array()) { + $locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale']; + $count = isset($options['count']) ? $options['count'] : null; + $values = $options; + unset($values['locale']); + $this->log($message, $options); + + $entry = $this->lookup($locale, $message); + + if (null === $entry) { + // Default to the root locale. + $entry = $message; + $locale = $this->_config['root_locale']; + } + + $entry = $this->pluralize($locale, $entry, $count); + + $entry = $this->interpolate($locale, $entry, $values); + + return $entry; + } + + private function lookup($locale, $message) { + if (!isset($this->_cache[$locale])) { + $this->_cache[$locale] = array(); + // TODO: Load data from locale file instead of the DB. + foreach (Database::instance() + ->select("key", "translation") + ->from("incoming_translations") + ->where(array("locale" => $locale)) + ->get() + ->as_array() as $row) { + $this->_cache[$locale][$row->key] = unserialize($row->translation); + } + + // Override incoming with outgoing... + foreach (Database::instance() + ->select("key", "translation") + ->from("outgoing_translations") + ->where(array("locale" => $locale)) + ->get() + ->as_array() as $row) { + $this->_cache[$locale][$row->key] = unserialize($row->translation); + } + } + + $key = self::get_message_key($message); + + if (isset($this->_cache[$locale][$key])) { + return $this->_cache[$locale][$key]; + } else { + return null; + } + } + + public function has_translation($message, $options=null) { + $locale = empty($options['locale']) ? $this->_config['default_locale'] : $options['locale']; + $count = empty($options['count']) ? null : $options['count']; + $values = $options; + unset($values['locale']); + $this->log($message, $options); + + $entry = $this->lookup($locale, $message); + + if (null === $entry) { + return false; + } else if (!is_array($entry)) { + return $entry !== ''; + } else { + $plural_key = self::get_plural_key($locale, $count); + return isset($entry[$plural_key]) + && $entry[$plural_key] !== null + && $entry[$plural_key] !== ''; + } + } + + public static function get_message_key($message) { + $as_string = is_array($message) ? implode('|', $message) : $message; + return md5($as_string); + } + + private function interpolate($locale, $string, $values) { + // TODO: Handle locale specific number formatting. + + // Replace x_y before replacing x. + krsort($values, SORT_STRING); + + $keys = array(); + foreach (array_keys($values) as $key) { + $keys[] = "%$key"; + } + return str_replace($keys, array_values($values), $string); + } + + private function pluralize($locale, $entry, $count) { + if (!is_array($entry)) { + return $entry; + } + + $plural_key = self::get_plural_key($locale, $count); + if (!isset($entry[$plural_key])) { + // Fallback to the default plural form. + $plural_key = 'other'; + } + + if (isset($entry[$plural_key])) { + return $entry[$plural_key]; + } else { + // Fallback to just any plural form. + list ($plural_key, $string) = each($entry); + return $string; + } + } + + private function log($message, $options) { + $key = self::get_message_key($message); + isset($this->_call_log[$key]) or $this->_call_log[$key] = array($message, $options); + } + + public function call_log() { + return $this->_call_log; + } + + private static function get_plural_key($locale, $count) { + $parts = explode('_', $locale); + $language = $parts[0]; + + // Data from CLDR 1.6 (http://unicode.org/cldr/data/common/supplemental/plurals.xml). + // Docs: http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html + switch ($language) { + case 'az': + case 'fa': + case 'hu': + case 'ja': + case 'ko': + case 'my': + case 'to': + case 'tr': + case 'vi': + case 'yo': + case 'zh': + case 'bo': + case 'dz': + case 'id': + case 'jv': + case 'ka': + case 'km': + case 'kn': + case 'ms': + case 'th': + return 'other'; + + case 'ar': + if ($count == 0) { + return 'zero'; + } else if ($count == 1) { + return 'one'; + } else if ($count == 2) { + return 'two'; + } else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 10) { + return 'few'; + } else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 99) { + return 'many'; + } else { + return 'other'; + } + + case 'pt': + case 'am': + case 'bh': + case 'fil': + case 'tl': + case 'guw': + case 'hi': + case 'ln': + case 'mg': + case 'nso': + case 'ti': + case 'wa': + if ($count == 0 || $count == 1) { + return 'one'; + } else { + return 'other'; + } + + case 'fr': + if ($count >= 0 and $count < 2) { + return 'one'; + } else { + return 'other'; + } + + case 'lv': + if ($count == 0) { + return 'zero'; + } else if ($count % 10 == 1 && $count % 100 != 11) { + return 'one'; + } else { + return 'other'; + } + + case 'ga': + case 'se': + case 'sma': + case 'smi': + case 'smj': + case 'smn': + case 'sms': + if ($count == 1) { + return 'one'; + } else if ($count == 2) { + return 'two'; + } else { + return 'other'; + } + + case 'ro': + case 'mo': + if ($count == 1) { + return 'one'; + } else if (is_int($count) && $count == 0 && ($i = $count % 100) >= 1 && $i <= 19) { + return 'few'; + } else { + return 'other'; + } + + case 'lt': + if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) { + return 'one'; + } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 9 && ($i = $count % 100) < 11 && $i > 19) { + return 'few'; + } else { + return 'other'; + } + + case 'hr': + case 'ru': + case 'sr': + case 'uk': + case 'be': + case 'bs': + case 'sh': + if (is_int($count) && $count % 10 == 1 && $count % 100 != 11) { + return 'one'; + } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 && ($i = $count % 100) < 12 && $i > 14) { + return 'few'; + } else if (is_int($count) && ($count % 10 == 0 || (($i = $count % 10) >= 5 && $i <= 9) || (($i = $count % 100) >= 11 && $i <= 14))) { + return 'many'; + } else { + return 'other'; + } + + case 'cs': + case 'sk': + if ($count == 1) { + return 'one'; + } else if (is_int($count) && $count >= 2 && $count <= 4) { + return 'few'; + } else { + return 'other'; + } + + case 'pl': + if ($count == 1) { + return 'one'; + } else if (is_int($count) && ($i = $count % 10) >= 2 && $i <= 4 && + ($i = $count % 100) < 12 && $i > 14 && ($i = $count % 100) < 22 && $i > 24) { + return 'few'; + } else { + return 'other'; + } + + case 'sl': + if ($count % 100 == 1) { + return 'one'; + } else if ($count % 100 == 2) { + return 'two'; + } else if (is_int($count) && ($i = $count % 100) >= 3 && $i <= 4) { + return 'few'; + } else { + return 'other'; + } + + case 'mt': + if ($count == 1) { + return 'one'; + } else if ($count == 0 || is_int($count) && ($i = $count % 100) >= 2 && $i <= 10) { + return 'few'; + } else if (is_int($count) && ($i = $count % 100) >= 11 && $i <= 19) { + return 'many'; + } else { + return 'other'; + } + + case 'mk': + if ($count % 10 == 1) { + return 'one'; + } else { + return 'other'; + } + + case 'cy': + if ($count == 1) { + return 'one'; + } else if ($count == 2) { + return 'two'; + } else if ($count == 8 || $count == 11) { + return 'many'; + } else { + return 'other'; + } + + default: // en, de, etc. + return $count == 1 ? 'one' : 'other'; + } + } +} \ No newline at end of file diff --git a/modules/gallery/libraries/MY_Database.php b/modules/gallery/libraries/MY_Database.php new file mode 100644 index 00000000..c56f16e8 --- /dev/null +++ b/modules/gallery/libraries/MY_Database.php @@ -0,0 +1,92 @@ +where[] = "("; + return $this; + } + + public function close_paren() { + // Search backwards for the last opening paren and resolve it + $i = count($this->where) - 1; + $this->where[$i] .= ")"; + while (--$i >= 0) { + if ($this->where[$i] == "(") { + // Remove the paren from the where clauses, and add it to the right of the operator of the + // next where clause. If removing the paren makes the next where clause the first element + // in the where list, then the operator shouldn't be there. It's there because we + // calculate whether or not we need an operator based on the number of where clauses, and + // the open paren seems like a where clause even though it isn't. + array_splice($this->where, $i, 1); + $this->where[$i] = preg_replace("/^(AND|OR) /", $i ? "\\1 (" : "(", $this->where[$i]); + return $this; + } + } + + throw new Kohana_Database_Exception('database.missing_open_paren'); + } + + /** + * Parse the query string and convert any strings of the form `\([a-zA-Z0-9_]*?)\] + * table prefix . $1 + */ + public function query($sql = '') { + if (!empty($sql)) { + $sql = $this->add_table_prefixes($sql); + } + return parent::query($sql); + } + + public function add_table_prefixes($sql) { + $prefix = $this->config["table_prefix"]; + if (strpos($sql, "SHOW TABLES") === 0) { + /* + * Don't ignore "show tables", otherwise we could have a infinite + * @todo this may have to be changed if we support more than mysql + */ + return $sql; + } else if (strpos($sql, "CREATE TABLE") === 0) { + // Creating a new table add it to the table cache. + $open_brace = strpos($sql, "{") + 1; + $close_brace = strpos($sql, "}", $open_brace); + $name = substr($sql, $open_brace, $close_brace - $open_brace); + $this->_table_names["{{$name}}"] = "{$prefix}$name"; + } + + if (!isset($this->_table_names)) { + // This should only run once on the first query + $this->_table_names =array(); + $len = strlen($prefix); + foreach($this->list_tables() as $table_name) { + if ($len > 0) { + $naked_name = strpos($table_name, $prefix) !== 0 ? + $table_name : substr($table_name, $len); + } else { + $naked_name = $table_name; + } + $this->_table_names["{{$naked_name}}"] = $table_name; + } + } + + return empty($this->_table_names) ? $sql : strtr($sql, $this->_table_names); + } +} \ No newline at end of file diff --git a/modules/gallery/libraries/MY_Forge.php b/modules/gallery/libraries/MY_Forge.php new file mode 100644 index 00000000..17d0465b --- /dev/null +++ b/modules/gallery/libraries/MY_Forge.php @@ -0,0 +1,59 @@ +hidden("csrf")->value(""); + } + /** + * Use our own template + */ + public function render($template="form.html", $custom=false) { + $this->hidden["csrf"]->value(access::csrf_token()); + return parent::render($template, $custom); + } + + /** + * Associate validation rules defined in the model with this form. + */ + public function add_rules_from($model) { + foreach ($this->inputs as $name => $input) { + if (isset($input->inputs)) { + $input->add_rules_from($model); + } + if (isset($model->rules[$name])) { + $input->rules($model->rules[$name]); + } + } + } + + /** + * Validate our CSRF value as a mandatory part of all form validation. + */ + public function validate() { + $status = parent::validate(); + access::verify_csrf(); + return $status; + } +} \ No newline at end of file diff --git a/modules/gallery/libraries/MY_ORM.php b/modules/gallery/libraries/MY_ORM.php new file mode 100644 index 00000000..fb2f80a7 --- /dev/null +++ b/modules/gallery/libraries/MY_ORM.php @@ -0,0 +1,46 @@ +db->open_paren(); + return $this; + } + + public function close_paren() { + $this->db->close_paren(); + return $this; + } +} + +/** + * Slide this in here for convenience. We won't ever be overloading ORM_Iterator without ORM. + */ +class ORM_Iterator extends ORM_Iterator_Core { + /** + * Cache the result row + */ + public function current() { + $row = parent::current(); + if (is_object($row)) { + model_cache::set($row); + } + return $row; + } +} \ No newline at end of file diff --git a/modules/gallery/libraries/MY_Pagination.php b/modules/gallery/libraries/MY_Pagination.php new file mode 100644 index 00000000..d06a974f --- /dev/null +++ b/modules/gallery/libraries/MY_Pagination.php @@ -0,0 +1,35 @@ +auto_hide === TRUE AND $this->total_pages <= 1) { + return ""; + } + + if ($style === NULL) { + // Use default style + $style = $this->style; + } + + // Return rendered pagination view + return View::factory("pager.html", get_object_vars($this))->render(); + } +} diff --git a/modules/gallery/libraries/MY_View.php b/modules/gallery/libraries/MY_View.php new file mode 100644 index 00000000..836d1087 --- /dev/null +++ b/modules/gallery/libraries/MY_View.php @@ -0,0 +1,46 @@ +set_global("csrf", access::csrf_token()); + } + + /** + * Override View_Core::render so that we trap errors stemming from bad PHP includes and show a + * visible stack trace to help developers. + * + * @see View_Core::render + */ + public function render($print=false, $renderer=false) { + try { + return parent::render($print, $renderer); + } catch (Exception $e) { + Kohana::Log('error', $e->getTraceAsString()); + Kohana::Log('debug', $e->getMessage()); + return ""; + } + } +} diff --git a/modules/gallery/libraries/Menu.php b/modules/gallery/libraries/Menu.php new file mode 100644 index 00000000..d19d8b1e --- /dev/null +++ b/modules/gallery/libraries/Menu.php @@ -0,0 +1,187 @@ +id = $id; + return $this; + } + + /** + * Set the label + * @chainable + */ + public function label($label) { + $this->label = $label; + return $this; + } + + /** + * Set the url + * @chainable + */ + public function url($url) { + $this->url = $url; + return $this; + } + + /** + * Set the css id + * @chainable + */ + public function css_id($css_id) { + $this->css_id = $css_id; + return $this; + } + + /** + * Set the css class + * @chainable + */ + public function css_class($css_class) { + $this->css_class = $css_class; + return $this; + } + +} + +/** + * Menu element that provides a link to a new page. + */ +class Menu_Element_Link extends Menu_Element { + public function __toString() { + if (isset($this->css_id) && !empty($this->css_id)) { + $css_id = " id=\"$this->css_id\""; + } else { + $css_id = ""; + } + if (isset($this->css_class) && !empty($this->css_class)) { + $css_class = " $this->css_class"; + } else { + $css_class = ""; + } + return "
    • url\" " . + "title=\"$this->label\">$this->label
    • "; + } +} + +/** + * Menu element that provides a pop-up dialog + */ +class Menu_Element_Dialog extends Menu_Element { + public function __toString() { + if (isset($this->css_id) && !empty($this->css_id)) { + $css_id = " id=\"$this->css_id\""; + } else { + $css_id = ""; + } + if (isset($this->css_class) && !empty($this->css_class)) { + $css_class = " $this->css_class"; + } else { + $css_class = ""; + } + return "
    • url\" " . + "title=\"$this->label\">$this->label
    • "; + } +} + +/** + * Root menu or submenu + */ +class Menu_Core extends Menu_Element { + public $elements; + public $is_root = false; + + /** + * Return an instance of a Menu_Element + * @chainable + */ + public static function factory($type) { + switch($type) { + case "link": + return new Menu_Element_Link(); + + case "dialog": + return new Menu_Element_Dialog(); + + case "root": + $menu = new Menu(); + $menu->is_root = true; + return $menu; + + case "submenu": + return new Menu(); + + default: + throw Exception("@todo UNKNOWN_MENU_TYPE"); + } + } + + public function __construct() { + $this->elements = array(); + } + + /** + * Add a new element to this menu + */ + public function append($menu_element) { + $this->elements[$menu_element->id] = $menu_element; + return $this; + } + + /** + * Add a new element to this menu + */ + public function add_after($target_id, $new_menu_element) { + $copy = array(); + foreach ($this->elements as $id => $menu_element) { + $copy[$id] = $menu_element; + if ($id == $target_id) { + $copy[$new_menu_element->id] = $new_menu_element; + } + } + $this->elements = $copy; + return $this; + } + + /** + * Retrieve a Menu_Element by id + */ + public function get($id) { + return $this->elements[$id]; + } + + public function __toString() { + $html = $this->is_root ? "
        " : + "
      • $this->label
          "; + $html .= implode("\n", $this->elements); + $html .= $this->is_root ? "
        " : "
      "; + return $html; + } +} diff --git a/modules/gallery/libraries/ORM_MPTT.php b/modules/gallery/libraries/ORM_MPTT.php new file mode 100644 index 00000000..46280d95 --- /dev/null +++ b/modules/gallery/libraries/ORM_MPTT.php @@ -0,0 +1,307 @@ +model_name = inflector::singular($this->table_name); + } + + /** + * Add this node as a child of the parent provided. + * + * @chainable + * @param integer $parent_id the id of the parent node + * @return ORM + */ + function add_to_parent($parent) { + $this->lock(); + + try { + // Make a hole in the parent for this new item + $this->db->query( + "UPDATE {{$this->table_name}} SET `left` = `left` + 2 WHERE `left` >= {$parent->right}"); + $this->db->query( + "UPDATE {{$this->table_name}} SET `right` = `right` + 2 WHERE `right` >= {$parent->right}"); + $parent->right += 2; + + // Insert this item into the hole + $this->left = $parent->right - 2; + $this->right = $parent->right - 1; + $this->parent_id = $parent->id; + $this->level = $parent->level + 1; + $this->save(); + $parent->reload(); + } catch (Exception $e) { + $this->unlock(); + throw $e; + } + + $this->unlock(); + return $this; + } + + /** + * Delete this node and all of its children. + */ + public function delete() { + $children = $this->children(); + if ($children) { + foreach ($this->children() as $item) { + // Deleting children affects the MPTT tree, so we have to reload each child before we + // delete it so that we have current left/right pointers. This is inefficient. + // @todo load each child once, not twice. + $item->reload()->delete(); + } + + // Deleting children has affected this item + $this->reload(); + } + + $this->lock(); + try { + $this->db->query( + "UPDATE {{$this->table_name}} SET `left` = `left` - 2 WHERE `left` > {$this->right}"); + $this->db->query( + "UPDATE {{$this->table_name}} SET `right` = `right` - 2 WHERE `right` > {$this->right}"); + } catch (Exception $e) { + $this->unlock(); + throw $e; + } + + $this->unlock(); + parent::delete(); + } + + /** + * Return true if the target is descendant of this item. + * @param ORM $target + * @return boolean + */ + function is_descendant($target) { + return ($this->left <= $target->left && $this->right >= $target->right); + } + + /** + * Return the parent of this node + * + * @return ORM + */ + function parent() { + if (!$this->parent_id) { + return null; + } + return model_cache::get($this->model_name, $this->parent_id); + } + + /** + * Return all the parents of this node, in order from root to this node's immediate parent. + * + * @return array ORM + */ + function parents() { + return $this + ->where("`left` <= {$this->left}") + ->where("`right` >= {$this->right}") + ->where("id <> {$this->id}") + ->orderby("left", "ASC") + ->find_all(); + } + + /** + * Return all of the children of this node, ordered by id. + * + * @chainable + * @param integer SQL limit + * @param integer SQL offset + * @param array orderby + * @return array ORM + */ + function children($limit=null, $offset=0, $orderby=null) { + $this->where("parent_id", $this->id); + if (empty($orderby)) { + $this->orderby("id", "ASC"); + } else { + $this->orderby($orderby); + } + return $this->find_all($limit, $offset); + } + + /** + * Return all of the children of this node, ordered by id. + * + * @chainable + * @param integer SQL limit + * @param integer SQL offset + * @return array ORM + */ + function children_count() { + return $this->where("parent_id", $this->id)->count_all(); + } + + /** + * Return all of the children of the specified type, ordered by id. + * + * @param integer SQL limit + * @param integer SQL offset + * @param string type to return + * @param array orderby + * @return object ORM_Iterator + */ + function descendants($limit=null, $offset=0, $type=null, $orderby=null) { + $this->where("left >", $this->left) + ->where("right <=", $this->right); + if ($type) { + $this->where("type", $type); + } + + if (empty($orderby)) { + $this->orderby("id", "ASC"); + } else { + $this->orderby($orderby); + } + + return $this->find_all($limit, $offset); + } + + /** + * Return the count of all the children of the specified type. + * + * @param string type to count + * @return integer child count + */ + function descendants_count($type=null) { + $this->where("left >", $this->left) + ->where("right <=", $this->right); + if ($type) { + $this->where("type", $type); + } + return $this->count_all(); + } + + /** + * Move this item to the specified target. + * + * @chainable + * @param Item_Model $target Target node + * @return ORM_MTPP + */ + function move_to($target) { + if ($this->left <= $target->left && + $this->right >= $target->right) { + throw new Exception("@todo INVALID_TARGET can't move item inside itself"); + } + + $number_to_move = (int)(($this->right - $this->left) / 2 + 1); + $size_of_hole = $number_to_move * 2; + $original_left = $this->left; + $original_right = $this->right; + $target_right = $target->right; + $level_delta = ($target->level + 1) - $this->level; + + $this->lock(); + try { + if ($level_delta) { + // Update the levels for the to-be-moved items + $this->db->query( + "UPDATE {{$this->table_name}} SET `level` = `level` + $level_delta" . + " WHERE `left` >= $original_left AND `right` <= $original_right"); + } + + // Make a hole in the target for the move + $target->db->query( + "UPDATE {{$this->table_name}} SET `left` = `left` + $size_of_hole" . + " WHERE `left` >= $target_right"); + $target->db->query( + "UPDATE {{$this->table_name}} SET `right` = `right` + $size_of_hole" . + " WHERE `right` >= $target_right"); + + // Change the parent. + $this->db->query( + "UPDATE {{$this->table_name}} SET `parent_id` = {$target->id}" . + " WHERE `id` = {$this->id}"); + + // If the source is to the right of the target then we just adjusted its left and right above. + $left = $original_left; + $right = $original_right; + if ($original_left > $target_right) { + $left += $size_of_hole; + $right += $size_of_hole; + } + + $new_offset = $target->right - $left; + $this->db->query( + "UPDATE {{$this->table_name}}" . + " SET `left` = `left` + $new_offset," . + " `right` = `right` + $new_offset" . + " WHERE `left` >= $left" . + " AND `right` <= $right"); + + // Close the hole in the source's parent after the move + $this->db->query( + "UPDATE {{$this->table_name}} SET `left` = `left` - $size_of_hole" . + " WHERE `left` > $right"); + $this->db->query( + "UPDATE {{$this->table_name}} SET `right` = `right` - $size_of_hole" . + " WHERE `right` > $right"); + } catch (Exception $e) { + $this->unlock(); + throw $e; + } + + $this->unlock(); + + // Lets reload to get the changes. + $this->reload(); + return $this; + } + + /** + * Lock the tree to prevent concurrent modification. + */ + protected function lock() { + $result = $this->db->query("SELECT GET_LOCK('{$this->table_name}', 1) AS l")->current(); + if (empty($result->l)) { + throw new Exception("@todo UNABLE_TO_LOCK_EXCEPTION"); + } + } + + /** + * Unlock the tree. + */ + protected function unlock() { + $this->db->query("SELECT RELEASE_LOCK('{$this->table_name}')"); + } +} diff --git a/modules/gallery/libraries/Sendmail.php b/modules/gallery/libraries/Sendmail.php new file mode 100644 index 00000000..90998457 --- /dev/null +++ b/modules/gallery/libraries/Sendmail.php @@ -0,0 +1,97 @@ +headers = array(); + $config = Kohana::config("sendmail"); + foreach ($config as $key => $value) { + $this->$key($value); + } + } + + public function __get($key) { + return null; + } + + public function __call($key, $value) { + switch ($key) { + case "to": + $this->to = is_array($value[0]) ? $value[0] : array($value[0]); + break; + case "header": + if (count($value) != 2) { + throw new Exception("@todo INVALID_HEADER_PARAMETERS"); + } + $this->headers[$value[0]] = $value[1]; + break; + case "from": + $this->headers["From"] = $value[0]; + break; + case "reply_to": + $this->headers["Reply-To"] = $value[0]; + break; + default: + $this->$key = $value[0]; + } + return $this; + } + + public function send() { + if (empty($this->to)) { + throw new Exception("@todo TO_IS_REQUIRED_FOR_MAIL"); + } + $to = implode(", ", $this->to); + $headers = array(); + foreach ($this->headers as $key => $value) { + $key = ucfirst($key); + $headers[] = "$key: $value"; + } + + // The docs say headers should be separated by \r\n, but occasionaly that doesn't work and you + // need to use a single \n. This can be set in config/sendmail.php + $headers = implode($this->header_separator, $headers); + $message = wordwrap($this->message, $this->line_length, "\n"); + if (!$this->mail($to, $this->subject, $message, $headers)) { + Kohana::log("error", wordwrap("Sending mail failed:\nTo: $to\n $this->subject\n" . + "Headers: $headers\n $this->message")); + throw new Exception("@todo SEND_MAIL_FAILED"); + } + return $this; + } + + public function mail($to, $subject, $message, $headers) { + return mail($to, $subject, $message, $headers); + } +} diff --git a/modules/gallery/libraries/Task_Definition.php b/modules/gallery/libraries/Task_Definition.php new file mode 100644 index 00000000..8d9c5922 --- /dev/null +++ b/modules/gallery/libraries/Task_Definition.php @@ -0,0 +1,50 @@ +callback = $callback; + return $this; + } + + function description($description) { + $this->description = $description; + return $this; + } + + function name($name) { + $this->name = $name; + return $this; + } + + function severity($severity) { + $this->severity = $severity; + return $this; + } +} diff --git a/modules/gallery/libraries/Theme_View.php b/modules/gallery/libraries/Theme_View.php new file mode 100644 index 00000000..b5b97666 --- /dev/null +++ b/modules/gallery/libraries/Theme_View.php @@ -0,0 +1,221 @@ +theme_name = module::get_var("core", "active_site_theme"); + if (user::active()->admin) { + $this->theme_name = Input::instance()->get("theme", $this->theme_name); + } + $this->item = null; + $this->tag = null; + $this->set_global("theme", $this); + $this->set_global("user", user::active()); + $this->set_global("page_type", $page_type); + if ($page_type == "album") { + $this->set_global("thumb_proportion", $this->thumb_proportion()); + } + + $maintenance_mode = Kohana::config("core.maintenance_mode", false, false); + if ($maintenance_mode) { + message::warning(t("This site is currently in maintenance mode")); + } + } + + /** + * Proportion of the current thumb_size's to default + * @return int + */ + public function thumb_proportion() { + // @TODO change the 200 to a theme supplied value when and if we come up with an + // API to allow the theme to set defaults. + return module::get_var("core", "thumb_size", 200) / 200; + } + + public function url($path, $absolute_url=false) { + $arg = "themes/{$this->theme_name}/$path"; + return $absolute_url ? url::abs_file($arg) : url::file($arg); + } + + public function item() { + return $this->item; + } + + public function tag() { + return $this->tag; + } + + public function page_type() { + return $this->page_type; + } + + public function display($page_name, $view_class="View") { + return new $view_class($page_name); + } + + public function site_menu() { + $menu = Menu::factory("root"); + if ($this->page_type != "login") { + core_menu::site($menu, $this); + + foreach (module::active() as $module) { + if ($module->name == "core") { + continue; + } + $class = "{$module->name}_menu"; + if (method_exists($class, "site")) { + call_user_func_array(array($class, "site"), array(&$menu, $this)); + } + } + } + + print $menu; + } + + public function album_menu() { + $menu = Menu::factory("root"); + core_menu::album($menu, $this); + + foreach (module::active() as $module) { + if ($module->name == "core") { + continue; + } + $class = "{$module->name}_menu"; + if (method_exists($class, "album")) { + call_user_func_array(array($class, "album"), array(&$menu, $this)); + } + } + + print $menu; + } + + public function photo_menu() { + $menu = Menu::factory("root"); + core_menu::photo($menu, $this); + + foreach (module::active() as $module) { + if ($module->name == "core") { + continue; + } + $class = "{$module->name}_menu"; + if (method_exists($class, "photo")) { + call_user_func_array(array($class, "photo"), array(&$menu, $this)); + } + } + + print $menu; + } + + public function pager() { + if ($this->children_count) { + $this->pagination = new Pagination(); + $this->pagination->initialize( + array('query_string' => 'page', + 'total_items' => $this->children_count, + 'items_per_page' => $this->page_size, + 'style' => 'classic')); + return $this->pagination->render(); + } + } + + /** + * Print out any site wide status information. + */ + public function site_status() { + return site_status::get(); + } + + /** + * Print out any messages waiting for this user. + */ + public function messages() { + return message::get(); + } + + /** + * Handle all theme functions that insert module content. + */ + public function __call($function, $args) { + switch ($function) { + case "album_blocks": + case "album_bottom": + case "album_top": + case "credits"; + case "dynamic_bottom": + case "dynamic_top": + case "footer": + case "head": + case "header_bottom": + case "header_top": + case "page_bottom": + case "page_top": + case "photo_blocks": + case "photo_bottom": + case "photo_top": + case "resize_bottom": + case "resize_top": + case "sidebar_blocks": + case "sidebar_bottom": + case "sidebar_top": + case "thumb_bottom": + case "thumb_info": + case "thumb_top": + $blocks = array(); + foreach (module::active() as $module) { + $helper_class = "{$module->name}_theme"; + if (method_exists($helper_class, $function)) { + $blocks[] = call_user_func_array( + array($helper_class, $function), + array_merge(array($this), $args)); + } + } + if (Session::instance()->get("debug")) { + if ($function != "head") { + array_unshift( + $blocks, "
      " . + "
      $function
      "); + $blocks[] = "
      "; + } + } + return implode("\n", $blocks); + + default: + throw new Exception("@todo UNKNOWN_THEME_FUNCTION: $function"); + } + } +} \ No newline at end of file diff --git a/modules/gallery/models/access_cache.php b/modules/gallery/models/access_cache.php new file mode 100644 index 00000000..10d05df7 --- /dev/null +++ b/modules/gallery/models/access_cache.php @@ -0,0 +1,21 @@ + "required|length[0,255]", + "title" => "required|length[0,255]", + "description" => "length[0,65535]" + ); + + /** + * Add a set of restrictions to any following queries to restrict access only to items + * viewable by the active user. + * @chainable + */ + public function viewable() { + if (is_null($this->view_restrictions)) { + if (user::active()->admin) { + $this->view_restrictions = array(); + } else { + foreach (user::group_ids() as $id) { + // Separate the first restriction from the rest to make it easier for us to formulate + // our where clause below + if (empty($this->view_restrictions)) { + $this->view_restrictions[0] = "view_$id"; + } else { + $this->view_restrictions[1]["view_$id"] = access::ALLOW; + } + } + } + } + switch (count($this->view_restrictions)) { + case 0: + break; + + case 1: + $this->where($this->view_restrictions[0], access::ALLOW); + break; + + default: + $this->open_paren(); + $this->where($this->view_restrictions[0], access::ALLOW); + $this->orwhere($this->view_restrictions[1]); + $this->close_paren(); + break; + } + + return $this; + } + + /** + * Is this item an album? + * @return true if it's an album + */ + public function is_album() { + return $this->type == 'album'; + } + + /** + * Is this item a photo? + * @return true if it's a photo + */ + public function is_photo() { + return $this->type == 'photo'; + } + + /** + * Is this item a movie? + * @return true if it's a movie + */ + public function is_movie() { + return $this->type == 'movie'; + } + + public function delete() { + module::event("item_before_delete", $this); + + $parent = $this->parent(); + if ($parent->album_cover_item_id == $this->id) { + item::remove_album_cover($parent); + } + + $path = $this->file_path(); + $resize_path = $this->resize_path(); + $thumb_path = $this->thumb_path(); + + parent::delete(); + if (is_dir($path)) { + @dir::unlink($path); + @dir::unlink(dirname($resize_path)); + @dir::unlink(dirname($thumb_path)); + } else { + @unlink($path); + @unlink($resize_path); + @unlink($thumb_path); + } + } + + /** + * Move this item to the specified target. + * @chainable + * @param Item_Model $target Target item (must be an album + * @return ORM_MTPP + */ + function move_to($target) { + if (!$target->is_album()) { + throw new Exception("@todo INVALID_MOVE_TYPE $target->type"); + } + + if ($this->id == 1) { + throw new Exception("@todo INVALID_SOURCE root album"); + } + + $original_path = $this->file_path(); + $original_resize_path = $this->resize_path(); + $original_thumb_path = $this->thumb_path(); + + parent::move_to($target, true); + $this->relative_path_cache = null; + + rename($original_path, $this->file_path()); + if ($this->is_album()) { + @rename(dirname($original_resize_path), dirname($this->resize_path())); + @rename(dirname($original_thumb_path), dirname($this->thumb_path())); + Database::instance() + ->update("items", + array("relative_path_cache" => null), + array("left >" => $this->left, "right <" => $this->right)); + } else { + @rename($original_resize_path, $this->resize_path()); + @rename($original_thumb_path, $this->thumb_path()); + } + + return $this; + } + + /** + * Rename the underlying file for this item to a new name. Move all the files. This requires a + * save. + * + * @chainable + */ + public function rename($new_name) { + if ($new_name == $this->name) { + return; + } + + if (strpos($new_name, "/")) { + throw new Exception("@todo NAME_CANNOT_CONTAIN_SLASH"); + } + + $old_relative_path = $this->relative_path(); + $new_relative_path = dirname($old_relative_path) . "/" . $new_name; + @rename(VARPATH . "albums/$old_relative_path", VARPATH . "albums/$new_relative_path"); + @rename(VARPATH . "resizes/$old_relative_path", VARPATH . "resizes/$new_relative_path"); + @rename(VARPATH . "thumbs/$old_relative_path", VARPATH . "thumbs/$new_relative_path"); + $this->name = $new_name; + + if ($this->is_album()) { + Database::instance() + ->update("items", + array("relative_path_cache" => null), + array("left >" => $this->left, "right <" => $this->right)); + } + + return $this; + } + + /** + * album: url::site("albums/2") + * photo: url::site("photos/3") + * + * @param string $query the query string (eg "show=3") + */ + public function url($query=array(), $full_uri=false) { + $url = ($full_uri ? url::abs_site("{$this->type}s/$this->id") + : url::site("{$this->type}s/$this->id")); + if ($query) { + $url .= "?$query"; + } + return $url; + } + + /** + * album: /var/albums/album1/album2 + * photo: /var/albums/album1/album2/photo.jpg + */ + public function file_path() { + return VARPATH . "albums/" . $this->relative_path(); + } + + /** + * album: http://example.com/gallery3/var/resizes/album1/ + * photo: http://example.com/gallery3/var/albums/album1/photo.jpg + */ + public function file_url($full_uri=false) { + return $full_uri ? + url::abs_file("var/albums/" . $this->relative_path()) : + url::file("var/albums/" . $this->relative_path()); + } + + /** + * album: /var/resizes/album1/.thumb.jpg + * photo: /var/albums/album1/photo.thumb.jpg + */ + public function thumb_path() { + $base = VARPATH . "thumbs/" . $this->relative_path(); + if ($this->is_photo()) { + return $base; + } else if ($this->is_album()) { + return $base . "/.album.jpg"; + } else if ($this->is_movie()) { + // Replace the extension with jpg + return preg_replace("/...$/", "jpg", $base); + } + } + + /** + * Return true if there is a thumbnail for this item. + */ + public function has_thumb() { + return $this->thumb_width && $this->thumb_height; + } + + /** + * album: http://example.com/gallery3/var/resizes/album1/.thumb.jpg + * photo: http://example.com/gallery3/var/albums/album1/photo.thumb.jpg + */ + public function thumb_url($full_uri=false) { + $cache_buster = "?m=" . $this->updated; + $base = ($full_uri ? + url::abs_file("var/thumbs/" . $this->relative_path()) : + url::file("var/thumbs/" . $this->relative_path())); + if ($this->is_photo()) { + return $base . $cache_buster; + } else if ($this->is_album()) { + return $base . "/.album.jpg" . $cache_buster; + } else if ($this->is_movie()) { + // Replace the extension with jpg + $base = preg_replace("/...$/", "jpg", $base); + return $base . $cache_buster; + } + } + + /** + * album: /var/resizes/album1/.resize.jpg + * photo: /var/albums/album1/photo.resize.jpg + */ + public function resize_path() { + return VARPATH . "resizes/" . $this->relative_path() . + ($this->is_album() ? "/.album.jpg" : ""); + } + + /** + * album: http://example.com/gallery3/var/resizes/album1/.resize.jpg + * photo: http://example.com/gallery3/var/albums/album1/photo.resize.jpg + */ + public function resize_url($full_uri=false) { + return ($full_uri ? + url::abs_file("var/resizes/" . $this->relative_path()) : + url::file("var/resizes/" . $this->relative_path())) . + ($this->is_album() ? "/.album.jpg" : ""); + } + + /** + * Return the relative path to this item's file. + * @return string + */ + public function relative_path() { + if (!isset($this->relative_path_cache)) { + $paths = array(); + foreach (Database::instance() + ->select("name") + ->from("items") + ->where("left <=", $this->left) + ->where("right >=", $this->right) + ->where("id <>", 1) + ->orderby("left", "ASC") + ->get() as $row) { + $paths[] = $row->name; + } + $this->relative_path_cache = implode($paths, "/"); + $this->save(); + } + return $this->relative_path_cache; + } + + /** + * @see ORM::__get() + */ + public function __get($column) { + if ($column == "owner") { + // This relationship depends on an outside module, which may not be present so handle + // failures gracefully. + try { + return model_cache::get("user", $this->owner_id); + } catch (Exception $e) { + return null; + } + } else { + return parent::__get($column); + } + } + + /** + * @see ORM::__set() + */ + public function __set($column, $value) { + if ($column == "name") { + // Clear the relative path as it is no longer valid. + $this->relative_path_cache = null; + } + parent::__set($column, $value); + } + + /** + * @see ORM::save() + */ + public function save() { + if (!empty($this->changed) && $this->changed != array("view_count" => "view_count")) { + $this->updated = time(); + if (!$this->loaded) { + $this->created = $this->updated; + $r = ORM::factory("item")->select("MAX(weight) as max_weight")->find(); + $this->weight = $r->max_weight + 1; + } + } + return parent::save(); + } + + /** + * Return the Item_Model representing the cover for this album. + * @return Item_Model or null if there's no cover + */ + public function album_cover() { + if (!$this->is_album()) { + return null; + } + + if (empty($this->album_cover_item_id)) { + return null; + } + + try { + return model_cache::get("item", $this->album_cover_item_id); + } catch (Exception $e) { + // It's possible (unlikely) that the item was deleted, if so keep going. + return null; + } + } + + /** + * Find the position of the given child id in this album. The resulting value is 1-indexed, so + * the first child in the album is at position 1. + */ + public function get_position($child_id) { + $result = Database::instance()->query(" + SELECT COUNT(*) AS position FROM {items} + WHERE parent_id = {$this->parent_id} + AND {$this->sort_column} <= (SELECT {$this->sort_column} + FROM {items} WHERE id = $child_id) + ORDER BY {$this->sort_column} {$this->sort_order}"); + + return $result->current()->position; + } + + /** + * Return an tag for the thumbnail. + * @param array $extra_attrs Extra attributes to add to the img tag + * @param int (optional) $max Maximum size of the thumbnail (default: null) + * @param boolean (optional) $center_vertically Center vertically (default: false) + * @return string + */ + public function thumb_tag($extra_attrs=array(), $max=null, $center_vertically=false) { + list ($height, $width) = $this->scale_dimensions($max); + if ($center_vertically && $max) { + // The constant is divide by 2 to calculate the file and 10 to convert to em + $margin_top = ($max - $height) / 20; + $extra_attrs["style"] = "margin-top: {$margin_top}em"; + $extra_attrs["title"] = $this->title; + } + $attrs = array_merge($extra_attrs, + array( + "src" => $this->thumb_url(), + "alt" => $this->title, + "width" => $width, + "height" => $height) + ); + // html::image forces an absolute url which we don't want + return ""; + } + + /** + * Calculate the largest width/height that fits inside the given maximum, while preserving the + * aspect ratio. + * @param int $max Maximum size of the largest dimension + * @return array + */ + public function scale_dimensions($max) { + $width = $this->thumb_width; + $height = $this->thumb_height; + + if ($height) { + if (isset($max)) { + if ($width > $height) { + $height = (int)($max * ($height / $width)); + $width = $max; + } else { + $width = (int)($max * ($width / $height)); + $height = $max; + } + } + } else { + // Missing thumbnail, can happen on albums with no photos yet. + // @todo we should enforce a placeholder for those albums. + $width = 0; + $height = 0; + } + return array($height, $width); + } + + /** + * Return an tag for the resize. + * @param array $extra_attrs Extra attributes to add to the img tag + * @return string + */ + public function resize_tag($extra_attrs) { + $attrs = array_merge($extra_attrs, + array("src" => $this->resize_url(), + "alt" => $this->title, + "width" => $this->resize_width, + "height" => $this->resize_height) + ); + // html::image forces an absolute url which we don't want + return ""; + } + + /** + * Return a flowplayer "; + } + + /** + * Return all of the children of this node, ordered by the defined sort order. + * + * @chainable + * @param integer SQL limit + * @param integer SQL offset + * @return array ORM + */ + function children($limit=null, $offset=0) { + return parent::children($limit, $offset, array($this->sort_column => $this->sort_order)); + } + + /** + * Return all of the children of the specified type, ordered by the defined sort order. + * @param integer SQL limit + * @param integer SQL offset + * @param string type to return + * @return object ORM_Iterator + */ + function descendants($limit=null, $offset=0, $type=null) { + return parent::descendants($limit, $offset, $type, + array($this->sort_column => $this->sort_order)); + } +} diff --git a/modules/gallery/models/log.php b/modules/gallery/models/log.php new file mode 100644 index 00000000..6734afb8 --- /dev/null +++ b/modules/gallery/models/log.php @@ -0,0 +1,22 @@ +context); + if (array_key_exists($key, $context)) { + return $context[$key]; + } else { + return $default; + } + } + + public function set($key, $value) { + $context = unserialize($this->context); + $context[$key] = $value; + $this->context = serialize($context); + } + + public function save() { + if (!empty($this->changed)) { + $this->updated = time(); + } + return parent::save(); + } + + public function owner() { + return user::lookup($this->owner_id); + } +} \ No newline at end of file diff --git a/modules/gallery/models/theme.php b/modules/gallery/models/theme.php new file mode 100644 index 00000000..f479fd5a --- /dev/null +++ b/modules/gallery/models/theme.php @@ -0,0 +1,21 @@ +where("name", "access_test")->find(); + if ($group->loaded) { + $group->delete(); + } + } catch (Exception $e) { } + + try { + access::delete_permission("access_test"); + } catch (Exception $e) { } + + try { + $user = ORM::factory("user")->where("name", "access_test")->find(); + if ($user->loaded) { + $user->delete(); + } + } catch (Exception $e) { } + } + + public function setup() { + user::set_active(user::guest()); + } + + public function groups_and_permissions_are_bound_to_columns_test() { + access::register_permission("access_test", "Access Test"); + $group = group::create("access_test"); + + // We have a new column for this perm / group combo + $fields = Database::instance()->list_fields("access_caches"); + $this->assert_true(array_key_exists("access_test_{$group->id}", $fields)); + + access::delete_permission("access_test"); + $group->delete(); + + // Now the column has gone away + $fields = Database::instance()->list_fields("access_caches"); + $this->assert_false(array_key_exists("access_test_{$group->id}", $fields)); + } + + public function adding_and_removing_items_adds_ands_removes_rows_test() { + $root = ORM::factory("item", 1); + $item = album::create($root, rand(), "test album"); + + // New rows exist + $this->assert_true(ORM::factory("access_cache")->where("item_id", $item->id)->find()->loaded); + $this->assert_true(ORM::factory("access_intent")->where("item_id", $item->id)->find()->loaded); + + // Delete the item + $item->delete(); + + // Rows are gone + $this->assert_false(ORM::factory("access_cache")->where("item_id", $item->id)->find()->loaded); + $this->assert_false(ORM::factory("access_intent")->where("item_id", $item->id)->find()->loaded); + } + + public function new_photos_inherit_parent_permissions_test() { + $root = ORM::factory("item", 1); + + $album = album::create($root, rand(), "test album"); + access::allow(group::everybody(), "view", $album); + + $photo = ORM::factory("item"); + $photo->type = "photo"; + $photo->add_to_parent($album); + access::add_item($photo); + + $this->assert_true($photo->__get("view_" . group::everybody()->id)); + } + + public function can_allow_deny_and_reset_intent_test() { + $root = ORM::factory("item", 1); + $album = album::create($root, rand(), "test album"); + $intent = ORM::factory("access_intent")->where("item_id", $album)->find(); + + // Allow + access::allow(group::everybody(), "view", $album); + $this->assert_same(access::ALLOW, $intent->reload()->view_1); + + // Deny + access::deny(group::everybody(), "view", $album); + $this->assert_same( + access::DENY, + ORM::factory("access_intent")->where("item_id", $album)->find()->view_1); + + // Allow again. If the initial value was allow, then the first Allow clause above may not + // have actually changed any values. + access::allow(group::everybody(), "view", $album); + $this->assert_same( + access::ALLOW, + ORM::factory("access_intent")->where("item_id", $album)->find()->view_1); + + access::reset(group::everybody(), "view", $album); + $this->assert_same( + null, + ORM::factory("access_intent")->where("item_id", $album)->find()->view_1); + } + + public function cant_reset_root_item_test() { + try { + access::reset(group::everybody(), "view", ORM::factory("item", 1)); + } catch (Exception $e) { + return; + } + $this->assert_true(false, "Should not be able to reset root intent"); + } + + public function can_view_item_test() { + $root = ORM::factory("item", 1); + access::allow(group::everybody(), "view", $root); + $this->assert_true(access::group_can(group::everybody(), "view", $root)); + } + + public function can_always_fails_on_unloaded_items_test() { + $root = ORM::factory("item", 1); + access::allow(group::everybody(), "view", $root); + $this->assert_true(access::group_can(group::everybody(), "view", $root)); + + $bogus = ORM::factory("item", -1); + $this->assert_false(access::group_can(group::everybody(), "view", $bogus)); + } + + public function cant_view_child_of_hidden_parent_test() { + $root = ORM::factory("item", 1); + $album = album::create($root, rand(), "test album"); + + $root->reload(); + access::deny(group::everybody(), "view", $root); + access::reset(group::everybody(), "view", $album); + + $album->reload(); + $this->assert_false(access::group_can(group::everybody(), "view", $album)); + } + + public function view_permissions_propagate_down_test() { + $root = ORM::factory("item", 1); + $album = album::create($root, rand(), "test album"); + + access::allow(group::everybody(), "view", $root); + access::reset(group::everybody(), "view", $album); + $album->reload(); + $this->assert_true(access::group_can(group::everybody(), "view", $album)); + } + + public function can_toggle_view_permissions_propagate_down_test() { + $root = ORM::factory("item", 1); + $album1 = album::create($root, rand(), "test album"); + $album2 = album::create($album1, rand(), "test album"); + $album3 = album::create($album2, rand(), "test album"); + $album4 = album::create($album3, rand(), "test album"); + + $album1->reload(); + $album2->reload(); + $album3->reload(); + $album4->reload(); + + access::allow(group::everybody(), "view", $root); + access::deny(group::everybody(), "view", $album1); + access::reset(group::everybody(), "view", $album2); + access::reset(group::everybody(), "view", $album3); + access::reset(group::everybody(), "view", $album4); + + $album4->reload(); + $this->assert_false(access::group_can(group::everybody(), "view", $album4)); + + access::allow(group::everybody(), "view", $album1); + $album4->reload(); + $this->assert_true(access::group_can(group::everybody(), "view", $album4)); + } + + public function revoked_view_permissions_cant_be_allowed_lower_down_test() { + $root = ORM::factory("item", 1); + $album1 = album::create($root, rand(), "test album"); + $album2 = album::create($album1, rand(), "test album"); + + $root->reload(); + access::deny(group::everybody(), "view", $root); + access::allow(group::everybody(), "view", $album2); + + $album1->reload(); + $this->assert_false(access::group_can(group::everybody(), "view", $album1)); + + $album2->reload(); + $this->assert_false(access::group_can(group::everybody(), "view", $album2)); + } + + public function can_edit_item_test() { + $root = ORM::factory("item", 1); + access::allow(group::everybody(), "edit", $root); + $this->assert_true(access::group_can(group::everybody(), "edit", $root)); + } + + public function non_view_permissions_propagate_down_test() { + $root = ORM::factory("item", 1); + $album = album::create($root, rand(), "test album"); + + access::allow(group::everybody(), "edit", $root); + access::reset(group::everybody(), "edit", $album); + $this->assert_true(access::group_can(group::everybody(), "edit", $album)); + } + + public function non_view_permissions_can_be_revoked_lower_down_test() { + $root = ORM::factory("item", 1); + $outer = album::create($root, rand(), "test album"); + $outer_photo = ORM::factory("item"); + $outer_photo->type = "photo"; + $outer_photo->add_to_parent($outer); + access::add_item($outer_photo); + + $inner = album::create($outer, rand(), "test album"); + $inner_photo = ORM::factory("item"); + $inner_photo->type = "photo"; + $inner_photo->add_to_parent($inner); + access::add_item($inner_photo); + + $outer->reload(); + $inner->reload(); + + access::allow(group::everybody(), "edit", $root); + access::deny(group::everybody(), "edit", $outer); + access::allow(group::everybody(), "edit", $inner); + + // Outer album is not editable, inner one is. + $this->assert_false(access::group_can(group::everybody(), "edit", $outer_photo)); + $this->assert_true(access::group_can(group::everybody(), "edit", $inner_photo)); + } + + public function i_can_edit_test() { + // Create a new user that belongs to no groups + $user = user::create("access_test", "Access Test", ""); + foreach ($user->groups as $group) { + $user->remove($group); + } + $user->save(); + user::set_active($user); + + // This user can't edit anything + $root = ORM::factory("item", 1); + $this->assert_false(access::can("edit", $root)); + + // Now add them to a group that has edit permission + $group = group::create("access_test"); + $group->add($user); + $group->save(); + access::allow($group, "edit", $root); + + $user = ORM::factory("user", $user->id); // reload() does not flush related columns + user::set_active($user); + + // And verify that the user can edit. + $this->assert_true(access::can("edit", $root)); + } + + public function everybody_view_permission_maintains_htaccess_files_test() { + $root = ORM::factory("item", 1); + $album = album::create($root, rand(), "test album"); + + $this->assert_false(file_exists($album->file_path() . "/.htaccess")); + + access::deny(group::everybody(), "view", $album); + $this->assert_true(file_exists($album->file_path() . "/.htaccess")); + + access::allow(group::everybody(), "view", $album); + $this->assert_false(file_exists($album->file_path() . "/.htaccess")); + + access::deny(group::everybody(), "view", $album); + $this->assert_true(file_exists($album->file_path() . "/.htaccess")); + + access::reset(group::everybody(), "view", $album); + $this->assert_false(file_exists($album->file_path() . "/.htaccess")); + } + + public function everybody_view_full_permission_maintains_htaccess_files_test() { + $root = ORM::factory("item", 1); + $album = album::create($root, rand(), "test album"); + + $this->assert_false(file_exists($album->file_path() . "/.htaccess")); + $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); + $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); + + access::deny(group::everybody(), "view_full", $album); + $this->assert_true(file_exists($album->file_path() . "/.htaccess")); + $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); + $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); + + access::allow(group::everybody(), "view_full", $album); + $this->assert_false(file_exists($album->file_path() . "/.htaccess")); + $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); + $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); + + access::deny(group::everybody(), "view_full", $album); + $this->assert_true(file_exists($album->file_path() . "/.htaccess")); + $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); + $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); + + access::reset(group::everybody(), "view_full", $album); + $this->assert_false(file_exists($album->file_path() . "/.htaccess")); + $this->assert_false(file_exists($album->resize_path() . "/.htaccess")); + $this->assert_false(file_exists($album->thumb_path() . "/.htaccess")); + } +} diff --git a/modules/gallery/tests/Album_Helper_Test.php b/modules/gallery/tests/Album_Helper_Test.php new file mode 100644 index 00000000..80afa8d1 --- /dev/null +++ b/modules/gallery/tests/Album_Helper_Test.php @@ -0,0 +1,87 @@ +assert_equal(VARPATH . "albums/$rand", $album->file_path()); + $this->assert_equal(VARPATH . "thumbs/$rand/.album.jpg", $album->thumb_path()); + $this->assert_true(is_dir(VARPATH . "thumbs/$rand"), "missing thumb dir"); + + // It's unclear that a resize makes sense for an album. But we have one. + $this->assert_equal(VARPATH . "resizes/$rand/.album.jpg", $album->resize_path()); + $this->assert_true(is_dir(VARPATH . "resizes/$rand"), "missing resizes dir"); + + $this->assert_equal(1, $album->parent_id); // MPTT tests will cover other hierarchy checks + $this->assert_equal($rand, $album->name); + $this->assert_equal($rand, $album->title); + $this->assert_equal($rand, $album->description); + } + + public function create_conflicting_album_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + $album1 = album::create($root, $rand, $rand, $rand); + $album2 = album::create($root, $rand, $rand, $rand); + $this->assert_true($album1->name != $album2->name); + } + + public function thumb_url_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + $album = album::create($root, $rand, $rand, $rand); + $this->assert_equal("http://./var/thumbs/$rand/.album.jpg", $album->thumb_url()); + } + + public function resize_url_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + $album = album::create($root, $rand, $rand, $rand); + $this->assert_equal("http://./var/resizes/$rand/.album.jpg", $album->resize_url()); + } + + public function create_album_shouldnt_allow_names_with_slash_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + try { + $album = album::create($root, $rand . "/", $rand, $rand); + } catch (Exception $e) { + // pass + return; + } + + $this->assert_true(false, "Shouldn't create an album with / in the name"); + } + + public function create_album_silently_trims_trailing_periods_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + try { + $album = album::create($root, $rand . "..", $rand, $rand); + } catch (Exception $e) { + $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); + return; + } + + $this->assert_true(false, "Shouldn't create an album with trailing . in the name"); + } +} diff --git a/modules/gallery/tests/Albums_Controller_Test.php b/modules/gallery/tests/Albums_Controller_Test.php new file mode 100644 index 00000000..ef1fac77 --- /dev/null +++ b/modules/gallery/tests/Albums_Controller_Test.php @@ -0,0 +1,76 @@ +_post = $_POST; + } + + public function teardown() { + $_POST = $this->_post; + } + + public function change_album_test() { + $controller = new Albums_Controller(); + $root = ORM::factory("item", 1); + $album = album::create($root, "test", "test", "test"); + $orig_name = $album->name; + + $_POST["dirname"] = "test"; + $_POST["name"] = "new name"; + $_POST["title"] = "new title"; + $_POST["description"] = "new description"; + $_POST["column"] = "weight"; + $_POST["direction"] = "ASC"; + $_POST["csrf"] = access::csrf_token(); + $_POST["_method"] = "put"; + access::allow(group::everybody(), "edit", $root); + + ob_start(); + $controller->_update($album); + $results = ob_get_contents(); + ob_end_clean(); + + $this->assert_equal( + json_encode(array("result" => "success", "location" => "http://./index.php/test")), + $results); + $this->assert_equal("new title", $album->title); + $this->assert_equal("new description", $album->description); + + // We don't change the name, yet. + $this->assert_equal($orig_name, $album->name); + } + + public function change_album_no_csrf_fails_test() { + $controller = new Albums_Controller(); + $root = ORM::factory("item", 1); + $album = album::create($root, "test", "test", "test"); + $_POST["name"] = "new name"; + $_POST["title"] = "new title"; + $_POST["description"] = "new description"; + access::allow(group::everybody(), "edit", $root); + + try { + $controller->_update($album); + $this->assert_true(false, "This should fail"); + } catch (Exception $e) { + // pass + } + } +} diff --git a/modules/gallery/tests/Core_Installer_Test.php b/modules/gallery/tests/Core_Installer_Test.php new file mode 100644 index 00000000..f7036286 --- /dev/null +++ b/modules/gallery/tests/Core_Installer_Test.php @@ -0,0 +1,50 @@ +assert_true(file_exists(VARPATH . "albums")); + $this->assert_true(file_exists(VARPATH . "resizes")); + } + + public function install_registers_core_module_test() { + $core = ORM::factory("module")->where("name", "core")->find(); + $this->assert_equal("core", $core->name); + + // This is probably too volatile to keep for long + $this->assert_equal(1, $core->version); + } + + public function install_creates_root_item_test() { + $max_right = ORM::factory("item") + ->select("MAX(`right`) AS `right`") + ->find()->right; + $root = ORM::factory('item')->find(1); + $this->assert_equal("Gallery", $root->title); + $this->assert_equal(1, $root->left); + $this->assert_equal($max_right, $root->right); + $this->assert_equal(null, $root->parent_id); + $this->assert_equal(1, $root->level); + } +} diff --git a/modules/gallery/tests/Database_Test.php b/modules/gallery/tests/Database_Test.php new file mode 100644 index 00000000..bd3d2f53 --- /dev/null +++ b/modules/gallery/tests/Database_Test.php @@ -0,0 +1,134 @@ +where("a", 1) + ->where("b", 2) + ->compile(); + $sql = str_replace("\n", " ", $sql); + $this->assert_same("SELECT * WHERE `a` = 1 AND `b` = 2", $sql); + } + + function compound_where_test() { + $sql = Database::instance() + ->where("outer1", 1) + ->open_paren() + ->where("inner1", 1) + ->orwhere("inner2", 2) + ->close_paren() + ->where("outer2", 2) + ->compile(); + $sql = str_replace("\n", " ", $sql); + $this->assert_same( + "SELECT * WHERE `outer1` = 1 AND (`inner1` = 1 OR `inner2` = 2) AND `outer2` = 2", + $sql); + } + + function group_first_test() { + $sql = Database::instance() + ->open_paren() + ->where("inner1", 1) + ->orwhere("inner2", 2) + ->close_paren() + ->where("outer1", 1) + ->where("outer2", 2) + ->compile(); + $sql = str_replace("\n", " ", $sql); + $this->assert_same( + "SELECT * WHERE (`inner1` = 1 OR `inner2` = 2) AND `outer1` = 1 AND `outer2` = 2", + $sql); + } + + function where_array_test() { + $sql = Database::instance() + ->where("outer1", 1) + ->open_paren() + ->where("inner1", 1) + ->orwhere(array("inner2" => 2, "inner3" => 3)) + ->close_paren() + ->compile(); + $sql = str_replace("\n", " ", $sql); + $this->assert_same( + "SELECT * WHERE `outer1` = 1 AND (`inner1` = 1 OR `inner2` = 2 OR `inner3` = 3)", + $sql); + } + + function notlike_test() { + $sql = Database::instance() + ->where("outer1", 1) + ->open_paren() + ->ornotlike("inner1", 1) + ->close_paren() + ->compile(); + $sql = str_replace("\n", " ", $sql); + $this->assert_same( + "SELECT * WHERE `outer1` = 1 OR ( `inner1` NOT LIKE '%1%')", + $sql); + } + + function prefix_replacement_test() { + $db = Database_For_Test::instance(); + $converted = $db->add_table_prefixes("CREATE TABLE IF NOT EXISTS {test_tables} ( + `id` int(9) NOT NULL auto_increment, + `name` varchar(32) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`name`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8"); + $expected = "CREATE TABLE IF NOT EXISTS g3test_test_tables ( + `id` int(9) NOT NULL auto_increment, + `name` varchar(32) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`name`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8"; + $this->assert_same($expected, $converted); + + $sql = "UPDATE {test_tables} SET `name` = '{test string}' " . + "WHERE `item_id` IN " . + " (SELECT `id` FROM {items} " . + " WHERE `left` >= 1 " . + " AND `right` <= 6)"; + $sql = $db->add_table_prefixes($sql); + + $expected = "UPDATE g3test_test_tables SET `name` = '{test string}' " . + "WHERE `item_id` IN " . + " (SELECT `id` FROM g3test_items " . + " WHERE `left` >= 1 " . + " AND `right` <= 6)"; + + $this->assert_same($expected, $sql); + } + + public function setup() { + } + + public function teardown() { + } + +} + +class Database_For_Test extends Database { + static function instance() { + $db = new Database_For_Test(); + $db->_table_names["{items}"] = "g3test_items"; + $db->config["table_prefix"] = "g3test_"; + return $db; + } +} diff --git a/modules/gallery/tests/Dir_Helper_Test.php b/modules/gallery/tests/Dir_Helper_Test.php new file mode 100644 index 00000000..46bb871c --- /dev/null +++ b/modules/gallery/tests/Dir_Helper_Test.php @@ -0,0 +1,32 @@ +assert_boolean(!file_exists($filename), "File not deleted"); + $this->assert_boolean(!file_exists($dirname), "Directory not deleted"); + } +} diff --git a/modules/gallery/tests/DrawForm_Test.php b/modules/gallery/tests/DrawForm_Test.php new file mode 100644 index 00000000..2c5aaba4 --- /dev/null +++ b/modules/gallery/tests/DrawForm_Test.php @@ -0,0 +1,84 @@ + "gTestGroupForm")); + $form->input("title")->label(t("Title")); + $form->textarea("description")->label(t("Text Area")); + $form->submit("")->value(t("Submit")); + $rendered = $form->__toString(); + + $expected = "
      \n" . + "\n" . + "
        \n" . + "
      • \n" . + " \n" . + " \n" . + "
      • \n" . + "
      • \n" . + " \n" . + " \n" . + "
      • \n" . + "
      • \n" . + " \n" . + "
      • \n" . + "
      \n" . + "
      \n"; + $this->assert_same($expected, $rendered); + } + + function group_test() { + $form = new Forge("test/controller", "", "post", array("id" => "gTestGroupForm")); + $group = $form->group("test_group")->label(t("Test Group")); + $group->input("title")->label(t("Title")); + $group->textarea("description")->label(t("Text Area")); + $group->submit("")->value(t("Submit")); + $rendered = $form->__toString(); + + $expected = "
      \n" . + "\n" . + "
      \n" . + " Test Group\n" . + "
        \n" . + "
      • \n" . + " \n" . + " \n" . + "
      • \n" . + "
      • \n" . + " \n" . + " \n" . + "
      • \n" . + "
      • \n" . + " \n" . + "
      • \n" . + "
      \n" . + "
      \n" . + "
      \n"; + $this->assert_same($expected, $rendered); + } + +} + diff --git a/modules/gallery/tests/File_Structure_Test.php b/modules/gallery/tests/File_Structure_Test.php new file mode 100644 index 00000000..1caa82ba --- /dev/null +++ b/modules/gallery/tests/File_Structure_Test.php @@ -0,0 +1,235 @@ +getPathname())) { + $this->assert_false( + preg_match('/\?\>\s*$/', file_get_contents($file)), + "{$file->getPathname()} ends in ?>"); + } + } + } + + public function view_files_correct_suffix_test() { + $dir = new GalleryCodeFilterIterator( + new RecursiveIteratorIterator(new RecursiveDirectoryIterator(DOCROOT))); + foreach ($dir as $file) { + if (strpos($file, "views")) { + $this->assert_true( + preg_match("#/views/.*?(\.html|mrss|txt)\.php$#", $file->getPathname()), + "{$file->getPathname()} should end in .{html,mrss,txt}.php"); + } + } + } + + public function no_windows_line_endings_test() { + $dir = new GalleryCodeFilterIterator( + new RecursiveIteratorIterator(new RecursiveDirectoryIterator(DOCROOT))); + foreach ($dir as $file) { + if (preg_match("/\.(php|css|html|js)$/", $file)) { + foreach (file($file) as $line) { + $this->assert_true(substr($line, -2) != "\r\n", "$file has windows style line endings"); + } + } + } + } + + private function _check_view_preamble($path, &$errors) { + // The preamble for views is a single line that prevents direct script access + if (strpos($path, SYSPATH) === 0) { + // Kohana preamble + $expected = "\n"; + } else { + // Gallery preamble + // @todo use the same preamble for both! + $expected = "\n"; + } + + $fp = fopen($path, "r"); + $actual = fgets($fp); + fclose($fp); + + if ($expected != $actual) { + $errors[] = "$path:1\n expected:\n\t$expected\n actual:\n\t$actual"; + } + } + + private function _check_php_preamble($path, &$errors) { + if (strpos($path, SYSPATH) === 0 || + strpos($path, MODPATH . "unit_test") === 0) { + // Kohana: we only care about the first line + $fp = fopen($path, "r"); + $actual = array(fgets($fp)); + fclose($fp); + $expected = array("_get_preamble($path); + $expected = array( + "getPathname(); + switch ($path) { + case DOCROOT . "installer/database_config.php": + case DOCROOT . "installer/init_var.php": + // Special case views + $this->_check_view_preamble($path, $errors); + break; + + case DOCROOT . "index.php": + case DOCROOT . "installer/index.php": + // Front controllers + break; + + case DOCROOT . "index.local.php": + // Special case optional file, not part of the codebase + break; + + default: + if (strpos($path, DOCROOT . "var/logs") === 0) { + continue; + } else if (preg_match("/views/", $path)) { + $this->_check_view_preamble($path, $errors); + } else { + $this->_check_php_preamble($path, $errors); + } + } + } + + if ($errors) { + $this->assert_false(true, "Preamble errors:\n" . join("\n", $errors)); + } + } + + public function no_tabs_in_our_code_test() { + $dir = new PhpCodeFilterIterator( + new GalleryCodeFilterIterator( + new RecursiveIteratorIterator( + new RecursiveDirectoryIterator(DOCROOT)))); + foreach ($dir as $file) { + $this->assert_false( + preg_match('/\t/', file_get_contents($file)), + "{$file->getPathname()} has tabs in it"); + } + } + + private function _get_preamble($file) { + $lines = file($file); + $copy = array(); + for ($i = 0; $i < count($lines); $i++) { + $copy[] = rtrim($lines[$i]); + if (!strncmp($lines[$i], ' */', 3)) { + return $copy; + } + } + return $copy; + } + + public function helpers_are_static_test() { + $dir = new PhpCodeFilterIterator( + new GalleryCodeFilterIterator( + new RecursiveIteratorIterator( + new RecursiveDirectoryIterator(DOCROOT)))); + foreach ($dir as $file) { + if (basename(dirname($file)) == "helpers") { + foreach (file($file) as $line) { + $this->assert_true( + !preg_match("/\sfunction\s.*\(/", $line) || + preg_match("/^\s*(private static function _|static function)/", $line), + "should be \"static function foo\" or \"private static function _foo\":\n" . + "$file\n$line\n"); + } + } + } + } +} + +class PhpCodeFilterIterator extends FilterIterator { + public function accept() { + $path_name = $this->getInnerIterator()->getPathName(); + return (substr($path_name, -4) == ".php" && + !(strpos($path_name, VARPATH) === 0)); + } +} + +class GalleryCodeFilterIterator extends FilterIterator { + public function accept() { + // Skip anything that we didn"t write + $path_name = $this->getInnerIterator()->getPathName(); + return !( + strpos($path_name, ".svn") || + strpos($path_name, "core/views/kohana_profiler.php") !== false || + strpos($path_name, DOCROOT . "test") !== false || + strpos($path_name, DOCROOT . "var") !== false || + strpos($path_name, MODPATH . "forge") !== false || + strpos($path_name, APPPATH . "views/kohana_error_page.php") !== false || + strpos($path_name, MODPATH . "gallery_unit_test/views/kohana_error_page.php") !== false || + strpos($path_name, MODPATH . "gallery_unit_test/views/kohana_unit_test_cli.php") !== false || + strpos($path_name, MODPATH . "unit_test") !== false || + strpos($path_name, MODPATH . "exif/lib") !== false || + strpos($path_name, MODPATH . "user/lib/PasswordHash") !== false || + strpos($path_name, DOCROOT . "lib/swfupload") !== false || + strpos($path_name, SYSPATH) !== false || + substr($path_name, -1, 1) == "~"); + } +} diff --git a/modules/gallery/tests/I18n_Test.php b/modules/gallery/tests/I18n_Test.php new file mode 100644 index 00000000..9010606a --- /dev/null +++ b/modules/gallery/tests/I18n_Test.php @@ -0,0 +1,108 @@ + 'en', + 'default_locale' => 'te_ST', + 'locale_dir' => VARPATH . 'locale/'); + $this->i18n = I18n::instance($config); + + ORM::factory("incoming_translation") + ->where("locale", "te_ST") + ->delete_all(); + + $messages_te_ST = array( + array('Hello world', 'Hallo Welt'), + array(array('one' => 'One item has been added', + 'other' => '%count elements have been added'), + array('one' => 'Ein Element wurde hinzugefuegt.', + 'other' => '%count Elemente wurden hinzugefuegt.')), + array('Hello %name, how are you today?', 'Hallo %name, wie geht es Dir heute?')); + + foreach ($messages_te_ST as $data) { + list ($message, $translation) = $data; + $entry = ORM::factory("incoming_translation"); + $entry->key = I18n::get_message_key($message); + $entry->message = serialize($message); + $entry->translation = serialize($translation); + $entry->locale = 'te_ST'; + $entry->revision = null; + $entry->save(); + } + } + + public function get_locale_test() { + $locale = $this->i18n->locale(); + $this->assert_equal("te_ST", $locale); + } + + public function set_locale_test() { + $this->i18n->locale("de_DE"); + $locale = $this->i18n->locale(); + $this->assert_equal("de_DE", $locale); + } + + public function translate_simple_test() { + $result = $this->i18n->translate('Hello world'); + $this->assert_equal('Hallo Welt', $result); + } + + public function translate_simple_root_fallback_test() { + $result = $this->i18n->translate('Hello world zzz'); + $this->assert_equal('Hello world zzz', $result); + } + + public function translate_plural_other_test() { + $result = $this->i18n->translate(array('one' => 'One item has been added', + 'other' => '%count elements have been added'), + array('count' => 5)); + $this->assert_equal('5 Elemente wurden hinzugefuegt.', $result); + } + + public function translate_plural_one_test() { + $result = $this->i18n->translate(array('one' => 'One item has been added', + 'other' => '%count elements have been added'), + array('count' => 1)); + $this->assert_equal('Ein Element wurde hinzugefuegt.', $result); + } + + public function translate_interpolate_test() { + $result = $this->i18n->translate('Hello %name, how are you today?', array('name' => 'John')); + $this->assert_equal('Hallo John, wie geht es Dir heute?', $result); + } + + public function translate_interpolate_missing_value_test() { + $result = $this->i18n->translate('Hello %name, how are you today?', array('foo' => 'bar')); + $this->assert_equal('Hallo %name, wie geht es Dir heute?', $result); + } + + public function translate_plural_zero_test() { + // te_ST has the same plural rules as en and de. + // For count 0, plural form "other" should be used. + $result = $this->i18n->translate(array('one' => 'One item has been added', + 'other' => '%count elements have been added'), + array('count' => 0)); + $this->assert_equal('0 Elemente wurden hinzugefuegt.', $result); + } +} \ No newline at end of file diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php new file mode 100644 index 00000000..615b8997 --- /dev/null +++ b/modules/gallery/tests/Item_Model_Test.php @@ -0,0 +1,143 @@ +assert_true(!empty($item->created)); + $this->assert_true(!empty($item->updated)); + } + + private function create_random_item() { + $item = ORM::factory("item"); + /* Set all required fields (values are irrelevant) */ + $item->name = rand(); + $item->type = "photo"; + return $item->add_to_parent(ORM::factory("item", 1)); + } + + public function updating_doesnt_change_created_date_test() { + $item = self::create_random_item(); + + // Force the creation date to something well known + $db = Database::instance(); + $db->update("items", array("created" => 0, "updated" => 0), array("id" => $item->id)); + $item->reload(); + $item->title = "foo"; // force a change + $item->save(); + + $this->assert_true(empty($item->created)); + $this->assert_true(!empty($item->updated)); + } + + public function updating_view_count_only_doesnt_change_updated_date_test() { + $item = self::create_random_item(); + $item->reload(); + $this->assert_same(0, $item->view_count); + + // Force the updated date to something well known + $db = Database::instance(); + $db->update("items", array("updated" => 0), array("id" => $item->id)); + $item->reload(); + $item->view_count++; + $item->save(); + + $this->assert_same(1, $item->view_count); + $this->assert_true(empty($item->updated)); + } + + public function move_photo_test() { + // Create a test photo + $item = self::create_random_item(); + + file_put_contents($item->thumb_path(), "thumb"); + file_put_contents($item->resize_path(), "resize"); + file_put_contents($item->file_path(), "file"); + + $original_name = $item->name; + $new_name = rand(); + + // Now rename it + $item->rename($new_name)->save(); + + // Expected: the name changed, the name is now baked into all paths, and all files were moved. + $this->assert_equal($new_name, $item->name); + $this->assert_equal($new_name, basename($item->file_path())); + $this->assert_equal($new_name, basename($item->thumb_path())); + $this->assert_equal($new_name, basename($item->resize_path())); + $this->assert_equal("thumb", file_get_contents($item->thumb_path())); + $this->assert_equal("resize", file_get_contents($item->resize_path())); + $this->assert_equal("file", file_get_contents($item->file_path())); + } + + public function move_album_test() { + // Create an album with a photo in it + $root = ORM::factory("item", 1); + $album = album::create($root, rand(), rand(), rand()); + $photo = ORM::factory("item"); + $photo->name = rand(); + $photo->type = "photo"; + $photo->add_to_parent($album); + + file_put_contents($photo->thumb_path(), "thumb"); + file_put_contents($photo->resize_path(), "resize"); + file_put_contents($photo->file_path(), "file"); + + $original_album_name = $album->name; + $original_photo_name = $photo->name; + $new_album_name = rand(); + + // Now rename the album + $album->rename($new_album_name)->save(); + $photo->reload(); + + // Expected: + // * the album name changed. + // * the album dirs are all moved + // * the photo's paths are all inside the albums paths + // * the photo files are all still intact and accessible + $this->assert_equal($new_album_name, $album->name); + $this->assert_equal($new_album_name, basename($album->file_path())); + $this->assert_equal($new_album_name, basename(dirname($album->thumb_path()))); + $this->assert_equal($new_album_name, basename(dirname($album->resize_path()))); + + $this->assert_same(0, strpos($photo->file_path(), $album->file_path())); + $this->assert_same(0, strpos($photo->thumb_path(), dirname($album->thumb_path()))); + $this->assert_same(0, strpos($photo->resize_path(), dirname($album->resize_path()))); + + $this->assert_equal("thumb", file_get_contents($photo->thumb_path())); + $this->assert_equal("resize", file_get_contents($photo->resize_path())); + $this->assert_equal("file", file_get_contents($photo->file_path())); + } + + public function item_rename_wont_accept_slash_test() { + // Create a test photo + $item = self::create_random_item(); + + $new_name = rand() . "/"; + + try { + $item->rename($new_name)->save(); + } catch (Exception $e) { + // pass + return; + } + $this->assert_false(true, "Item_Model::rename should not accept / characters"); + } +} diff --git a/modules/gallery/tests/Menu_Test.php b/modules/gallery/tests/Menu_Test.php new file mode 100644 index 00000000..c91aee0b --- /dev/null +++ b/modules/gallery/tests/Menu_Test.php @@ -0,0 +1,32 @@ +append(Menu::factory("link")->id("element_1")) + ->append(Menu::factory("dialog")->id("element_2")) + ->append(Menu::factory("submenu")->id("element_3") + ->append(Menu::factory("link")->id("element_3_1"))); + + $this->assert_equal("element_2", $menu->get("element_2")->id); + $this->assert_equal("element_3_1", $menu->get("element_3")->get("element_3_1")->id); + } +} \ No newline at end of file diff --git a/modules/gallery/tests/Movie_Helper_Test.php b/modules/gallery/tests/Movie_Helper_Test.php new file mode 100644 index 00000000..b92ef3f8 --- /dev/null +++ b/modules/gallery/tests/Movie_Helper_Test.php @@ -0,0 +1,46 @@ +assert_true(false, "Shouldn't create a movie with / in the name"); + } + + public function create_movie_shouldnt_allow_names_with_trailing_periods_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + try { + $movie = movie::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg.", $rand, $rand); + } catch (Exception $e) { + $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); + return; + } + + $this->assert_true(false, "Shouldn't create a movie with trailing . in the name"); + } +} diff --git a/modules/gallery/tests/ORM_MPTT_Test.php b/modules/gallery/tests/ORM_MPTT_Test.php new file mode 100644 index 00000000..200c8a74 --- /dev/null +++ b/modules/gallery/tests/ORM_MPTT_Test.php @@ -0,0 +1,221 @@ +type = "album"; + $album->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); + $album->sort_column = "weight"; + $album->sort_order = "ASC"; + $album->add_to_parent($root); + + $this->assert_equal($album->parent()->right - 2, $album->left); + $this->assert_equal($album->parent()->right - 1, $album->right); + $this->assert_equal($album->parent()->level + 1, $album->level); + $this->assert_equal($album->parent()->id, $album->parent_id); + } + + public function add_hierarchy_test() { + $root = ORM::factory("item", 1); + $album1 = self::create_item_and_add_to_parent($root); + $album1_1 = self::create_item_and_add_to_parent($album1); + $album1_2 = self::create_item_and_add_to_parent($album1); + $album1_1_1 = self::create_item_and_add_to_parent($album1_1); + $album1_1_2 = self::create_item_and_add_to_parent($album1_1); + + $album1->reload(); + $this->assert_equal(9, $album1->right - $album1->left); + + $album1_1->reload(); + $this->assert_equal(5, $album1_1->right - $album1_1->left); + } + + public function delete_hierarchy_test() { + $root = ORM::factory("item", 1); + $album1 = self::create_item_and_add_to_parent($root); + $album1_1 = self::create_item_and_add_to_parent($album1); + $album1_2 = self::create_item_and_add_to_parent($album1); + $album1_1_1 = self::create_item_and_add_to_parent($album1_1); + $album1_1_2 = self::create_item_and_add_to_parent($album1_1); + + $album1_1->delete(); + $album1->reload(); + + // Now album1 contains only album1_2 + $this->assert_equal(3, $album1->right - $album1->left); + } + + public function move_to_test() { + $root = ORM::factory("item", 1); + $album1 = album::create($root, "move_to_test_1", "move_to_test_1"); + $album1_1 = album::create($album1, "move_to_test_1_1", "move_to_test_1_1"); + $album1_2 = album::create($album1, "move_to_test_1_2", "move_to_test_1_2"); + $album1_1_1 = album::create($album1_1, "move_to_test_1_1_1", "move_to_test_1_1_1"); + $album1_1_2 = album::create($album1_1, "move_to_test_1_1_2", "move_to_test_1_1_2"); + + $album1_2->reload(); + $album1_1_1->reload(); + + $album1_1_1->move_to($album1_2); + + $album1_1->reload(); + $album1_2->reload(); + + $this->assert_equal(3, $album1_1->right - $album1_1->left); + $this->assert_equal(3, $album1_2->right - $album1_2->left); + + $this->assert_equal( + array($album1_1_2->id => "move_to_test_1_1_2"), + $album1_1->children()->select_list()); + + $this->assert_equal( + array($album1_1_1->id => "move_to_test_1_1_1"), + $album1_2->children()->select_list()); + } + + public function parent_test() { + $root = ORM::factory("item", 1); + $album = self::create_item_and_add_to_parent($root); + + $parent = ORM::factory("item", 1); + $this->assert_equal($parent->id, $album->parent()->id); + } + + public function parents_test() { + $root = ORM::factory("item", 1); + $outer = self::create_item_and_add_to_parent($root); + $inner = self::create_item_and_add_to_parent($outer); + + $parent_ids = array(); + foreach ($inner->parents() as $parent) { + $parent_ids[] = $parent->id; + } + $this->assert_equal(array(1, $outer->id), $parent_ids); + } + + public function children_test() { + $root = ORM::factory("item", 1); + $outer = self::create_item_and_add_to_parent($root); + $inner1 = self::create_item_and_add_to_parent($outer); + $inner2 = self::create_item_and_add_to_parent($outer); + + $child_ids = array(); + foreach ($outer->children() as $child) { + $child_ids[] = $child->id; + } + $this->assert_equal(array($inner1->id, $inner2->id), $child_ids); + } + + public function children_limit_test() { + $root = ORM::factory("item", 1); + $outer = self::create_item_and_add_to_parent($root); + $inner1 = self::create_item_and_add_to_parent($outer); + $inner2 = self::create_item_and_add_to_parent($outer); + + $this->assert_equal(array($inner2->id => $inner2->name), + $outer->children(1, 1)->select_list('id')); + } + + public function children_count_test() { + $root = ORM::factory("item", 1); + $outer = self::create_item_and_add_to_parent($root); + $inner1 = self::create_item_and_add_to_parent($outer); + $inner2 = self::create_item_and_add_to_parent($outer); + + $this->assert_equal(2, $outer->children_count()); + } + + public function descendant_test() { + $root = ORM::factory("item", 1); + + $parent = ORM::factory("item"); + $parent->type = "album"; + $parent->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); + $parent->sort_column = "weight"; + $parent->sort_order = "ASC"; + $parent->add_to_parent($root); + + $photo = ORM::factory("item"); + $photo->type = "photo"; + $photo->add_to_parent($parent); + + $album1 = ORM::factory("item"); + $album1->type = "album"; + $album1->rand_key = ((float)mt_rand()) / (float)mt_getrandmax(); + $album1->sort_column = "weight"; + $album1->sort_order = "ASC"; + $album1->add_to_parent($parent); + + $photo1 = ORM::factory("item"); + $photo1->type = "photo"; + $photo1->add_to_parent($album1); + + $parent->reload(); + + $this->assert_equal(3, $parent->descendants()->count()); + $this->assert_equal(2, $parent->descendants(null, 0, "photo")->count()); + $this->assert_equal(1, $parent->descendants(null, 0, "album")->count()); + } + + public function descendant_limit_test() { + $root = ORM::factory("item", 1); + + $parent = self::create_item_and_add_to_parent($root); + $album1 = self::create_item_and_add_to_parent($parent); + $album2 = self::create_item_and_add_to_parent($parent); + $album3 = self::create_item_and_add_to_parent($parent); + + $parent->reload(); + $this->assert_equal(2, $parent->descendants(2)->count()); + } + + public function descendant_count_test() { + $root = ORM::factory("item", 1); + + $parent = ORM::factory("item"); + $parent->type = "album"; + $parent->add_to_parent($root); + + $photo = ORM::factory("item"); + $photo->type = "photo"; + $photo->add_to_parent($parent); + + $album1 = ORM::factory("item"); + $album1->type = "album"; + $album1->add_to_parent($parent); + + $photo1 = ORM::factory("item"); + $photo1->type = "photo"; + $photo1->add_to_parent($album1); + + $parent->reload(); + + $this->assert_equal(3, $parent->descendants_count()); + $this->assert_equal(2, $parent->descendants_count("photo")); + $this->assert_equal(1, $parent->descendants_count("album")); + } +} diff --git a/modules/gallery/tests/Photo_Helper_Test.php b/modules/gallery/tests/Photo_Helper_Test.php new file mode 100644 index 00000000..deb11bb9 --- /dev/null +++ b/modules/gallery/tests/Photo_Helper_Test.php @@ -0,0 +1,109 @@ +assert_equal(VARPATH . "albums/$rand.jpg", $photo->file_path()); + $this->assert_equal(VARPATH . "thumbs/{$rand}.jpg", $photo->thumb_path()); + $this->assert_equal(VARPATH . "resizes/{$rand}.jpg", $photo->resize_path()); + + $this->assert_true(is_file($photo->file_path()), "missing: {$photo->file_path()}"); + $this->assert_true(is_file($photo->resize_path()), "missing: {$photo->resize_path()}"); + $this->assert_true(is_file($photo->thumb_path()), "missing: {$photo->thumb_path()}"); + + $this->assert_equal($root->id, $photo->parent_id); // MPTT tests cover other hierarchy checks + $this->assert_equal("$rand.jpg", $photo->name); + $this->assert_equal($rand, $photo->title); + $this->assert_equal($rand, $photo->description); + $this->assert_equal("image/jpeg", $photo->mime_type); + $this->assert_equal($image_info[0], $photo->width); + $this->assert_equal($image_info[1], $photo->height); + + $this->assert_equal($photo->parent()->right - 2, $photo->left); + $this->assert_equal($photo->parent()->right - 1, $photo->right); + } + + public function create_conflicting_photo_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + $photo1 = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); + $photo2 = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); + $this->assert_true($photo1->name != $photo2->name); + } + + public function create_photo_with_no_extension_test() { + $root = ORM::factory("item", 1); + try { + photo::create($root, "/tmp", "name", "title", "description"); + $this->assert_false("should fail with an exception"); + } catch (Exception $e) { + // pass + } + } + + public function thumb_url_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); + $this->assert_equal("http://./var/thumbs/{$rand}.jpg", $photo->thumb_url()); + } + + public function resize_url_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + $album = album::create($root, $rand, $rand, $rand); + $photo = photo::create($album, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); + + $this->assert_equal("http://./var/resizes/{$rand}/{$rand}.jpg", $photo->resize_url()); + } + + public function create_photo_shouldnt_allow_names_with_slash_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + try { + $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand/.jpg", $rand, $rand); + } catch (Exception $e) { + // pass + return; + } + + $this->assert_true(false, "Shouldn't create a photo with / in the name"); + } + + public function create_photo_silently_trims_trailing_periods_test() { + $rand = rand(); + $root = ORM::factory("item", 1); + try { + $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg.", $rand, $rand); + } catch (Exception $e) { + $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); + return; + } + + $this->assert_true(false, "Shouldn't create a photo with trailing . in the name"); + } +} diff --git a/modules/gallery/tests/Photos_Controller_Test.php b/modules/gallery/tests/Photos_Controller_Test.php new file mode 100644 index 00000000..71319315 --- /dev/null +++ b/modules/gallery/tests/Photos_Controller_Test.php @@ -0,0 +1,74 @@ +_post = $_POST; + } + + public function teardown() { + $_POST = $this->_post; + } + + public function change_photo_test() { + $controller = new Photos_Controller(); + $root = ORM::factory("item", 1); + $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "test.jpeg", "test", "test"); + $orig_name = $photo->name; + + $_POST["filename"] = "test.jpeg"; + $_POST["name"] = "new name"; + $_POST["title"] = "new title"; + $_POST["description"] = "new description"; + $_POST["csrf"] = access::csrf_token(); + access::allow(group::everybody(), "edit", $root); + + ob_start(); + $controller->_update($photo); + $results = ob_get_contents(); + ob_end_clean(); + + $this->assert_equal( + json_encode(array("result" => "success", + "location" => "http://./index.php/test.jpeg")), + $results); + $this->assert_equal("new title", $photo->title); + $this->assert_equal("new description", $photo->description); + + // We don't change the name, yet. + $this->assert_equal($orig_name, $photo->name); + } + + public function change_photo_no_csrf_fails_test() { + $controller = new Photos_Controller(); + $root = ORM::factory("item", 1); + $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "test", "test", "test"); + $_POST["name"] = "new name"; + $_POST["title"] = "new title"; + $_POST["description"] = "new description"; + access::allow(group::everybody(), "edit", $root); + + try { + $controller->_update($photo); + $this->assert_true(false, "This should fail"); + } catch (Exception $e) { + // pass + } + } +} diff --git a/modules/gallery/tests/REST_Controller_Test.php b/modules/gallery/tests/REST_Controller_Test.php new file mode 100644 index 00000000..8fb04d86 --- /dev/null +++ b/modules/gallery/tests/REST_Controller_Test.php @@ -0,0 +1,197 @@ +_post = $_POST; + $this->mock_controller = new Mock_RESTful_Controller("mock"); + $this->mock_not_loaded_controller = new Mock_RESTful_Controller("mock_not_loaded"); + $_POST = array(); + } + + public function teardown() { + $_POST = $this->_post; + } + + public function dispatch_index_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + $_POST["_method"] = ""; + $this->mock_controller->__call("index", ""); + $this->assert_equal("index", $this->mock_controller->method_called); + } + + public function dispatch_show_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + $_POST["_method"] = ""; + $this->mock_controller->__call("3", ""); + $this->assert_equal("show", $this->mock_controller->method_called); + $this->assert_equal("Mock_Model", get_class($this->mock_controller->resource)); + } + + public function dispatch_update_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_POST["_method"] = "PUT"; + $_POST["csrf"] = access::csrf_token(); + $this->mock_controller->__call("3", ""); + $this->assert_equal("update", $this->mock_controller->method_called); + $this->assert_equal("Mock_Model", get_class($this->mock_controller->resource)); + } + + public function dispatch_update_fails_without_csrf_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_POST["_method"] = "PUT"; + try { + $this->mock_controller->__call("3", ""); + $this->assert_false(true, "this should fail with a forbidden exception"); + } catch (Exception $e) { + // pass + } + } + + public function dispatch_delete_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_POST["_method"] = "DELETE"; + $_POST["csrf"] = access::csrf_token(); + $this->mock_controller->__call("3", ""); + $this->assert_equal("delete", $this->mock_controller->method_called); + $this->assert_equal("Mock_Model", get_class($this->mock_controller->resource)); + } + + public function dispatch_delete_fails_without_csrf_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_POST["_method"] = "DELETE"; + try { + $this->mock_controller->__call("3", ""); + $this->assert_false(true, "this should fail with a forbidden exception"); + } catch (Exception $e) { + // pass + } + } + + public function dispatch_404_test() { + /* The dispatcher should throw a 404 if the resource isn't loaded and the method isn't POST. */ + $methods = array( + array("GET", ""), + array("POST", "PUT"), + array("POST", "DELETE")); + + foreach ($methods as $method) { + $_SERVER["REQUEST_METHOD"] = $method[0]; + $_POST["_method"] = $method[1]; + $exception_caught = false; + try { + $this->mock_not_loaded_controller->__call(rand(), ""); + } catch (Kohana_404_Exception $e) { + $exception_caught = true; + } + $this->assert_true($exception_caught, "$method[0], $method[1]"); + } + } + + public function dispatch_create_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_POST["_method"] = ""; + $_POST["csrf"] = access::csrf_token(); + $this->mock_not_loaded_controller->__call("", ""); + $this->assert_equal("create", $this->mock_not_loaded_controller->method_called); + $this->assert_equal( + "Mock_Not_Loaded_Model", get_class($this->mock_not_loaded_controller->resource)); + } + + public function dispatch_create_fails_without_csrf_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_POST["_method"] = ""; + try { + $this->mock_not_loaded_controller->__call("", ""); + $this->assert_false(true, "this should fail with a forbidden exception"); + } catch (Exception $e) { + // pass + } + } + + public function dispatch_form_test_add() { + $this->mock_controller->form_add("args"); + $this->assert_equal("form_add", $this->mock_controller->method_called); + $this->assert_equal("args", $this->mock_controller->resource); + } + + public function dispatch_form_test_edit() { + $this->mock_controller->form_edit("1"); + $this->assert_equal("form_edit", $this->mock_controller->method_called); + $this->assert_equal("Mock_Model", get_class($this->mock_controller->resource)); + } + + public function routes_test() { + $this->assert_equal("mock/form_add/args", router::routed_uri("form/add/mock/args")); + $this->assert_equal("mock/form_edit/args", router::routed_uri("form/edit/mock/args")); + $this->assert_equal(null, router::routed_uri("rest/args")); + } +} + +class Mock_RESTful_Controller extends REST_Controller { + public $method_called; + public $resource; + + public function __construct($type) { + $this->resource_type = $type; + parent::__construct(); + } + + public function _index() { + $this->method_called = "index"; + } + + public function _create($resource) { + $this->method_called = "create"; + $this->resource = $resource; + } + + public function _show($resource) { + $this->method_called = "show"; + $this->resource = $resource; + } + + public function _update($resource) { + $this->method_called = "update"; + $this->resource = $resource; + } + + public function _delete($resource) { + $this->method_called = "delete"; + $this->resource = $resource; + } + + public function _form_add($args) { + $this->method_called = "form_add"; + $this->resource = $args; + } + + public function _form_edit($resource) { + $this->method_called = "form_edit"; + $this->resource = $resource; + } +} + +class Mock_Model { + public $loaded = true; +} + +class Mock_Not_Loaded_Model { + public $loaded = false; +} diff --git a/modules/gallery/tests/REST_Helper_Test.php b/modules/gallery/tests/REST_Helper_Test.php new file mode 100644 index 00000000..1bfc63ab --- /dev/null +++ b/modules/gallery/tests/REST_Helper_Test.php @@ -0,0 +1,45 @@ +_post = $_POST; + } + + public function teardown() { + $_POST = $this->_post; + } + + public function request_method_test() { + foreach (array("GET", "POST") as $method) { + foreach (array("", "PUT", "DELETE") as $tunnel) { + if ($method == "GET") { + $expected = "GET"; + } else { + $expected = $tunnel == "" ? $method : $tunnel; + } + $_SERVER["REQUEST_METHOD"] = $method; + $_POST["_method"] = $tunnel; + + $this->assert_equal(strtolower(rest::request_method()), strtolower($expected), + "Request method: {$method}, tunneled: {$tunnel}"); + } + } + } +} diff --git a/modules/gallery/tests/Sendmail_Test.php b/modules/gallery/tests/Sendmail_Test.php new file mode 100644 index 00000000..64c1fff0 --- /dev/null +++ b/modules/gallery/tests/Sendmail_Test.php @@ -0,0 +1,115 @@ +to("receiver@someemail.com") + /* + * @todo figure out why this test fails so badly, when the following + * line is not supplied. It doesn't seem to be set by setup method + * as you would expect. + */ + ->from("from@gallery3.com") + ->subject("Test Email Unit test") + ->message("The mail message body") + ->send() + ->send_text; + + $this->assert_equal($expected, $result); + } + + public function sendmail_reply_to_test() { + $expected = "To: receiver@someemail.com\r\n" . + "From: from@gallery3.com\n" . + "Reply-To: reply-to@gallery3.com\r\n" . + "Subject: Test Email Unit test\r\n\r\n" . + "The mail message body"; + $result = Sendmail_For_Test::factory() + ->to("receiver@someemail.com") + ->subject("Test Email Unit test") + ->reply_to("reply-to@gallery3.com") + ->message("The mail message body") + ->send() + ->send_text; + $this->assert_equal($expected, $result); + } + + public function sendmail_html_message_test() { + $expected = "To: receiver@someemail.com\r\n" . + "From: from@gallery3.com\n" . + "Reply-To: public@gallery3.com\n" . + "MIME-Version: 1.0\n" . + "Content-type: text/html; charset=iso-8859-1\r\n" . + "Subject: Test Email Unit test\r\n\r\n" . + "

      This is an html msg

      "; + $result = Sendmail_For_Test::factory() + ->to("receiver@someemail.com") + ->subject("Test Email Unit test") + ->header("MIME-Version", "1.0") + ->header("Content-type", "text/html; charset=iso-8859-1") + ->message("

      This is an html msg

      ") + ->send() + ->send_text; + $this->assert_equal($expected, $result); + } + + public function sendmail_wrapped_message_test() { + $expected = "To: receiver@someemail.com\r\n" . + "From: from@gallery3.com\n" . + "Reply-To: public@gallery3.com\r\n" . + "Subject: Test Email Unit test\r\n\r\n" . + "This is a long message that needs to go\n" . + "over forty characters If we get lucky we\n" . + "might make it long enought to wrap a\n" . + "couple of times."; + $result = Sendmail_For_Test::factory() + ->to("receiver@someemail.com") + ->subject("Test Email Unit test") + ->line_length(40) + ->message("This is a long message that needs to go over forty characters " . + "If we get lucky we might make it long enought to wrap a couple " . + "of times.") + ->send() + ->send_text; + $this->assert_equal($expected, $result); + } +} + +class Sendmail_For_Test extends Sendmail { + static function factory() { + return new Sendmail_For_Test(); + } + + public function mail($to, $subject, $message, $headers) { + $this->send_text = "To: $to\r\n{$headers}\r\nSubject: $this->subject\r\n\r\n$message"; + return true; + } +} \ No newline at end of file diff --git a/modules/gallery/tests/Var_Test.php b/modules/gallery/tests/Var_Test.php new file mode 100644 index 00000000..82370631 --- /dev/null +++ b/modules/gallery/tests/Var_Test.php @@ -0,0 +1,49 @@ +assert_equal("original value", module::get_var("core", "Parameter")); + + module::set_var("core", "Parameter", "updated value"); + $this->assert_equal("updated value", module::get_var("core", "Parameter")); + } + + public function clear_parameter_test() { + module::set_var("core", "Parameter", "original value"); + $this->assert_equal("original value", module::get_var("core", "Parameter")); + + module::clear_var("core", "Parameter"); + $this->assert_equal(null, module::get_var("core", "Parameter")); + } + + public function incr_parameter_test() { + module::set_var("core", "Parameter", "original value"); + module::incr_var("core", "Parameter"); + $this->assert_equal("1", module::get_var("core", "Parameter")); + + module::set_var("core", "Parameter", "2"); + module::incr_var("core", "Parameter", "9"); + $this->assert_equal("11", module::get_var("core", "Parameter")); + + module::incr_var("core", "NonExistent", "9"); + $this->assert_equal(null, module::get_var("core", "NonExistent")); + } +} \ No newline at end of file diff --git a/modules/gallery/tests/images/DSC_0003.jpg b/modules/gallery/tests/images/DSC_0003.jpg new file mode 100644 index 00000000..5780d9d8 Binary files /dev/null and b/modules/gallery/tests/images/DSC_0003.jpg differ diff --git a/modules/gallery/tests/images/DSC_0005.jpg b/modules/gallery/tests/images/DSC_0005.jpg new file mode 100644 index 00000000..4d2b53a9 Binary files /dev/null and b/modules/gallery/tests/images/DSC_0005.jpg differ diff --git a/modules/gallery/tests/images/DSC_0017.jpg b/modules/gallery/tests/images/DSC_0017.jpg new file mode 100644 index 00000000..b7f7bb90 Binary files /dev/null and b/modules/gallery/tests/images/DSC_0017.jpg differ diff --git a/modules/gallery/tests/images/DSC_0019.jpg b/modules/gallery/tests/images/DSC_0019.jpg new file mode 100644 index 00000000..0ce25aa4 Binary files /dev/null and b/modules/gallery/tests/images/DSC_0019.jpg differ diff --git a/modules/gallery/tests/images/DSC_0067.jpg b/modules/gallery/tests/images/DSC_0067.jpg new file mode 100644 index 00000000..84f134cb Binary files /dev/null and b/modules/gallery/tests/images/DSC_0067.jpg differ diff --git a/modules/gallery/tests/images/DSC_0072.jpg b/modules/gallery/tests/images/DSC_0072.jpg new file mode 100644 index 00000000..dfad82b0 Binary files /dev/null and b/modules/gallery/tests/images/DSC_0072.jpg differ diff --git a/modules/gallery/tests/images/P4050088.jpg b/modules/gallery/tests/images/P4050088.jpg new file mode 100644 index 00000000..62f4749d Binary files /dev/null and b/modules/gallery/tests/images/P4050088.jpg differ diff --git a/modules/gallery/tests/selenium/Add_Album.html b/modules/gallery/tests/selenium/Add_Album.html new file mode 100644 index 00000000..ccd4d0b7 --- /dev/null +++ b/modules/gallery/tests/selenium/Add_Album.html @@ -0,0 +1,52 @@ + + + + + + +AddAlbum + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      AddAlbum
      open/index.php/albums/1
      clicklink=Add album
      typenameseleniumtest
      typetitleSelenium Test Album
      typedescriptionTest
      click//button[@type='button']
      assertTextPresentSelenium Test Album
      + + diff --git a/modules/gallery/tests/selenium/Add_Comment.html b/modules/gallery/tests/selenium/Add_Comment.html new file mode 100644 index 00000000..b4b96ed2 --- /dev/null +++ b/modules/gallery/tests/selenium/Add_Comment.html @@ -0,0 +1,52 @@ + + + + + + +Add comment + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Add comment
      open/index.php/albums/1
      clickAndWaitgPhotoId-2
      typegAuthorTest
      typegEmailtest@gmail.com
      typegTextThis is a selenium test comment.
      click//button[@type='submit']
      assertTextPresentThis is a selenium test comment.
      + + diff --git a/modules/gallery/tests/selenium/Add_Item.html b/modules/gallery/tests/selenium/Add_Item.html new file mode 100644 index 00000000..741dff65 --- /dev/null +++ b/modules/gallery/tests/selenium/Add_Item.html @@ -0,0 +1,62 @@ + + + + + + +AddItem + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      AddItem
      open/index.php/albums/1
      clicklink=Add an item
      typenameseleniumitem.jpg
      typetitleSelenium Item
      typedescriptionTest item
      typefile/Users/ckieffer/Sites/gallery3.0/core/tests/images/DSC_0003.jpg
      click//button[@type='button']
      clicklink=X
      assertTextPresentSelenium Item
      + + diff --git a/modules/gallery/tests/selenium/Login.html b/modules/gallery/tests/selenium/Login.html new file mode 100644 index 00000000..5e17a3c7 --- /dev/null +++ b/modules/gallery/tests/selenium/Login.html @@ -0,0 +1,47 @@ + + + + + + +Login + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Login
      open/index.php/albums/1
      clickgLoginLink
      typegNameadmin
      typegPasswordadmin
      clickAndWait//button[@type='button']
      clickAndWaitgUserProfileLink
      + + diff --git a/modules/gallery/tests/test.jpg b/modules/gallery/tests/test.jpg new file mode 100644 index 00000000..1f3525e5 Binary files /dev/null and b/modules/gallery/tests/test.jpg differ diff --git a/modules/gallery/views/admin_advanced_settings.html.php b/modules/gallery/views/admin_advanced_settings.html.php new file mode 100644 index 00000000..1f3825bd --- /dev/null +++ b/modules/gallery/views/admin_advanced_settings.html.php @@ -0,0 +1,34 @@ + +
      +

      +

      + +

      +
        +
      • + ") ?> +
      • +
      + + + + + + + + + module_name == "core" && $var->name == "_cache") continue ?> + + + + + + +
      module_name ?> name ?> + module_name/$var->name") ?>" + class="gDialogLink" + title=" $var->name, "module_name" => $var->module_name)) ?>"> + value ?> + +
      +
      diff --git a/modules/gallery/views/admin_block_log_entries.html.php b/modules/gallery/views/admin_block_log_entries.html.php new file mode 100644 index 00000000..db6313e1 --- /dev/null +++ b/modules/gallery/views/admin_block_log_entries.html.php @@ -0,0 +1,11 @@ + + diff --git a/modules/gallery/views/admin_block_news.html.php b/modules/gallery/views/admin_block_news.html.php new file mode 100644 index 00000000..cb276ae5 --- /dev/null +++ b/modules/gallery/views/admin_block_news.html.php @@ -0,0 +1,11 @@ + +
        + +
      • + "> +

        + +

        +
      • + +
      diff --git a/modules/gallery/views/admin_block_photo_stream.html.php b/modules/gallery/views/admin_block_photo_stream.html.php new file mode 100644 index 00000000..e8a4d933 --- /dev/null +++ b/modules/gallery/views/admin_block_photo_stream.html.php @@ -0,0 +1,14 @@ + + +

      + +

      diff --git a/modules/gallery/views/admin_block_platform.html.php b/modules/gallery/views/admin_block_platform.html.php new file mode 100644 index 00000000..6b79f047 --- /dev/null +++ b/modules/gallery/views/admin_block_platform.html.php @@ -0,0 +1,18 @@ + +
        +
      • + PHP_OS)) ?> +
      • +
      • + function_exists("apache_get_version") ? apache_get_version() : t("Unknown"))) ?> +
      • +
      • + phpversion())) ?> +
      • +
      • + Database::instance()->query("SELECT version() as v")->current()->v)) ?> +
      • +
      • + $load_average)) ?> +
      • +
      diff --git a/modules/gallery/views/admin_block_stats.html.php b/modules/gallery/views/admin_block_stats.html.php new file mode 100644 index 00000000..2d975073 --- /dev/null +++ b/modules/gallery/views/admin_block_stats.html.php @@ -0,0 +1,12 @@ + +
        +
      • + module::get_var("core", "version"))) ?> +
      • +
      • + $album_count)) ?> +
      • +
      • + $photo_count)) ?> +
      • +
      diff --git a/modules/gallery/views/admin_block_welcome.html.php b/modules/gallery/views/admin_block_welcome.html.php new file mode 100644 index 00000000..488fa908 --- /dev/null +++ b/modules/gallery/views/admin_block_welcome.html.php @@ -0,0 +1,20 @@ + +

      + +

      +
        +
      • + graphics and language settings.", + array("graphics_url" => url::site("admin/graphics"), + "language_url" => url::site("admin/languages"))) ?> +
      • +
      • + choose a theme, or customize the way it looks.", + array("theme_url" => url::site("admin/theme"), + "theme_details_url" => url::site("admin/theme_details"))) ?> +
      • +
      • + install modules to add cool features!", + array("modules_url" => url::site("admin/modules"))) ?> +
      • +
      diff --git a/modules/gallery/views/admin_dashboard.html.php b/modules/gallery/views/admin_dashboard.html.php new file mode 100644 index 00000000..c266d7e1 --- /dev/null +++ b/modules/gallery/views/admin_dashboard.html.php @@ -0,0 +1,38 @@ + + +
      + +
      diff --git a/modules/gallery/views/admin_graphics.html.php b/modules/gallery/views/admin_graphics.html.php new file mode 100644 index 00000000..08374471 --- /dev/null +++ b/modules/gallery/views/admin_graphics.html.php @@ -0,0 +1,28 @@ + + +
      +

      +

      + +

      + +

      + + +
      +

      + +
      +
      + diff --git a/modules/gallery/views/admin_graphics_gd.html.php b/modules/gallery/views/admin_graphics_gd.html.php new file mode 100644 index 00000000..cae68b74 --- /dev/null +++ b/modules/gallery/views/admin_graphics_gd.html.php @@ -0,0 +1,29 @@ + +
      gd["GD Version"] ? " gInstalledToolkit" : " gUnavailable" ?>"> + " alt="" /> +

      +

      + GD website for more information.", + array("url" => "http://www.boutell.com/gd")) ?> +

      + gd["GD Version"] && function_exists('imagerotate')): ?> +

      + $tk->gd["GD Version"])) ?> +

      +

      + +

      + gd["GD Version"]): ?> +

      + $tk->gd["GD Version"])) ?> +

      +

      + +

      + +

      + +

      + +
      diff --git a/modules/gallery/views/admin_graphics_graphicsmagick.html.php b/modules/gallery/views/admin_graphics_graphicsmagick.html.php new file mode 100644 index 00000000..720a9459 --- /dev/null +++ b/modules/gallery/views/admin_graphics_graphicsmagick.html.php @@ -0,0 +1,21 @@ + +
      graphicsmagick ? " gInstalledToolkit" : " gUnavailable" ?>"> +

      + " alt="" /> +

      + GraphicsMagick website for more information.", + array("url" => "http://www.graphicsmagick.org")) ?> +

      + graphicsmagick): ?> +

      + $tk->graphicsmagick)) ?> +

      +

      + +

      + +

      + +

      + +
      diff --git a/modules/gallery/views/admin_graphics_imagemagick.html.php b/modules/gallery/views/admin_graphics_imagemagick.html.php new file mode 100644 index 00000000..c7468eed --- /dev/null +++ b/modules/gallery/views/admin_graphics_imagemagick.html.php @@ -0,0 +1,21 @@ + +
      imagemagick ? " gInstalledToolkit" : " gUnavailable" ?>"> +

      + " alt="" /> +

      + ImageMagick website for more information.", + array("url" => "http://www.imagemagick.org")) ?> +

      + imagemagick): ?> +

      + $tk->imagemagick)) ?> +

      +

      + +

      + +

      + +

      + +
      diff --git a/modules/gallery/views/admin_graphics_none.html.php b/modules/gallery/views/admin_graphics_none.html.php new file mode 100644 index 00000000..5306a70d --- /dev/null +++ b/modules/gallery/views/admin_graphics_none.html.php @@ -0,0 +1,7 @@ + +
      +

      +

      + +

      +
      diff --git a/modules/gallery/views/admin_languages.html.php b/modules/gallery/views/admin_languages.html.php new file mode 100644 index 00000000..2b43f1b4 --- /dev/null +++ b/modules/gallery/views/admin_languages.html.php @@ -0,0 +1,15 @@ + +
      +

      + + + +

      + " + class="gDialogLink"> + + + +

      + +
      diff --git a/modules/gallery/views/admin_maintenance.html.php b/modules/gallery/views/admin_maintenance.html.php new file mode 100644 index 00000000..bc060a7b --- /dev/null +++ b/modules/gallery/views/admin_maintenance.html.php @@ -0,0 +1,181 @@ + +
      +

      +

      + +

      + +
      +

      + + + + + + + + + + + + + +
      + + + + + +
      + name ?> + + description ?> + + callback?csrf=$csrf") ?>" + class="gDialogLink"> + + +
      +
      + + count()): ?> +
      +

      + " + class="gButtonLink ui-icon-left ui-state-default ui-corner-all right"> + + + + + + + + + + + + + "> + + + + + + + + +
      + + + + + + + + + + + +
      + updated) ?> + + name ?> + + done): ?> + state == "cancelled"): ?> + + + + state == "stalled"): ?> + + + $task->percent_complete)) ?> + + + status ?> + + owner()->name ?> + + state == "stalled"): ?> + id?csrf=$csrf") ?>"> + + + + id?csrf=$csrf") ?>"> + + +
      +
      + + + count()): ?> +
      + " + class="gButtonLink ui-icon-left ui-state-default ui-corner-all right"> + + +

      + + + + + + + + + + + "> + + + + + + + + +
      + + + + + + + + + + + +
      + updated) ?> + + name ?> + + state == "success"): ?> + + state == "error"): ?> + + state == "cancelled"): ?> + + + + status ?> + + owner()->name ?> + + done): ?> + id?csrf=$csrf") ?>"> + + + + id?csrf=$csrf") ?>"> + + + id?csrf=$csrf") ?>"> + + + +
      +
      + +
      diff --git a/modules/gallery/views/admin_maintenance_task.html.php b/modules/gallery/views/admin_maintenance_task.html.php new file mode 100644 index 00000000..1ee02311 --- /dev/null +++ b/modules/gallery/views/admin_maintenance_task.html.php @@ -0,0 +1,32 @@ + + +
      +
      +
      +
      + + +
      +
      diff --git a/modules/gallery/views/admin_modules.html.php b/modules/gallery/views/admin_modules.html.php new file mode 100644 index 00000000..3fddd6cd --- /dev/null +++ b/modules/gallery/views/admin_modules.html.php @@ -0,0 +1,32 @@ + +
      +

      +

      + +

      + +
      "> + + + + + + + + + + $module_info): ?> + "> + $module_name); ?> + locked) $data["disabled"] = 1; ?> + + + + + + + +
      name) ?> version ?> description) ?>
      + "/> +
      +
      diff --git a/modules/gallery/views/admin_theme_details.html.php b/modules/gallery/views/admin_theme_details.html.php new file mode 100644 index 00000000..eb450b16 --- /dev/null +++ b/modules/gallery/views/admin_theme_details.html.php @@ -0,0 +1,6 @@ + +
      +

      + + +
      diff --git a/modules/gallery/views/admin_themes.html.php b/modules/gallery/views/admin_themes.html.php new file mode 100644 index 00000000..f85bce70 --- /dev/null +++ b/modules/gallery/views/admin_themes.html.php @@ -0,0 +1,89 @@ + + + +

      +

      + +

      + +
      +

      +
      + " + alt="name ?>" /> +

      name ?>

      +

      + description ?> +

      +
      + +

      + +
      + +
      +

      +
      + " + alt="name ?>" /> +

      name ?>

      +

      + description ?> +

      +
      + +

      + +
      \ No newline at end of file diff --git a/modules/gallery/views/admin_themes_preview.html.php b/modules/gallery/views/admin_themes_preview.html.php new file mode 100644 index 00000000..a7aea172 --- /dev/null +++ b/modules/gallery/views/admin_themes_preview.html.php @@ -0,0 +1,7 @@ + +

      + "> + %theme_name", array("theme_name" => $info->name)) ?> + +

      + diff --git a/modules/gallery/views/after_install.html.php b/modules/gallery/views/after_install.html.php new file mode 100644 index 00000000..aa26858a --- /dev/null +++ b/modules/gallery/views/after_install.html.php @@ -0,0 +1,29 @@ + +

      + +

      + +

      + +

      + +

      + %user_name account. The very first thing you should do is to change your password to something that you'll remember.", array("user_name" => $user->name)) ?> +

      + +

      + id}") ?>" + title="" + id="gAfterInstallChangePasswordLink" class="gButtonLink ui-state-default ui-corners-all"> + +

      + +

      + Gallery website has news and information about Gallery Project and community.", array("url" => "http://gallery.menalto.com")) ?> +

      + +

      + documentation site or you can ask for help in the forums!", array("codex_url" => "http://codex.gallery2.org/Main_Page", "forum_url" => "http://gallery.menalto.com/forum")) ?> +

    diff --git a/modules/gallery/views/after_install_loader.html.php b/modules/gallery/views/after_install_loader.html.php new file mode 100644 index 00000000..baf91eed --- /dev/null +++ b/modules/gallery/views/after_install_loader.html.php @@ -0,0 +1,7 @@ + +" + href=""/> + diff --git a/modules/gallery/views/form.html.php b/modules/gallery/views/form.html.php new file mode 100644 index 00000000..ec2a56a9 --- /dev/null +++ b/modules/gallery/views/form.html.php @@ -0,0 +1,75 @@ + +"; +} +if ($title) { + print $title; +} + +if (!function_exists("DrawForm")) { + function DrawForm($inputs, $level=1) { + $error_messages = array(); + $prefix = str_repeat(" ", $level); + $haveGroup = false; + // On the first level, make sure we have a group if not add the
      tag now + if ($level == 1) { + foreach ($inputs as $input) { + $haveGroup |= $input->type == 'group'; + } + if (!$haveGroup) { + print "$prefix
        \n"; + } + } + + foreach ($inputs as $input) { + if ($input->type == 'group') { + print "$prefix
        \n"; + print "$prefix {$input->label}\n"; + print "$prefix
          \n"; + + DrawForm($input->inputs, $level + 2); + print "$prefix
        \n"; + + // Since hidden fields can only have name and value attributes lets just render it now + $hidden_prefix = "$prefix "; + foreach ($input->hidden as $hidden) { + print "$prefix {$hidden->render()}\n"; + } + print "$prefix
        \n"; + } else { + if ($input->error_messages()) { + print "$prefix
      • \n"; + } else { + print "$prefix
      • \n"; + } + + if ($input->label()) { + print "$prefix {$input->label()}\n"; + } + print "$prefix {$input->render()}\n"; + if ($input->message()) { + print "$prefix

        {$input->message()}

        \n"; + } + if ($input->error_messages()) { + foreach ($input->error_messages() as $error_message) { + print "$prefix

        \n"; + print "$prefix $error_message\n"; + print "$prefix

        \n"; + } + } + print "$prefix
      • \n"; + } + } + if ($level == 1 && !$haveGroup) { + print "$prefix
      \n"; + } + } +} +DrawForm($inputs); + +print($close); +?> diff --git a/modules/gallery/views/kohana_error_page.php b/modules/gallery/views/kohana_error_page.php new file mode 100644 index 00000000..d9bf9698 --- /dev/null +++ b/modules/gallery/views/kohana_error_page.php @@ -0,0 +1,118 @@ + + + + + + + <?= t("Something went wrong!") ?> + + + + admin ?> +
      +

      + +

      +

      + +

      + +

      + +

      + +
      + +
      +

      + +

      + + + + + + + diff --git a/modules/gallery/views/kohana_profiler.php b/modules/gallery/views/kohana_profiler.php new file mode 100644 index 00000000..c7534349 --- /dev/null +++ b/modules/gallery/views/kohana_profiler.php @@ -0,0 +1,35 @@ + + + +
      + + render(); ?> + +

      s

      +
      diff --git a/modules/gallery/views/l10n_client.html.php b/modules/gallery/views/l10n_client.html.php new file mode 100644 index 00000000..8f4092c7 --- /dev/null +++ b/modules/gallery/views/l10n_client.html.php @@ -0,0 +1,31 @@ + + diff --git a/modules/gallery/views/maintenance.html.php b/modules/gallery/views/maintenance.html.php new file mode 100644 index 00000000..f80b6e7a --- /dev/null +++ b/modules/gallery/views/maintenance.html.php @@ -0,0 +1,50 @@ + + + + + <?= t("Gallery - Maintenance Mode") ?> + + + + +

      + +

      +

      + +

      + + + + + diff --git a/modules/gallery/views/move_browse.html.php b/modules/gallery/views/move_browse.html.php new file mode 100644 index 00000000..4f69c0e9 --- /dev/null +++ b/modules/gallery/views/move_browse.html.php @@ -0,0 +1,47 @@ + + +

      + type == "photo"): ?> + + type == "movie"): ?> + + type == "album"): ?> + + +

      +
      +
        +
      • + +
      • +
      +
      id") ?>"> + + + " disabled="disabled"/> +
      +
      diff --git a/modules/gallery/views/move_tree.html.php b/modules/gallery/views/move_tree.html.php new file mode 100644 index 00000000..a3a4bc8f --- /dev/null +++ b/modules/gallery/views/move_tree.html.php @@ -0,0 +1,19 @@ + +thumb_tag(array(), 25); ?> +is_descendant($parent)): ?> + title ?> + + title ?> + + diff --git a/modules/gallery/views/permissions_browse.html.php b/modules/gallery/views/permissions_browse.html.php new file mode 100644 index 00000000..afd87c2b --- /dev/null +++ b/modules/gallery/views/permissions_browse.html.php @@ -0,0 +1,56 @@ + + +
      + +
        +
      • + AllowOverride FileInfo Options to fix this.", array("url" => "http://httpd.apache.org/docs/2.0/mod/core.html#allowoverride")) ?> +
      • +
      + + + +
      diff --git a/modules/gallery/views/permissions_form.html.php b/modules/gallery/views/permissions_form.html.php new file mode 100644 index 00000000..3dbd0d98 --- /dev/null +++ b/modules/gallery/views/permissions_form.html.php @@ -0,0 +1,94 @@ + +
      + +
      + + + + + + + + + + + + + + + name, $item) ?> + name, $item) ?> + name, $item) ?> + + + + + + + + + + + + + + + + + + + + + +
      name ?>
      display_name) ?> + <?= t('denied icon') ?> + + <?= t('locked icon') ?> + + + + <?= t('passive allowed icon') ?> + + + <?= t('inactive denied icon') ?> + + + + <?= t('inactive allowed icon') ?> + + + <?= t('passive denied icon') ?> + + + + <?= t('inactive allowed icon') ?> + + id == 1): ?> + <?= t('denied icon') ?> + + + <?= t('denied icon') ?> + + + + id == 1): ?> + <?= t('allowed icon') ?> + + + <?= t('allowed icon') ?> + + + + <?= t('inactive denied icon') ?> + +
      +
      +
      diff --git a/modules/gallery/views/quick_pane.html.php b/modules/gallery/views/quick_pane.html.php new file mode 100644 index 00000000..95de972b --- /dev/null +++ b/modules/gallery/views/quick_pane.html.php @@ -0,0 +1,108 @@ + +type == "photo"): ?> + +type == "movie"): ?> + +type == "album"): ?> + + +id?page_type=$page_type") ?>" + title=""> + + + + + +is_photo() && graphics::can("rotate")): ?> +id/ccw?csrf=$csrf&page_type=$page_type") ?>" + title=""> + + + + + +id/cw?csrf=$csrf&page_type=$page_type") ?>" + title=""> + + + + + + + + +type == "photo"): ?> + +type == "movie"): ?> + +type == "album"): ?> + + +id") ?>" + title=""> + + + + + + + +parent())): ?> +type == "photo"): ?> + +type == "movie"): ?> + +type == "album"): ?> +album_cover_item_id)): ?> +album_cover_item_id) ? " ui-state-disabled" : "" ?> + + + +id?csrf=$csrf&page_type=$page_type") ?>" + title=""> + + + + + +type == "photo"): ?> + + +type == "movie"): ?> + + +type == "album"): ?> + + + +id?csrf=$csrf&page_type=$page_type") ?>" ref="" id="gQuickDelete" title=""> + + + + + + +is_album()): ?> +"> + + + + + + + diff --git a/modules/gallery/views/scaffold.html.php b/modules/gallery/views/scaffold.html.php new file mode 100644 index 00000000..765464b5 --- /dev/null +++ b/modules/gallery/views/scaffold.html.php @@ -0,0 +1,169 @@ + + + + Gallery3 Scaffold + + + +
      +
      + "/> +
      +
      +

      Gallery3 Scaffold

      +

      + This is + a scaffold: + a temporary structure built to support the developers as + they create the real product. As we flesh out Gallery 3, + we'll make it possible for you to peer inside and see the + application taking shape. Eventually, this page will go + away and you'll start in the application itself. In the + meantime, here are some useful links to get you started. +

      + + 0): ?> +
      +

      + + ( albums, photos, comments, tags) +

      +
      + + +
      +
      + Generate Test Data +

      + add: [ + + + + ] photos and albums +

      +

      + add: [ + + + + ] albums only +

      +

      + add: [ + + + + ] comments +

      +

      + add: [ + + + + ] tags +

      +
      +
      + Packaging + ">Make Package +
      +
      +
      +
      + + diff --git a/modules/gallery/views/simple_uploader.html.php b/modules/gallery/views/simple_uploader.html.php new file mode 100644 index 00000000..b6725c31 --- /dev/null +++ b/modules/gallery/views/simple_uploader.html.php @@ -0,0 +1,249 @@ + + + + + +
      "> +
      + $item->title)) ?> +
      +
      + +
      + +
        +
      • + suhosin.session.encrypt setting from Suhosin. You must disable this setting to upload photos.", + array("encrypt_url" => "http://www.hardened-php.net/suhosin/configuration.html#suhosin.session.encrypt", + "suhosin_url" => "http://www.hardened-php.net/suhosin/")) ?> +
      • +
      + + +

      + +

      +
        + parents() as $parent): ?> +
      • title ?>
      • + +
      • title ?>
      • +
      + +

      +
      +
      +
      + +
      + + + + +
      + + + + -- cgit v1.2.3 From 3413fe6bfd423d45d083ff4ed62c0f72c2cc272f Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Thu, 28 May 2009 06:11:53 +0800 Subject: Rename 'kohana' to 'system' to conform to the Kohana filesystem layout. I'm comfortable with us not clearly drawing the distinction about the fact that it's Kohana. Signed-off-by: Gallery Role Account --- index.php | 2 +- kohana/KohanaLicense.html | 30 - kohana/config/cache.php | 32 - kohana/config/cache_memcache.php | 20 - kohana/config/cache_sqlite.php | 10 - kohana/config/cache_xcache.php | 12 - kohana/config/cookie.php | 32 - kohana/config/credit_cards.php | 60 - kohana/config/database.php | 45 - kohana/config/email.php | 22 - kohana/config/encryption.php | 31 - kohana/config/http.php | 19 - kohana/config/image.php | 13 - kohana/config/inflector.php | 58 - kohana/config/locale.php | 16 - kohana/config/mimes.php | 224 --- kohana/config/pagination.php | 25 - kohana/config/profiler.php | 8 - kohana/config/routes.php | 7 - kohana/config/session.php | 47 - kohana/config/sql_types.php | 58 - kohana/config/upload.php | 17 - kohana/config/user_agents.php | 112 -- kohana/config/view.php | 17 - kohana/controllers/template.php | 54 - kohana/core/Benchmark.php | 125 -- kohana/core/Bootstrap.php | 58 - kohana/core/Event.php | 232 --- kohana/core/Kohana.php | 1744 --------------------- kohana/core/utf8.php | 743 --------- kohana/core/utf8/from_unicode.php | 68 - kohana/core/utf8/ltrim.php | 22 - kohana/core/utf8/ord.php | 88 -- kohana/core/utf8/rtrim.php | 22 - kohana/core/utf8/str_ireplace.php | 70 - kohana/core/utf8/str_pad.php | 54 - kohana/core/utf8/str_split.php | 33 - kohana/core/utf8/strcasecmp.php | 19 - kohana/core/utf8/strcspn.php | 30 - kohana/core/utf8/stristr.php | 28 - kohana/core/utf8/strlen.php | 21 - kohana/core/utf8/strpos.php | 30 - kohana/core/utf8/strrev.php | 18 - kohana/core/utf8/strrpos.php | 30 - kohana/core/utf8/strspn.php | 30 - kohana/core/utf8/strtolower.php | 84 - kohana/core/utf8/strtoupper.php | 84 - kohana/core/utf8/substr.php | 75 - kohana/core/utf8/substr_replace.php | 22 - kohana/core/utf8/to_unicode.php | 141 -- kohana/core/utf8/transliterate_to_ascii.php | 77 - kohana/core/utf8/trim.php | 17 - kohana/core/utf8/ucfirst.php | 18 - kohana/core/utf8/ucwords.php | 26 - kohana/helpers/arr.php | 312 ---- kohana/helpers/cookie.php | 84 - kohana/helpers/date.php | 405 ----- kohana/helpers/download.php | 105 -- kohana/helpers/email.php | 181 --- kohana/helpers/expires.php | 111 -- kohana/helpers/feed.php | 122 -- kohana/helpers/file.php | 186 --- kohana/helpers/form.php | 542 ------- kohana/helpers/format.php | 66 - kohana/helpers/html.php | 440 ------ kohana/helpers/inflector.php | 193 --- kohana/helpers/num.php | 26 - kohana/helpers/remote.php | 66 - kohana/helpers/request.php | 239 --- kohana/helpers/security.php | 47 - kohana/helpers/text.php | 410 ----- kohana/helpers/upload.php | 162 -- kohana/helpers/url.php | 252 --- kohana/helpers/valid.php | 338 ---- kohana/i18n/en_US/cache.php | 10 - kohana/i18n/en_US/calendar.php | 59 - kohana/i18n/en_US/core.php | 34 - kohana/i18n/en_US/database.php | 15 - kohana/i18n/en_US/encrypt.php | 8 - kohana/i18n/en_US/errors.php | 16 - kohana/i18n/en_US/event.php | 7 - kohana/i18n/en_US/image.php | 33 - kohana/i18n/en_US/orm.php | 3 - kohana/i18n/en_US/profiler.php | 15 - kohana/i18n/en_US/session.php | 6 - kohana/i18n/en_US/upload.php | 6 - kohana/i18n/en_US/validation.php | 41 - kohana/libraries/Cache.php | 208 --- kohana/libraries/Controller.php | 78 - kohana/libraries/Database.php | 1444 ----------------- kohana/libraries/Database_Expression.php | 26 - kohana/libraries/Encrypt.php | 164 -- kohana/libraries/Event_Observer.php | 70 - kohana/libraries/Event_Subject.php | 67 - kohana/libraries/Image.php | 460 ------ kohana/libraries/Input.php | 452 ------ kohana/libraries/Model.php | 31 - kohana/libraries/ORM.php | 1431 ----------------- kohana/libraries/ORM_Iterator.php | 228 --- kohana/libraries/ORM_Tree.php | 76 - kohana/libraries/ORM_Versioned.php | 143 -- kohana/libraries/Pagination.php | 236 --- kohana/libraries/Profiler.php | 271 ---- kohana/libraries/Profiler_Table.php | 69 - kohana/libraries/Router.php | 304 ---- kohana/libraries/Session.php | 458 ------ kohana/libraries/URI.php | 279 ---- kohana/libraries/Validation.php | 826 ---------- kohana/libraries/View.php | 309 ---- kohana/libraries/drivers/Cache.php | 40 - kohana/libraries/drivers/Cache/Apc.php | 64 - kohana/libraries/drivers/Cache/Eaccelerator.php | 66 - kohana/libraries/drivers/Cache/File.php | 261 --- kohana/libraries/drivers/Cache/Memcache.php | 191 --- kohana/libraries/drivers/Cache/Sqlite.php | 257 --- kohana/libraries/drivers/Cache/Xcache.php | 119 -- kohana/libraries/drivers/Captcha.php | 227 --- kohana/libraries/drivers/Captcha/Alpha.php | 92 -- kohana/libraries/drivers/Captcha/Basic.php | 81 - kohana/libraries/drivers/Captcha/Black.php | 72 - kohana/libraries/drivers/Captcha/Math.php | 61 - kohana/libraries/drivers/Captcha/Riddle.php | 47 - kohana/libraries/drivers/Captcha/Word.php | 37 - kohana/libraries/drivers/Database.php | 636 -------- kohana/libraries/drivers/Database/Mssql.php | 462 ------ kohana/libraries/drivers/Database/Mysql.php | 496 ------ kohana/libraries/drivers/Database/Mysqli.php | 358 ----- kohana/libraries/drivers/Database/Pdosqlite.php | 486 ------ kohana/libraries/drivers/Database/Pgsql.php | 538 ------- kohana/libraries/drivers/Image.php | 156 -- kohana/libraries/drivers/Image/GD.php | 401 ----- kohana/libraries/drivers/Image/GraphicsMagick.php | 221 --- kohana/libraries/drivers/Image/ImageMagick.php | 222 --- kohana/libraries/drivers/Session.php | 70 - kohana/libraries/drivers/Session/Cache.php | 105 -- kohana/libraries/drivers/Session/Cookie.php | 80 - kohana/libraries/drivers/Session/Database.php | 163 -- kohana/views/kohana/template.php | 36 - kohana/views/kohana_error_disabled.php | 17 - kohana/views/kohana_error_page.php | 27 - kohana/views/kohana_errors.css | 21 - kohana/views/kohana_profiler.php | 37 - kohana/views/kohana_profiler_table.css | 53 - kohana/views/kohana_profiler_table.php | 25 - system/KohanaLicense.html | 30 + system/config/cache.php | 32 + system/config/cache_memcache.php | 20 + system/config/cache_sqlite.php | 10 + system/config/cache_xcache.php | 12 + system/config/cookie.php | 32 + system/config/credit_cards.php | 60 + system/config/database.php | 45 + system/config/email.php | 22 + system/config/encryption.php | 31 + system/config/http.php | 19 + system/config/image.php | 13 + system/config/inflector.php | 58 + system/config/locale.php | 16 + system/config/mimes.php | 224 +++ system/config/pagination.php | 25 + system/config/profiler.php | 8 + system/config/routes.php | 7 + system/config/session.php | 47 + system/config/sql_types.php | 58 + system/config/upload.php | 17 + system/config/user_agents.php | 112 ++ system/config/view.php | 17 + system/controllers/template.php | 54 + system/core/Benchmark.php | 125 ++ system/core/Bootstrap.php | 58 + system/core/Event.php | 232 +++ system/core/Kohana.php | 1744 +++++++++++++++++++++ system/core/utf8.php | 743 +++++++++ system/core/utf8/from_unicode.php | 68 + system/core/utf8/ltrim.php | 22 + system/core/utf8/ord.php | 88 ++ system/core/utf8/rtrim.php | 22 + system/core/utf8/str_ireplace.php | 70 + system/core/utf8/str_pad.php | 54 + system/core/utf8/str_split.php | 33 + system/core/utf8/strcasecmp.php | 19 + system/core/utf8/strcspn.php | 30 + system/core/utf8/stristr.php | 28 + system/core/utf8/strlen.php | 21 + system/core/utf8/strpos.php | 30 + system/core/utf8/strrev.php | 18 + system/core/utf8/strrpos.php | 30 + system/core/utf8/strspn.php | 30 + system/core/utf8/strtolower.php | 84 + system/core/utf8/strtoupper.php | 84 + system/core/utf8/substr.php | 75 + system/core/utf8/substr_replace.php | 22 + system/core/utf8/to_unicode.php | 141 ++ system/core/utf8/transliterate_to_ascii.php | 77 + system/core/utf8/trim.php | 17 + system/core/utf8/ucfirst.php | 18 + system/core/utf8/ucwords.php | 26 + system/helpers/arr.php | 312 ++++ system/helpers/cookie.php | 84 + system/helpers/date.php | 405 +++++ system/helpers/download.php | 105 ++ system/helpers/email.php | 181 +++ system/helpers/expires.php | 111 ++ system/helpers/feed.php | 122 ++ system/helpers/file.php | 186 +++ system/helpers/form.php | 542 +++++++ system/helpers/format.php | 66 + system/helpers/html.php | 440 ++++++ system/helpers/inflector.php | 193 +++ system/helpers/num.php | 26 + system/helpers/remote.php | 66 + system/helpers/request.php | 239 +++ system/helpers/security.php | 47 + system/helpers/text.php | 410 +++++ system/helpers/upload.php | 162 ++ system/helpers/url.php | 252 +++ system/helpers/valid.php | 338 ++++ system/i18n/en_US/cache.php | 10 + system/i18n/en_US/calendar.php | 59 + system/i18n/en_US/core.php | 34 + system/i18n/en_US/database.php | 15 + system/i18n/en_US/encrypt.php | 8 + system/i18n/en_US/errors.php | 16 + system/i18n/en_US/event.php | 7 + system/i18n/en_US/image.php | 33 + system/i18n/en_US/orm.php | 3 + system/i18n/en_US/profiler.php | 15 + system/i18n/en_US/session.php | 6 + system/i18n/en_US/upload.php | 6 + system/i18n/en_US/validation.php | 41 + system/libraries/Cache.php | 208 +++ system/libraries/Controller.php | 78 + system/libraries/Database.php | 1444 +++++++++++++++++ system/libraries/Database_Expression.php | 26 + system/libraries/Encrypt.php | 164 ++ system/libraries/Event_Observer.php | 70 + system/libraries/Event_Subject.php | 67 + system/libraries/Image.php | 460 ++++++ system/libraries/Input.php | 452 ++++++ system/libraries/Model.php | 31 + system/libraries/ORM.php | 1431 +++++++++++++++++ system/libraries/ORM_Iterator.php | 228 +++ system/libraries/ORM_Tree.php | 76 + system/libraries/ORM_Versioned.php | 143 ++ system/libraries/Pagination.php | 236 +++ system/libraries/Profiler.php | 271 ++++ system/libraries/Profiler_Table.php | 69 + system/libraries/Router.php | 304 ++++ system/libraries/Session.php | 458 ++++++ system/libraries/URI.php | 279 ++++ system/libraries/Validation.php | 826 ++++++++++ system/libraries/View.php | 309 ++++ system/libraries/drivers/Cache.php | 40 + system/libraries/drivers/Cache/Apc.php | 64 + system/libraries/drivers/Cache/Eaccelerator.php | 66 + system/libraries/drivers/Cache/File.php | 261 +++ system/libraries/drivers/Cache/Memcache.php | 191 +++ system/libraries/drivers/Cache/Sqlite.php | 257 +++ system/libraries/drivers/Cache/Xcache.php | 119 ++ system/libraries/drivers/Captcha.php | 227 +++ system/libraries/drivers/Captcha/Alpha.php | 92 ++ system/libraries/drivers/Captcha/Basic.php | 81 + system/libraries/drivers/Captcha/Black.php | 72 + system/libraries/drivers/Captcha/Math.php | 61 + system/libraries/drivers/Captcha/Riddle.php | 47 + system/libraries/drivers/Captcha/Word.php | 37 + system/libraries/drivers/Database.php | 636 ++++++++ system/libraries/drivers/Database/Mssql.php | 462 ++++++ system/libraries/drivers/Database/Mysql.php | 496 ++++++ system/libraries/drivers/Database/Mysqli.php | 358 +++++ system/libraries/drivers/Database/Pdosqlite.php | 486 ++++++ system/libraries/drivers/Database/Pgsql.php | 538 +++++++ system/libraries/drivers/Image.php | 156 ++ system/libraries/drivers/Image/GD.php | 401 +++++ system/libraries/drivers/Image/GraphicsMagick.php | 221 +++ system/libraries/drivers/Image/ImageMagick.php | 222 +++ system/libraries/drivers/Session.php | 70 + system/libraries/drivers/Session/Cache.php | 105 ++ system/libraries/drivers/Session/Cookie.php | 80 + system/libraries/drivers/Session/Database.php | 163 ++ system/views/kohana/template.php | 36 + system/views/kohana_error_disabled.php | 17 + system/views/kohana_error_page.php | 27 + system/views/kohana_errors.css | 21 + system/views/kohana_profiler.php | 37 + system/views/kohana_profiler_table.css | 53 + system/views/kohana_profiler_table.php | 25 + 287 files changed, 23374 insertions(+), 23374 deletions(-) delete mode 100644 kohana/KohanaLicense.html delete mode 100644 kohana/config/cache.php delete mode 100644 kohana/config/cache_memcache.php delete mode 100644 kohana/config/cache_sqlite.php delete mode 100644 kohana/config/cache_xcache.php delete mode 100644 kohana/config/cookie.php delete mode 100644 kohana/config/credit_cards.php delete mode 100644 kohana/config/database.php delete mode 100644 kohana/config/email.php delete mode 100644 kohana/config/encryption.php delete mode 100644 kohana/config/http.php delete mode 100644 kohana/config/image.php delete mode 100644 kohana/config/inflector.php delete mode 100644 kohana/config/locale.php delete mode 100644 kohana/config/mimes.php delete mode 100644 kohana/config/pagination.php delete mode 100644 kohana/config/profiler.php delete mode 100644 kohana/config/routes.php delete mode 100644 kohana/config/session.php delete mode 100644 kohana/config/sql_types.php delete mode 100644 kohana/config/upload.php delete mode 100644 kohana/config/user_agents.php delete mode 100644 kohana/config/view.php delete mode 100644 kohana/controllers/template.php delete mode 100644 kohana/core/Benchmark.php delete mode 100644 kohana/core/Bootstrap.php delete mode 100644 kohana/core/Event.php delete mode 100644 kohana/core/Kohana.php delete mode 100644 kohana/core/utf8.php delete mode 100644 kohana/core/utf8/from_unicode.php delete mode 100644 kohana/core/utf8/ltrim.php delete mode 100644 kohana/core/utf8/ord.php delete mode 100644 kohana/core/utf8/rtrim.php delete mode 100644 kohana/core/utf8/str_ireplace.php delete mode 100644 kohana/core/utf8/str_pad.php delete mode 100644 kohana/core/utf8/str_split.php delete mode 100644 kohana/core/utf8/strcasecmp.php delete mode 100644 kohana/core/utf8/strcspn.php delete mode 100644 kohana/core/utf8/stristr.php delete mode 100644 kohana/core/utf8/strlen.php delete mode 100644 kohana/core/utf8/strpos.php delete mode 100644 kohana/core/utf8/strrev.php delete mode 100644 kohana/core/utf8/strrpos.php delete mode 100644 kohana/core/utf8/strspn.php delete mode 100644 kohana/core/utf8/strtolower.php delete mode 100644 kohana/core/utf8/strtoupper.php delete mode 100644 kohana/core/utf8/substr.php delete mode 100644 kohana/core/utf8/substr_replace.php delete mode 100644 kohana/core/utf8/to_unicode.php delete mode 100644 kohana/core/utf8/transliterate_to_ascii.php delete mode 100644 kohana/core/utf8/trim.php delete mode 100644 kohana/core/utf8/ucfirst.php delete mode 100644 kohana/core/utf8/ucwords.php delete mode 100644 kohana/helpers/arr.php delete mode 100644 kohana/helpers/cookie.php delete mode 100644 kohana/helpers/date.php delete mode 100644 kohana/helpers/download.php delete mode 100644 kohana/helpers/email.php delete mode 100644 kohana/helpers/expires.php delete mode 100644 kohana/helpers/feed.php delete mode 100644 kohana/helpers/file.php delete mode 100644 kohana/helpers/form.php delete mode 100644 kohana/helpers/format.php delete mode 100644 kohana/helpers/html.php delete mode 100644 kohana/helpers/inflector.php delete mode 100644 kohana/helpers/num.php delete mode 100644 kohana/helpers/remote.php delete mode 100644 kohana/helpers/request.php delete mode 100644 kohana/helpers/security.php delete mode 100644 kohana/helpers/text.php delete mode 100644 kohana/helpers/upload.php delete mode 100644 kohana/helpers/url.php delete mode 100644 kohana/helpers/valid.php delete mode 100644 kohana/i18n/en_US/cache.php delete mode 100644 kohana/i18n/en_US/calendar.php delete mode 100644 kohana/i18n/en_US/core.php delete mode 100644 kohana/i18n/en_US/database.php delete mode 100644 kohana/i18n/en_US/encrypt.php delete mode 100644 kohana/i18n/en_US/errors.php delete mode 100644 kohana/i18n/en_US/event.php delete mode 100644 kohana/i18n/en_US/image.php delete mode 100644 kohana/i18n/en_US/orm.php delete mode 100644 kohana/i18n/en_US/profiler.php delete mode 100644 kohana/i18n/en_US/session.php delete mode 100644 kohana/i18n/en_US/upload.php delete mode 100644 kohana/i18n/en_US/validation.php delete mode 100644 kohana/libraries/Cache.php delete mode 100644 kohana/libraries/Controller.php delete mode 100644 kohana/libraries/Database.php delete mode 100644 kohana/libraries/Database_Expression.php delete mode 100644 kohana/libraries/Encrypt.php delete mode 100644 kohana/libraries/Event_Observer.php delete mode 100644 kohana/libraries/Event_Subject.php delete mode 100644 kohana/libraries/Image.php delete mode 100644 kohana/libraries/Input.php delete mode 100644 kohana/libraries/Model.php delete mode 100644 kohana/libraries/ORM.php delete mode 100644 kohana/libraries/ORM_Iterator.php delete mode 100644 kohana/libraries/ORM_Tree.php delete mode 100644 kohana/libraries/ORM_Versioned.php delete mode 100644 kohana/libraries/Pagination.php delete mode 100644 kohana/libraries/Profiler.php delete mode 100644 kohana/libraries/Profiler_Table.php delete mode 100644 kohana/libraries/Router.php delete mode 100644 kohana/libraries/Session.php delete mode 100644 kohana/libraries/URI.php delete mode 100644 kohana/libraries/Validation.php delete mode 100644 kohana/libraries/View.php delete mode 100644 kohana/libraries/drivers/Cache.php delete mode 100644 kohana/libraries/drivers/Cache/Apc.php delete mode 100644 kohana/libraries/drivers/Cache/Eaccelerator.php delete mode 100644 kohana/libraries/drivers/Cache/File.php delete mode 100644 kohana/libraries/drivers/Cache/Memcache.php delete mode 100644 kohana/libraries/drivers/Cache/Sqlite.php delete mode 100644 kohana/libraries/drivers/Cache/Xcache.php delete mode 100644 kohana/libraries/drivers/Captcha.php delete mode 100644 kohana/libraries/drivers/Captcha/Alpha.php delete mode 100644 kohana/libraries/drivers/Captcha/Basic.php delete mode 100644 kohana/libraries/drivers/Captcha/Black.php delete mode 100644 kohana/libraries/drivers/Captcha/Math.php delete mode 100644 kohana/libraries/drivers/Captcha/Riddle.php delete mode 100644 kohana/libraries/drivers/Captcha/Word.php delete mode 100644 kohana/libraries/drivers/Database.php delete mode 100644 kohana/libraries/drivers/Database/Mssql.php delete mode 100644 kohana/libraries/drivers/Database/Mysql.php delete mode 100644 kohana/libraries/drivers/Database/Mysqli.php delete mode 100644 kohana/libraries/drivers/Database/Pdosqlite.php delete mode 100644 kohana/libraries/drivers/Database/Pgsql.php delete mode 100644 kohana/libraries/drivers/Image.php delete mode 100644 kohana/libraries/drivers/Image/GD.php delete mode 100644 kohana/libraries/drivers/Image/GraphicsMagick.php delete mode 100644 kohana/libraries/drivers/Image/ImageMagick.php delete mode 100644 kohana/libraries/drivers/Session.php delete mode 100644 kohana/libraries/drivers/Session/Cache.php delete mode 100644 kohana/libraries/drivers/Session/Cookie.php delete mode 100644 kohana/libraries/drivers/Session/Database.php delete mode 100644 kohana/views/kohana/template.php delete mode 100644 kohana/views/kohana_error_disabled.php delete mode 100644 kohana/views/kohana_error_page.php delete mode 100644 kohana/views/kohana_errors.css delete mode 100644 kohana/views/kohana_profiler.php delete mode 100644 kohana/views/kohana_profiler_table.css delete mode 100644 kohana/views/kohana_profiler_table.php create mode 100644 system/KohanaLicense.html create mode 100644 system/config/cache.php create mode 100644 system/config/cache_memcache.php create mode 100644 system/config/cache_sqlite.php create mode 100644 system/config/cache_xcache.php create mode 100644 system/config/cookie.php create mode 100644 system/config/credit_cards.php create mode 100644 system/config/database.php create mode 100644 system/config/email.php create mode 100644 system/config/encryption.php create mode 100644 system/config/http.php create mode 100644 system/config/image.php create mode 100644 system/config/inflector.php create mode 100644 system/config/locale.php create mode 100644 system/config/mimes.php create mode 100644 system/config/pagination.php create mode 100644 system/config/profiler.php create mode 100644 system/config/routes.php create mode 100644 system/config/session.php create mode 100644 system/config/sql_types.php create mode 100644 system/config/upload.php create mode 100644 system/config/user_agents.php create mode 100644 system/config/view.php create mode 100644 system/controllers/template.php create mode 100644 system/core/Benchmark.php create mode 100644 system/core/Bootstrap.php create mode 100644 system/core/Event.php create mode 100644 system/core/Kohana.php create mode 100644 system/core/utf8.php create mode 100644 system/core/utf8/from_unicode.php create mode 100644 system/core/utf8/ltrim.php create mode 100644 system/core/utf8/ord.php create mode 100644 system/core/utf8/rtrim.php create mode 100644 system/core/utf8/str_ireplace.php create mode 100644 system/core/utf8/str_pad.php create mode 100644 system/core/utf8/str_split.php create mode 100644 system/core/utf8/strcasecmp.php create mode 100644 system/core/utf8/strcspn.php create mode 100644 system/core/utf8/stristr.php create mode 100644 system/core/utf8/strlen.php create mode 100644 system/core/utf8/strpos.php create mode 100644 system/core/utf8/strrev.php create mode 100644 system/core/utf8/strrpos.php create mode 100644 system/core/utf8/strspn.php create mode 100644 system/core/utf8/strtolower.php create mode 100644 system/core/utf8/strtoupper.php create mode 100644 system/core/utf8/substr.php create mode 100644 system/core/utf8/substr_replace.php create mode 100644 system/core/utf8/to_unicode.php create mode 100644 system/core/utf8/transliterate_to_ascii.php create mode 100644 system/core/utf8/trim.php create mode 100644 system/core/utf8/ucfirst.php create mode 100644 system/core/utf8/ucwords.php create mode 100644 system/helpers/arr.php create mode 100644 system/helpers/cookie.php create mode 100644 system/helpers/date.php create mode 100644 system/helpers/download.php create mode 100644 system/helpers/email.php create mode 100644 system/helpers/expires.php create mode 100644 system/helpers/feed.php create mode 100644 system/helpers/file.php create mode 100644 system/helpers/form.php create mode 100644 system/helpers/format.php create mode 100644 system/helpers/html.php create mode 100644 system/helpers/inflector.php create mode 100644 system/helpers/num.php create mode 100644 system/helpers/remote.php create mode 100644 system/helpers/request.php create mode 100644 system/helpers/security.php create mode 100644 system/helpers/text.php create mode 100644 system/helpers/upload.php create mode 100644 system/helpers/url.php create mode 100644 system/helpers/valid.php create mode 100644 system/i18n/en_US/cache.php create mode 100644 system/i18n/en_US/calendar.php create mode 100644 system/i18n/en_US/core.php create mode 100644 system/i18n/en_US/database.php create mode 100644 system/i18n/en_US/encrypt.php create mode 100644 system/i18n/en_US/errors.php create mode 100644 system/i18n/en_US/event.php create mode 100644 system/i18n/en_US/image.php create mode 100644 system/i18n/en_US/orm.php create mode 100644 system/i18n/en_US/profiler.php create mode 100644 system/i18n/en_US/session.php create mode 100644 system/i18n/en_US/upload.php create mode 100644 system/i18n/en_US/validation.php create mode 100644 system/libraries/Cache.php create mode 100644 system/libraries/Controller.php create mode 100644 system/libraries/Database.php create mode 100644 system/libraries/Database_Expression.php create mode 100644 system/libraries/Encrypt.php create mode 100644 system/libraries/Event_Observer.php create mode 100644 system/libraries/Event_Subject.php create mode 100644 system/libraries/Image.php create mode 100644 system/libraries/Input.php create mode 100644 system/libraries/Model.php create mode 100644 system/libraries/ORM.php create mode 100644 system/libraries/ORM_Iterator.php create mode 100644 system/libraries/ORM_Tree.php create mode 100644 system/libraries/ORM_Versioned.php create mode 100644 system/libraries/Pagination.php create mode 100644 system/libraries/Profiler.php create mode 100644 system/libraries/Profiler_Table.php create mode 100644 system/libraries/Router.php create mode 100644 system/libraries/Session.php create mode 100644 system/libraries/URI.php create mode 100644 system/libraries/Validation.php create mode 100644 system/libraries/View.php create mode 100644 system/libraries/drivers/Cache.php create mode 100644 system/libraries/drivers/Cache/Apc.php create mode 100644 system/libraries/drivers/Cache/Eaccelerator.php create mode 100644 system/libraries/drivers/Cache/File.php create mode 100644 system/libraries/drivers/Cache/Memcache.php create mode 100644 system/libraries/drivers/Cache/Sqlite.php create mode 100644 system/libraries/drivers/Cache/Xcache.php create mode 100644 system/libraries/drivers/Captcha.php create mode 100644 system/libraries/drivers/Captcha/Alpha.php create mode 100644 system/libraries/drivers/Captcha/Basic.php create mode 100644 system/libraries/drivers/Captcha/Black.php create mode 100644 system/libraries/drivers/Captcha/Math.php create mode 100644 system/libraries/drivers/Captcha/Riddle.php create mode 100644 system/libraries/drivers/Captcha/Word.php create mode 100644 system/libraries/drivers/Database.php create mode 100644 system/libraries/drivers/Database/Mssql.php create mode 100644 system/libraries/drivers/Database/Mysql.php create mode 100644 system/libraries/drivers/Database/Mysqli.php create mode 100644 system/libraries/drivers/Database/Pdosqlite.php create mode 100644 system/libraries/drivers/Database/Pgsql.php create mode 100644 system/libraries/drivers/Image.php create mode 100644 system/libraries/drivers/Image/GD.php create mode 100644 system/libraries/drivers/Image/GraphicsMagick.php create mode 100644 system/libraries/drivers/Image/ImageMagick.php create mode 100644 system/libraries/drivers/Session.php create mode 100644 system/libraries/drivers/Session/Cache.php create mode 100644 system/libraries/drivers/Session/Cookie.php create mode 100644 system/libraries/drivers/Session/Database.php create mode 100644 system/views/kohana/template.php create mode 100644 system/views/kohana_error_disabled.php create mode 100644 system/views/kohana_error_page.php create mode 100644 system/views/kohana_errors.css create mode 100644 system/views/kohana_profiler.php create mode 100644 system/views/kohana_profiler_table.css create mode 100644 system/views/kohana_profiler_table.php diff --git a/index.php b/index.php index 02bd3d3d..d8fc2742 100644 --- a/index.php +++ b/index.php @@ -44,7 +44,7 @@ is_link(basename(__FILE__)) and chdir(dirname(realpath(__FILE__))); define('APPPATH', strtr(realpath('application') . '/', DIRECTORY_SEPARATOR, '/')); define('MODPATH', strtr(realpath('modules') . '/', DIRECTORY_SEPARATOR, '/')); define('THEMEPATH', strtr(realpath('themes') . '/', DIRECTORY_SEPARATOR, '/')); -define('SYSPATH', strtr(realpath('kohana') . '/', DIRECTORY_SEPARATOR, '/')); +define('SYSPATH', strtr(realpath('system') . '/', DIRECTORY_SEPARATOR, '/')); // Force a test run if we're in command line mode. if (PHP_SAPI == 'cli') { diff --git a/kohana/KohanaLicense.html b/kohana/KohanaLicense.html deleted file mode 100644 index de2d5299..00000000 --- a/kohana/KohanaLicense.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - -Kohana License - - - - -

      Kohana License Agreement

      - -

      This license is a legal agreement between you and the Kohana Software Foundation for the use of Kohana Framework (the "Software"). By obtaining the Software you agree to comply with the terms and conditions of this license.

      - -

      Copyright (c) 2007-2008 Kohana Team
      All rights reserved.

      - -

      Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

      - -
        -
      • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
      • -
      • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
      • -
      • Neither the name of the Kohana nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
      • -
      - -

      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

      - -

      NOTE: This license is modeled after the BSD software license.

      - - - \ No newline at end of file diff --git a/kohana/config/cache.php b/kohana/config/cache.php deleted file mode 100644 index ccd3da4d..00000000 --- a/kohana/config/cache.php +++ /dev/null @@ -1,32 +0,0 @@ - File cache is fast and reliable, but requires many filesystem lookups. - * > Database cache can be used to cache items remotely, but is slower. - * > Memcache is very high performance, but prevents cache tags from being used. - * - * params - Driver parameters, specific to each driver. - * - * lifetime - Default lifetime of caches in seconds. By default caches are stored for - * thirty minutes. Specific lifetime can also be set when creating a new cache. - * Setting this to 0 will never automatically delete caches. - * - * requests - Average number of cache requests that will processed before all expired - * caches are deleted. This is commonly referred to as "garbage collection". - * Setting this to 0 or a negative number will disable automatic garbage collection. - */ -$config['default'] = array -( - 'driver' => 'file', - 'params' => APPPATH.'cache', - 'lifetime' => 1800, - 'requests' => 1000 -); diff --git a/kohana/config/cache_memcache.php b/kohana/config/cache_memcache.php deleted file mode 100644 index 43d8f205..00000000 --- a/kohana/config/cache_memcache.php +++ /dev/null @@ -1,20 +0,0 @@ - '127.0.0.1', - 'port' => 11211, - 'persistent' => FALSE, - ) -); - -/** - * Enable cache data compression. - */ -$config['compression'] = FALSE; diff --git a/kohana/config/cache_sqlite.php b/kohana/config/cache_sqlite.php deleted file mode 100644 index 818b8932..00000000 --- a/kohana/config/cache_sqlite.php +++ /dev/null @@ -1,10 +0,0 @@ - array - ( - 'length' => '13,14,15,16,17,18,19', - 'prefix' => '', - 'luhn' => TRUE - ), - 'american express' => array - ( - 'length' => '15', - 'prefix' => '3[47]', - 'luhn' => TRUE - ), - 'diners club' => array - ( - 'length' => '14,16', - 'prefix' => '36|55|30[0-5]', - 'luhn' => TRUE - ), - 'discover' => array - ( - 'length' => '16', - 'prefix' => '6(?:5|011)', - 'luhn' => TRUE, - ), - 'jcb' => array - ( - 'length' => '15,16', - 'prefix' => '3|1800|2131', - 'luhn' => TRUE - ), - 'maestro' => array - ( - 'length' => '16,18', - 'prefix' => '50(?:20|38)|6(?:304|759)', - 'luhn' => TRUE - ), - 'mastercard' => array - ( - 'length' => '16', - 'prefix' => '5[1-5]', - 'luhn' => TRUE - ), - 'visa' => array - ( - 'length' => '13,16', - 'prefix' => '4', - 'luhn' => TRUE - ), -); \ No newline at end of file diff --git a/kohana/config/database.php b/kohana/config/database.php deleted file mode 100644 index 6519156a..00000000 --- a/kohana/config/database.php +++ /dev/null @@ -1,45 +0,0 @@ - 'mysql://dbuser:secret@localhost/kohana' - * character_set - Database character set - * table_prefix - Database table prefix - * object - Enable or disable object results - * cache - Enable or disable query caching - * escape - Enable automatic query builder escaping - */ -$config['default'] = array -( - 'benchmark' => TRUE, - 'persistent' => FALSE, - 'connection' => array - ( - 'type' => 'mysql', - 'user' => 'dbuser', - 'pass' => 'p@ssw0rd', - 'host' => 'localhost', - 'port' => FALSE, - 'socket' => FALSE, - 'database' => 'kohana' - ), - 'character_set' => 'utf8', - 'table_prefix' => '', - 'object' => TRUE, - 'cache' => FALSE, - 'escape' => TRUE -); \ No newline at end of file diff --git a/kohana/config/email.php b/kohana/config/email.php deleted file mode 100644 index c768367c..00000000 --- a/kohana/config/email.php +++ /dev/null @@ -1,22 +0,0 @@ - 'K0H@NA+PHP_7hE-SW!FtFraM3w0R|<', - 'mode' => MCRYPT_MODE_NOFB, - 'cipher' => MCRYPT_RIJNDAEL_128 -); diff --git a/kohana/config/http.php b/kohana/config/http.php deleted file mode 100644 index 3c4a86ac..00000000 --- a/kohana/config/http.php +++ /dev/null @@ -1,19 +0,0 @@ - 'children', - 'clothes' => 'clothing', - 'man' => 'men', - 'movie' => 'movies', - 'person' => 'people', - 'woman' => 'women', - 'mouse' => 'mice', - 'goose' => 'geese', - 'ox' => 'oxen', - 'leaf' => 'leaves', - 'course' => 'courses', - 'size' => 'sizes', -); diff --git a/kohana/config/locale.php b/kohana/config/locale.php deleted file mode 100644 index 3a268820..00000000 --- a/kohana/config/locale.php +++ /dev/null @@ -1,16 +0,0 @@ - array('text/h323'), - '7z' => array('application/x-7z-compressed'), - 'abw' => array('application/x-abiword'), - 'acx' => array('application/internet-property-stream'), - 'ai' => array('application/postscript'), - 'aif' => array('audio/x-aiff'), - 'aifc' => array('audio/x-aiff'), - 'aiff' => array('audio/x-aiff'), - 'asf' => array('video/x-ms-asf'), - 'asr' => array('video/x-ms-asf'), - 'asx' => array('video/x-ms-asf'), - 'atom' => array('application/atom+xml'), - 'avi' => array('video/avi', 'video/msvideo', 'video/x-msvideo'), - 'bin' => array('application/octet-stream','application/macbinary'), - 'bmp' => array('image/bmp'), - 'c' => array('text/x-csrc'), - 'c++' => array('text/x-c++src'), - 'cab' => array('application/x-cab'), - 'cc' => array('text/x-c++src'), - 'cda' => array('application/x-cdf'), - 'class' => array('application/octet-stream'), - 'cpp' => array('text/x-c++src'), - 'cpt' => array('application/mac-compactpro'), - 'csh' => array('text/x-csh'), - 'css' => array('text/css'), - 'csv' => array('text/x-comma-separated-values', 'application/vnd.ms-excel', 'text/comma-separated-values', 'text/csv'), - 'dbk' => array('application/docbook+xml'), - 'dcr' => array('application/x-director'), - 'deb' => array('application/x-debian-package'), - 'diff' => array('text/x-diff'), - 'dir' => array('application/x-director'), - 'divx' => array('video/divx'), - 'dll' => array('application/octet-stream', 'application/x-msdos-program'), - 'dmg' => array('application/x-apple-diskimage'), - 'dms' => array('application/octet-stream'), - 'doc' => array('application/msword'), - 'dvi' => array('application/x-dvi'), - 'dxr' => array('application/x-director'), - 'eml' => array('message/rfc822'), - 'eps' => array('application/postscript'), - 'evy' => array('application/envoy'), - 'exe' => array('application/x-msdos-program', 'application/octet-stream'), - 'fla' => array('application/octet-stream'), - 'flac' => array('application/x-flac'), - 'flc' => array('video/flc'), - 'fli' => array('video/fli'), - 'flv' => array('video/x-flv'), - 'gif' => array('image/gif'), - 'gtar' => array('application/x-gtar'), - 'gz' => array('application/x-gzip'), - 'h' => array('text/x-chdr'), - 'h++' => array('text/x-c++hdr'), - 'hh' => array('text/x-c++hdr'), - 'hpp' => array('text/x-c++hdr'), - 'hqx' => array('application/mac-binhex40'), - 'hs' => array('text/x-haskell'), - 'htm' => array('text/html'), - 'html' => array('text/html'), - 'ico' => array('image/x-icon'), - 'ics' => array('text/calendar'), - 'iii' => array('application/x-iphone'), - 'ins' => array('application/x-internet-signup'), - 'iso' => array('application/x-iso9660-image'), - 'isp' => array('application/x-internet-signup'), - 'jar' => array('application/java-archive'), - 'java' => array('application/x-java-applet'), - 'jpe' => array('image/jpeg', 'image/pjpeg'), - 'jpeg' => array('image/jpeg', 'image/pjpeg'), - 'jpg' => array('image/jpeg', 'image/pjpeg'), - 'js' => array('application/x-javascript'), - 'json' => array('application/json'), - 'latex' => array('application/x-latex'), - 'lha' => array('application/octet-stream'), - 'log' => array('text/plain', 'text/x-log'), - 'lzh' => array('application/octet-stream'), - 'm4a' => array('audio/mpeg'), - 'm4p' => array('video/mp4v-es'), - 'm4v' => array('video/mp4'), - 'man' => array('application/x-troff-man'), - 'mdb' => array('application/x-msaccess'), - 'midi' => array('audio/midi'), - 'mid' => array('audio/midi'), - 'mif' => array('application/vnd.mif'), - 'mka' => array('audio/x-matroska'), - 'mkv' => array('video/x-matroska'), - 'mov' => array('video/quicktime'), - 'movie' => array('video/x-sgi-movie'), - 'mp2' => array('audio/mpeg'), - 'mp3' => array('audio/mpeg'), - 'mp4' => array('application/mp4','audio/mp4','video/mp4'), - 'mpa' => array('video/mpeg'), - 'mpe' => array('video/mpeg'), - 'mpeg' => array('video/mpeg'), - 'mpg' => array('video/mpeg'), - 'mpg4' => array('video/mp4'), - 'mpga' => array('audio/mpeg'), - 'mpp' => array('application/vnd.ms-project'), - 'mpv' => array('video/x-matroska'), - 'mpv2' => array('video/mpeg'), - 'ms' => array('application/x-troff-ms'), - 'msg' => array('application/msoutlook','application/x-msg'), - 'msi' => array('application/x-msi'), - 'nws' => array('message/rfc822'), - 'oda' => array('application/oda'), - 'odb' => array('application/vnd.oasis.opendocument.database'), - 'odc' => array('application/vnd.oasis.opendocument.chart'), - 'odf' => array('application/vnd.oasis.opendocument.forumla'), - 'odg' => array('application/vnd.oasis.opendocument.graphics'), - 'odi' => array('application/vnd.oasis.opendocument.image'), - 'odm' => array('application/vnd.oasis.opendocument.text-master'), - 'odp' => array('application/vnd.oasis.opendocument.presentation'), - 'ods' => array('application/vnd.oasis.opendocument.spreadsheet'), - 'odt' => array('application/vnd.oasis.opendocument.text'), - 'oga' => array('audio/ogg'), - 'ogg' => array('application/ogg'), - 'ogv' => array('video/ogg'), - 'otg' => array('application/vnd.oasis.opendocument.graphics-template'), - 'oth' => array('application/vnd.oasis.opendocument.web'), - 'otp' => array('application/vnd.oasis.opendocument.presentation-template'), - 'ots' => array('application/vnd.oasis.opendocument.spreadsheet-template'), - 'ott' => array('application/vnd.oasis.opendocument.template'), - 'p' => array('text/x-pascal'), - 'pas' => array('text/x-pascal'), - 'patch' => array('text/x-diff'), - 'pbm' => array('image/x-portable-bitmap'), - 'pdf' => array('application/pdf', 'application/x-download'), - 'php' => array('application/x-httpd-php'), - 'php3' => array('application/x-httpd-php'), - 'php4' => array('application/x-httpd-php'), - 'php5' => array('application/x-httpd-php'), - 'phps' => array('application/x-httpd-php-source'), - 'phtml' => array('application/x-httpd-php'), - 'pl' => array('text/x-perl'), - 'pm' => array('text/x-perl'), - 'png' => array('image/png', 'image/x-png'), - 'po' => array('text/x-gettext-translation'), - 'pot' => array('application/vnd.ms-powerpoint'), - 'pps' => array('application/vnd.ms-powerpoint'), - 'ppt' => array('application/powerpoint'), - 'ps' => array('application/postscript'), - 'psd' => array('application/x-photoshop', 'image/x-photoshop'), - 'pub' => array('application/x-mspublisher'), - 'py' => array('text/x-python'), - 'qt' => array('video/quicktime'), - 'ra' => array('audio/x-realaudio'), - 'ram' => array('audio/x-realaudio', 'audio/x-pn-realaudio'), - 'rar' => array('application/rar'), - 'rgb' => array('image/x-rgb'), - 'rm' => array('audio/x-pn-realaudio'), - 'rpm' => array('audio/x-pn-realaudio-plugin', 'application/x-redhat-package-manager'), - 'rss' => array('application/rss+xml'), - 'rtf' => array('text/rtf'), - 'rtx' => array('text/richtext'), - 'rv' => array('video/vnd.rn-realvideo'), - 'sea' => array('application/octet-stream'), - 'sh' => array('text/x-sh'), - 'shtml' => array('text/html'), - 'sit' => array('application/x-stuffit'), - 'smi' => array('application/smil'), - 'smil' => array('application/smil'), - 'so' => array('application/octet-stream'), - 'src' => array('application/x-wais-source'), - 'svg' => array('image/svg+xml'), - 'swf' => array('application/x-shockwave-flash'), - 't' => array('application/x-troff'), - 'tar' => array('application/x-tar'), - 'tcl' => array('text/x-tcl'), - 'tex' => array('application/x-tex'), - 'text' => array('text/plain'), - 'texti' => array('application/x-texinfo'), - 'textinfo' => array('application/x-texinfo'), - 'tgz' => array('application/x-tar'), - 'tif' => array('image/tiff'), - 'tiff' => array('image/tiff'), - 'torrent' => array('application/x-bittorrent'), - 'tr' => array('application/x-troff'), - 'tsv' => array('text/tab-separated-values'), - 'txt' => array('text/plain'), - 'wav' => array('audio/x-wav'), - 'wax' => array('audio/x-ms-wax'), - 'wbxml' => array('application/wbxml'), - 'wm' => array('video/x-ms-wm'), - 'wma' => array('audio/x-ms-wma'), - 'wmd' => array('application/x-ms-wmd'), - 'wmlc' => array('application/wmlc'), - 'wmv' => array('video/x-ms-wmv', 'application/octet-stream'), - 'wmx' => array('video/x-ms-wmx'), - 'wmz' => array('application/x-ms-wmz'), - 'word' => array('application/msword', 'application/octet-stream'), - 'wp5' => array('application/wordperfect5.1'), - 'wpd' => array('application/vnd.wordperfect'), - 'wvx' => array('video/x-ms-wvx'), - 'xbm' => array('image/x-xbitmap'), - 'xcf' => array('image/xcf'), - 'xhtml' => array('application/xhtml+xml'), - 'xht' => array('application/xhtml+xml'), - 'xl' => array('application/excel', 'application/vnd.ms-excel'), - 'xla' => array('application/excel', 'application/vnd.ms-excel'), - 'xlc' => array('application/excel', 'application/vnd.ms-excel'), - 'xlm' => array('application/excel', 'application/vnd.ms-excel'), - 'xls' => array('application/excel', 'application/vnd.ms-excel'), - 'xlt' => array('application/excel', 'application/vnd.ms-excel'), - 'xml' => array('text/xml'), - 'xof' => array('x-world/x-vrml'), - 'xpm' => array('image/x-xpixmap'), - 'xsl' => array('text/xml'), - 'xvid' => array('video/x-xvid'), - 'xwd' => array('image/x-xwindowdump'), - 'z' => array('application/x-compress'), - 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed') -); diff --git a/kohana/config/pagination.php b/kohana/config/pagination.php deleted file mode 100644 index 808fc315..00000000 --- a/kohana/config/pagination.php +++ /dev/null @@ -1,25 +0,0 @@ - 'pagination', - 'style' => 'classic', - 'uri_segment' => 3, - 'query_string' => '', - 'items_per_page' => 20, - 'auto_hide' => FALSE, -); diff --git a/kohana/config/profiler.php b/kohana/config/profiler.php deleted file mode 100644 index 98ab5a49..00000000 --- a/kohana/config/profiler.php +++ /dev/null @@ -1,8 +0,0 @@ - array('type' => 'int', 'max' => 127), - 'smallint' => array('type' => 'int', 'max' => 32767), - 'mediumint' => array('type' => 'int', 'max' => 8388607), - 'int' => array('type' => 'int', 'max' => 2147483647), - 'integer' => array('type' => 'int', 'max' => 2147483647), - 'bigint' => array('type' => 'int', 'max' => 9223372036854775807), - 'float' => array('type' => 'float'), - 'float unsigned' => array('type' => 'float', 'min' => 0), - 'boolean' => array('type' => 'boolean'), - 'time' => array('type' => 'string', 'format' => '00:00:00'), - 'time with time zone' => array('type' => 'string'), - 'date' => array('type' => 'string', 'format' => '0000-00-00'), - 'year' => array('type' => 'string', 'format' => '0000'), - 'datetime' => array('type' => 'string', 'format' => '0000-00-00 00:00:00'), - 'timestamp with time zone' => array('type' => 'string'), - 'char' => array('type' => 'string', 'exact' => TRUE), - 'binary' => array('type' => 'string', 'binary' => TRUE, 'exact' => TRUE), - 'varchar' => array('type' => 'string'), - 'varbinary' => array('type' => 'string', 'binary' => TRUE), - 'blob' => array('type' => 'string', 'binary' => TRUE), - 'text' => array('type' => 'string') -); - -// DOUBLE -$config['double'] = $config['double precision'] = $config['decimal'] = $config['real'] = $config['numeric'] = $config['float']; -$config['double unsigned'] = $config['float unsigned']; - -// BIT -$config['bit'] = $config['boolean']; - -// TIMESTAMP -$config['timestamp'] = $config['timestamp without time zone'] = $config['datetime']; - -// ENUM -$config['enum'] = $config['set'] = $config['varchar']; - -// TEXT -$config['tinytext'] = $config['mediumtext'] = $config['longtext'] = $config['text']; - -// BLOB -$config['tinyblob'] = $config['mediumblob'] = $config['longblob'] = $config['clob'] = $config['bytea'] = $config['blob']; - -// CHARACTER -$config['character'] = $config['char']; -$config['character varying'] = $config['varchar']; - -// TIME -$config['time without time zone'] = $config['time']; diff --git a/kohana/config/upload.php b/kohana/config/upload.php deleted file mode 100644 index df26a2d2..00000000 --- a/kohana/config/upload.php +++ /dev/null @@ -1,17 +0,0 @@ - 'Windows Vista', - 'windows nt 5.2' => 'Windows 2003', - 'windows nt 5.0' => 'Windows 2000', - 'windows nt 5.1' => 'Windows XP', - 'windows nt 4.0' => 'Windows NT', - 'winnt4.0' => 'Windows NT', - 'winnt 4.0' => 'Windows NT', - 'winnt' => 'Windows NT', - 'windows 98' => 'Windows 98', - 'win98' => 'Windows 98', - 'windows 95' => 'Windows 95', - 'win95' => 'Windows 95', - 'windows' => 'Unknown Windows OS', - 'os x' => 'Mac OS X', - 'intel mac' => 'Intel Mac', - 'ppc mac' => 'PowerPC Mac', - 'powerpc' => 'PowerPC', - 'ppc' => 'PowerPC', - 'cygwin' => 'Cygwin', - 'linux' => 'Linux', - 'debian' => 'Debian', - 'openvms' => 'OpenVMS', - 'sunos' => 'Sun Solaris', - 'amiga' => 'Amiga', - 'beos' => 'BeOS', - 'apachebench' => 'ApacheBench', - 'freebsd' => 'FreeBSD', - 'netbsd' => 'NetBSD', - 'bsdi' => 'BSDi', - 'openbsd' => 'OpenBSD', - 'os/2' => 'OS/2', - 'warp' => 'OS/2', - 'aix' => 'AIX', - 'irix' => 'Irix', - 'osf' => 'DEC OSF', - 'hp-ux' => 'HP-UX', - 'hurd' => 'GNU/Hurd', - 'unix' => 'Unknown Unix OS', -); - -// The order of this array should NOT be changed. Many browsers return -// multiple browser types so we want to identify the sub-type first. -$config['browser'] = array -( - 'Opera' => 'Opera', - 'MSIE' => 'Internet Explorer', - 'Internet Explorer' => 'Internet Explorer', - 'Shiira' => 'Shiira', - 'Firefox' => 'Firefox', - 'Chimera' => 'Chimera', - 'Phoenix' => 'Phoenix', - 'Firebird' => 'Firebird', - 'Camino' => 'Camino', - 'Netscape' => 'Netscape', - 'OmniWeb' => 'OmniWeb', - 'Chrome' => 'Chrome', - 'Safari' => 'Safari', - 'Konqueror' => 'Konqueror', - 'Epiphany' => 'Epiphany', - 'Galeon' => 'Galeon', - 'Mozilla' => 'Mozilla', - 'icab' => 'iCab', - 'lynx' => 'Lynx', - 'links' => 'Links', - 'hotjava' => 'HotJava', - 'amaya' => 'Amaya', - 'IBrowse' => 'IBrowse', -); - -$config['mobile'] = array -( - 'mobileexplorer' => 'Mobile Explorer', - 'openwave' => 'Open Wave', - 'opera mini' => 'Opera Mini', - 'operamini' => 'Opera Mini', - 'elaine' => 'Palm', - 'palmsource' => 'Palm', - 'digital paths' => 'Palm', - 'avantgo' => 'Avantgo', - 'xiino' => 'Xiino', - 'palmscape' => 'Palmscape', - 'nokia' => 'Nokia', - 'ericsson' => 'Ericsson', - 'blackBerry' => 'BlackBerry', - 'motorola' => 'Motorola', - 'iphone' => 'iPhone', - 'android' => 'Android', -); - -// There are hundreds of bots but these are the most common. -$config['robot'] = array -( - 'googlebot' => 'Googlebot', - 'msnbot' => 'MSNBot', - 'slurp' => 'Inktomi Slurp', - 'yahoo' => 'Yahoo', - 'askjeeves' => 'AskJeeves', - 'fastcrawler' => 'FastCrawler', - 'infoseek' => 'InfoSeek Robot 1.0', - 'lycos' => 'Lycos', -); \ No newline at end of file diff --git a/kohana/config/view.php b/kohana/config/view.php deleted file mode 100644 index 6bed22e0..00000000 --- a/kohana/config/view.php +++ /dev/null @@ -1,17 +0,0 @@ -template = new View($this->template); - - if ($this->auto_render == TRUE) - { - // Render the template immediately after the controller method - Event::add('system.post_controller', array($this, '_render')); - } - } - - /** - * Render the loaded template. - */ - public function _render() - { - if ($this->auto_render == TRUE) - { - // Render the template when the class is destroyed - $this->template->render(TRUE); - } - } - -} // End Template_Controller \ No newline at end of file diff --git a/kohana/core/Benchmark.php b/kohana/core/Benchmark.php deleted file mode 100644 index ce230f11..00000000 --- a/kohana/core/Benchmark.php +++ /dev/null @@ -1,125 +0,0 @@ - microtime(TRUE), - 'stop' => FALSE, - 'memory_start' => self::memory_usage(), - 'memory_stop' => FALSE - ); - - array_unshift(self::$marks[$name], $mark); - } - - /** - * Set a benchmark stop point. - * - * @param string benchmark name - * @return void - */ - public static function stop($name) - { - if (isset(self::$marks[$name]) AND self::$marks[$name][0]['stop'] === FALSE) - { - self::$marks[$name][0]['stop'] = microtime(TRUE); - self::$marks[$name][0]['memory_stop'] = self::memory_usage(); - } - } - - /** - * Get the elapsed time between a start and stop. - * - * @param string benchmark name, TRUE for all - * @param integer number of decimal places to count to - * @return array - */ - public static function get($name, $decimals = 4) - { - if ($name === TRUE) - { - $times = array(); - $names = array_keys(self::$marks); - - foreach ($names as $name) - { - // Get each mark recursively - $times[$name] = self::get($name, $decimals); - } - - // Return the array - return $times; - } - - if ( ! isset(self::$marks[$name])) - return FALSE; - - if (self::$marks[$name][0]['stop'] === FALSE) - { - // Stop the benchmark to prevent mis-matched results - self::stop($name); - } - - // Return a string version of the time between the start and stop points - // Properly reading a float requires using number_format or sprintf - $time = $memory = 0; - for ($i = 0; $i < count(self::$marks[$name]); $i++) - { - $time += self::$marks[$name][$i]['stop'] - self::$marks[$name][$i]['start']; - $memory += self::$marks[$name][$i]['memory_stop'] - self::$marks[$name][$i]['memory_start']; - } - - return array - ( - 'time' => number_format($time, $decimals), - 'memory' => $memory, - 'count' => count(self::$marks[$name]) - ); - } - - /** - * Returns the current memory usage. This is only possible if the - * memory_get_usage function is supported in PHP. - * - * @return integer - */ - private static function memory_usage() - { - static $func; - - if ($func === NULL) - { - // Test if memory usage can be seen - $func = function_exists('memory_get_usage'); - } - - return $func ? memory_get_usage() : 0; - } - -} // End Benchmark diff --git a/kohana/core/Bootstrap.php b/kohana/core/Bootstrap.php deleted file mode 100644 index edfb086d..00000000 --- a/kohana/core/Bootstrap.php +++ /dev/null @@ -1,58 +0,0 @@ - $event_callback) - { - if ($callback === $event_callback) - { - unset(self::$events[$name][$i]); - } - } - } - } - - /** - * Execute all of the callbacks attached to an event. - * - * @param string event name - * @param array data can be processed as Event::$data by the callbacks - * @return void - */ - public static function run($name, & $data = NULL) - { - if ( ! empty(self::$events[$name])) - { - // So callbacks can access Event::$data - self::$data =& $data; - $callbacks = self::get($name); - - foreach ($callbacks as $callback) - { - call_user_func($callback); - } - - // Do this to prevent data from getting 'stuck' - $clear_data = ''; - self::$data =& $clear_data; - } - - // The event has been run! - self::$has_run[$name] = $name; - } - - /** - * Check if a given event has been run. - * - * @param string event name - * @return boolean - */ - public static function has_run($name) - { - return isset(self::$has_run[$name]); - } - -} // End Event \ No newline at end of file diff --git a/kohana/core/Kohana.php b/kohana/core/Kohana.php deleted file mode 100644 index ac64b5b8..00000000 --- a/kohana/core/Kohana.php +++ /dev/null @@ -1,1744 +0,0 @@ - 1, - 'alert' => 2, - 'info' => 3, - 'debug' => 4, - ); - - // Internal caches and write status - private static $internal_cache = array(); - private static $write_cache; - private static $internal_cache_path; - - /** - * Sets up the PHP environment. Adds error/exception handling, output - * buffering, and adds an auto-loading method for loading classes. - * - * This method is run immediately when this file is loaded, and is - * benchmarked as environment_setup. - * - * For security, this function also destroys the $_REQUEST global variable. - * Using the proper global (GET, POST, COOKIE, etc) is inherently more secure. - * The recommended way to fetch a global variable is using the Input library. - * @see http://www.php.net/globals - * - * @return void - */ - public static function setup() - { - static $run; - - // This function can only be run once - if ($run === TRUE) - return; - - // Start the environment setup benchmark - Benchmark::start(SYSTEM_BENCHMARK.'_environment_setup'); - - // Define Kohana error constant - define('E_KOHANA', 42); - - // Define 404 error constant - define('E_PAGE_NOT_FOUND', 43); - - // Define database error constant - define('E_DATABASE_ERROR', 44); - - if (self::$cache_lifetime = self::config('core.internal_cache')) - { - // Set the directory to be used for the internal cache - if ( ! self::$internal_cache_path = self::config('core.internal_cache_path')) - { - self::$internal_cache_path = APPPATH.'cache/'; - } - - // Load cached configuration and language files - self::$internal_cache['configuration'] = self::cache('configuration', self::$cache_lifetime); - self::$internal_cache['language'] = self::cache('language', self::$cache_lifetime); - - // Load cached file paths - self::$internal_cache['find_file_paths'] = self::cache('find_file_paths', self::$cache_lifetime); - - // Enable cache saving - Event::add('system.shutdown', array(__CLASS__, 'internal_cache_save')); - } - - // Disable notices and "strict" errors - $ER = error_reporting(~E_NOTICE & ~E_STRICT); - - // Set the user agent - self::$user_agent = ( ! empty($_SERVER['HTTP_USER_AGENT']) ? trim($_SERVER['HTTP_USER_AGENT']) : ''); - - if (function_exists('date_default_timezone_set')) - { - $timezone = self::config('locale.timezone'); - - // Set default timezone, due to increased validation of date settings - // which cause massive amounts of E_NOTICEs to be generated in PHP 5.2+ - date_default_timezone_set(empty($timezone) ? date_default_timezone_get() : $timezone); - } - - // Restore error reporting - error_reporting($ER); - - // Start output buffering - ob_start(array(__CLASS__, 'output_buffer')); - - // Save buffering level - self::$buffer_level = ob_get_level(); - - // Set autoloader - spl_autoload_register(array('Kohana', 'auto_load')); - - // Set error handler - set_error_handler(array('Kohana', 'exception_handler')); - - // Set exception handler - set_exception_handler(array('Kohana', 'exception_handler')); - - // Send default text/html UTF-8 header - header('Content-Type: text/html; charset=UTF-8'); - - // Load locales - $locales = self::config('locale.language'); - - // Make first locale UTF-8 - $locales[0] .= '.UTF-8'; - - // Set locale information - self::$locale = setlocale(LC_ALL, $locales); - - if (self::$configuration['core']['log_threshold'] > 0) - { - // Set the log directory - self::log_directory(self::$configuration['core']['log_directory']); - - // Enable log writing at shutdown - register_shutdown_function(array(__CLASS__, 'log_save')); - } - - // Enable Kohana routing - Event::add('system.routing', array('Router', 'find_uri')); - Event::add('system.routing', array('Router', 'setup')); - - // Enable Kohana controller initialization - Event::add('system.execute', array('Kohana', 'instance')); - - // Enable Kohana 404 pages - Event::add('system.404', array('Kohana', 'show_404')); - - // Enable Kohana output handling - Event::add('system.shutdown', array('Kohana', 'shutdown')); - - if (self::config('core.enable_hooks') === TRUE) - { - // Find all the hook files - $hooks = self::list_files('hooks', TRUE); - - foreach ($hooks as $file) - { - // Load the hook - include $file; - } - } - - // Setup is complete, prevent it from being run again - $run = TRUE; - - // Stop the environment setup routine - Benchmark::stop(SYSTEM_BENCHMARK.'_environment_setup'); - } - - /** - * Loads the controller and initializes it. Runs the pre_controller, - * post_controller_constructor, and post_controller events. Triggers - * a system.404 event when the route cannot be mapped to a controller. - * - * This method is benchmarked as controller_setup and controller_execution. - * - * @return object instance of controller - */ - public static function & instance() - { - if (self::$instance === NULL) - { - Benchmark::start(SYSTEM_BENCHMARK.'_controller_setup'); - - // Include the Controller file - require Router::$controller_path; - - try - { - // Start validation of the controller - $class = new ReflectionClass(ucfirst(Router::$controller).'_Controller'); - } - catch (ReflectionException $e) - { - // Controller does not exist - Event::run('system.404'); - } - - if ($class->isAbstract() OR (IN_PRODUCTION AND $class->getConstant('ALLOW_PRODUCTION') == FALSE)) - { - // Controller is not allowed to run in production - Event::run('system.404'); - } - - // Run system.pre_controller - Event::run('system.pre_controller'); - - // Create a new controller instance - $controller = $class->newInstance(); - - // Controller constructor has been executed - Event::run('system.post_controller_constructor'); - - try - { - // Load the controller method - $method = $class->getMethod(Router::$method); - - // Method exists - if (Router::$method[0] === '_') - { - // Do not allow access to hidden methods - Event::run('system.404'); - } - - if ($method->isProtected() or $method->isPrivate()) - { - // Do not attempt to invoke protected methods - throw new ReflectionException('protected controller method'); - } - - // Default arguments - $arguments = Router::$arguments; - } - catch (ReflectionException $e) - { - // Use __call instead - $method = $class->getMethod('__call'); - - // Use arguments in __call format - $arguments = array(Router::$method, Router::$arguments); - } - - // Stop the controller setup benchmark - Benchmark::stop(SYSTEM_BENCHMARK.'_controller_setup'); - - // Start the controller execution benchmark - Benchmark::start(SYSTEM_BENCHMARK.'_controller_execution'); - - // Execute the controller method - $method->invokeArgs($controller, $arguments); - - // Controller method has been executed - Event::run('system.post_controller'); - - // Stop the controller execution benchmark - Benchmark::stop(SYSTEM_BENCHMARK.'_controller_execution'); - } - - return self::$instance; - } - - /** - * Get all include paths. APPPATH is the first path, followed by module - * paths in the order they are configured, follow by the SYSPATH. - * - * @param boolean re-process the include paths - * @return array - */ - public static function include_paths($process = FALSE) - { - if ($process === TRUE) - { - self::$include_paths = array(); - - foreach (self::$configuration['core']['modules'] as $path) - { - if ($path = str_replace('\\', '/', realpath($path))) - { - // Add a valid path - self::$include_paths[] = $path.'/'; - } - } - - // Add APPPATH after all modules - self::$include_paths[] = APPPATH; - - // Add SYSPATH as the last path - self::$include_paths[] = SYSPATH; - } - - return self::$include_paths; - } - - /** - * Get a config item or group. - * - * @param string item name - * @param boolean force a forward slash (/) at the end of the item - * @param boolean is the item required? - * @return mixed - */ - public static function config($key, $slash = FALSE, $required = TRUE) - { - if (self::$configuration === NULL) - { - // Load core configuration - self::$configuration['core'] = self::config_load('core'); - - // Re-parse the include paths - self::include_paths(TRUE); - } - - // Get the group name from the key - $group = explode('.', $key, 2); - $group = $group[0]; - - if ( ! isset(self::$configuration[$group])) - { - // Load the configuration group - self::$configuration[$group] = self::config_load($group, $required); - } - - // Get the value of the key string - $value = self::key_string(self::$configuration, $key); - - if ($slash === TRUE AND is_string($value) AND $value !== '') - { - // Force the value to end with "/" - $value = rtrim($value, '/').'/'; - } - - return $value; - } - - /** - * Sets a configuration item, if allowed. - * - * @param string config key string - * @param string config value - * @return boolean - */ - public static function config_set($key, $value) - { - // Do this to make sure that the config array is already loaded - self::config($key); - - if (substr($key, 0, 7) === 'routes.') - { - // Routes cannot contain sub keys due to possible dots in regex - $keys = explode('.', $key, 2); - } - else - { - // Convert dot-noted key string to an array - $keys = explode('.', $key); - } - - // Used for recursion - $conf =& self::$configuration; - $last = count($keys) - 1; - - foreach ($keys as $i => $k) - { - if ($i === $last) - { - $conf[$k] = $value; - } - else - { - $conf =& $conf[$k]; - } - } - - if ($key === 'core.modules') - { - // Reprocess the include paths - self::include_paths(TRUE); - } - - return TRUE; - } - - /** - * Load a config file. - * - * @param string config filename, without extension - * @param boolean is the file required? - * @return array - */ - public static function config_load($name, $required = TRUE) - { - if ($name === 'core') - { - // Load the application configuration file - require APPPATH.'config/config'.EXT; - - if ( ! isset($config['site_domain'])) - { - // Invalid config file - die('Your Kohana application configuration file is not valid.'); - } - - return $config; - } - - if (isset(self::$internal_cache['configuration'][$name])) - return self::$internal_cache['configuration'][$name]; - - // Load matching configs - $configuration = array(); - - if ($files = self::find_file('config', $name, $required)) - { - foreach ($files as $file) - { - require $file; - - if (isset($config) AND is_array($config)) - { - // Merge in configuration - $configuration = array_merge($configuration, $config); - } - } - } - - if ( ! isset(self::$write_cache['configuration'])) - { - // Cache has changed - self::$write_cache['configuration'] = TRUE; - } - - return self::$internal_cache['configuration'][$name] = $configuration; - } - - /** - * Clears a config group from the cached configuration. - * - * @param string config group - * @return void - */ - public static function config_clear($group) - { - // Remove the group from config - unset(self::$configuration[$group], self::$internal_cache['configuration'][$group]); - - if ( ! isset(self::$write_cache['configuration'])) - { - // Cache has changed - self::$write_cache['configuration'] = TRUE; - } - } - - /** - * Add a new message to the log. - * - * @param string type of message - * @param string message text - * @return void - */ - public static function log($type, $message) - { - if (self::$log_levels[$type] <= self::$configuration['core']['log_threshold']) - { - $message = array(date('Y-m-d H:i:s P'), $type, $message); - - // Run the system.log event - Event::run('system.log', $message); - - self::$log[] = $message; - } - } - - /** - * Save all currently logged messages. - * - * @return void - */ - public static function log_save() - { - if (empty(self::$log) OR self::$configuration['core']['log_threshold'] < 1) - return; - - // Filename of the log - $filename = self::log_directory().date('Y-m-d').'.log'.EXT; - - if ( ! is_file($filename)) - { - // Write the SYSPATH checking header - file_put_contents($filename, - ''.PHP_EOL.PHP_EOL); - - // Prevent external writes - chmod($filename, 0644); - } - - // Messages to write - $messages = array(); - - do - { - // Load the next mess - list ($date, $type, $text) = array_shift(self::$log); - - // Add a new message line - $messages[] = $date.' --- '.$type.': '.$text; - } - while ( ! empty(self::$log)); - - // Write messages to log file - file_put_contents($filename, implode(PHP_EOL, $messages).PHP_EOL, FILE_APPEND); - } - - /** - * Get or set the logging directory. - * - * @param string new log directory - * @return string - */ - public static function log_directory($dir = NULL) - { - static $directory; - - if ( ! empty($dir)) - { - // Get the directory path - $dir = realpath($dir); - - if (is_dir($dir) AND is_writable($dir)) - { - // Change the log directory - $directory = str_replace('\\', '/', $dir).'/'; - } - else - { - // Log directory is invalid - throw new Kohana_Exception('core.log_dir_unwritable', $dir); - } - } - - return $directory; - } - - /** - * Load data from a simple cache file. This should only be used internally, - * and is NOT a replacement for the Cache library. - * - * @param string unique name of cache - * @param integer expiration in seconds - * @return mixed - */ - public static function cache($name, $lifetime) - { - if ($lifetime > 0) - { - $path = self::$internal_cache_path.'kohana_'.$name; - - if (is_file($path)) - { - // Check the file modification time - if ((time() - filemtime($path)) < $lifetime) - { - // Cache is valid - return unserialize(file_get_contents($path)); - } - else - { - // Cache is invalid, delete it - unlink($path); - } - } - } - - // No cache found - return NULL; - } - - /** - * Save data to a simple cache file. This should only be used internally, and - * is NOT a replacement for the Cache library. - * - * @param string cache name - * @param mixed data to cache - * @param integer expiration in seconds - * @return boolean - */ - public static function cache_save($name, $data, $lifetime) - { - if ($lifetime < 1) - return FALSE; - - $path = self::$internal_cache_path.'kohana_'.$name; - - if ($data === NULL) - { - // Delete cache - return (is_file($path) and unlink($path)); - } - else - { - // Write data to cache file - return (bool) file_put_contents($path, serialize($data)); - } - } - - /** - * Kohana output handler. - * - * @param string current output buffer - * @return string - */ - public static function output_buffer($output) - { - if ( ! Event::has_run('system.send_headers')) - { - // Run the send_headers event, specifically for cookies being set - Event::run('system.send_headers'); - } - - // Set final output - self::$output = $output; - - // Set and return the final output - return $output; - } - - /** - * Closes all open output buffers, either by flushing or cleaning all - * open buffers, including the Kohana output buffer. - * - * @param boolean disable to clear buffers, rather than flushing - * @return void - */ - public static function close_buffers($flush = TRUE) - { - if (ob_get_level() >= self::$buffer_level) - { - // Set the close function - $close = ($flush === TRUE) ? 'ob_end_flush' : 'ob_end_clean'; - - while (ob_get_level() > self::$buffer_level) - { - // Flush or clean the buffer - $close(); - } - - // This will flush the Kohana buffer, which sets self::$output - ob_end_clean(); - - // Reset the buffer level - self::$buffer_level = ob_get_level(); - } - } - - /** - * Triggers the shutdown of Kohana by closing the output buffer, runs the system.display event. - * - * @return void - */ - public static function shutdown() - { - // Close output buffers - self::close_buffers(TRUE); - - // Run the output event - Event::run('system.display', self::$output); - - // Render the final output - self::render(self::$output); - } - - /** - * Inserts global Kohana variables into the generated output and prints it. - * - * @param string final output that will displayed - * @return void - */ - public static function render($output) - { - if (self::config('core.render_stats') === TRUE) - { - // Fetch memory usage in MB - $memory = function_exists('memory_get_usage') ? (memory_get_usage() / 1024 / 1024) : 0; - - // Fetch benchmark for page execution time - $benchmark = Benchmark::get(SYSTEM_BENCHMARK.'_total_execution'); - - // Replace the global template variables - $output = str_replace( - array - ( - '{kohana_version}', - '{kohana_codename}', - '{execution_time}', - '{memory_usage}', - '{included_files}', - ), - array - ( - KOHANA_VERSION, - KOHANA_CODENAME, - $benchmark['time'], - number_format($memory, 2).'MB', - count(get_included_files()), - ), - $output - ); - } - - if ($level = self::config('core.output_compression') AND ini_get('output_handler') !== 'ob_gzhandler' AND (int) ini_get('zlib.output_compression') === 0) - { - if ($level < 1 OR $level > 9) - { - // Normalize the level to be an integer between 1 and 9. This - // step must be done to prevent gzencode from triggering an error - $level = max(1, min($level, 9)); - } - - if (stripos(@$_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) - { - $compress = 'gzip'; - } - elseif (stripos(@$_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== FALSE) - { - $compress = 'deflate'; - } - } - - if (isset($compress) AND $level > 0) - { - switch ($compress) - { - case 'gzip': - // Compress output using gzip - $output = gzencode($output, $level); - break; - case 'deflate': - // Compress output using zlib (HTTP deflate) - $output = gzdeflate($output, $level); - break; - } - - // This header must be sent with compressed content to prevent - // browser caches from breaking - header('Vary: Accept-Encoding'); - - // Send the content encoding header - header('Content-Encoding: '.$compress); - - // Sending Content-Length in CGI can result in unexpected behavior - if (stripos(PHP_SAPI, 'cgi') === FALSE) - { - header('Content-Length: '.strlen($output)); - } - } - - echo $output; - } - - /** - * Displays a 404 page. - * - * @throws Kohana_404_Exception - * @param string URI of page - * @param string custom template - * @return void - */ - public static function show_404($page = FALSE, $template = FALSE) - { - throw new Kohana_404_Exception($page, $template); - } - - /** - * Dual-purpose PHP error and exception handler. Uses the kohana_error_page - * view to display the message. - * - * @param integer|object exception object or error code - * @param string error message - * @param string filename - * @param integer line number - * @return void - */ - public static function exception_handler($exception, $message = NULL, $file = NULL, $line = NULL) - { - try - { - // PHP errors have 5 args, always - $PHP_ERROR = (func_num_args() === 5); - - // Test to see if errors should be displayed - if ($PHP_ERROR AND (error_reporting() & $exception) === 0) - return; - - // This is useful for hooks to determine if a page has an error - self::$has_error = TRUE; - - // Error handling will use exactly 5 args, every time - if ($PHP_ERROR) - { - $code = $exception; - $type = 'PHP Error'; - $template = 'kohana_error_page'; - } - else - { - $code = $exception->getCode(); - $type = get_class($exception); - $message = $exception->getMessage(); - $file = $exception->getFile(); - $line = $exception->getLine(); - $template = ($exception instanceof Kohana_Exception) ? $exception->getTemplate() : 'kohana_error_page'; - } - - if (is_numeric($code)) - { - $codes = self::lang('errors'); - - if ( ! empty($codes[$code])) - { - list($level, $error, $description) = $codes[$code]; - } - else - { - $level = 1; - $error = $PHP_ERROR ? 'Unknown Error' : get_class($exception); - $description = ''; - } - } - else - { - // Custom error message, this will never be logged - $level = 5; - $error = $code; - $description = ''; - } - - // Remove the DOCROOT from the path, as a security precaution - $file = str_replace('\\', '/', realpath($file)); - $file = preg_replace('|^'.preg_quote(DOCROOT).'|', '', $file); - - if ($level <= self::$configuration['core']['log_threshold']) - { - // Log the error - self::log('error', self::lang('core.uncaught_exception', $type, $message, $file, $line)); - } - - if ($PHP_ERROR) - { - $description = self::lang('errors.'.E_RECOVERABLE_ERROR); - $description = is_array($description) ? $description[2] : ''; - - if ( ! headers_sent()) - { - // Send the 500 header - header('HTTP/1.1 500 Internal Server Error'); - } - } - else - { - if (method_exists($exception, 'sendHeaders') AND ! headers_sent()) - { - // Send the headers if they have not already been sent - $exception->sendHeaders(); - } - } - - while (ob_get_level() > self::$buffer_level) - { - // Close open buffers - ob_end_clean(); - } - - // Test if display_errors is on - if (self::$configuration['core']['display_errors'] === TRUE) - { - if ( ! IN_PRODUCTION AND $line != FALSE) - { - // Remove the first entry of debug_backtrace(), it is the exception_handler call - $trace = $PHP_ERROR ? array_slice(debug_backtrace(), 1) : $exception->getTrace(); - - // Beautify backtrace - $trace = self::backtrace($trace); - } - - // Load the error - require self::find_file('views', empty($template) ? 'kohana_error_page' : $template); - } - else - { - // Get the i18n messages - $error = self::lang('core.generic_error'); - $message = self::lang('core.errors_disabled', url::site(), url::site(Router::$current_uri)); - - // Load the errors_disabled view - require self::find_file('views', 'kohana_error_disabled'); - } - - if ( ! Event::has_run('system.shutdown')) - { - // Run the shutdown even to ensure a clean exit - Event::run('system.shutdown'); - } - - // Turn off error reporting - error_reporting(0); - exit; - } - catch (Exception $e) - { - if (IN_PRODUCTION) - { - die('Fatal Error'); - } - else - { - die('Fatal Error: '.$e->getMessage().' File: '.$e->getFile().' Line: '.$e->getLine()); - } - } - } - - /** - * Provides class auto-loading. - * - * @throws Kohana_Exception - * @param string name of class - * @return bool - */ - public static function auto_load($class) - { - if (class_exists($class, FALSE)) - return TRUE; - - if (($suffix = strrpos($class, '_')) > 0) - { - // Find the class suffix - $suffix = substr($class, $suffix + 1); - } - else - { - // No suffix - $suffix = FALSE; - } - - if ($suffix === 'Core') - { - $type = 'libraries'; - $file = substr($class, 0, -5); - } - elseif ($suffix === 'Controller') - { - $type = 'controllers'; - // Lowercase filename - $file = strtolower(substr($class, 0, -11)); - } - elseif ($suffix === 'Model') - { - $type = 'models'; - // Lowercase filename - $file = strtolower(substr($class, 0, -6)); - } - elseif ($suffix === 'Driver') - { - $type = 'libraries/drivers'; - $file = str_replace('_', '/', substr($class, 0, -7)); - } - else - { - // This could be either a library or a helper, but libraries must - // always be capitalized, so we check if the first character is - // uppercase. If it is, we are loading a library, not a helper. - $type = ($class[0] < 'a') ? 'libraries' : 'helpers'; - $file = $class; - } - - if ($filename = self::find_file($type, $file)) - { - // Load the class - require $filename; - } - else - { - // The class could not be found - return FALSE; - } - - if ($filename = self::find_file($type, self::$configuration['core']['extension_prefix'].$class)) - { - // Load the class extension - require $filename; - } - elseif ($suffix !== 'Core' AND class_exists($class.'_Core', FALSE)) - { - // Class extension to be evaluated - $extension = 'class '.$class.' extends '.$class.'_Core { }'; - - // Start class analysis - $core = new ReflectionClass($class.'_Core'); - - if ($core->isAbstract()) - { - // Make the extension abstract - $extension = 'abstract '.$extension; - } - - // Transparent class extensions are handled using eval. This is - // a disgusting hack, but it gets the job done. - eval($extension); - } - - return TRUE; - } - - /** - * Find a resource file in a given directory. Files will be located according - * to the order of the include paths. Configuration and i18n files will be - * returned in reverse order. - * - * @throws Kohana_Exception if file is required and not found - * @param string directory to search in - * @param string filename to look for (without extension) - * @param boolean file required - * @param string file extension - * @return array if the type is config, i18n or l10n - * @return string if the file is found - * @return FALSE if the file is not found - */ - public static function find_file($directory, $filename, $required = FALSE, $ext = FALSE) - { - // NOTE: This test MUST be not be a strict comparison (===), or empty - // extensions will be allowed! - if ($ext == '') - { - // Use the default extension - $ext = EXT; - } - else - { - // Add a period before the extension - $ext = '.'.$ext; - } - - // Search path - $search = $directory.'/'.$filename.$ext; - - if (isset(self::$internal_cache['find_file_paths'][$search])) - return self::$internal_cache['find_file_paths'][$search]; - - // Load include paths - $paths = self::$include_paths; - - // Nothing found, yet - $found = NULL; - - if ($directory === 'config' OR $directory === 'i18n') - { - // Search in reverse, for merging - $paths = array_reverse($paths); - - foreach ($paths as $path) - { - if (is_file($path.$search)) - { - // A matching file has been found - $found[] = $path.$search; - } - } - } - else - { - foreach ($paths as $path) - { - if (is_file($path.$search)) - { - // A matching file has been found - $found = $path.$search; - - // Stop searching - break; - } - } - } - - if ($found === NULL) - { - if ($required === TRUE) - { - // Directory i18n key - $directory = 'core.'.inflector::singular($directory); - - // If the file is required, throw an exception - throw new Kohana_Exception('core.resource_not_found', self::lang($directory), $filename); - } - else - { - // Nothing was found, return FALSE - $found = FALSE; - } - } - - if ( ! isset(self::$write_cache['find_file_paths'])) - { - // Write cache at shutdown - self::$write_cache['find_file_paths'] = TRUE; - } - - return self::$internal_cache['find_file_paths'][$search] = $found; - } - - /** - * Lists all files and directories in a resource path. - * - * @param string directory to search - * @param boolean list all files to the maximum depth? - * @param string full path to search (used for recursion, *never* set this manually) - * @return array filenames and directories - */ - public static function list_files($directory, $recursive = FALSE, $path = FALSE) - { - $files = array(); - - if ($path === FALSE) - { - $paths = array_reverse(self::include_paths()); - - foreach ($paths as $path) - { - // Recursively get and merge all files - $files = array_merge($files, self::list_files($directory, $recursive, $path.$directory)); - } - } - else - { - $path = rtrim($path, '/').'/'; - - if (is_readable($path)) - { - $items = (array) glob($path.'*'); - - if ( ! empty($items)) - { - foreach ($items as $index => $item) - { - $files[] = $item = str_replace('\\', '/', $item); - - // Handle recursion - if (is_dir($item) AND $recursive == TRUE) - { - // Filename should only be the basename - $item = pathinfo($item, PATHINFO_BASENAME); - - // Append sub-directory search - $files = array_merge($files, self::list_files($directory, TRUE, $path.$item)); - } - } - } - } - } - - return $files; - } - - /** - * Fetch an i18n language item. - * - * @param string language key to fetch - * @param array additional information to insert into the line - * @return string i18n language string, or the requested key if the i18n item is not found - */ - public static function lang($key, $args = array()) - { - // Extract the main group from the key - $group = explode('.', $key, 2); - $group = $group[0]; - - // Get locale name - $locale = self::config('locale.language.0'); - - if ( ! isset(self::$internal_cache['language'][$locale][$group])) - { - // Messages for this group - $messages = array(); - - if ($files = self::find_file('i18n', $locale.'/'.$group)) - { - foreach ($files as $file) - { - include $file; - - // Merge in configuration - if ( ! empty($lang) AND is_array($lang)) - { - foreach ($lang as $k => $v) - { - $messages[$k] = $v; - } - } - } - } - - if ( ! isset(self::$write_cache['language'])) - { - // Write language cache - self::$write_cache['language'] = TRUE; - } - - self::$internal_cache['language'][$locale][$group] = $messages; - } - - // Get the line from cache - $line = self::key_string(self::$internal_cache['language'][$locale], $key); - - if ($line === NULL) - { - self::log('error', 'Missing i18n entry '.$key.' for language '.$locale); - - // Return the key string as fallback - return $key; - } - - if (is_string($line) AND func_num_args() > 1) - { - $args = array_slice(func_get_args(), 1); - - // Add the arguments into the line - $line = vsprintf($line, is_array($args[0]) ? $args[0] : $args); - } - - return $line; - } - - /** - * Returns the value of a key, defined by a 'dot-noted' string, from an array. - * - * @param array array to search - * @param string dot-noted string: foo.bar.baz - * @return string if the key is found - * @return void if the key is not found - */ - public static function key_string($array, $keys) - { - if (empty($array)) - return NULL; - - // Prepare for loop - $keys = explode('.', $keys); - - do - { - // Get the next key - $key = array_shift($keys); - - if (isset($array[$key])) - { - if (is_array($array[$key]) AND ! empty($keys)) - { - // Dig down to prepare the next loop - $array = $array[$key]; - } - else - { - // Requested key was found - return $array[$key]; - } - } - else - { - // Requested key is not set - break; - } - } - while ( ! empty($keys)); - - return NULL; - } - - /** - * Sets values in an array by using a 'dot-noted' string. - * - * @param array array to set keys in (reference) - * @param string dot-noted string: foo.bar.baz - * @return mixed fill value for the key - * @return void - */ - public static function key_string_set( & $array, $keys, $fill = NULL) - { - if (is_object($array) AND ($array instanceof ArrayObject)) - { - // Copy the array - $array_copy = $array->getArrayCopy(); - - // Is an object - $array_object = TRUE; - } - else - { - if ( ! is_array($array)) - { - // Must always be an array - $array = (array) $array; - } - - // Copy is a reference to the array - $array_copy =& $array; - } - - if (empty($keys)) - return $array; - - // Create keys - $keys = explode('.', $keys); - - // Create reference to the array - $row =& $array_copy; - - for ($i = 0, $end = count($keys) - 1; $i <= $end; $i++) - { - // Get the current key - $key = $keys[$i]; - - if ( ! isset($row[$key])) - { - if (isset($keys[$i + 1])) - { - // Make the value an array - $row[$key] = array(); - } - else - { - // Add the fill key - $row[$key] = $fill; - } - } - elseif (isset($keys[$i + 1])) - { - // Make the value an array - $row[$key] = (array) $row[$key]; - } - - // Go down a level, creating a new row reference - $row =& $row[$key]; - } - - if (isset($array_object)) - { - // Swap the array back in - $array->exchangeArray($array_copy); - } - } - - /** - * Retrieves current user agent information: - * keys: browser, version, platform, mobile, robot, referrer, languages, charsets - * tests: is_browser, is_mobile, is_robot, accept_lang, accept_charset - * - * @param string key or test name - * @param string used with "accept" tests: user_agent(accept_lang, en) - * @return array languages and charsets - * @return string all other keys - * @return boolean all tests - */ - public static function user_agent($key = 'agent', $compare = NULL) - { - static $info; - - // Return the raw string - if ($key === 'agent') - return self::$user_agent; - - if ($info === NULL) - { - // Parse the user agent and extract basic information - $agents = self::config('user_agents'); - - foreach ($agents as $type => $data) - { - foreach ($data as $agent => $name) - { - if (stripos(self::$user_agent, $agent) !== FALSE) - { - if ($type === 'browser' AND preg_match('|'.preg_quote($agent).'[^0-9.]*+([0-9.][0-9.a-z]*)|i', self::$user_agent, $match)) - { - // Set the browser version - $info['version'] = $match[1]; - } - - // Set the agent name - $info[$type] = $name; - break; - } - } - } - } - - if (empty($info[$key])) - { - switch ($key) - { - case 'is_robot': - case 'is_browser': - case 'is_mobile': - // A boolean result - $return = ! empty($info[substr($key, 3)]); - break; - case 'languages': - $return = array(); - if ( ! empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) - { - if (preg_match_all('/[-a-z]{2,}/', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])), $matches)) - { - // Found a result - $return = $matches[0]; - } - } - break; - case 'charsets': - $return = array(); - if ( ! empty($_SERVER['HTTP_ACCEPT_CHARSET'])) - { - if (preg_match_all('/[-a-z0-9]{2,}/', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET'])), $matches)) - { - // Found a result - $return = $matches[0]; - } - } - break; - case 'referrer': - if ( ! empty($_SERVER['HTTP_REFERER'])) - { - // Found a result - $return = trim($_SERVER['HTTP_REFERER']); - } - break; - } - - // Cache the return value - isset($return) and $info[$key] = $return; - } - - if ( ! empty($compare)) - { - // The comparison must always be lowercase - $compare = strtolower($compare); - - switch ($key) - { - case 'accept_lang': - // Check if the lange is accepted - return in_array($compare, self::user_agent('languages')); - break; - case 'accept_charset': - // Check if the charset is accepted - return in_array($compare, self::user_agent('charsets')); - break; - default: - // Invalid comparison - return FALSE; - break; - } - } - - // Return the key, if set - return isset($info[$key]) ? $info[$key] : NULL; - } - - /** - * Quick debugging of any variable. Any number of parameters can be set. - * - * @return string - */ - public static function debug() - { - if (func_num_args() === 0) - return; - - // Get params - $params = func_get_args(); - $output = array(); - - foreach ($params as $var) - { - $output[] = '
      ('.gettype($var).') '.html::specialchars(print_r($var, TRUE)).'
      '; - } - - return implode("\n", $output); - } - - /** - * Displays nice backtrace information. - * @see http://php.net/debug_backtrace - * - * @param array backtrace generated by an exception or debug_backtrace - * @return string - */ - public static function backtrace($trace) - { - if ( ! is_array($trace)) - return; - - // Final output - $output = array(); - - foreach ($trace as $entry) - { - $temp = '
    • '; - - if (isset($entry['file'])) - { - $temp .= self::lang('core.error_file_line', preg_replace('!^'.preg_quote(DOCROOT).'!', '', $entry['file']), $entry['line']); - } - - $temp .= '
      ';
      -
      -			if (isset($entry['class']))
      -			{
      -				// Add class and call type
      -				$temp .= $entry['class'].$entry['type'];
      -			}
      -
      -			// Add function
      -			$temp .= $entry['function'].'( ';
      -
      -			// Add function args
      -			if (isset($entry['args']) AND is_array($entry['args']))
      -			{
      -				// Separator starts as nothing
      -				$sep = '';
      -
      -				while ($arg = array_shift($entry['args']))
      -				{
      -					if (is_string($arg) AND is_file($arg))
      -					{
      -						// Remove docroot from filename
      -						$arg = preg_replace('!^'.preg_quote(DOCROOT).'!', '', $arg);
      -					}
      -
      -					$temp .= $sep.html::specialchars(print_r($arg, TRUE));
      -
      -					// Change separator to a comma
      -					$sep = ', ';
      -				}
      -			}
      -
      -			$temp .= ' )
    • '; - - $output[] = $temp; - } - - return '
        '.implode("\n", $output).'
      '; - } - - /** - * Saves the internal caches: configuration, include paths, etc. - * - * @return boolean - */ - public static function internal_cache_save() - { - if ( ! is_array(self::$write_cache)) - return FALSE; - - // Get internal cache names - $caches = array_keys(self::$write_cache); - - // Nothing written - $written = FALSE; - - foreach ($caches as $cache) - { - if (isset(self::$internal_cache[$cache])) - { - // Write the cache file - self::cache_save($cache, self::$internal_cache[$cache], self::$configuration['core']['internal_cache']); - - // A cache has been written - $written = TRUE; - } - } - - return $written; - } - -} // End Kohana - -/** - * Creates a generic i18n exception. - */ -class Kohana_Exception extends Exception { - - // Template file - protected $template = 'kohana_error_page'; - - // Header - protected $header = FALSE; - - // Error code - protected $code = E_KOHANA; - - /** - * Set exception message. - * - * @param string i18n language key for the message - * @param array addition line parameters - */ - public function __construct($error) - { - $args = array_slice(func_get_args(), 1); - - // Fetch the error message - $message = Kohana::lang($error, $args); - - if ($message === $error OR empty($message)) - { - // Unable to locate the message for the error - $message = 'Unknown Exception: '.$error; - } - - // Sets $this->message the proper way - parent::__construct($message); - } - - /** - * Magic method for converting an object to a string. - * - * @return string i18n message - */ - public function __toString() - { - return (string) $this->message; - } - - /** - * Fetch the template name. - * - * @return string - */ - public function getTemplate() - { - return $this->template; - } - - /** - * Sends an Internal Server Error header. - * - * @return void - */ - public function sendHeaders() - { - // Send the 500 header - header('HTTP/1.1 500 Internal Server Error'); - } - -} // End Kohana Exception - -/** - * Creates a custom exception. - */ -class Kohana_User_Exception extends Kohana_Exception { - - /** - * Set exception title and message. - * - * @param string exception title string - * @param string exception message string - * @param string custom error template - */ - public function __construct($title, $message, $template = FALSE) - { - Exception::__construct($message); - - $this->code = $title; - - if ($template !== FALSE) - { - $this->template = $template; - } - } - -} // End Kohana PHP Exception - -/** - * Creates a Page Not Found exception. - */ -class Kohana_404_Exception extends Kohana_Exception { - - protected $code = E_PAGE_NOT_FOUND; - - /** - * Set internal properties. - * - * @param string URL of page - * @param string custom error template - */ - public function __construct($page = FALSE, $template = FALSE) - { - if ($page === FALSE) - { - // Construct the page URI using Router properties - $page = Router::$current_uri.Router::$url_suffix.Router::$query_string; - } - - Exception::__construct(Kohana::lang('core.page_not_found', $page)); - - $this->template = $template; - } - - /** - * Sends "File Not Found" headers, to emulate server behavior. - * - * @return void - */ - public function sendHeaders() - { - // Send the 404 header - header('HTTP/1.1 404 File Not Found'); - } - -} // End Kohana 404 Exception diff --git a/kohana/core/utf8.php b/kohana/core/utf8.php deleted file mode 100644 index 9f20f421..00000000 --- a/kohana/core/utf8.php +++ /dev/null @@ -1,743 +0,0 @@ -PCRE has not been compiled with UTF-8 support. '. - 'See PCRE Pattern Modifiers '. - 'for more information. This application cannot be run without UTF-8 support.', - E_USER_ERROR - ); -} - -if ( ! extension_loaded('iconv')) -{ - trigger_error - ( - 'The iconv extension is not loaded. '. - 'Without iconv, strings cannot be properly translated to UTF-8 from user input. '. - 'This application cannot be run without UTF-8 support.', - E_USER_ERROR - ); -} - -if (extension_loaded('mbstring') AND (ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING)) -{ - trigger_error - ( - 'The mbstring extension is overloading PHP\'s native string functions. '. - 'Disable this by setting mbstring.func_overload to 0, 1, 4 or 5 in php.ini or a .htaccess file.'. - 'This application cannot be run without UTF-8 support.', - E_USER_ERROR - ); -} - -// Check PCRE support for Unicode properties such as \p and \X. -$ER = error_reporting(0); -define('PCRE_UNICODE_PROPERTIES', (bool) preg_match('/^\pL$/u', 'ñ')); -error_reporting($ER); - -// SERVER_UTF8 ? use mb_* functions : use non-native functions -if (extension_loaded('mbstring')) -{ - mb_internal_encoding('UTF-8'); - define('SERVER_UTF8', TRUE); -} -else -{ - define('SERVER_UTF8', FALSE); -} - -// Convert all global variables to UTF-8. -$_GET = utf8::clean($_GET); -$_POST = utf8::clean($_POST); -$_COOKIE = utf8::clean($_COOKIE); -$_SERVER = utf8::clean($_SERVER); - -if (PHP_SAPI == 'cli') -{ - // Convert command line arguments - $_SERVER['argv'] = utf8::clean($_SERVER['argv']); -} - -final class utf8 { - - // Called methods - static $called = array(); - - /** - * Recursively cleans arrays, objects, and strings. Removes ASCII control - * codes and converts to UTF-8 while silently discarding incompatible - * UTF-8 characters. - * - * @param string string to clean - * @return string - */ - public static function clean($str) - { - if (is_array($str) OR is_object($str)) - { - foreach ($str as $key => $val) - { - // Recursion! - $str[self::clean($key)] = self::clean($val); - } - } - elseif (is_string($str) AND $str !== '') - { - // Remove control characters - $str = self::strip_ascii_ctrl($str); - - if ( ! self::is_ascii($str)) - { - // Disable notices - $ER = error_reporting(~E_NOTICE); - - // iconv is expensive, so it is only used when needed - $str = iconv('UTF-8', 'UTF-8//IGNORE', $str); - - // Turn notices back on - error_reporting($ER); - } - } - - return $str; - } - - /** - * Tests whether a string contains only 7bit ASCII bytes. This is used to - * determine when to use native functions or UTF-8 functions. - * - * @param string string to check - * @return bool - */ - public static function is_ascii($str) - { - return ! preg_match('/[^\x00-\x7F]/S', $str); - } - - /** - * Strips out device control codes in the ASCII range. - * - * @param string string to clean - * @return string - */ - public static function strip_ascii_ctrl($str) - { - return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $str); - } - - /** - * Strips out all non-7bit ASCII bytes. - * - * @param string string to clean - * @return string - */ - public static function strip_non_ascii($str) - { - return preg_replace('/[^\x00-\x7F]+/S', '', $str); - } - - /** - * Replaces special/accented UTF-8 characters by ASCII-7 'equivalents'. - * - * @author Andreas Gohr - * - * @param string string to transliterate - * @param integer -1 lowercase only, +1 uppercase only, 0 both cases - * @return string - */ - public static function transliterate_to_ascii($str, $case = 0) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _transliterate_to_ascii($str, $case); - } - - /** - * Returns the length of the given string. - * @see http://php.net/strlen - * - * @param string string being measured for length - * @return integer - */ - public static function strlen($str) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _strlen($str); - } - - /** - * Finds position of first occurrence of a UTF-8 string. - * @see http://php.net/strlen - * - * @author Harry Fuecks - * - * @param string haystack - * @param string needle - * @param integer offset from which character in haystack to start searching - * @return integer position of needle - * @return boolean FALSE if the needle is not found - */ - public static function strpos($str, $search, $offset = 0) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _strpos($str, $search, $offset); - } - - /** - * Finds position of last occurrence of a char in a UTF-8 string. - * @see http://php.net/strrpos - * - * @author Harry Fuecks - * - * @param string haystack - * @param string needle - * @param integer offset from which character in haystack to start searching - * @return integer position of needle - * @return boolean FALSE if the needle is not found - */ - public static function strrpos($str, $search, $offset = 0) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _strrpos($str, $search, $offset); - } - - /** - * Returns part of a UTF-8 string. - * @see http://php.net/substr - * - * @author Chris Smith - * - * @param string input string - * @param integer offset - * @param integer length limit - * @return string - */ - public static function substr($str, $offset, $length = NULL) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _substr($str, $offset, $length); - } - - /** - * Replaces text within a portion of a UTF-8 string. - * @see http://php.net/substr_replace - * - * @author Harry Fuecks - * - * @param string input string - * @param string replacement string - * @param integer offset - * @return string - */ - public static function substr_replace($str, $replacement, $offset, $length = NULL) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _substr_replace($str, $replacement, $offset, $length); - } - - /** - * Makes a UTF-8 string lowercase. - * @see http://php.net/strtolower - * - * @author Andreas Gohr - * - * @param string mixed case string - * @return string - */ - public static function strtolower($str) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _strtolower($str); - } - - /** - * Makes a UTF-8 string uppercase. - * @see http://php.net/strtoupper - * - * @author Andreas Gohr - * - * @param string mixed case string - * @return string - */ - public static function strtoupper($str) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _strtoupper($str); - } - - /** - * Makes a UTF-8 string's first character uppercase. - * @see http://php.net/ucfirst - * - * @author Harry Fuecks - * - * @param string mixed case string - * @return string - */ - public static function ucfirst($str) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _ucfirst($str); - } - - /** - * Makes the first character of every word in a UTF-8 string uppercase. - * @see http://php.net/ucwords - * - * @author Harry Fuecks - * - * @param string mixed case string - * @return string - */ - public static function ucwords($str) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _ucwords($str); - } - - /** - * Case-insensitive UTF-8 string comparison. - * @see http://php.net/strcasecmp - * - * @author Harry Fuecks - * - * @param string string to compare - * @param string string to compare - * @return integer less than 0 if str1 is less than str2 - * @return integer greater than 0 if str1 is greater than str2 - * @return integer 0 if they are equal - */ - public static function strcasecmp($str1, $str2) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _strcasecmp($str1, $str2); - } - - /** - * Returns a string or an array with all occurrences of search in subject (ignoring case). - * replaced with the given replace value. - * @see http://php.net/str_ireplace - * - * @note It's not fast and gets slower if $search and/or $replace are arrays. - * @author Harry Fuecks - * - * @param string input string - * @param string needle - * @return string matched substring if found - * @return boolean FALSE if the substring was not found - */ - public static function stristr($str, $search) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _stristr($str, $search); - } - - /** - * Finds the length of the initial segment matching mask. - * @see http://php.net/strspn - * - * @author Harry Fuecks - * - * @param string input string - * @param string mask for search - * @param integer start position of the string to examine - * @param integer length of the string to examine - * @return integer length of the initial segment that contains characters in the mask - */ - public static function strspn($str, $mask, $offset = NULL, $length = NULL) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _strspn($str, $mask, $offset, $length); - } - - /** - * Finds the length of the initial segment not matching mask. - * @see http://php.net/strcspn - * - * @author Harry Fuecks - * - * @param string input string - * @param string mask for search - * @param integer start position of the string to examine - * @param integer length of the string to examine - * @return integer length of the initial segment that contains characters not in the mask - */ - public static function strcspn($str, $mask, $offset = NULL, $length = NULL) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _strcspn($str, $mask, $offset, $length); - } - - /** - * Pads a UTF-8 string to a certain length with another string. - * @see http://php.net/str_pad - * - * @author Harry Fuecks - * - * @param string input string - * @param integer desired string length after padding - * @param string string to use as padding - * @param string padding type: STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH - * @return string - */ - public static function str_pad($str, $final_str_length, $pad_str = ' ', $pad_type = STR_PAD_RIGHT) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _str_pad($str, $final_str_length, $pad_str, $pad_type); - } - - /** - * Converts a UTF-8 string to an array. - * @see http://php.net/str_split - * - * @author Harry Fuecks - * - * @param string input string - * @param integer maximum length of each chunk - * @return array - */ - public static function str_split($str, $split_length = 1) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _str_split($str, $split_length); - } - - /** - * Reverses a UTF-8 string. - * @see http://php.net/strrev - * - * @author Harry Fuecks - * - * @param string string to be reversed - * @return string - */ - public static function strrev($str) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _strrev($str); - } - - /** - * Strips whitespace (or other UTF-8 characters) from the beginning and - * end of a string. - * @see http://php.net/trim - * - * @author Andreas Gohr - * - * @param string input string - * @param string string of characters to remove - * @return string - */ - public static function trim($str, $charlist = NULL) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _trim($str, $charlist); - } - - /** - * Strips whitespace (or other UTF-8 characters) from the beginning of a string. - * @see http://php.net/ltrim - * - * @author Andreas Gohr - * - * @param string input string - * @param string string of characters to remove - * @return string - */ - public static function ltrim($str, $charlist = NULL) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _ltrim($str, $charlist); - } - - /** - * Strips whitespace (or other UTF-8 characters) from the end of a string. - * @see http://php.net/rtrim - * - * @author Andreas Gohr - * - * @param string input string - * @param string string of characters to remove - * @return string - */ - public static function rtrim($str, $charlist = NULL) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _rtrim($str, $charlist); - } - - /** - * Returns the unicode ordinal for a character. - * @see http://php.net/ord - * - * @author Harry Fuecks - * - * @param string UTF-8 encoded character - * @return integer - */ - public static function ord($chr) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _ord($chr); - } - - /** - * Takes an UTF-8 string and returns an array of ints representing the Unicode characters. - * Astral planes are supported i.e. the ints in the output can be > 0xFFFF. - * Occurrances of the BOM are ignored. Surrogates are not allowed. - * - * The Original Code is Mozilla Communicator client code. - * The Initial Developer of the Original Code is Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer. - * Ported to PHP by Henri Sivonen , see http://hsivonen.iki.fi/php-utf8/. - * Slight modifications to fit with phputf8 library by Harry Fuecks . - * - * @param string UTF-8 encoded string - * @return array unicode code points - * @return boolean FALSE if the string is invalid - */ - public static function to_unicode($str) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _to_unicode($str); - } - - /** - * Takes an array of ints representing the Unicode characters and returns a UTF-8 string. - * Astral planes are supported i.e. the ints in the input can be > 0xFFFF. - * Occurrances of the BOM are ignored. Surrogates are not allowed. - * - * The Original Code is Mozilla Communicator client code. - * The Initial Developer of the Original Code is Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer. - * Ported to PHP by Henri Sivonen , see http://hsivonen.iki.fi/php-utf8/. - * Slight modifications to fit with phputf8 library by Harry Fuecks . - * - * @param array unicode code points representing a string - * @return string utf8 string of characters - * @return boolean FALSE if a code point cannot be found - */ - public static function from_unicode($arr) - { - if ( ! isset(self::$called[__FUNCTION__])) - { - require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; - - // Function has been called - self::$called[__FUNCTION__] = TRUE; - } - - return _from_unicode($arr); - } - -} // End utf8 \ No newline at end of file diff --git a/kohana/core/utf8/from_unicode.php b/kohana/core/utf8/from_unicode.php deleted file mode 100644 index 66c6742d..00000000 --- a/kohana/core/utf8/from_unicode.php +++ /dev/null @@ -1,68 +0,0 @@ -= 0) AND ($arr[$k] <= 0x007f)) - { - echo chr($arr[$k]); - } - // 2 byte sequence - elseif ($arr[$k] <= 0x07ff) - { - echo chr(0xc0 | ($arr[$k] >> 6)); - echo chr(0x80 | ($arr[$k] & 0x003f)); - } - // Byte order mark (skip) - elseif ($arr[$k] == 0xFEFF) - { - // nop -- zap the BOM - } - // Test for illegal surrogates - elseif ($arr[$k] >= 0xD800 AND $arr[$k] <= 0xDFFF) - { - // Found a surrogate - trigger_error('utf8::from_unicode: Illegal surrogate at index: '.$k.', value: '.$arr[$k], E_USER_WARNING); - return FALSE; - } - // 3 byte sequence - elseif ($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 - elseif ($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)); - } - // Out of range - else - { - trigger_error('utf8::from_unicode: Codepoint out of Unicode range at index: '.$k.', value: '.$arr[$k], E_USER_WARNING); - return FALSE; - } - } - - $result = ob_get_contents(); - ob_end_clean(); - return $result; -} diff --git a/kohana/core/utf8/ltrim.php b/kohana/core/utf8/ltrim.php deleted file mode 100644 index 556fe07f..00000000 --- a/kohana/core/utf8/ltrim.php +++ /dev/null @@ -1,22 +0,0 @@ -= 0 AND $ord0 <= 127) - { - return $ord0; - } - - if ( ! isset($chr[1])) - { - trigger_error('Short sequence - at least 2 bytes expected, only 1 seen', E_USER_WARNING); - return FALSE; - } - - $ord1 = ord($chr[1]); - - if ($ord0 >= 192 AND $ord0 <= 223) - { - return ($ord0 - 192) * 64 + ($ord1 - 128); - } - - if ( ! isset($chr[2])) - { - trigger_error('Short sequence - at least 3 bytes expected, only 2 seen', E_USER_WARNING); - return FALSE; - } - - $ord2 = ord($chr[2]); - - if ($ord0 >= 224 AND $ord0 <= 239) - { - return ($ord0 - 224) * 4096 + ($ord1 - 128) * 64 + ($ord2 - 128); - } - - if ( ! isset($chr[3])) - { - trigger_error('Short sequence - at least 4 bytes expected, only 3 seen', E_USER_WARNING); - return FALSE; - } - - $ord3 = ord($chr[3]); - - if ($ord0 >= 240 AND $ord0 <= 247) - { - return ($ord0 - 240) * 262144 + ($ord1 - 128) * 4096 + ($ord2-128) * 64 + ($ord3 - 128); - } - - if ( ! isset($chr[4])) - { - trigger_error('Short sequence - at least 5 bytes expected, only 4 seen', E_USER_WARNING); - return FALSE; - } - - $ord4 = ord($chr[4]); - - if ($ord0 >= 248 AND $ord0 <= 251) - { - return ($ord0 - 248) * 16777216 + ($ord1-128) * 262144 + ($ord2 - 128) * 4096 + ($ord3 - 128) * 64 + ($ord4 - 128); - } - - if ( ! isset($chr[5])) - { - trigger_error('Short sequence - at least 6 bytes expected, only 5 seen', E_USER_WARNING); - return FALSE; - } - - if ($ord0 >= 252 AND $ord0 <= 253) - { - return ($ord0 - 252) * 1073741824 + ($ord1 - 128) * 16777216 + ($ord2 - 128) * 262144 + ($ord3 - 128) * 4096 + ($ord4 - 128) * 64 + (ord($chr[5]) - 128); - } - - if ($ord0 >= 254 AND $ord0 <= 255) - { - trigger_error('Invalid UTF-8 with surrogate ordinal '.$ord0, E_USER_WARNING); - return FALSE; - } -} \ No newline at end of file diff --git a/kohana/core/utf8/rtrim.php b/kohana/core/utf8/rtrim.php deleted file mode 100644 index efa0e19d..00000000 --- a/kohana/core/utf8/rtrim.php +++ /dev/null @@ -1,22 +0,0 @@ - $val) - { - $str[$key] = utf8::str_ireplace($search, $replace, $val, $count); - } - return $str; - } - - if (is_array($search)) - { - $keys = array_keys($search); - - foreach ($keys as $k) - { - if (is_array($replace)) - { - if (array_key_exists($k, $replace)) - { - $str = utf8::str_ireplace($search[$k], $replace[$k], $str, $count); - } - else - { - $str = utf8::str_ireplace($search[$k], '', $str, $count); - } - } - else - { - $str = utf8::str_ireplace($search[$k], $replace, $str, $count); - } - } - return $str; - } - - $search = utf8::strtolower($search); - $str_lower = utf8::strtolower($str); - - $total_matched_strlen = 0; - $i = 0; - - while (preg_match('/(.*?)'.preg_quote($search, '/').'/s', $str_lower, $matches)) - { - $matched_strlen = strlen($matches[0]); - $str_lower = substr($str_lower, $matched_strlen); - - $offset = $total_matched_strlen + strlen($matches[1]) + ($i * (strlen($replace) - 1)); - $str = substr_replace($str, $replace, $offset, strlen($search)); - - $total_matched_strlen += $matched_strlen; - $i++; - } - - $count += $i; - return $str; -} diff --git a/kohana/core/utf8/str_pad.php b/kohana/core/utf8/str_pad.php deleted file mode 100644 index aab4ccc7..00000000 --- a/kohana/core/utf8/str_pad.php +++ /dev/null @@ -1,54 +0,0 @@ -0x0061, 0x03A6=>0x03C6, 0x0162=>0x0163, 0x00C5=>0x00E5, 0x0042=>0x0062, - 0x0139=>0x013A, 0x00C1=>0x00E1, 0x0141=>0x0142, 0x038E=>0x03CD, 0x0100=>0x0101, - 0x0490=>0x0491, 0x0394=>0x03B4, 0x015A=>0x015B, 0x0044=>0x0064, 0x0393=>0x03B3, - 0x00D4=>0x00F4, 0x042A=>0x044A, 0x0419=>0x0439, 0x0112=>0x0113, 0x041C=>0x043C, - 0x015E=>0x015F, 0x0143=>0x0144, 0x00CE=>0x00EE, 0x040E=>0x045E, 0x042F=>0x044F, - 0x039A=>0x03BA, 0x0154=>0x0155, 0x0049=>0x0069, 0x0053=>0x0073, 0x1E1E=>0x1E1F, - 0x0134=>0x0135, 0x0427=>0x0447, 0x03A0=>0x03C0, 0x0418=>0x0438, 0x00D3=>0x00F3, - 0x0420=>0x0440, 0x0404=>0x0454, 0x0415=>0x0435, 0x0429=>0x0449, 0x014A=>0x014B, - 0x0411=>0x0431, 0x0409=>0x0459, 0x1E02=>0x1E03, 0x00D6=>0x00F6, 0x00D9=>0x00F9, - 0x004E=>0x006E, 0x0401=>0x0451, 0x03A4=>0x03C4, 0x0423=>0x0443, 0x015C=>0x015D, - 0x0403=>0x0453, 0x03A8=>0x03C8, 0x0158=>0x0159, 0x0047=>0x0067, 0x00C4=>0x00E4, - 0x0386=>0x03AC, 0x0389=>0x03AE, 0x0166=>0x0167, 0x039E=>0x03BE, 0x0164=>0x0165, - 0x0116=>0x0117, 0x0108=>0x0109, 0x0056=>0x0076, 0x00DE=>0x00FE, 0x0156=>0x0157, - 0x00DA=>0x00FA, 0x1E60=>0x1E61, 0x1E82=>0x1E83, 0x00C2=>0x00E2, 0x0118=>0x0119, - 0x0145=>0x0146, 0x0050=>0x0070, 0x0150=>0x0151, 0x042E=>0x044E, 0x0128=>0x0129, - 0x03A7=>0x03C7, 0x013D=>0x013E, 0x0422=>0x0442, 0x005A=>0x007A, 0x0428=>0x0448, - 0x03A1=>0x03C1, 0x1E80=>0x1E81, 0x016C=>0x016D, 0x00D5=>0x00F5, 0x0055=>0x0075, - 0x0176=>0x0177, 0x00DC=>0x00FC, 0x1E56=>0x1E57, 0x03A3=>0x03C3, 0x041A=>0x043A, - 0x004D=>0x006D, 0x016A=>0x016B, 0x0170=>0x0171, 0x0424=>0x0444, 0x00CC=>0x00EC, - 0x0168=>0x0169, 0x039F=>0x03BF, 0x004B=>0x006B, 0x00D2=>0x00F2, 0x00C0=>0x00E0, - 0x0414=>0x0434, 0x03A9=>0x03C9, 0x1E6A=>0x1E6B, 0x00C3=>0x00E3, 0x042D=>0x044D, - 0x0416=>0x0436, 0x01A0=>0x01A1, 0x010C=>0x010D, 0x011C=>0x011D, 0x00D0=>0x00F0, - 0x013B=>0x013C, 0x040F=>0x045F, 0x040A=>0x045A, 0x00C8=>0x00E8, 0x03A5=>0x03C5, - 0x0046=>0x0066, 0x00DD=>0x00FD, 0x0043=>0x0063, 0x021A=>0x021B, 0x00CA=>0x00EA, - 0x0399=>0x03B9, 0x0179=>0x017A, 0x00CF=>0x00EF, 0x01AF=>0x01B0, 0x0045=>0x0065, - 0x039B=>0x03BB, 0x0398=>0x03B8, 0x039C=>0x03BC, 0x040C=>0x045C, 0x041F=>0x043F, - 0x042C=>0x044C, 0x00DE=>0x00FE, 0x00D0=>0x00F0, 0x1EF2=>0x1EF3, 0x0048=>0x0068, - 0x00CB=>0x00EB, 0x0110=>0x0111, 0x0413=>0x0433, 0x012E=>0x012F, 0x00C6=>0x00E6, - 0x0058=>0x0078, 0x0160=>0x0161, 0x016E=>0x016F, 0x0391=>0x03B1, 0x0407=>0x0457, - 0x0172=>0x0173, 0x0178=>0x00FF, 0x004F=>0x006F, 0x041B=>0x043B, 0x0395=>0x03B5, - 0x0425=>0x0445, 0x0120=>0x0121, 0x017D=>0x017E, 0x017B=>0x017C, 0x0396=>0x03B6, - 0x0392=>0x03B2, 0x0388=>0x03AD, 0x1E84=>0x1E85, 0x0174=>0x0175, 0x0051=>0x0071, - 0x0417=>0x0437, 0x1E0A=>0x1E0B, 0x0147=>0x0148, 0x0104=>0x0105, 0x0408=>0x0458, - 0x014C=>0x014D, 0x00CD=>0x00ED, 0x0059=>0x0079, 0x010A=>0x010B, 0x038F=>0x03CE, - 0x0052=>0x0072, 0x0410=>0x0430, 0x0405=>0x0455, 0x0402=>0x0452, 0x0126=>0x0127, - 0x0136=>0x0137, 0x012A=>0x012B, 0x038A=>0x03AF, 0x042B=>0x044B, 0x004C=>0x006C, - 0x0397=>0x03B7, 0x0124=>0x0125, 0x0218=>0x0219, 0x00DB=>0x00FB, 0x011E=>0x011F, - 0x041E=>0x043E, 0x1E40=>0x1E41, 0x039D=>0x03BD, 0x0106=>0x0107, 0x03AB=>0x03CB, - 0x0426=>0x0446, 0x00DE=>0x00FE, 0x00C7=>0x00E7, 0x03AA=>0x03CA, 0x0421=>0x0441, - 0x0412=>0x0432, 0x010E=>0x010F, 0x00D8=>0x00F8, 0x0057=>0x0077, 0x011A=>0x011B, - 0x0054=>0x0074, 0x004A=>0x006A, 0x040B=>0x045B, 0x0406=>0x0456, 0x0102=>0x0103, - 0x039B=>0x03BB, 0x00D1=>0x00F1, 0x041D=>0x043D, 0x038C=>0x03CC, 0x00C9=>0x00E9, - 0x00D0=>0x00F0, 0x0407=>0x0457, 0x0122=>0x0123, - ); - } - - $uni = utf8::to_unicode($str); - - if ($uni === FALSE) - return FALSE; - - for ($i = 0, $c = count($uni); $i < $c; $i++) - { - if (isset($UTF8_UPPER_TO_LOWER[$uni[$i]])) - { - $uni[$i] = $UTF8_UPPER_TO_LOWER[$uni[$i]]; - } - } - - return utf8::from_unicode($uni); -} \ No newline at end of file diff --git a/kohana/core/utf8/strtoupper.php b/kohana/core/utf8/strtoupper.php deleted file mode 100644 index f3ded739..00000000 --- a/kohana/core/utf8/strtoupper.php +++ /dev/null @@ -1,84 +0,0 @@ -0x0041, 0x03C6=>0x03A6, 0x0163=>0x0162, 0x00E5=>0x00C5, 0x0062=>0x0042, - 0x013A=>0x0139, 0x00E1=>0x00C1, 0x0142=>0x0141, 0x03CD=>0x038E, 0x0101=>0x0100, - 0x0491=>0x0490, 0x03B4=>0x0394, 0x015B=>0x015A, 0x0064=>0x0044, 0x03B3=>0x0393, - 0x00F4=>0x00D4, 0x044A=>0x042A, 0x0439=>0x0419, 0x0113=>0x0112, 0x043C=>0x041C, - 0x015F=>0x015E, 0x0144=>0x0143, 0x00EE=>0x00CE, 0x045E=>0x040E, 0x044F=>0x042F, - 0x03BA=>0x039A, 0x0155=>0x0154, 0x0069=>0x0049, 0x0073=>0x0053, 0x1E1F=>0x1E1E, - 0x0135=>0x0134, 0x0447=>0x0427, 0x03C0=>0x03A0, 0x0438=>0x0418, 0x00F3=>0x00D3, - 0x0440=>0x0420, 0x0454=>0x0404, 0x0435=>0x0415, 0x0449=>0x0429, 0x014B=>0x014A, - 0x0431=>0x0411, 0x0459=>0x0409, 0x1E03=>0x1E02, 0x00F6=>0x00D6, 0x00F9=>0x00D9, - 0x006E=>0x004E, 0x0451=>0x0401, 0x03C4=>0x03A4, 0x0443=>0x0423, 0x015D=>0x015C, - 0x0453=>0x0403, 0x03C8=>0x03A8, 0x0159=>0x0158, 0x0067=>0x0047, 0x00E4=>0x00C4, - 0x03AC=>0x0386, 0x03AE=>0x0389, 0x0167=>0x0166, 0x03BE=>0x039E, 0x0165=>0x0164, - 0x0117=>0x0116, 0x0109=>0x0108, 0x0076=>0x0056, 0x00FE=>0x00DE, 0x0157=>0x0156, - 0x00FA=>0x00DA, 0x1E61=>0x1E60, 0x1E83=>0x1E82, 0x00E2=>0x00C2, 0x0119=>0x0118, - 0x0146=>0x0145, 0x0070=>0x0050, 0x0151=>0x0150, 0x044E=>0x042E, 0x0129=>0x0128, - 0x03C7=>0x03A7, 0x013E=>0x013D, 0x0442=>0x0422, 0x007A=>0x005A, 0x0448=>0x0428, - 0x03C1=>0x03A1, 0x1E81=>0x1E80, 0x016D=>0x016C, 0x00F5=>0x00D5, 0x0075=>0x0055, - 0x0177=>0x0176, 0x00FC=>0x00DC, 0x1E57=>0x1E56, 0x03C3=>0x03A3, 0x043A=>0x041A, - 0x006D=>0x004D, 0x016B=>0x016A, 0x0171=>0x0170, 0x0444=>0x0424, 0x00EC=>0x00CC, - 0x0169=>0x0168, 0x03BF=>0x039F, 0x006B=>0x004B, 0x00F2=>0x00D2, 0x00E0=>0x00C0, - 0x0434=>0x0414, 0x03C9=>0x03A9, 0x1E6B=>0x1E6A, 0x00E3=>0x00C3, 0x044D=>0x042D, - 0x0436=>0x0416, 0x01A1=>0x01A0, 0x010D=>0x010C, 0x011D=>0x011C, 0x00F0=>0x00D0, - 0x013C=>0x013B, 0x045F=>0x040F, 0x045A=>0x040A, 0x00E8=>0x00C8, 0x03C5=>0x03A5, - 0x0066=>0x0046, 0x00FD=>0x00DD, 0x0063=>0x0043, 0x021B=>0x021A, 0x00EA=>0x00CA, - 0x03B9=>0x0399, 0x017A=>0x0179, 0x00EF=>0x00CF, 0x01B0=>0x01AF, 0x0065=>0x0045, - 0x03BB=>0x039B, 0x03B8=>0x0398, 0x03BC=>0x039C, 0x045C=>0x040C, 0x043F=>0x041F, - 0x044C=>0x042C, 0x00FE=>0x00DE, 0x00F0=>0x00D0, 0x1EF3=>0x1EF2, 0x0068=>0x0048, - 0x00EB=>0x00CB, 0x0111=>0x0110, 0x0433=>0x0413, 0x012F=>0x012E, 0x00E6=>0x00C6, - 0x0078=>0x0058, 0x0161=>0x0160, 0x016F=>0x016E, 0x03B1=>0x0391, 0x0457=>0x0407, - 0x0173=>0x0172, 0x00FF=>0x0178, 0x006F=>0x004F, 0x043B=>0x041B, 0x03B5=>0x0395, - 0x0445=>0x0425, 0x0121=>0x0120, 0x017E=>0x017D, 0x017C=>0x017B, 0x03B6=>0x0396, - 0x03B2=>0x0392, 0x03AD=>0x0388, 0x1E85=>0x1E84, 0x0175=>0x0174, 0x0071=>0x0051, - 0x0437=>0x0417, 0x1E0B=>0x1E0A, 0x0148=>0x0147, 0x0105=>0x0104, 0x0458=>0x0408, - 0x014D=>0x014C, 0x00ED=>0x00CD, 0x0079=>0x0059, 0x010B=>0x010A, 0x03CE=>0x038F, - 0x0072=>0x0052, 0x0430=>0x0410, 0x0455=>0x0405, 0x0452=>0x0402, 0x0127=>0x0126, - 0x0137=>0x0136, 0x012B=>0x012A, 0x03AF=>0x038A, 0x044B=>0x042B, 0x006C=>0x004C, - 0x03B7=>0x0397, 0x0125=>0x0124, 0x0219=>0x0218, 0x00FB=>0x00DB, 0x011F=>0x011E, - 0x043E=>0x041E, 0x1E41=>0x1E40, 0x03BD=>0x039D, 0x0107=>0x0106, 0x03CB=>0x03AB, - 0x0446=>0x0426, 0x00FE=>0x00DE, 0x00E7=>0x00C7, 0x03CA=>0x03AA, 0x0441=>0x0421, - 0x0432=>0x0412, 0x010F=>0x010E, 0x00F8=>0x00D8, 0x0077=>0x0057, 0x011B=>0x011A, - 0x0074=>0x0054, 0x006A=>0x004A, 0x045B=>0x040B, 0x0456=>0x0406, 0x0103=>0x0102, - 0x03BB=>0x039B, 0x00F1=>0x00D1, 0x043D=>0x041D, 0x03CC=>0x038C, 0x00E9=>0x00C9, - 0x00F0=>0x00D0, 0x0457=>0x0407, 0x0123=>0x0122, - ); - } - - $uni = utf8::to_unicode($str); - - if ($uni === FALSE) - return FALSE; - - for ($i = 0, $c = count($uni); $i < $c; $i++) - { - if (isset($UTF8_LOWER_TO_UPPER[$uni[$i]])) - { - $uni[$i] = $UTF8_LOWER_TO_UPPER[$uni[$i]]; - } - } - - return utf8::from_unicode($uni); -} \ No newline at end of file diff --git a/kohana/core/utf8/substr.php b/kohana/core/utf8/substr.php deleted file mode 100644 index daf66b81..00000000 --- a/kohana/core/utf8/substr.php +++ /dev/null @@ -1,75 +0,0 @@ -= $strlen OR ($length < 0 AND $length <= $offset - $strlen)) - return ''; - - // Whole string - if ($offset == 0 AND ($length === NULL OR $length >= $strlen)) - return $str; - - // Build regex - $regex = '^'; - - // Create an offset expression - if ($offset > 0) - { - // PCRE repeating quantifiers must be less than 65536, so repeat when necessary - $x = (int) ($offset / 65535); - $y = (int) ($offset % 65535); - $regex .= ($x == 0) ? '' : '(?:.{65535}){'.$x.'}'; - $regex .= ($y == 0) ? '' : '.{'.$y.'}'; - } - - // Create a length expression - if ($length === NULL) - { - $regex .= '(.*)'; // No length set, grab it all - } - // Find length from the left (positive length) - elseif ($length > 0) - { - // Reduce length so that it can't go beyond the end of the string - $length = min($strlen - $offset, $length); - - $x = (int) ($length / 65535); - $y = (int) ($length % 65535); - $regex .= '('; - $regex .= ($x == 0) ? '' : '(?:.{65535}){'.$x.'}'; - $regex .= '.{'.$y.'})'; - } - // Find length from the right (negative length) - else - { - $x = (int) (-$length / 65535); - $y = (int) (-$length % 65535); - $regex .= '(.*)'; - $regex .= ($x == 0) ? '' : '(?:.{65535}){'.$x.'}'; - $regex .= '.{'.$y.'}'; - } - - preg_match('/'.$regex.'/us', $str, $matches); - return $matches[1]; -} \ No newline at end of file diff --git a/kohana/core/utf8/substr_replace.php b/kohana/core/utf8/substr_replace.php deleted file mode 100644 index 45e2d2a3..00000000 --- a/kohana/core/utf8/substr_replace.php +++ /dev/null @@ -1,22 +0,0 @@ - 0x10FFFF)) - { - 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 UTF-8 cache - $mState = 0; - $mUcs4 = 0; - $mBytes = 1; - } - } - else - { - // ((0xC0 & (*in) != 0x80) AND (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; -} \ No newline at end of file diff --git a/kohana/core/utf8/transliterate_to_ascii.php b/kohana/core/utf8/transliterate_to_ascii.php deleted file mode 100644 index 07461fbb..00000000 --- a/kohana/core/utf8/transliterate_to_ascii.php +++ /dev/null @@ -1,77 +0,0 @@ - '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', 'ö' => 'o', 'è' => 'e', 'ŷ' => 'y', 'ą' => 'a', 'ł' => 'l', - 'ų' => 'u', 'ů' => 'u', 'ş' => 's', 'ğ' => 'g', 'ļ' => 'l', 'ƒ' => 'f', 'ž' => 'z', - 'ẃ' => 'w', 'ḃ' => 'b', 'å' => 'a', 'ì' => 'i', 'ï' => 'i', 'ḋ' => 'd', 'ť' => 't', - 'ŗ' => 'r', 'ä' => 'a', 'í' => 'i', 'ŕ' => 'r', 'ê' => 'e', 'ü' => 'u', 'ò' => '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', - ); - } - - $str = str_replace( - array_keys($UTF8_LOWER_ACCENTS), - array_values($UTF8_LOWER_ACCENTS), - $str - ); - } - - if ($case >= 0) - { - if ($UTF8_UPPER_ACCENTS === NULL) - { - $UTF8_UPPER_ACCENTS = array( - 'À' => 'A', 'Ô' => 'O', 'Ď' => 'D', 'Ḟ' => 'F', 'Ë' => 'E', 'Š' => 'S', 'Ơ' => 'O', - 'Ă' => 'A', 'Ř' => 'R', 'Ț' => 'T', 'Ň' => 'N', 'Ā' => 'A', 'Ķ' => 'K', 'Ĕ' => 'E', - 'Ŝ' => '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', 'Ö' => 'O', 'È' => 'E', 'Ŷ' => 'Y', 'Ą' => 'A', 'Ł' => 'L', - 'Ų' => 'U', 'Ů' => 'U', 'Ş' => 'S', 'Ğ' => 'G', 'Ļ' => 'L', 'Ƒ' => 'F', 'Ž' => 'Z', - 'Ẃ' => 'W', 'Ḃ' => 'B', 'Å' => 'A', 'Ì' => 'I', 'Ï' => 'I', 'Ḋ' => 'D', 'Ť' => 'T', - 'Ŗ' => 'R', 'Ä' => 'A', 'Í' => 'I', 'Ŕ' => 'R', 'Ê' => 'E', 'Ü' => 'U', 'Ò' => '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', - ); - } - - $str = str_replace( - array_keys($UTF8_UPPER_ACCENTS), - array_values($UTF8_UPPER_ACCENTS), - $str - ); - } - - return $str; -} \ No newline at end of file diff --git a/kohana/core/utf8/trim.php b/kohana/core/utf8/trim.php deleted file mode 100644 index 7434102a..00000000 --- a/kohana/core/utf8/trim.php +++ /dev/null @@ -1,17 +0,0 @@ - $value) - { - $value = ($keep_keys === TRUE) ? $value : array_values($value); - foreach ($value as $k => $v) - { - $new_array[$k][$key] = $v; - } - } - - return $new_array; - } - - /** - * Removes a key from an array and returns the value. - * - * @param string key to return - * @param array array to work on - * @return mixed value of the requested array key - */ - public static function remove($key, & $array) - { - if ( ! array_key_exists($key, $array)) - return NULL; - - $val = $array[$key]; - unset($array[$key]); - - return $val; - } - - - /** - * Extract one or more keys from an array. Each key given after the first - * argument (the array) will be extracted. Keys that do not exist in the - * search array will be NULL in the extracted data. - * - * @param array array to search - * @param string key name - * @return array - */ - public static function extract(array $search, $keys) - { - // Get the keys, removing the $search array - $keys = array_slice(func_get_args(), 1); - - $found = array(); - foreach ($keys as $key) - { - if (isset($search[$key])) - { - $found[$key] = $search[$key]; - } - else - { - $found[$key] = NULL; - } - } - - return $found; - } - - /** - * Because PHP does not have this function. - * - * @param array array to unshift - * @param string key to unshift - * @param mixed value to unshift - * @return array - */ - public static function unshift_assoc( array & $array, $key, $val) - { - $array = array_reverse($array, TRUE); - $array[$key] = $val; - $array = array_reverse($array, TRUE); - - return $array; - } - - /** - * Because PHP does not have this function, and array_walk_recursive creates - * references in arrays and is not truly recursive. - * - * @param mixed callback to apply to each member of the array - * @param array array to map to - * @return array - */ - public static function map_recursive($callback, array $array) - { - foreach ($array as $key => $val) - { - // Map the callback to the key - $array[$key] = is_array($val) ? arr::map_recursive($callback, $val) : call_user_func($callback, $val); - } - - return $array; - } - - /** - * @param mixed $needle the value to search for - * @param array $haystack an array of values to search in - * @param boolean $sort sort the array now - * @return integer|FALSE the index of the match or FALSE when not found - */ - public static function binary_search($needle, $haystack, $sort = FALSE) - { - if ($sort) - { - sort($haystack); - } - - $high = count($haystack) - 1; - $low = 0; - - while ($low <= $high) - { - $mid = ($low + $high) >> 1; - - if ($haystack[$mid] < $needle) - { - $low = $mid + 1; - } - elseif ($haystack[$mid] > $needle) - { - $high = $mid - 1; - } - else - { - return $mid; - } - } - - return FALSE; - } - - - /** - * Emulates array_merge_recursive, but appends numeric keys and replaces - * associative keys, instead of appending all keys. - * - * @param array any number of arrays - * @return array - */ - public static function merge() - { - $total = func_num_args(); - - $result = array(); - for ($i = 0; $i < $total; $i++) - { - foreach (func_get_arg($i) as $key => $val) - { - if (isset($result[$key])) - { - if (is_array($val)) - { - // Arrays are merged recursively - $result[$key] = arr::merge($result[$key], $val); - } - elseif (is_int($key)) - { - // Indexed arrays are appended - array_push($result, $val); - } - else - { - // Associative arrays are replaced - $result[$key] = $val; - } - } - else - { - // New values are added - $result[$key] = $val; - } - } - } - - return $result; - } - - /** - * Overwrites an array with values from input array(s). - * Non-existing keys will not be appended! - * - * @param array key array - * @param array input array(s) that will overwrite key array values - * @return array - */ - public static function overwrite($array1, $array2) - { - foreach (array_intersect_key($array2, $array1) as $key => $value) - { - $array1[$key] = $value; - } - - if (func_num_args() > 2) - { - foreach (array_slice(func_get_args(), 2) as $array2) - { - foreach (array_intersect_key($array2, $array1) as $key => $value) - { - $array1[$key] = $value; - } - } - } - - return $array1; - } - - /** - * Fill an array with a range of numbers. - * - * @param integer stepping - * @param integer ending number - * @return array - */ - public static function range($step = 10, $max = 100) - { - if ($step < 1) - return array(); - - $array = array(); - for ($i = $step; $i <= $max; $i += $step) - { - $array[$i] = $i; - } - - return $array; - } - - /** - * Recursively convert an array to an object. - * - * @param array array to convert - * @return object - */ - public static function to_object(array $array, $class = 'stdClass') - { - $object = new $class; - - foreach ($array as $key => $value) - { - if (is_array($value)) - { - // Convert the array to an object - $value = arr::to_object($value, $class); - } - - // Add the value to the object - $object->{$key} = $value; - } - - return $object; - } - -} // End arr diff --git a/kohana/helpers/cookie.php b/kohana/helpers/cookie.php deleted file mode 100644 index 901b6d86..00000000 --- a/kohana/helpers/cookie.php +++ /dev/null @@ -1,84 +0,0 @@ -cookie($name, $default, $xss_clean); - } - - /** - * Nullify and unset a cookie. - * - * @param string cookie name - * @param string URL path - * @param string URL domain - * @return boolean - */ - public static function delete($name, $path = NULL, $domain = NULL) - { - if ( ! isset($_COOKIE[$name])) - return FALSE; - - // Delete the cookie from globals - unset($_COOKIE[$name]); - - // Sets the cookie value to an empty string, and the expiration to 24 hours ago - return cookie::set($name, '', -86400, $path, $domain, FALSE, FALSE); - } - -} // End cookie \ No newline at end of file diff --git a/kohana/helpers/date.php b/kohana/helpers/date.php deleted file mode 100644 index 7d5a9ab6..00000000 --- a/kohana/helpers/date.php +++ /dev/null @@ -1,405 +0,0 @@ -> 1); - } - - /** - * Converts a DOS timestamp to UNIX format. - * - * @param integer DOS timestamp - * @return integer - */ - public static function dos2unix($timestamp = FALSE) - { - $sec = 2 * ($timestamp & 0x1f); - $min = ($timestamp >> 5) & 0x3f; - $hrs = ($timestamp >> 11) & 0x1f; - $day = ($timestamp >> 16) & 0x1f; - $mon = ($timestamp >> 21) & 0x0f; - $year = ($timestamp >> 25) & 0x7f; - - return mktime($hrs, $min, $sec, $mon, $day, $year + 1980); - } - - /** - * Returns the offset (in seconds) between two time zones. - * @see http://php.net/timezones - * - * @param string timezone that to find the offset of - * @param string|boolean timezone used as the baseline - * @return integer - */ - public static function offset($remote, $local = TRUE) - { - static $offsets; - - // Default values - $remote = (string) $remote; - $local = ($local === TRUE) ? date_default_timezone_get() : (string) $local; - - // Cache key name - $cache = $remote.$local; - - if (empty($offsets[$cache])) - { - // Create timezone objects - $remote = new DateTimeZone($remote); - $local = new DateTimeZone($local); - - // Create date objects from timezones - $time_there = new DateTime('now', $remote); - $time_here = new DateTime('now', $local); - - // Find the offset - $offsets[$cache] = $remote->getOffset($time_there) - $local->getOffset($time_here); - } - - return $offsets[$cache]; - } - - /** - * Number of seconds in a minute, incrementing by a step. - * - * @param integer amount to increment each step by, 1 to 30 - * @param integer start value - * @param integer end value - * @return array A mirrored (foo => foo) array from 1-60. - */ - public static function seconds($step = 1, $start = 0, $end = 60) - { - // Always integer - $step = (int) $step; - - $seconds = array(); - - for ($i = $start; $i < $end; $i += $step) - { - $seconds[$i] = ($i < 10) ? '0'.$i : $i; - } - - return $seconds; - } - - /** - * Number of minutes in an hour, incrementing by a step. - * - * @param integer amount to increment each step by, 1 to 30 - * @return array A mirrored (foo => foo) array from 1-60. - */ - public static function minutes($step = 5) - { - // Because there are the same number of minutes as seconds in this set, - // we choose to re-use seconds(), rather than creating an entirely new - // function. Shhhh, it's cheating! ;) There are several more of these - // in the following methods. - return date::seconds($step); - } - - /** - * Number of hours in a day. - * - * @param integer amount to increment each step by - * @param boolean use 24-hour time - * @param integer the hour to start at - * @return array A mirrored (foo => foo) array from start-12 or start-23. - */ - public static function hours($step = 1, $long = FALSE, $start = NULL) - { - // Default values - $step = (int) $step; - $long = (bool) $long; - $hours = array(); - - // Set the default start if none was specified. - if ($start === NULL) - { - $start = ($long === FALSE) ? 1 : 0; - } - - $hours = array(); - - // 24-hour time has 24 hours, instead of 12 - $size = ($long === TRUE) ? 23 : 12; - - for ($i = $start; $i <= $size; $i += $step) - { - $hours[$i] = $i; - } - - return $hours; - } - - /** - * Returns AM or PM, based on a given hour. - * - * @param integer number of the hour - * @return string - */ - public static function ampm($hour) - { - // Always integer - $hour = (int) $hour; - - return ($hour > 11) ? 'PM' : 'AM'; - } - - /** - * Adjusts a non-24-hour number into a 24-hour number. - * - * @param integer hour to adjust - * @param string AM or PM - * @return string - */ - public static function adjust($hour, $ampm) - { - $hour = (int) $hour; - $ampm = strtolower($ampm); - - switch ($ampm) - { - case 'am': - if ($hour == 12) - $hour = 0; - break; - case 'pm': - if ($hour < 12) - $hour += 12; - break; - } - - return sprintf('%02s', $hour); - } - - /** - * Number of days in month. - * - * @param integer number of month - * @param integer number of year to check month, defaults to the current year - * @return array A mirrored (foo => foo) array of the days. - */ - public static function days($month, $year = FALSE) - { - static $months; - - // Always integers - $month = (int) $month; - $year = (int) $year; - - // Use the current year by default - $year = ($year == FALSE) ? date('Y') : $year; - - // We use caching for months, because time functions are used - if (empty($months[$year][$month])) - { - $months[$year][$month] = array(); - - // Use date to find the number of days in the given month - $total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1; - - for ($i = 1; $i < $total; $i++) - { - $months[$year][$month][$i] = $i; - } - } - - return $months[$year][$month]; - } - - /** - * Number of months in a year - * - * @return array A mirrored (foo => foo) array from 1-12. - */ - public static function months() - { - return date::hours(); - } - - /** - * Returns an array of years between a starting and ending year. - * Uses the current year +/- 5 as the max/min. - * - * @param integer starting year - * @param integer ending year - * @return array - */ - public static function years($start = FALSE, $end = FALSE) - { - // Default values - $start = ($start === FALSE) ? date('Y') - 5 : (int) $start; - $end = ($end === FALSE) ? date('Y') + 5 : (int) $end; - - $years = array(); - - // Add one, so that "less than" works - $end += 1; - - for ($i = $start; $i < $end; $i++) - { - $years[$i] = $i; - } - - return $years; - } - - /** - * Returns time difference between two timestamps, in human readable format. - * - * @param integer timestamp - * @param integer timestamp, defaults to the current time - * @param string formatting string - * @return string|array - */ - public static function timespan($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') - { - // Array with the output formats - $output = preg_split('/[^a-z]+/', strtolower((string) $output)); - - // Invalid output - if (empty($output)) - return FALSE; - - // Make the output values into keys - extract(array_flip($output), EXTR_SKIP); - - // Default values - $time1 = max(0, (int) $time1); - $time2 = empty($time2) ? time() : max(0, (int) $time2); - - // Calculate timespan (seconds) - $timespan = abs($time1 - $time2); - - // All values found using Google Calculator. - // Years and months do not match the formula exactly, due to leap years. - - // Years ago, 60 * 60 * 24 * 365 - isset($years) and $timespan -= 31556926 * ($years = (int) floor($timespan / 31556926)); - - // Months ago, 60 * 60 * 24 * 30 - isset($months) and $timespan -= 2629744 * ($months = (int) floor($timespan / 2629743.83)); - - // Weeks ago, 60 * 60 * 24 * 7 - isset($weeks) and $timespan -= 604800 * ($weeks = (int) floor($timespan / 604800)); - - // Days ago, 60 * 60 * 24 - isset($days) and $timespan -= 86400 * ($days = (int) floor($timespan / 86400)); - - // Hours ago, 60 * 60 - isset($hours) and $timespan -= 3600 * ($hours = (int) floor($timespan / 3600)); - - // Minutes ago, 60 - isset($minutes) and $timespan -= 60 * ($minutes = (int) floor($timespan / 60)); - - // Seconds ago, 1 - isset($seconds) and $seconds = $timespan; - - // Remove the variables that cannot be accessed - unset($timespan, $time1, $time2); - - // Deny access to these variables - $deny = array_flip(array('deny', 'key', 'difference', 'output')); - - // Return the difference - $difference = array(); - foreach ($output as $key) - { - if (isset($$key) AND ! isset($deny[$key])) - { - // Add requested key to the output - $difference[$key] = $$key; - } - } - - // Invalid output formats string - if (empty($difference)) - return FALSE; - - // If only one output format was asked, don't put it in an array - if (count($difference) === 1) - return current($difference); - - // Return array - return $difference; - } - - /** - * Returns time difference between two timestamps, in the format: - * N year, N months, N weeks, N days, N hours, N minutes, and N seconds ago - * - * @param integer timestamp - * @param integer timestamp, defaults to the current time - * @param string formatting string - * @return string - */ - public static function timespan_string($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') - { - if ($difference = date::timespan($time1, $time2, $output) AND is_array($difference)) - { - // Determine the key of the last item in the array - $last = end($difference); - $last = key($difference); - - $span = array(); - foreach ($difference as $name => $amount) - { - if ($amount === 0) - { - // Skip empty amounts - continue; - } - - // Add the amount to the span - $span[] = ($name === $last ? ' and ' : ', ').$amount.' '.($amount === 1 ? inflector::singular($name) : $name); - } - - // If the difference is less than 60 seconds, remove the preceding and. - if (count($span) === 1) - { - $span[0] = ltrim($span[0], 'and '); - } - - // Replace difference by making the span into a string - $difference = trim(implode('', $span), ','); - } - elseif (is_int($difference)) - { - // Single-value return - $difference = $difference.' '.($difference === 1 ? inflector::singular($output) : $output); - } - - return $difference; - } - -} // End date \ No newline at end of file diff --git a/kohana/helpers/download.php b/kohana/helpers/download.php deleted file mode 100644 index 49fed42c..00000000 --- a/kohana/helpers/download.php +++ /dev/null @@ -1,105 +0,0 @@ -setUsername($config['options']['username']); - empty($config['options']['password']) or $connection->setPassword($config['options']['password']); - - if ( ! empty($config['options']['auth'])) - { - // Get the class name and params - list ($class, $params) = arr::callback_string($config['options']['auth']); - - if ($class === 'PopB4Smtp') - { - // Load the PopB4Smtp class manually, due to its odd filename - require Kohana::find_file('vendor', 'swift/Swift/Authenticator/$PopB4Smtp$'); - } - - // Prepare the class name for auto-loading - $class = 'Swift_Authenticator_'.$class; - - // Attach the authenticator - $connection->attachAuthenticator(($params === NULL) ? new $class : new $class($params[0])); - } - - // Set the timeout to 5 seconds - $connection->setTimeout(empty($config['options']['timeout']) ? 5 : (int) $config['options']['timeout']); - break; - case 'sendmail': - // Create a sendmail connection - $connection = new Swift_Connection_Sendmail - ( - empty($config['options']) ? Swift_Connection_Sendmail::AUTO_DETECT : $config['options'] - ); - - // Set the timeout to 5 seconds - $connection->setTimeout(5); - break; - default: - // Use the native connection - $connection = new Swift_Connection_NativeMail($config['options']); - break; - } - - // Create the SwiftMailer instance - return email::$mail = new Swift($connection); - } - - /** - * Send an email message. - * - * @param string|array recipient email (and name), or an array of To, Cc, Bcc names - * @param string|array sender email (and name) - * @param string message subject - * @param string message body - * @param boolean send email as HTML - * @return integer number of emails sent - */ - public static function send($to, $from, $subject, $message, $html = FALSE) - { - // Connect to SwiftMailer - (email::$mail === NULL) and email::connect(); - - // Determine the message type - $html = ($html === TRUE) ? 'text/html' : 'text/plain'; - - // Create the message - $message = new Swift_Message($subject, $message, $html, '8bit', 'utf-8'); - - if (is_string($to)) - { - // Single recipient - $recipients = new Swift_Address($to); - } - elseif (is_array($to)) - { - if (isset($to[0]) AND isset($to[1])) - { - // Create To: address set - $to = array('to' => $to); - } - - // Create a list of recipients - $recipients = new Swift_RecipientList; - - foreach ($to as $method => $set) - { - if ( ! in_array($method, array('to', 'cc', 'bcc'))) - { - // Use To: by default - $method = 'to'; - } - - // Create method name - $method = 'add'.ucfirst($method); - - if (is_array($set)) - { - // Add a recipient with name - $recipients->$method($set[0], $set[1]); - } - else - { - // Add a recipient without name - $recipients->$method($set); - } - } - } - - if (is_string($from)) - { - // From without a name - $from = new Swift_Address($from); - } - elseif (is_array($from)) - { - // From with a name - $from = new Swift_Address($from[0], $from[1]); - } - - return email::$mail->send($message, $recipients, $from); - } - -} // End email \ No newline at end of file diff --git a/kohana/helpers/expires.php b/kohana/helpers/expires.php deleted file mode 100644 index c43cc0cc..00000000 --- a/kohana/helpers/expires.php +++ /dev/null @@ -1,111 +0,0 @@ - 0) - { - // Re-send headers - header('Last-Modified: '.gmdate('D, d M Y H:i:s T', $mod_time)); - header('Expires: '.gmdate('D, d M Y H:i:s T', time() + $mod_time_diff)); - header('Cache-Control: max-age='.$mod_time_diff); - header('Status: 304 Not Modified', TRUE, 304); - - // Prevent any output - Event::add('system.display', array('expires', 'prevent_output')); - - // Exit to prevent other output - exit; - } - } - - return FALSE; - } - - /** - * Check headers already created to not step on download or Img_lib's feet - * - * @return boolean - */ - public static function check_headers() - { - foreach (headers_list() as $header) - { - if ((session_cache_limiter() == '' AND stripos($header, 'Last-Modified:') === 0) - OR stripos($header, 'Expires:') === 0) - { - return FALSE; - } - } - - return TRUE; - } - - /** - * Prevent any output from being displayed. Executed during system.display. - * - * @return void - */ - public static function prevent_output() - { - Kohana::$output = ''; - } - -} // End expires \ No newline at end of file diff --git a/kohana/helpers/feed.php b/kohana/helpers/feed.php deleted file mode 100644 index 74bb2f6b..00000000 --- a/kohana/helpers/feed.php +++ /dev/null @@ -1,122 +0,0 @@ -channel) ? $feed->xpath('//item') : $feed->entry; - - $i = 0; - $items = array(); - - foreach ($feed as $item) - { - if ($limit > 0 AND $i++ === $limit) - break; - - $items[] = (array) $item; - } - - return $items; - } - - /** - * Creates a feed from the given parameters. - * - * @param array feed information - * @param array items to add to the feed - * @param string define which format to use - * @param string define which encoding to use - * @return string - */ - public static function create($info, $items, $format = 'rss2', $encoding = 'UTF-8') - { - $info += array('title' => 'Generated Feed', 'link' => '', 'generator' => 'KohanaPHP'); - - $feed = ''; - $feed = simplexml_load_string($feed); - - foreach ($info as $name => $value) - { - if (($name === 'pubDate' OR $name === 'lastBuildDate') AND (is_int($value) OR ctype_digit($value))) - { - // Convert timestamps to RFC 822 formatted dates - $value = date(DATE_RFC822, $value); - } - elseif (($name === 'link' OR $name === 'docs') AND strpos($value, '://') === FALSE) - { - // Convert URIs to URLs - $value = url::site($value, 'http'); - } - - // Add the info to the channel - $feed->channel->addChild($name, $value); - } - - foreach ($items as $item) - { - // Add the item to the channel - $row = $feed->channel->addChild('item'); - - foreach ($item as $name => $value) - { - if ($name === 'pubDate' AND (is_int($value) OR ctype_digit($value))) - { - // Convert timestamps to RFC 822 formatted dates - $value = date(DATE_RFC822, $value); - } - elseif (($name === 'link' OR $name === 'guid') AND strpos($value, '://') === FALSE) - { - // Convert URIs to URLs - $value = url::site($value, 'http'); - } - - // Add the info to the row - $row->addChild($name, $value); - } - } - - return $feed->asXML(); - } - -} // End feed \ No newline at end of file diff --git a/kohana/helpers/file.php b/kohana/helpers/file.php deleted file mode 100644 index b1b71740..00000000 --- a/kohana/helpers/file.php +++ /dev/null @@ -1,186 +0,0 @@ -'."\n"; - - // Add hidden fields immediate after opening tag - empty($hidden) or $form .= form::hidden($hidden); - - return $form; - } - - /** - * Generates an opening HTML form tag that can be used for uploading files. - * - * @param string form action attribute - * @param array extra attributes - * @param array hidden fields to be created immediately after the form tag - * @return string - */ - public static function open_multipart($action = NULL, $attr = array(), $hidden = array()) - { - // Set multi-part form type - $attr['enctype'] = 'multipart/form-data'; - - return form::open($action, $attr, $hidden); - } - - /** - * Generates a fieldset opening tag. - * - * @param array html attributes - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function open_fieldset($data = NULL, $extra = '') - { - return ''."\n"; - } - - /** - * Generates a fieldset closing tag. - * - * @return string - */ - public static function close_fieldset() - { - return ''."\n"; - } - - /** - * Generates a legend tag for use with a fieldset. - * - * @param string legend text - * @param array HTML attributes - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function legend($text = '', $data = NULL, $extra = '') - { - return ''.$text.''."\n"; - } - - /** - * Generates hidden form fields. - * You can pass a simple key/value string or an associative array with multiple values. - * - * @param string|array input name (string) or key/value pairs (array) - * @param string input value, if using an input name - * @return string - */ - public static function hidden($data, $value = '') - { - if ( ! is_array($data)) - { - $data = array - ( - $data => $value - ); - } - - $input = ''; - foreach ($data as $name => $value) - { - $attr = array - ( - 'type' => 'hidden', - 'name' => $name, - 'value' => $value - ); - - $input .= form::input($attr)."\n"; - } - - return $input; - } - - /** - * Creates an HTML form input tag. Defaults to a text type. - * - * @param string|array input name or an array of HTML attributes - * @param string input value, when using a name - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function input($data, $value = '', $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - // Type and value are required attributes - $data += array - ( - 'type' => 'text', - 'value' => $value - ); - - return ''; - } - - /** - * Creates a HTML form password input tag. - * - * @param string|array input name or an array of HTML attributes - * @param string input value, when using a name - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function password($data, $value = '', $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - $data['type'] = 'password'; - - return form::input($data, $value, $extra); - } - - /** - * Creates an HTML form upload input tag. - * - * @param string|array input name or an array of HTML attributes - * @param string input value, when using a name - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function upload($data, $value = '', $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - $data['type'] = 'file'; - - return form::input($data, $value, $extra); - } - - /** - * Creates an HTML form textarea tag. - * - * @param string|array input name or an array of HTML attributes - * @param string input value, when using a name - * @param string a string to be attached to the end of the attributes - * @param boolean encode existing entities - * @return string - */ - public static function textarea($data, $value = '', $extra = '', $double_encode = TRUE) - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - // Use the value from $data if possible, or use $value - $value = isset($data['value']) ? $data['value'] : $value; - - // Value is not part of the attributes - unset($data['value']); - - return ''.html::specialchars($value, $double_encode).''; - } - - /** - * Creates an HTML form select tag, or "dropdown menu". - * - * @param string|array input name or an array of HTML attributes - * @param array select options, when using a name - * @param string|array option key(s) that should be selected by default - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function dropdown($data, $options = NULL, $selected = NULL, $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - else - { - if (isset($data['options'])) - { - // Use data options - $options = $data['options']; - } - - if (isset($data['selected'])) - { - // Use data selected - $selected = $data['selected']; - } - } - - if (is_array($selected)) - { - // Multi-select box - $data['multiple'] = 'multiple'; - } - else - { - // Single selection (but converted to an array) - $selected = array($selected); - } - - $input = ''."\n"; - foreach ((array) $options as $key => $val) - { - // Key should always be a string - $key = (string) $key; - - if (is_array($val)) - { - $input .= ''."\n"; - foreach ($val as $inner_key => $inner_val) - { - // Inner key should always be a string - $inner_key = (string) $inner_key; - - $sel = in_array($inner_key, $selected) ? ' selected="selected"' : ''; - $input .= ''."\n"; - } - $input .= ''."\n"; - } - else - { - $sel = in_array($key, $selected) ? ' selected="selected"' : ''; - $input .= ''."\n"; - } - } - $input .= ''; - - return $input; - } - - /** - * Creates an HTML form checkbox input tag. - * - * @param string|array input name or an array of HTML attributes - * @param string input value, when using a name - * @param boolean make the checkbox checked by default - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function checkbox($data, $value = '', $checked = FALSE, $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - $data['type'] = 'checkbox'; - - if ($checked == TRUE OR (isset($data['checked']) AND $data['checked'] == TRUE)) - { - $data['checked'] = 'checked'; - } - else - { - unset($data['checked']); - } - - return form::input($data, $value, $extra); - } - - /** - * Creates an HTML form radio input tag. - * - * @param string|array input name or an array of HTML attributes - * @param string input value, when using a name - * @param boolean make the radio selected by default - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function radio($data = '', $value = '', $checked = FALSE, $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - $data['type'] = 'radio'; - - if ($checked == TRUE OR (isset($data['checked']) AND $data['checked'] == TRUE)) - { - $data['checked'] = 'checked'; - } - else - { - unset($data['checked']); - } - - return form::input($data, $value, $extra); - } - - /** - * Creates an HTML form submit input tag. - * - * @param string|array input name or an array of HTML attributes - * @param string input value, when using a name - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function submit($data = '', $value = '', $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - if (empty($data['name'])) - { - // Remove the name if it is empty - unset($data['name']); - } - - $data['type'] = 'submit'; - - return form::input($data, $value, $extra); - } - - /** - * Creates an HTML form button input tag. - * - * @param string|array input name or an array of HTML attributes - * @param string input value, when using a name - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function button($data = '', $value = '', $extra = '') - { - if ( ! is_array($data)) - { - $data = array('name' => $data); - } - - if (empty($data['name'])) - { - // Remove the name if it is empty - unset($data['name']); - } - - if (isset($data['value']) AND empty($value)) - { - $value = arr::remove('value', $data); - } - - return ''.$value.''; - } - - /** - * Closes an open form tag. - * - * @param string string to be attached after the closing tag - * @return string - */ - public static function close($extra = '') - { - return ''."\n".$extra; - } - - /** - * Creates an HTML form label tag. - * - * @param string|array label "for" name or an array of HTML attributes - * @param string label text or HTML - * @param string a string to be attached to the end of the attributes - * @return string - */ - public static function label($data = '', $text = NULL, $extra = '') - { - if ( ! is_array($data)) - { - if (is_string($data)) - { - // Specify the input this label is for - $data = array('for' => $data); - } - else - { - // No input specified - $data = array(); - } - } - - if ($text === NULL AND isset($data['for'])) - { - // Make the text the human-readable input name - $text = ucwords(inflector::humanize($data['for'])); - } - - return ''.$text.''; - } - - /** - * Sorts a key/value array of HTML attributes, putting form attributes first, - * and returns an attribute string. - * - * @param array HTML attributes array - * @return string - */ - public static function attributes($attr, $type = NULL) - { - if (empty($attr)) - return ''; - - if (isset($attr['name']) AND empty($attr['id']) AND strpos($attr['name'], '[') === FALSE) - { - if ($type === NULL AND ! empty($attr['type'])) - { - // Set the type by the attributes - $type = $attr['type']; - } - - switch ($type) - { - case 'text': - case 'textarea': - case 'password': - case 'select': - case 'checkbox': - case 'file': - case 'image': - case 'button': - case 'submit': - // Only specific types of inputs use name to id matching - $attr['id'] = $attr['name']; - break; - } - } - - $order = array - ( - 'action', - 'method', - 'type', - 'id', - 'name', - 'value', - 'src', - 'size', - 'maxlength', - 'rows', - 'cols', - 'accept', - 'tabindex', - 'accesskey', - 'align', - 'alt', - 'title', - 'class', - 'style', - 'selected', - 'checked', - 'readonly', - 'disabled' - ); - - $sorted = array(); - foreach ($order as $key) - { - if (isset($attr[$key])) - { - // Move the attribute to the sorted array - $sorted[$key] = $attr[$key]; - - // Remove the attribute from unsorted array - unset($attr[$key]); - } - } - - // Combine the sorted and unsorted attributes and create an HTML string - return html::attributes(array_merge($sorted, $attr)); - } - -} // End form \ No newline at end of file diff --git a/kohana/helpers/format.php b/kohana/helpers/format.php deleted file mode 100644 index fb8a0294..00000000 --- a/kohana/helpers/format.php +++ /dev/null @@ -1,66 +0,0 @@ -=')) - { - $str = htmlspecialchars($str, ENT_QUOTES, 'UTF-8', FALSE); - } - else - { - $str = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&', $str); - $str = str_replace(array('<', '>', '\'', '"'), array('<', '>', ''', '"'), $str); - } - } - - return $str; - } - - /** - * Perform a html::specialchars() with additional URL specific encoding. - * - * @param string string to convert - * @param boolean encode existing entities - * @return string - */ - public static function specialurlencode($str, $double_encode = TRUE) - { - return str_replace(' ', '%20', html::specialchars($str, $double_encode)); - } - - /** - * Create HTML link anchors. - * - * @param string URL or URI string - * @param string link text - * @param array HTML anchor attributes - * @param string non-default protocol, eg: https - * @return string - */ - public static function anchor($uri, $title = NULL, $attributes = NULL, $protocol = NULL) - { - if ($uri === '') - { - $site_url = url::base(FALSE); - } - elseif (strpos($uri, '://') === FALSE AND strpos($uri, '#') !== 0) - { - $site_url = url::site($uri, $protocol); - } - else - { - if (html::$windowed_urls === TRUE AND empty($attributes['target'])) - { - $attributes['target'] = '_blank'; - } - - $site_url = $uri; - } - - return - // Parsed URL - '' - // Title empty? Use the parsed URL - .(($title === NULL) ? $site_url : $title).''; - } - - /** - * Creates an HTML anchor to a file. - * - * @param string name of file to link to - * @param string link text - * @param array HTML anchor attributes - * @param string non-default protocol, eg: ftp - * @return string - */ - public static function file_anchor($file, $title = NULL, $attributes = NULL, $protocol = NULL) - { - return - // Base URL + URI = full URL - '' - // Title empty? Use the filename part of the URI - .(($title === NULL) ? end(explode('/', $file)) : $title) .''; - } - - /** - * Similar to anchor, but with the protocol parameter first. - * - * @param string link protocol - * @param string URI or URL to link to - * @param string link text - * @param array HTML anchor attributes - * @return string - */ - public static function panchor($protocol, $uri, $title = NULL, $attributes = FALSE) - { - return html::anchor($uri, $title, $attributes, $protocol); - } - - /** - * Create an array of anchors from an array of link/title pairs. - * - * @param array link/title pairs - * @return array - */ - public static function anchor_array(array $array) - { - $anchors = array(); - foreach ($array as $link => $title) - { - // Create list of anchors - $anchors[] = html::anchor($link, $title); - } - return $anchors; - } - - /** - * Generates an obfuscated version of an email address. - * - * @param string email address - * @return string - */ - public static function email($email) - { - $safe = ''; - foreach (str_split($email) as $letter) - { - switch (($letter === '@') ? rand(1, 2) : rand(1, 3)) - { - // HTML entity code - case 1: $safe .= '&#'.ord($letter).';'; break; - // Hex character code - case 2: $safe .= '&#x'.dechex(ord($letter)).';'; break; - // Raw (no) encoding - case 3: $safe .= $letter; - } - } - - return $safe; - } - - /** - * Creates an email anchor. - * - * @param string email address to send to - * @param string link text - * @param array HTML anchor attributes - * @return string - */ - public static function mailto($email, $title = NULL, $attributes = NULL) - { - if (empty($email)) - return $title; - - // Remove the subject or other parameters that do not need to be encoded - if (strpos($email, '?') !== FALSE) - { - // Extract the parameters from the email address - list ($email, $params) = explode('?', $email, 2); - - // Make the params into a query string, replacing spaces - $params = '?'.str_replace(' ', '%20', $params); - } - else - { - // No parameters - $params = ''; - } - - // Obfuscate email address - $safe = html::email($email); - - // Title defaults to the encoded email address - empty($title) and $title = $safe; - - // Parse attributes - empty($attributes) or $attributes = html::attributes($attributes); - - // Encoded start of the href="" is a static encoded version of 'mailto:' - return ''.$title.''; - } - - /** - * Generate a "breadcrumb" list of anchors representing the URI. - * - * @param array segments to use as breadcrumbs, defaults to using Router::$segments - * @return string - */ - public static function breadcrumb($segments = NULL) - { - empty($segments) and $segments = Router::$segments; - - $array = array(); - while ($segment = array_pop($segments)) - { - $array[] = html::anchor - ( - // Complete URI for the URL - implode('/', $segments).'/'.$segment, - // Title for the current segment - ucwords(inflector::humanize($segment)) - ); - } - - // Retrun the array of all the segments - return array_reverse($array); - } - - /** - * Creates a meta tag. - * - * @param string|array tag name, or an array of tags - * @param string tag "content" value - * @return string - */ - public static function meta($tag, $value = NULL) - { - if (is_array($tag)) - { - $tags = array(); - foreach ($tag as $t => $v) - { - // Build each tag and add it to the array - $tags[] = html::meta($t, $v); - } - - // Return all of the tags as a string - return implode("\n", $tags); - } - - // Set the meta attribute value - $attr = in_array(strtolower($tag), Kohana::config('http.meta_equiv')) ? 'http-equiv' : 'name'; - - return ''; - } - - /** - * Creates a stylesheet link. - * - * @param string|array filename, or array of filenames to match to array of medias - * @param string|array media type of stylesheet, or array to match filenames - * @param boolean include the index_page in the link - * @return string - */ - public static function stylesheet($style, $media = FALSE, $index = FALSE) - { - return html::link($style, 'stylesheet', 'text/css', '.css', $media, $index); - } - - /** - * Creates a link tag. - * - * @param string|array filename - * @param string|array relationship - * @param string|array mimetype - * @param string specifies suffix of the file - * @param string|array specifies on what device the document will be displayed - * @param boolean include the index_page in the link - * @return string - */ - public static function link($href, $rel, $type, $suffix = FALSE, $media = FALSE, $index = FALSE) - { - $compiled = ''; - - if (is_array($href)) - { - foreach ($href as $_href) - { - $_rel = is_array($rel) ? array_shift($rel) : $rel; - $_type = is_array($type) ? array_shift($type) : $type; - $_media = is_array($media) ? array_shift($media) : $media; - - $compiled .= html::link($_href, $_rel, $_type, $suffix, $_media, $index); - } - } - else - { - if (strpos($href, '://') === FALSE) - { - // Make the URL absolute - $href = url::base($index).$href; - } - - $length = strlen($suffix); - - if ( $length > 0 AND substr_compare($href, $suffix, -$length, $length, FALSE) !== 0) - { - // Add the defined suffix - $href .= $suffix; - } - - $attr = array - ( - 'rel' => $rel, - 'type' => $type, - 'href' => $href, - ); - - if ( ! empty($media)) - { - // Add the media type to the attributes - $attr['media'] = $media; - } - - $compiled = ''; - } - - return $compiled."\n"; - } - - /** - * Creates a script link. - * - * @param string|array filename - * @param boolean include the index_page in the link - * @return string - */ - public static function script($script, $index = FALSE) - { - $compiled = ''; - - if (is_array($script)) - { - foreach ($script as $name) - { - $compiled .= html::script($name, $index); - } - } - else - { - if (strpos($script, '://') === FALSE) - { - // Add the suffix only when it's not already present - $script = url::base((bool) $index).$script; - } - - if (substr_compare($script, '.js', -3, 3, FALSE) !== 0) - { - // Add the javascript suffix - $script .= '.js'; - } - - $compiled = ''; - } - - return $compiled."\n"; - } - - /** - * Creates a image link. - * - * @param string image source, or an array of attributes - * @param string|array image alt attribute, or an array of attributes - * @param boolean include the index_page in the link - * @return string - */ - public static function image($src = NULL, $alt = NULL, $index = FALSE) - { - // Create attribute list - $attributes = is_array($src) ? $src : array('src' => $src); - - if (is_array($alt)) - { - $attributes += $alt; - } - elseif ( ! empty($alt)) - { - // Add alt to attributes - $attributes['alt'] = $alt; - } - - if (strpos($attributes['src'], '://') === FALSE) - { - // Make the src attribute into an absolute URL - $attributes['src'] = url::base($index).$attributes['src']; - } - - return ''; - } - - /** - * Compiles an array of HTML attributes into an attribute string. - * - * @param string|array array of attributes - * @return string - */ - public static function attributes($attrs) - { - if (empty($attrs)) - return ''; - - if (is_string($attrs)) - return ' '.$attrs; - - $compiled = ''; - foreach ($attrs as $key => $val) - { - $compiled .= ' '.$key.'="'.html::specialchars($val).'"'; - } - - return $compiled; - } - -} // End html diff --git a/kohana/helpers/inflector.php b/kohana/helpers/inflector.php deleted file mode 100644 index 1e4fee23..00000000 --- a/kohana/helpers/inflector.php +++ /dev/null @@ -1,193 +0,0 @@ - 1) - return $str; - - // Cache key name - $key = 'singular_'.$str.$count; - - if (isset(inflector::$cache[$key])) - return inflector::$cache[$key]; - - if (inflector::uncountable($str)) - return inflector::$cache[$key] = $str; - - if (empty(inflector::$irregular)) - { - // Cache irregular words - inflector::$irregular = Kohana::config('inflector.irregular'); - } - - if ($irregular = array_search($str, inflector::$irregular)) - { - $str = $irregular; - } - elseif (preg_match('/[sxz]es$/', $str) OR preg_match('/[^aeioudgkprt]hes$/', $str)) - { - // Remove "es" - $str = substr($str, 0, -2); - } - elseif (preg_match('/[^aeiou]ies$/', $str)) - { - $str = substr($str, 0, -3).'y'; - } - elseif (substr($str, -1) === 's' AND substr($str, -2) !== 'ss') - { - $str = substr($str, 0, -1); - } - - return inflector::$cache[$key] = $str; - } - - /** - * Makes a singular word plural. - * - * @param string word to pluralize - * @return string - */ - public static function plural($str, $count = NULL) - { - // Remove garbage - $str = strtolower(trim($str)); - - if (is_string($count)) - { - // Convert to integer when using a digit string - $count = (int) $count; - } - - // Do nothing with singular - if ($count === 1) - return $str; - - // Cache key name - $key = 'plural_'.$str.$count; - - if (isset(inflector::$cache[$key])) - return inflector::$cache[$key]; - - if (inflector::uncountable($str)) - return inflector::$cache[$key] = $str; - - if (empty(inflector::$irregular)) - { - // Cache irregular words - inflector::$irregular = Kohana::config('inflector.irregular'); - } - - if (isset(inflector::$irregular[$str])) - { - $str = inflector::$irregular[$str]; - } - elseif (preg_match('/[sxz]$/', $str) OR preg_match('/[^aeioudgkprt]h$/', $str)) - { - $str .= 'es'; - } - elseif (preg_match('/[^aeiou]y$/', $str)) - { - // Change "y" to "ies" - $str = substr_replace($str, 'ies', -1); - } - else - { - $str .= 's'; - } - - // Set the cache and return - return inflector::$cache[$key] = $str; - } - - /** - * Makes a phrase camel case. - * - * @param string phrase to camelize - * @return string - */ - public static function camelize($str) - { - $str = 'x'.strtolower(trim($str)); - $str = ucwords(preg_replace('/[\s_]+/', ' ', $str)); - - return substr(str_replace(' ', '', $str), 1); - } - - /** - * Makes a phrase underscored instead of spaced. - * - * @param string phrase to underscore - * @return string - */ - public static function underscore($str) - { - return preg_replace('/\s+/', '_', trim($str)); - } - - /** - * Makes an underscored or dashed phrase human-reable. - * - * @param string phrase to make human-reable - * @return string - */ - public static function humanize($str) - { - return preg_replace('/[_-]+/', ' ', trim($str)); - } - -} // End inflector \ No newline at end of file diff --git a/kohana/helpers/num.php b/kohana/helpers/num.php deleted file mode 100644 index 3eb5d5ac..00000000 --- a/kohana/helpers/num.php +++ /dev/null @@ -1,26 +0,0 @@ - 0); - } - - /** - * Compare the q values for given array of content types and return the one with the highest value. - * If items are found to have the same q value, the first one encountered in the given array wins. - * If all items in the given array have a q value of 0, FALSE is returned. - * - * @param array content types - * @param boolean set to TRUE to disable wildcard checking - * @return mixed string mime type with highest q value, FALSE if none of the given types are accepted - */ - public static function preferred_accept($types, $explicit_check = FALSE) - { - // Initialize - $mime_types = array(); - $max_q = 0; - $preferred = FALSE; - - // Load q values for all given content types - foreach (array_unique($types) as $type) - { - $mime_types[$type] = request::accepts_at_quality($type, $explicit_check); - } - - // Look for the highest q value - foreach ($mime_types as $type => $q) - { - if ($q > $max_q) - { - $max_q = $q; - $preferred = $type; - } - } - - return $preferred; - } - - /** - * Returns quality factor at which the client accepts content type. - * - * @param string content type (e.g. "image/jpg", "jpg") - * @param boolean set to TRUE to disable wildcard checking - * @return integer|float - */ - public static function accepts_at_quality($type = NULL, $explicit_check = FALSE) - { - request::parse_accept_header(); - - // Normalize type - $type = strtolower((string) $type); - - // General content type (e.g. "jpg") - if (strpos($type, '/') === FALSE) - { - // Don't accept anything by default - $q = 0; - - // Look up relevant mime types - foreach ((array) Kohana::config('mimes.'.$type) as $type) - { - $q2 = request::accepts_at_quality($type, $explicit_check); - $q = ($q2 > $q) ? $q2 : $q; - } - - return $q; - } - - // Content type with subtype given (e.g. "image/jpg") - $type = explode('/', $type, 2); - - // Exact match - if (isset(request::$accept_types[$type[0]][$type[1]])) - return request::$accept_types[$type[0]][$type[1]]; - - // Wildcard match (if not checking explicitly) - if ($explicit_check === FALSE AND isset(request::$accept_types[$type[0]]['*'])) - return request::$accept_types[$type[0]]['*']; - - // Catch-all wildcard match (if not checking explicitly) - if ($explicit_check === FALSE AND isset(request::$accept_types['*']['*'])) - return request::$accept_types['*']['*']; - - // Content type not accepted - return 0; - } - - /** - * Parses client's HTTP Accept request header, and builds array structure representing it. - * - * @return void - */ - protected static function parse_accept_header() - { - // Run this function just once - if (request::$accept_types !== NULL) - return; - - // Initialize accept_types array - request::$accept_types = array(); - - // No HTTP Accept header found - if (empty($_SERVER['HTTP_ACCEPT'])) - { - // Accept everything - request::$accept_types['*']['*'] = 1; - return; - } - - // Remove linebreaks and parse the HTTP Accept header - foreach (explode(',', str_replace(array("\r", "\n"), '', $_SERVER['HTTP_ACCEPT'])) as $accept_entry) - { - // Explode each entry in content type and possible quality factor - $accept_entry = explode(';', trim($accept_entry), 2); - - // Explode each content type (e.g. "text/html") - $type = explode('/', $accept_entry[0], 2); - - // Skip invalid content types - if ( ! isset($type[1])) - continue; - - // Assume a default quality factor of 1 if no custom q value found - $q = (isset($accept_entry[1]) AND preg_match('~\bq\s*+=\s*+([.0-9]+)~', $accept_entry[1], $match)) ? (float) $match[1] : 1; - - // Populate accept_types array - if ( ! isset(request::$accept_types[$type[0]][$type[1]]) OR $q > request::$accept_types[$type[0]][$type[1]]) - { - request::$accept_types[$type[0]][$type[1]] = $q; - } - } - } - -} // End request \ No newline at end of file diff --git a/kohana/helpers/security.php b/kohana/helpers/security.php deleted file mode 100644 index cd48d2e0..00000000 --- a/kohana/helpers/security.php +++ /dev/null @@ -1,47 +0,0 @@ -xss_clean($str); - } - - /** - * Remove image tags from a string. - * - * @param string string to sanitize - * @return string - */ - public static function strip_image_tags($str) - { - return preg_replace('#\s]*)["\']?[^>]*)?>#is', '$1', $str); - } - - /** - * Remove PHP tags from a string. - * - * @param string string to sanitize - * @return string - */ - public static function encode_php_tags($str) - { - return str_replace(array(''), array('<?', '?>'), $str); - } - -} // End security \ No newline at end of file diff --git a/kohana/helpers/text.php b/kohana/helpers/text.php deleted file mode 100644 index d0e573ec..00000000 --- a/kohana/helpers/text.php +++ /dev/null @@ -1,410 +0,0 @@ - 1) - { - if (ctype_alpha($str)) - { - // Add a random digit - $str[mt_rand(0, $length - 1)] = chr(mt_rand(48, 57)); - } - elseif (ctype_digit($str)) - { - // Add a random letter - $str[mt_rand(0, $length - 1)] = chr(mt_rand(65, 90)); - } - } - - return $str; - } - - /** - * Reduces multiple slashes in a string to single slashes. - * - * @param string string to reduce slashes of - * @return string - */ - public static function reduce_slashes($str) - { - return preg_replace('#(? $badword) - { - $badwords[$key] = str_replace('\*', '\S*?', preg_quote((string) $badword)); - } - - $regex = '('.implode('|', $badwords).')'; - - if ($replace_partial_words == TRUE) - { - // Just using \b isn't sufficient when we need to replace a badword that already contains word boundaries itself - $regex = '(?<=\b|\s|^)'.$regex.'(?=\b|\s|$)'; - } - - $regex = '!'.$regex.'!ui'; - - if (utf8::strlen($replacement) == 1) - { - $regex .= 'e'; - return preg_replace($regex, 'str_repeat($replacement, utf8::strlen(\'$1\'))', $str); - } - - return preg_replace($regex, $replacement, $str); - } - - /** - * Finds the text that is similar between a set of words. - * - * @param array words to find similar text of - * @return string - */ - public static function similar(array $words) - { - // First word is the word to match against - $word = current($words); - - for ($i = 0, $max = strlen($word); $i < $max; ++$i) - { - foreach ($words as $w) - { - // Once a difference is found, break out of the loops - if ( ! isset($w[$i]) OR $w[$i] !== $word[$i]) - break 2; - } - } - - // Return the similar text - return substr($word, 0, $i); - } - - /** - * Converts text email addresses and anchors into links. - * - * @param string text to auto link - * @return string - */ - public static function auto_link($text) - { - // Auto link emails first to prevent problems with "www.domain.com@example.com" - return text::auto_link_urls(text::auto_link_emails($text)); - } - - /** - * Converts text anchors into links. - * - * @param string text to auto link - * @return string - */ - public static function auto_link_urls($text) - { - // Finds all http/https/ftp/ftps links that are not part of an existing html anchor - if (preg_match_all('~\b(?)(?:ht|f)tps?://\S+(?:/|\b)~i', $text, $matches)) - { - foreach ($matches[0] as $match) - { - // Replace each link with an anchor - $text = str_replace($match, html::anchor($match), $text); - } - } - - // Find all naked www.links.com (without http://) - if (preg_match_all('~\b(?|58;)(?!\.)[-+_a-z0-9.]++(? and
      markup to text. Basically nl2br() on steroids. - * - * @param string subject - * @return string - */ - public static function auto_p($str) - { - // Trim whitespace - if (($str = trim($str)) === '') - return ''; - - // Standardize newlines - $str = str_replace(array("\r\n", "\r"), "\n", $str); - - // Trim whitespace on each line - $str = preg_replace('~^[ \t]+~m', '', $str); - $str = preg_replace('~[ \t]+$~m', '', $str); - - // The following regexes only need to be executed if the string contains html - if ($html_found = (strpos($str, '<') !== FALSE)) - { - // Elements that should not be surrounded by p tags - $no_p = '(?:p|div|h[1-6r]|ul|ol|li|blockquote|d[dlt]|pre|t[dhr]|t(?:able|body|foot|head)|c(?:aption|olgroup)|form|s(?:elect|tyle)|a(?:ddress|rea)|ma(?:p|th))'; - - // Put at least two linebreaks before and after $no_p elements - $str = preg_replace('~^<'.$no_p.'[^>]*+>~im', "\n$0", $str); - $str = preg_replace('~$~im', "$0\n", $str); - } - - // Do the

      magic! - $str = '

      '.trim($str).'

      '; - $str = preg_replace('~\n{2,}~', "

      \n\n

      ", $str); - - // The following regexes only need to be executed if the string contains html - if ($html_found !== FALSE) - { - // Remove p tags around $no_p elements - $str = preg_replace('~

      (?=]*+>)~i', '', $str); - $str = preg_replace('~(]*+>)

      ~i', '$1', $str); - } - - // Convert single linebreaks to
      - $str = preg_replace('~(?\n", $str); - - return $str; - } - - /** - * Returns human readable sizes. - * @see Based on original functions written by: - * @see Aidan Lister: http://aidanlister.com/repos/v/function.size_readable.php - * @see Quentin Zervaas: http://www.phpriot.com/d/code/strings/filesize-format/ - * - * @param integer size in bytes - * @param string a definitive unit - * @param string the return string format - * @param boolean whether to use SI prefixes or IEC - * @return string - */ - public static function bytes($bytes, $force_unit = NULL, $format = NULL, $si = TRUE) - { - // Format string - $format = ($format === NULL) ? '%01.2f %s' : (string) $format; - - // IEC prefixes (binary) - if ($si == FALSE OR strpos($force_unit, 'i') !== FALSE) - { - $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'); - $mod = 1024; - } - // SI prefixes (decimal) - else - { - $units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB'); - $mod = 1000; - } - - // Determine unit to use - if (($power = array_search((string) $force_unit, $units)) === FALSE) - { - $power = ($bytes > 0) ? floor(log($bytes, $mod)) : 0; - } - - return sprintf($format, $bytes / pow($mod, $power), $units[$power]); - } - - /** - * Prevents widow words by inserting a non-breaking space between the last two words. - * @see http://www.shauninman.com/archive/2006/08/22/widont_wordpress_plugin - * - * @param string string to remove widows from - * @return string - */ - public static function widont($str) - { - $str = rtrim($str); - $space = strrpos($str, ' '); - - if ($space !== FALSE) - { - $str = substr($str, 0, $space).' '.substr($str, $space + 1); - } - - return $str; - } - -} // End text \ No newline at end of file diff --git a/kohana/helpers/upload.php b/kohana/helpers/upload.php deleted file mode 100644 index 422e9e8d..00000000 --- a/kohana/helpers/upload.php +++ /dev/null @@ -1,162 +0,0 @@ - 'Refresh', - '300' => 'Multiple Choices', - '301' => 'Moved Permanently', - '302' => 'Found', - '303' => 'See Other', - '304' => 'Not Modified', - '305' => 'Use Proxy', - '307' => 'Temporary Redirect' - ); - - // Validate the method and default to 302 - $method = isset($codes[$method]) ? (string) $method : '302'; - - if ($method === '300') - { - $uri = (array) $uri; - - $output = '
        '; - foreach ($uri as $link) - { - $output .= '
      • '.html::anchor($link).'
      • '; - } - $output .= '
      '; - - // The first URI will be used for the Location header - $uri = $uri[0]; - } - else - { - $output = '

      '.html::anchor($uri).'

      '; - } - - // Run the redirect event - Event::run('system.redirect', $uri); - - if (strpos($uri, '://') === FALSE) - { - // HTTP headers expect absolute URLs - $uri = url::site($uri, request::protocol()); - } - - if ($method === 'refresh') - { - header('Refresh: 0; url='.$uri); - } - else - { - header('HTTP/1.1 '.$method.' '.$codes[$method]); - header('Location: '.$uri); - } - - // We are about to exit, so run the send_headers event - Event::run('system.send_headers'); - - exit('

      '.$method.' - '.$codes[$method].'

      '.$output); - } - -} // End url \ No newline at end of file diff --git a/kohana/helpers/valid.php b/kohana/helpers/valid.php deleted file mode 100644 index 610076f3..00000000 --- a/kohana/helpers/valid.php +++ /dev/null @@ -1,338 +0,0 @@ -= 0; $i -= 2) - { - // Add up every 2nd digit, starting from the right - $checksum += substr($number, $i, 1); - } - - for ($i = $length - 2; $i >= 0; $i -= 2) - { - // Add up every 2nd digit doubled, starting from the right - $double = substr($number, $i, 1) * 2; - - // Subtract 9 from the double where value is greater than 10 - $checksum += ($double >= 10) ? $double - 9 : $double; - } - - // If the checksum is a multiple of 10, the number is valid - return ($checksum % 10 === 0); - } - - /** - * Checks if a phone number is valid. - * - * @param string phone number to check - * @return boolean - */ - public static function phone($number, $lengths = NULL) - { - if ( ! is_array($lengths)) - { - $lengths = array(7,10,11); - } - - // Remove all non-digit characters from the number - $number = preg_replace('/\D+/', '', $number); - - // Check if the number is within range - return in_array(strlen($number), $lengths); - } - - /** - * Tests if a string is a valid date string. - * - * @param string date to check - * @return boolean - */ - public static function date($str) - { - return (strtotime($str) !== FALSE); - } - - /** - * Checks whether a string consists of alphabetical characters only. - * - * @param string input string - * @param boolean trigger UTF-8 compatibility - * @return boolean - */ - public static function alpha($str, $utf8 = FALSE) - { - return ($utf8 === TRUE) - ? (bool) preg_match('/^\pL++$/uD', (string) $str) - : ctype_alpha((string) $str); - } - - /** - * Checks whether a string consists of alphabetical characters and numbers only. - * - * @param string input string - * @param boolean trigger UTF-8 compatibility - * @return boolean - */ - public static function alpha_numeric($str, $utf8 = FALSE) - { - return ($utf8 === TRUE) - ? (bool) preg_match('/^[\pL\pN]++$/uD', (string) $str) - : ctype_alnum((string) $str); - } - - /** - * Checks whether a string consists of alphabetical characters, numbers, underscores and dashes only. - * - * @param string input string - * @param boolean trigger UTF-8 compatibility - * @return boolean - */ - public static function alpha_dash($str, $utf8 = FALSE) - { - return ($utf8 === TRUE) - ? (bool) preg_match('/^[-\pL\pN_]++$/uD', (string) $str) - : (bool) preg_match('/^[-a-z0-9_]++$/iD', (string) $str); - } - - /** - * Checks whether a string consists of digits only (no dots or dashes). - * - * @param string input string - * @param boolean trigger UTF-8 compatibility - * @return boolean - */ - public static function digit($str, $utf8 = FALSE) - { - return ($utf8 === TRUE) - ? (bool) preg_match('/^\pN++$/uD', (string) $str) - : ctype_digit((string) $str); - } - - /** - * Checks whether a string is a valid number (negative and decimal numbers allowed). - * - * @see Uses locale conversion to allow decimal point to be locale specific. - * @see http://www.php.net/manual/en/function.localeconv.php - * - * @param string input string - * @return boolean - */ - public static function numeric($str) - { - // Use localeconv to set the decimal_point value: Usually a comma or period. - $locale = localeconv(); - return (bool) preg_match('/^-?[0-9'.$locale['decimal_point'].']++$/D', (string) $str); - } - - /** - * Checks whether a string is a valid text. Letters, numbers, whitespace, - * dashes, periods, and underscores are allowed. - * - * @param string text to check - * @return boolean - */ - public static function standard_text($str) - { - // pL matches letters - // pN matches numbers - // pZ matches whitespace - // pPc matches underscores - // pPd matches dashes - // pPo matches normal puncuation - return (bool) preg_match('/^[\pL\pN\pZ\p{Pc}\p{Pd}\p{Po}]++$/uD', (string) $str); - } - - /** - * Checks if a string is a proper decimal format. The format array can be - * used to specify a decimal length, or a number and decimal length, eg: - * array(2) would force the number to have 2 decimal places, array(4,2) - * would force the number to have 4 digits and 2 decimal places. - * - * @param string input string - * @param array decimal format: y or x,y - * @return boolean - */ - public static function decimal($str, $format = NULL) - { - // Create the pattern - $pattern = '/^[0-9]%s\.[0-9]%s$/'; - - if ( ! empty($format)) - { - if (count($format) > 1) - { - // Use the format for number and decimal length - $pattern = sprintf($pattern, '{'.$format[0].'}', '{'.$format[1].'}'); - } - elseif (count($format) > 0) - { - // Use the format as decimal length - $pattern = sprintf($pattern, '+', '{'.$format[0].'}'); - } - } - else - { - // No format - $pattern = sprintf($pattern, '+', '+'); - } - - return (bool) preg_match($pattern, (string) $str); - } - -} // End valid \ No newline at end of file diff --git a/kohana/i18n/en_US/cache.php b/kohana/i18n/en_US/cache.php deleted file mode 100644 index bef02793..00000000 --- a/kohana/i18n/en_US/cache.php +++ /dev/null @@ -1,10 +0,0 @@ - 'The %s group is not defined in your configuration.', - 'extension_not_loaded' => 'The %s PHP extension must be loaded to use this driver.', - 'unwritable' => 'The configured storage location, %s, is not writable.', - 'resources' => 'Caching of resources is impossible, because resources cannot be serialized.', - 'driver_error' => '%s', -); \ No newline at end of file diff --git a/kohana/i18n/en_US/calendar.php b/kohana/i18n/en_US/calendar.php deleted file mode 100644 index 21dad220..00000000 --- a/kohana/i18n/en_US/calendar.php +++ /dev/null @@ -1,59 +0,0 @@ - 'Su', - 'mo' => 'Mo', - 'tu' => 'Tu', - 'we' => 'We', - 'th' => 'Th', - 'fr' => 'Fr', - 'sa' => 'Sa', - - // Short day names - 'sun' => 'Sun', - 'mon' => 'Mon', - 'tue' => 'Tue', - 'wed' => 'Wed', - 'thu' => 'Thu', - 'fri' => 'Fri', - 'sat' => 'Sat', - - // Long day names - 'sunday' => 'Sunday', - 'monday' => 'Monday', - 'tuesday' => 'Tuesday', - 'wednesday' => 'Wednesday', - 'thursday' => 'Thursday', - 'friday' => 'Friday', - 'saturday' => 'Saturday', - - // Short month names - 'jan' => 'Jan', - 'feb' => 'Feb', - 'mar' => 'Mar', - 'apr' => 'Apr', - 'may' => 'May', - 'jun' => 'Jun', - 'jul' => 'Jul', - 'aug' => 'Aug', - 'sep' => 'Sep', - 'oct' => 'Oct', - 'nov' => 'Nov', - 'dec' => 'Dec', - - // Long month names - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', - 'april' => 'April', - 'mayl' => 'May', - 'june' => 'June', - 'july' => 'July', - 'august' => 'August', - 'september' => 'September', - 'october' => 'October', - 'november' => 'November', - 'december' => 'December' -); \ No newline at end of file diff --git a/kohana/i18n/en_US/core.php b/kohana/i18n/en_US/core.php deleted file mode 100644 index 9711b7cd..00000000 --- a/kohana/i18n/en_US/core.php +++ /dev/null @@ -1,34 +0,0 @@ - 'There can be only one instance of Kohana per page request', - 'uncaught_exception' => 'Uncaught %s: %s in file %s on line %s', - 'invalid_method' => 'Invalid method %s called in %s', - 'invalid_property' => 'The %s property does not exist in the %s class.', - 'log_dir_unwritable' => 'The log directory is not writable: %s', - 'resource_not_found' => 'The requested %s, %s, could not be found', - 'invalid_filetype' => 'The requested filetype, .%s, is not allowed in your view configuration file', - 'view_set_filename' => 'You must set the the view filename before calling render', - 'no_default_route' => 'Please set a default route in config/routes.php', - 'no_controller' => 'Kohana was not able to determine a controller to process this request: %s', - 'page_not_found' => 'The page you requested, %s, could not be found.', - 'stats_footer' => 'Loaded in {execution_time} seconds, using {memory_usage} of memory. Generated by Kohana v{kohana_version}.', - 'error_file_line' => '%s [%s]:', - 'stack_trace' => 'Stack Trace', - 'generic_error' => 'Unable to Complete Request', - 'errors_disabled' => 'You can go to the home page or try again.', - - // Drivers - 'driver_implements' => 'The %s driver for the %s library must implement the %s interface', - 'driver_not_found' => 'The %s driver for the %s library could not be found', - - // Resource names - 'config' => 'config file', - 'controller' => 'controller', - 'helper' => 'helper', - 'library' => 'library', - 'driver' => 'driver', - 'model' => 'model', - 'view' => 'view', -); diff --git a/kohana/i18n/en_US/database.php b/kohana/i18n/en_US/database.php deleted file mode 100644 index 172e5c90..00000000 --- a/kohana/i18n/en_US/database.php +++ /dev/null @@ -1,15 +0,0 @@ - 'The %s group is not defined in your configuration.', - 'error' => 'There was an SQL error: %s', - 'connection' => 'There was an error connecting to the database: %s', - 'invalid_dsn' => 'The DSN you supplied is not valid: %s', - 'must_use_set' => 'You must set a SET clause for your query.', - 'must_use_where' => 'You must set a WHERE clause for your query.', - 'must_use_table' => 'You must set a database table for your query.', - 'table_not_found' => 'Table %s does not exist in your database.', - 'not_implemented' => 'The method you called, %s, is not supported by this driver.', - 'result_read_only' => 'Query results are read only.' -); \ No newline at end of file diff --git a/kohana/i18n/en_US/encrypt.php b/kohana/i18n/en_US/encrypt.php deleted file mode 100644 index 9afd6208..00000000 --- a/kohana/i18n/en_US/encrypt.php +++ /dev/null @@ -1,8 +0,0 @@ - 'The %s group is not defined in your configuration.', - 'requires_mcrypt' => 'To use the Encrypt library, mcrypt must be enabled in your PHP installation', - 'no_encryption_key' => 'To use the Encrypt library, you must set an encryption key in your config file' -); diff --git a/kohana/i18n/en_US/errors.php b/kohana/i18n/en_US/errors.php deleted file mode 100644 index 83341a2c..00000000 --- a/kohana/i18n/en_US/errors.php +++ /dev/null @@ -1,16 +0,0 @@ - array( 1, 'Framework Error', 'Please check the Kohana documentation for information about the following error.'), - E_PAGE_NOT_FOUND => array( 1, 'Page Not Found', 'The requested page was not found. It may have moved, been deleted, or archived.'), - E_DATABASE_ERROR => array( 1, 'Database Error', 'A database error occurred while performing the requested procedure. Please review the database error below for more information.'), - E_RECOVERABLE_ERROR => array( 1, 'Recoverable Error', 'An error was detected which prevented the loading of this page. If this problem persists, please contact the website administrator.'), - E_ERROR => array( 1, 'Fatal Error', ''), - E_USER_ERROR => array( 1, 'Fatal Error', ''), - E_PARSE => array( 1, 'Syntax Error', ''), - E_WARNING => array( 1, 'Warning Message', ''), - E_USER_WARNING => array( 1, 'Warning Message', ''), - E_STRICT => array( 2, 'Strict Mode Error', ''), - E_NOTICE => array( 2, 'Runtime Message', ''), -); \ No newline at end of file diff --git a/kohana/i18n/en_US/event.php b/kohana/i18n/en_US/event.php deleted file mode 100644 index 282a0a25..00000000 --- a/kohana/i18n/en_US/event.php +++ /dev/null @@ -1,7 +0,0 @@ - 'Attempt to attach invalid subject %s to %s failed: Subjects must extend the Event_Subject class', - 'invalid_observer' => 'Attempt to attach invalid observer %s to %s failed: Observers must extend the Event_Observer class', -); diff --git a/kohana/i18n/en_US/image.php b/kohana/i18n/en_US/image.php deleted file mode 100644 index 9f184930..00000000 --- a/kohana/i18n/en_US/image.php +++ /dev/null @@ -1,33 +0,0 @@ - 'The Image library requires the getimagesize() PHP function, which is not available in your installation.', - 'unsupported_method' => 'Your configured driver does not support the %s image transformation.', - 'file_not_found' => 'The specified image, %s, was not found. Please verify that images exist by using file_exists() before manipulating them.', - 'type_not_allowed' => 'The specified image, %s, is not an allowed image type.', - 'invalid_width' => 'The width you specified, %s, is not valid.', - 'invalid_height' => 'The height you specified, %s, is not valid.', - 'invalid_dimensions' => 'The dimensions specified for %s are not valid.', - 'invalid_master' => 'The master dimension specified is not valid.', - 'invalid_flip' => 'The flip direction specified is not valid.', - 'directory_unwritable' => 'The specified directory, %s, is not writable.', - - // ImageMagick specific messages - 'imagemagick' => array - ( - 'not_found' => 'The ImageMagick directory specified does not contain a required program, %s.', - ), - - // GraphicsMagick specific messages - 'graphicsmagick' => array - ( - 'not_found' => 'The GraphicsMagick directory specified does not contain a required program, %s.', - ), - - // GD specific messages - 'gd' => array - ( - 'requires_v2' => 'The Image library requires GD2. Please see http://php.net/gd_info for more information.', - ), -); diff --git a/kohana/i18n/en_US/orm.php b/kohana/i18n/en_US/orm.php deleted file mode 100644 index 3c5720bc..00000000 --- a/kohana/i18n/en_US/orm.php +++ /dev/null @@ -1,3 +0,0 @@ - 'Benchmarks', - 'post_data' => 'Post Data', - 'no_post' => 'No post data', - 'session_data' => 'Session Data', - 'no_session' => 'No session data', - 'queries' => 'Database Queries', - 'no_queries' => 'No queries', - 'no_database' => 'Database not loaded', - 'cookie_data' => 'Cookie Data', - 'no_cookie' => 'No cookie data', -); diff --git a/kohana/i18n/en_US/session.php b/kohana/i18n/en_US/session.php deleted file mode 100644 index ee781c65..00000000 --- a/kohana/i18n/en_US/session.php +++ /dev/null @@ -1,6 +0,0 @@ - 'The session_name, %s, is invalid. It must contain only alphanumeric characters and underscores. Also at least one letter must be present.', -); \ No newline at end of file diff --git a/kohana/i18n/en_US/upload.php b/kohana/i18n/en_US/upload.php deleted file mode 100644 index 7f6e216d..00000000 --- a/kohana/i18n/en_US/upload.php +++ /dev/null @@ -1,6 +0,0 @@ - 'The upload destination folder, %s, does not appear to be writable.', -); \ No newline at end of file diff --git a/kohana/i18n/en_US/validation.php b/kohana/i18n/en_US/validation.php deleted file mode 100644 index d98e548f..00000000 --- a/kohana/i18n/en_US/validation.php +++ /dev/null @@ -1,41 +0,0 @@ - 'Invalid validation rule used: %s', - 'i18n_array' => 'The %s i18n key must be an array to be used with the in_lang rule', - 'not_callable' => 'Callback %s used for Validation is not callable', - - // General errors - 'unknown_error' => 'Unknown validation error while validating the %s field.', - 'required' => 'The %s field is required.', - 'min_length' => 'The %s field must be at least %d characters long.', - 'max_length' => 'The %s field must be %d characters or fewer.', - 'exact_length' => 'The %s field must be exactly %d characters.', - 'in_array' => 'The %s field must be selected from the options listed.', - 'matches' => 'The %s field must match the %s field.', - 'valid_url' => 'The %s field must contain a valid URL.', - 'valid_email' => 'The %s field must contain a valid email address.', - 'valid_ip' => 'The %s field must contain a valid IP address.', - 'valid_type' => 'The %s field must only contain %s characters.', - 'range' => 'The %s field must be between specified ranges.', - 'regex' => 'The %s field does not match accepted input.', - 'depends_on' => 'The %s field depends on the %s field.', - - // Upload errors - 'user_aborted' => 'The %s file was aborted during upload.', - 'invalid_type' => 'The %s file is not an allowed file type.', - 'max_size' => 'The %s file you uploaded was too large. The maximum size allowed is %s.', - 'max_width' => 'The %s file you uploaded was too big. The maximum allowed width is %spx.', - 'max_height' => 'The %s file you uploaded was too big. The maximum allowed height is %spx.', - 'min_width' => 'The %s file you uploaded was too small. The minimum allowed width is %spx.', - 'min_height' => 'The %s file you uploaded was too small. The minimum allowed height is %spx.', - - // Field types - 'alpha' => 'alphabetical', - 'alpha_numeric' => 'alphabetical and numeric', - 'alpha_dash' => 'alphabetical, dash, and underscore', - 'digit' => 'digit', - 'numeric' => 'numeric', -); diff --git a/kohana/libraries/Cache.php b/kohana/libraries/Cache.php deleted file mode 100644 index 8a02a905..00000000 --- a/kohana/libraries/Cache.php +++ /dev/null @@ -1,208 +0,0 @@ -config = $config; - - // Set driver name - $driver = 'Cache_'.ucfirst($this->config['driver']).'_Driver'; - - // Load the driver - if ( ! Kohana::auto_load($driver)) - throw new Kohana_Exception('core.driver_not_found', $this->config['driver'], get_class($this)); - - // Initialize the driver - $this->driver = new $driver($this->config['params']); - - // Validate the driver - if ( ! ($this->driver instanceof Cache_Driver)) - throw new Kohana_Exception('core.driver_implements', $this->config['driver'], get_class($this), 'Cache_Driver'); - - Kohana::log('debug', 'Cache Library initialized'); - - if (Cache::$loaded !== TRUE) - { - $this->config['requests'] = (int) $this->config['requests']; - - if ($this->config['requests'] > 0 AND mt_rand(1, $this->config['requests']) === 1) - { - // Do garbage collection - $this->driver->delete_expired(); - - Kohana::log('debug', 'Cache: Expired caches deleted.'); - } - - // Cache has been loaded once - Cache::$loaded = TRUE; - } - } - - /** - * Fetches a cache by id. NULL is returned when a cache item is not found. - * - * @param string cache id - * @return mixed cached data or NULL - */ - public function get($id) - { - // Sanitize the ID - $id = $this->sanitize_id($id); - - return $this->driver->get($id); - } - - /** - * Fetches all of the caches for a given tag. An empty array will be - * returned when no matching caches are found. - * - * @param string cache tag - * @return array all cache items matching the tag - */ - public function find($tag) - { - return $this->driver->find($tag); - } - - /** - * Set a cache item by id. Tags may also be added and a custom lifetime - * can be set. Non-string data is automatically serialized. - * - * @param string unique cache id - * @param mixed data to cache - * @param array|string tags for this item - * @param integer number of seconds until the cache expires - * @return boolean - */ - function set($id, $data, $tags = NULL, $lifetime = NULL) - { - if (is_resource($data)) - throw new Kohana_Exception('cache.resources'); - - // Sanitize the ID - $id = $this->sanitize_id($id); - - if ($lifetime === NULL) - { - // Get the default lifetime - $lifetime = $this->config['lifetime']; - } - - return $this->driver->set($id, $data, (array) $tags, $lifetime); - } - - /** - * Delete a cache item by id. - * - * @param string cache id - * @return boolean - */ - public function delete($id) - { - // Sanitize the ID - $id = $this->sanitize_id($id); - - return $this->driver->delete($id); - } - - /** - * Delete all cache items with a given tag. - * - * @param string cache tag name - * @return boolean - */ - public function delete_tag($tag) - { - return $this->driver->delete($tag, TRUE); - } - - /** - * Delete ALL cache items items. - * - * @return boolean - */ - public function delete_all() - { - return $this->driver->delete(TRUE); - } - - /** - * Replaces troublesome characters with underscores. - * - * @param string cache id - * @return string - */ - protected function sanitize_id($id) - { - // Change slashes and spaces to underscores - return str_replace(array('/', '\\', ' '), '_', $id); - } - -} // End Cache diff --git a/kohana/libraries/Controller.php b/kohana/libraries/Controller.php deleted file mode 100644 index d111f25e..00000000 --- a/kohana/libraries/Controller.php +++ /dev/null @@ -1,78 +0,0 @@ -uri = URI::instance(); - - // Input should always be available - $this->input = Input::instance(); - } - - /** - * Handles methods that do not exist. - * - * @param string method name - * @param array arguments - * @return void - */ - public function __call($method, $args) - { - // Default to showing a 404 page - Event::run('system.404'); - } - - /** - * Includes a View within the controller scope. - * - * @param string view filename - * @param array array of view variables - * @return string - */ - public function _kohana_load_view($kohana_view_filename, $kohana_input_data) - { - if ($kohana_view_filename == '') - return; - - // Buffering on - ob_start(); - - // Import the view variables to local namespace - extract($kohana_input_data, EXTR_SKIP); - - // Views are straight HTML pages with embedded PHP, so importing them - // this way insures that $this can be accessed as if the user was in - // the controller, which gives the easiest access to libraries in views - include $kohana_view_filename; - - // Fetch the output and close the buffer - return ob_get_clean(); - } - -} // End Controller Class \ No newline at end of file diff --git a/kohana/libraries/Database.php b/kohana/libraries/Database.php deleted file mode 100644 index 6267f63a..00000000 --- a/kohana/libraries/Database.php +++ /dev/null @@ -1,1444 +0,0 @@ - TRUE, - 'persistent' => FALSE, - 'connection' => '', - 'character_set' => 'utf8', - 'table_prefix' => '', - 'object' => TRUE, - 'cache' => FALSE, - 'escape' => TRUE, - ); - - // Database driver object - protected $driver; - protected $link; - - // Un-compiled parts of the SQL query - protected $select = array(); - protected $set = array(); - protected $from = array(); - protected $join = array(); - protected $where = array(); - protected $orderby = array(); - protected $order = array(); - protected $groupby = array(); - protected $having = array(); - protected $distinct = FALSE; - protected $limit = FALSE; - protected $offset = FALSE; - protected $last_query = ''; - - // Stack of queries for push/pop - protected $query_history = array(); - - /** - * Returns a singleton instance of Database. - * - * @param mixed configuration array or DSN - * @return Database_Core - */ - public static function & instance($name = 'default', $config = NULL) - { - if ( ! isset(Database::$instances[$name])) - { - // Create a new instance - Database::$instances[$name] = new Database($config === NULL ? $name : $config); - } - - return Database::$instances[$name]; - } - - /** - * Returns the name of a given database instance. - * - * @param Database instance of Database - * @return string - */ - public static function instance_name(Database $db) - { - return array_search($db, Database::$instances, TRUE); - } - - /** - * Sets up the database configuration, loads the Database_Driver. - * - * @throws Kohana_Database_Exception - */ - public function __construct($config = array()) - { - if (empty($config)) - { - // Load the default group - $config = Kohana::config('database.default'); - } - elseif (is_array($config) AND count($config) > 0) - { - if ( ! array_key_exists('connection', $config)) - { - $config = array('connection' => $config); - } - } - elseif (is_string($config)) - { - // The config is a DSN string - if (strpos($config, '://') !== FALSE) - { - $config = array('connection' => $config); - } - // The config is a group name - else - { - $name = $config; - - // Test the config group name - if (($config = Kohana::config('database.'.$config)) === NULL) - throw new Kohana_Database_Exception('database.undefined_group', $name); - } - } - - // Merge the default config with the passed config - $this->config = array_merge($this->config, $config); - - if (is_string($this->config['connection'])) - { - // Make sure the connection is valid - if (strpos($this->config['connection'], '://') === FALSE) - throw new Kohana_Database_Exception('database.invalid_dsn', $this->config['connection']); - - // Parse the DSN, creating an array to hold the connection parameters - $db = array - ( - 'type' => FALSE, - 'user' => FALSE, - 'pass' => FALSE, - 'host' => FALSE, - 'port' => FALSE, - 'socket' => FALSE, - 'database' => FALSE - ); - - // Get the protocol and arguments - list ($db['type'], $connection) = explode('://', $this->config['connection'], 2); - - if (strpos($connection, '@') !== FALSE) - { - // Get the username and password - list ($db['pass'], $connection) = explode('@', $connection, 2); - // Check if a password is supplied - $logindata = explode(':', $db['pass'], 2); - $db['pass'] = (count($logindata) > 1) ? $logindata[1] : ''; - $db['user'] = $logindata[0]; - - // Prepare for finding the database - $connection = explode('/', $connection); - - // Find the database name - $db['database'] = array_pop($connection); - - // Reset connection string - $connection = implode('/', $connection); - - // Find the socket - if (preg_match('/^unix\([^)]++\)/', $connection)) - { - // This one is a little hairy: we explode based on the end of - // the socket, removing the 'unix(' from the connection string - list ($db['socket'], $connection) = explode(')', substr($connection, 5), 2); - } - elseif (strpos($connection, ':') !== FALSE) - { - // Fetch the host and port name - list ($db['host'], $db['port']) = explode(':', $connection, 2); - } - else - { - $db['host'] = $connection; - } - } - else - { - // File connection - $connection = explode('/', $connection); - - // Find database file name - $db['database'] = array_pop($connection); - - // Find database directory name - $db['socket'] = implode('/', $connection).'/'; - } - - // Reset the connection array to the database config - $this->config['connection'] = $db; - } - // Set driver name - $driver = 'Database_'.ucfirst($this->config['connection']['type']).'_Driver'; - - // Load the driver - if ( ! Kohana::auto_load($driver)) - throw new Kohana_Database_Exception('core.driver_not_found', $this->config['connection']['type'], get_class($this)); - - // Initialize the driver - $this->driver = new $driver($this->config); - - // Validate the driver - if ( ! ($this->driver instanceof Database_Driver)) - throw new Kohana_Database_Exception('core.driver_implements', $this->config['connection']['type'], get_class($this), 'Database_Driver'); - - Kohana::log('debug', 'Database Library initialized'); - } - - /** - * Simple connect method to get the database queries up and running. - * - * @return void - */ - public function connect() - { - // A link can be a resource or an object - if ( ! is_resource($this->link) AND ! is_object($this->link)) - { - $this->link = $this->driver->connect(); - if ( ! is_resource($this->link) AND ! is_object($this->link)) - throw new Kohana_Database_Exception('database.connection', $this->driver->show_error()); - - // Clear password after successful connect - $this->config['connection']['pass'] = NULL; - } - } - - /** - * Runs a query into the driver and returns the result. - * - * @param string SQL query to execute - * @return Database_Result - */ - public function query($sql = '') - { - if ($sql == '') return FALSE; - - // No link? Connect! - $this->link or $this->connect(); - - // Start the benchmark - $start = microtime(TRUE); - - if (func_num_args() > 1) //if we have more than one argument ($sql) - { - $argv = func_get_args(); - $binds = (is_array(next($argv))) ? current($argv) : array_slice($argv, 1); - } - - // Compile binds if needed - if (isset($binds)) - { - $sql = $this->compile_binds($sql, $binds); - } - - // Fetch the result - $result = $this->driver->query($this->last_query = $sql); - - // Stop the benchmark - $stop = microtime(TRUE); - - if ($this->config['benchmark'] == TRUE) - { - // Benchmark the query - Database::$benchmarks[] = array('query' => $sql, 'time' => $stop - $start, 'rows' => count($result)); - } - - return $result; - } - - /** - * Selects the column names for a database query. - * - * @param string string or array of column names to select - * @return Database_Core This Database object. - */ - public function select($sql = '*') - { - if (func_num_args() > 1) - { - $sql = func_get_args(); - } - elseif (is_string($sql)) - { - $sql = explode(',', $sql); - } - else - { - $sql = (array) $sql; - } - - foreach ($sql as $val) - { - if (($val = trim($val)) === '') continue; - - if (strpos($val, '(') === FALSE AND $val !== '*') - { - if (preg_match('/^DISTINCT\s++(.+)$/i', $val, $matches)) - { - // Only prepend with table prefix if table name is specified - $val = (strpos($matches[1], '.') !== FALSE) ? $this->config['table_prefix'].$matches[1] : $matches[1]; - - $this->distinct = TRUE; - } - else - { - $val = (strpos($val, '.') !== FALSE) ? $this->config['table_prefix'].$val : $val; - } - - $val = $this->driver->escape_column($val); - } - - $this->select[] = $val; - } - - return $this; - } - - /** - * Selects the from table(s) for a database query. - * - * @param string string or array of tables to select - * @return Database_Core This Database object. - */ - public function from($sql) - { - if (func_num_args() > 1) - { - $sql = func_get_args(); - } - elseif (is_string($sql)) - { - $sql = explode(',', $sql); - } - else - { - $sql = array($sql); - } - - foreach ($sql as $val) - { - if (is_string($val)) - { - if (($val = trim($val)) === '') continue; - - // TODO: Temporary solution, this should be moved to database driver (AS is checked for twice) - if (stripos($val, ' AS ') !== FALSE) - { - $val = str_ireplace(' AS ', ' AS ', $val); - - list($table, $alias) = explode(' AS ', $val); - - // Attach prefix to both sides of the AS - $val = $this->config['table_prefix'].$table.' AS '.$this->config['table_prefix'].$alias; - } - else - { - $val = $this->config['table_prefix'].$val; - } - } - - $this->from[] = $val; - } - - return $this; - } - - /** - * Generates the JOIN portion of the query. - * - * @param string table name - * @param string|array where key or array of key => value pairs - * @param string where value - * @param string type of join - * @return Database_Core This Database object. - */ - public function join($table, $key, $value = NULL, $type = '') - { - $join = array(); - - if ( ! empty($type)) - { - $type = strtoupper(trim($type)); - - if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'), TRUE)) - { - $type = ''; - } - else - { - $type .= ' '; - } - } - - $cond = array(); - $keys = is_array($key) ? $key : array($key => $value); - foreach ($keys as $key => $value) - { - $key = (strpos($key, '.') !== FALSE) ? $this->config['table_prefix'].$key : $key; - - if (is_string($value)) - { - // Only escape if it's a string - $value = $this->driver->escape_column($this->config['table_prefix'].$value); - } - - $cond[] = $this->driver->where($key, $value, 'AND ', count($cond), FALSE); - } - - if ( ! is_array($this->join)) - { - $this->join = array(); - } - - if ( ! is_array($table)) - { - $table = array($table); - } - - foreach ($table as $t) - { - if (is_string($t)) - { - // TODO: Temporary solution, this should be moved to database driver (AS is checked for twice) - if (stripos($t, ' AS ') !== FALSE) - { - $t = str_ireplace(' AS ', ' AS ', $t); - - list($table, $alias) = explode(' AS ', $t); - - // Attach prefix to both sides of the AS - $t = $this->config['table_prefix'].$table.' AS '.$this->config['table_prefix'].$alias; - } - else - { - $t = $this->config['table_prefix'].$t; - } - } - - $join['tables'][] = $this->driver->escape_column($t); - } - - $join['conditions'] = '('.trim(implode(' ', $cond)).')'; - $join['type'] = $type; - - $this->join[] = $join; - - return $this; - } - - - /** - * Selects the where(s) for a database query. - * - * @param string|array key name or array of key => value pairs - * @param string value to match with key - * @param boolean disable quoting of WHERE clause - * @return Database_Core This Database object. - */ - public function where($key, $value = NULL, $quote = TRUE) - { - $quote = (func_num_args() < 2 AND ! is_array($key)) ? -1 : $quote; - if (is_object($key)) - { - $keys = array((string) $key => ''); - } - elseif ( ! is_array($key)) - { - $keys = array($key => $value); - } - else - { - $keys = $key; - } - - foreach ($keys as $key => $value) - { - $key = (strpos($key, '.') !== FALSE) ? $this->config['table_prefix'].$key : $key; - $this->where[] = $this->driver->where($key, $value, 'AND ', count($this->where), $quote); - } - - return $this; - } - - /** - * Selects the or where(s) for a database query. - * - * @param string|array key name or array of key => value pairs - * @param string value to match with key - * @param boolean disable quoting of WHERE clause - * @return Database_Core This Database object. - */ - public function orwhere($key, $value = NULL, $quote = TRUE) - { - $quote = (func_num_args() < 2 AND ! is_array($key)) ? -1 : $quote; - if (is_object($key)) - { - $keys = array((string) $key => ''); - } - elseif ( ! is_array($key)) - { - $keys = array($key => $value); - } - else - { - $keys = $key; - } - - foreach ($keys as $key => $value) - { - $key = (strpos($key, '.') !== FALSE) ? $this->config['table_prefix'].$key : $key; - $this->where[] = $this->driver->where($key, $value, 'OR ', count($this->where), $quote); - } - - return $this; - } - - /** - * Selects the like(s) for a database query. - * - * @param string|array field name or array of field => match pairs - * @param string like value to match with field - * @param boolean automatically add starting and ending wildcards - * @return Database_Core This Database object. - */ - public function like($field, $match = '', $auto = TRUE) - { - $fields = is_array($field) ? $field : array($field => $match); - - foreach ($fields as $field => $match) - { - $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; - $this->where[] = $this->driver->like($field, $match, $auto, 'AND ', count($this->where)); - } - - return $this; - } - - /** - * Selects the or like(s) for a database query. - * - * @param string|array field name or array of field => match pairs - * @param string like value to match with field - * @param boolean automatically add starting and ending wildcards - * @return Database_Core This Database object. - */ - public function orlike($field, $match = '', $auto = TRUE) - { - $fields = is_array($field) ? $field : array($field => $match); - - foreach ($fields as $field => $match) - { - $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; - $this->where[] = $this->driver->like($field, $match, $auto, 'OR ', count($this->where)); - } - - return $this; - } - - /** - * Selects the not like(s) for a database query. - * - * @param string|array field name or array of field => match pairs - * @param string like value to match with field - * @param boolean automatically add starting and ending wildcards - * @return Database_Core This Database object. - */ - public function notlike($field, $match = '', $auto = TRUE) - { - $fields = is_array($field) ? $field : array($field => $match); - - foreach ($fields as $field => $match) - { - $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; - $this->where[] = $this->driver->notlike($field, $match, $auto, 'AND ', count($this->where)); - } - - return $this; - } - - /** - * Selects the or not like(s) for a database query. - * - * @param string|array field name or array of field => match pairs - * @param string like value to match with field - * @return Database_Core This Database object. - */ - public function ornotlike($field, $match = '', $auto = TRUE) - { - $fields = is_array($field) ? $field : array($field => $match); - - foreach ($fields as $field => $match) - { - $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; - $this->where[] = $this->driver->notlike($field, $match, $auto, 'OR ', count($this->where)); - } - - return $this; - } - - /** - * Selects the like(s) for a database query. - * - * @param string|array field name or array of field => match pairs - * @param string like value to match with field - * @return Database_Core This Database object. - */ - public function regex($field, $match = '') - { - $fields = is_array($field) ? $field : array($field => $match); - - foreach ($fields as $field => $match) - { - $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; - $this->where[] = $this->driver->regex($field, $match, 'AND ', count($this->where)); - } - - return $this; - } - - /** - * Selects the or like(s) for a database query. - * - * @param string|array field name or array of field => match pairs - * @param string like value to match with field - * @return Database_Core This Database object. - */ - public function orregex($field, $match = '') - { - $fields = is_array($field) ? $field : array($field => $match); - - foreach ($fields as $field => $match) - { - $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; - $this->where[] = $this->driver->regex($field, $match, 'OR ', count($this->where)); - } - - return $this; - } - - /** - * Selects the not regex(s) for a database query. - * - * @param string|array field name or array of field => match pairs - * @param string regex value to match with field - * @return Database_Core This Database object. - */ - public function notregex($field, $match = '') - { - $fields = is_array($field) ? $field : array($field => $match); - - foreach ($fields as $field => $match) - { - $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; - $this->where[] = $this->driver->notregex($field, $match, 'AND ', count($this->where)); - } - - return $this; - } - - /** - * Selects the or not regex(s) for a database query. - * - * @param string|array field name or array of field => match pairs - * @param string regex value to match with field - * @return Database_Core This Database object. - */ - public function ornotregex($field, $match = '') - { - $fields = is_array($field) ? $field : array($field => $match); - - foreach ($fields as $field => $match) - { - $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; - $this->where[] = $this->driver->notregex($field, $match, 'OR ', count($this->where)); - } - - return $this; - } - - /** - * Chooses the column to group by in a select query. - * - * @param string column name to group by - * @return Database_Core This Database object. - */ - public function groupby($by) - { - if ( ! is_array($by)) - { - $by = explode(',', (string) $by); - } - - foreach ($by as $val) - { - $val = trim($val); - - if ($val != '') - { - // Add the table prefix if we are using table.column names - if(strpos($val, '.')) - { - $val = $this->config['table_prefix'].$val; - } - - $this->groupby[] = $this->driver->escape_column($val); - } - } - - return $this; - } - - /** - * Selects the having(s) for a database query. - * - * @param string|array key name or array of key => value pairs - * @param string value to match with key - * @param boolean disable quoting of WHERE clause - * @return Database_Core This Database object. - */ - public function having($key, $value = '', $quote = TRUE) - { - $this->having[] = $this->driver->where($key, $value, 'AND', count($this->having), TRUE); - return $this; - } - - /** - * Selects the or having(s) for a database query. - * - * @param string|array key name or array of key => value pairs - * @param string value to match with key - * @param boolean disable quoting of WHERE clause - * @return Database_Core This Database object. - */ - public function orhaving($key, $value = '', $quote = TRUE) - { - $this->having[] = $this->driver->where($key, $value, 'OR', count($this->having), TRUE); - return $this; - } - - /** - * Chooses which column(s) to order the select query by. - * - * @param string|array column(s) to order on, can be an array, single column, or comma seperated list of columns - * @param string direction of the order - * @return Database_Core This Database object. - */ - public function orderby($orderby, $direction = NULL) - { - if ( ! is_array($orderby)) - { - $orderby = array($orderby => $direction); - } - - foreach ($orderby as $column => $direction) - { - $direction = strtoupper(trim($direction)); - - // Add a direction if the provided one isn't valid - if ( ! in_array($direction, array('ASC', 'DESC', 'RAND()', 'RANDOM()', 'NULL'))) - { - $direction = 'ASC'; - } - - // Add the table prefix if a table.column was passed - if (strpos($column, '.')) - { - $column = $this->config['table_prefix'].$column; - } - - $this->orderby[] = $this->driver->escape_column($column).' '.$direction; - } - - return $this; - } - - /** - * Selects the limit section of a query. - * - * @param integer number of rows to limit result to - * @param integer offset in result to start returning rows from - * @return Database_Core This Database object. - */ - public function limit($limit, $offset = NULL) - { - $this->limit = (int) $limit; - - if ($offset !== NULL OR ! is_int($this->offset)) - { - $this->offset($offset); - } - - return $this; - } - - /** - * Sets the offset portion of a query. - * - * @param integer offset value - * @return Database_Core This Database object. - */ - public function offset($value) - { - $this->offset = (int) $value; - - return $this; - } - - /** - * Allows key/value pairs to be set for inserting or updating. - * - * @param string|array key name or array of key => value pairs - * @param string value to match with key - * @return Database_Core This Database object. - */ - public function set($key, $value = '') - { - if ( ! is_array($key)) - { - $key = array($key => $value); - } - - foreach ($key as $k => $v) - { - // Add a table prefix if the column includes the table. - if (strpos($k, '.')) - $k = $this->config['table_prefix'].$k; - - $this->set[$k] = $this->driver->escape($v); - } - - return $this; - } - - /** - * Compiles the select statement based on the other functions called and runs the query. - * - * @param string table name - * @param string limit clause - * @param string offset clause - * @return Database_Result - */ - public function get($table = '', $limit = NULL, $offset = NULL) - { - if ($table != '') - { - $this->from($table); - } - - if ( ! is_null($limit)) - { - $this->limit($limit, $offset); - } - - $sql = $this->driver->compile_select(get_object_vars($this)); - - $this->reset_select(); - - $result = $this->query($sql); - - $this->last_query = $sql; - - return $result; - } - - /** - * Compiles the select statement based on the other functions called and runs the query. - * - * @param string table name - * @param array where clause - * @param string limit clause - * @param string offset clause - * @return Database_Core This Database object. - */ - public function getwhere($table = '', $where = NULL, $limit = NULL, $offset = NULL) - { - if ($table != '') - { - $this->from($table); - } - - if ( ! is_null($where)) - { - $this->where($where); - } - - if ( ! is_null($limit)) - { - $this->limit($limit, $offset); - } - - $sql = $this->driver->compile_select(get_object_vars($this)); - - $this->reset_select(); - - $result = $this->query($sql); - - return $result; - } - - /** - * Compiles the select statement based on the other functions called and returns the query string. - * - * @param string table name - * @param string limit clause - * @param string offset clause - * @return string sql string - */ - public function compile($table = '', $limit = NULL, $offset = NULL) - { - if ($table != '') - { - $this->from($table); - } - - if ( ! is_null($limit)) - { - $this->limit($limit, $offset); - } - - $sql = $this->driver->compile_select(get_object_vars($this)); - - $this->reset_select(); - - return $sql; - } - - /** - * Compiles an insert string and runs the query. - * - * @param string table name - * @param array array of key/value pairs to insert - * @return Database_Result Query result - */ - public function insert($table = '', $set = NULL) - { - if ( ! is_null($set)) - { - $this->set($set); - } - - if ($this->set == NULL) - throw new Kohana_Database_Exception('database.must_use_set'); - - if ($table == '') - { - if ( ! isset($this->from[0])) - throw new Kohana_Database_Exception('database.must_use_table'); - - $table = $this->from[0]; - } - - // If caching is enabled, clear the cache before inserting - ($this->config['cache'] === TRUE) and $this->clear_cache(); - - $sql = $this->driver->insert($this->config['table_prefix'].$table, array_keys($this->set), array_values($this->set)); - - $this->reset_write(); - - return $this->query($sql); - } - - /** - * Adds an "IN" condition to the where clause - * - * @param string Name of the column being examined - * @param mixed An array or string to match against - * @param bool Generate a NOT IN clause instead - * @return Database_Core This Database object. - */ - public function in($field, $values, $not = FALSE) - { - if (is_array($values)) - { - $escaped_values = array(); - foreach ($values as $v) - { - if (is_numeric($v)) - { - $escaped_values[] = $v; - } - else - { - $escaped_values[] = "'".$this->driver->escape_str($v)."'"; - } - } - $values = implode(",", $escaped_values); - } - - $where = $this->driver->escape_column(((strpos($field,'.') !== FALSE) ? $this->config['table_prefix'] : ''). $field).' '.($not === TRUE ? 'NOT ' : '').'IN ('.$values.')'; - $this->where[] = $this->driver->where($where, '', 'AND ', count($this->where), -1); - - return $this; - } - - /** - * Adds a "NOT IN" condition to the where clause - * - * @param string Name of the column being examined - * @param mixed An array or string to match against - * @return Database_Core This Database object. - */ - public function notin($field, $values) - { - return $this->in($field, $values, TRUE); - } - - /** - * Compiles a merge string and runs the query. - * - * @param string table name - * @param array array of key/value pairs to merge - * @return Database_Result Query result - */ - public function merge($table = '', $set = NULL) - { - if ( ! is_null($set)) - { - $this->set($set); - } - - if ($this->set == NULL) - throw new Kohana_Database_Exception('database.must_use_set'); - - if ($table == '') - { - if ( ! isset($this->from[0])) - throw new Kohana_Database_Exception('database.must_use_table'); - - $table = $this->from[0]; - } - - $sql = $this->driver->merge($this->config['table_prefix'].$table, array_keys($this->set), array_values($this->set)); - - $this->reset_write(); - return $this->query($sql); - } - - /** - * Compiles an update string and runs the query. - * - * @param string table name - * @param array associative array of update values - * @param array where clause - * @return Database_Result Query result - */ - public function update($table = '', $set = NULL, $where = NULL) - { - if ( is_array($set)) - { - $this->set($set); - } - - if ( ! is_null($where)) - { - $this->where($where); - } - - if ($this->set == FALSE) - throw new Kohana_Database_Exception('database.must_use_set'); - - if ($table == '') - { - if ( ! isset($this->from[0])) - throw new Kohana_Database_Exception('database.must_use_table'); - - $table = $this->from[0]; - } - - $sql = $this->driver->update($this->config['table_prefix'].$table, $this->set, $this->where); - - $this->reset_write(); - return $this->query($sql); - } - - /** - * Compiles a delete string and runs the query. - * - * @param string table name - * @param array where clause - * @return Database_Result Query result - */ - public function delete($table = '', $where = NULL) - { - if ($table == '') - { - if ( ! isset($this->from[0])) - throw new Kohana_Database_Exception('database.must_use_table'); - - $table = $this->from[0]; - } - else - { - $table = $this->config['table_prefix'].$table; - } - - if (! is_null($where)) - { - $this->where($where); - } - - if (count($this->where) < 1) - throw new Kohana_Database_Exception('database.must_use_where'); - - $sql = $this->driver->delete($table, $this->where); - - $this->reset_write(); - return $this->query($sql); - } - - /** - * Returns the last query run. - * - * @return string SQL - */ - public function last_query() - { - return $this->last_query; - } - - /** - * Count query records. - * - * @param string table name - * @param array where clause - * @return integer - */ - public function count_records($table = FALSE, $where = NULL) - { - if (count($this->from) < 1) - { - if ($table == FALSE) - throw new Kohana_Database_Exception('database.must_use_table'); - - $this->from($table); - } - - if ($where !== NULL) - { - $this->where($where); - } - - $query = $this->select('COUNT(*) AS '.$this->escape_column('records_found'))->get()->result(TRUE); - - return (int) $query->current()->records_found; - } - - /** - * Resets all private select variables. - * - * @return void - */ - protected function reset_select() - { - $this->select = array(); - $this->from = array(); - $this->join = array(); - $this->where = array(); - $this->orderby = array(); - $this->groupby = array(); - $this->having = array(); - $this->distinct = FALSE; - $this->limit = FALSE; - $this->offset = FALSE; - } - - /** - * Resets all private insert and update variables. - * - * @return void - */ - protected function reset_write() - { - $this->set = array(); - $this->from = array(); - $this->where = array(); - } - - /** - * Lists all the tables in the current database. - * - * @return array - */ - public function list_tables() - { - $this->link or $this->connect(); - - return $this->driver->list_tables(); - } - - /** - * See if a table exists in the database. - * - * @param string table name - * @param boolean True to attach table prefix - * @return boolean - */ - public function table_exists($table_name, $prefix = TRUE) - { - if ($prefix) - return in_array($this->config['table_prefix'].$table_name, $this->list_tables()); - else - return in_array($table_name, $this->list_tables()); - } - - /** - * Combine a SQL statement with the bind values. Used for safe queries. - * - * @param string query to bind to the values - * @param array array of values to bind to the query - * @return string - */ - public function compile_binds($sql, $binds) - { - foreach ((array) $binds as $val) - { - // If the SQL contains no more bind marks ("?"), we're done. - if (($next_bind_pos = strpos($sql, '?')) === FALSE) - break; - - // Properly escape the bind value. - $val = $this->driver->escape($val); - - // Temporarily replace possible bind marks ("?"), in the bind value itself, with a placeholder. - $val = str_replace('?', '{%B%}', $val); - - // Replace the first bind mark ("?") with its corresponding value. - $sql = substr($sql, 0, $next_bind_pos).$val.substr($sql, $next_bind_pos + 1); - } - - // Restore placeholders. - return str_replace('{%B%}', '?', $sql); - } - - /** - * Get the field data for a database table, along with the field's attributes. - * - * @param string table name - * @return array - */ - public function field_data($table = '') - { - $this->link or $this->connect(); - - return $this->driver->field_data($this->config['table_prefix'].$table); - } - - /** - * Get the field data for a database table, along with the field's attributes. - * - * @param string table name - * @return array - */ - public function list_fields($table = '') - { - $this->link or $this->connect(); - - return $this->driver->list_fields($this->config['table_prefix'].$table); - } - - /** - * Escapes a value for a query. - * - * @param mixed value to escape - * @return string - */ - public function escape($value) - { - return $this->driver->escape($value); - } - - /** - * Escapes a string for a query. - * - * @param string string to escape - * @return string - */ - public function escape_str($str) - { - return $this->driver->escape_str($str); - } - - /** - * Escapes a table name for a query. - * - * @param string string to escape - * @return string - */ - public function escape_table($table) - { - return $this->driver->escape_table($table); - } - - /** - * Escapes a column name for a query. - * - * @param string string to escape - * @return string - */ - public function escape_column($table) - { - return $this->driver->escape_column($table); - } - - /** - * Returns table prefix of current configuration. - * - * @return string - */ - public function table_prefix() - { - return $this->config['table_prefix']; - } - - /** - * Clears the query cache. - * - * @param string|TRUE clear cache by SQL statement or TRUE for last query - * @return Database_Core This Database object. - */ - public function clear_cache($sql = NULL) - { - if ($sql === TRUE) - { - $this->driver->clear_cache($this->last_query); - } - elseif (is_string($sql)) - { - $this->driver->clear_cache($sql); - } - else - { - $this->driver->clear_cache(); - } - - return $this; - } - - /** - * Pushes existing query space onto the query stack. Use push - * and pop to prevent queries from clashing before they are - * executed - * - * @return Database_Core This Databaes object - */ - public function push() - { - array_push($this->query_history, array( - $this->select, - $this->from, - $this->join, - $this->where, - $this->orderby, - $this->order, - $this->groupby, - $this->having, - $this->distinct, - $this->limit, - $this->offset - )); - - $this->reset_select(); - - return $this; - } - - /** - * Pops from query stack into the current query space. - * - * @return Database_Core This Databaes object - */ - public function pop() - { - if (count($this->query_history) == 0) - { - // No history - return $this; - } - - list( - $this->select, - $this->from, - $this->join, - $this->where, - $this->orderby, - $this->order, - $this->groupby, - $this->having, - $this->distinct, - $this->limit, - $this->offset - ) = array_pop($this->query_history); - - return $this; - } - - /** - * Count the number of records in the last query, without LIMIT or OFFSET applied. - * - * @return integer - */ - public function count_last_query() - { - if ($sql = $this->last_query()) - { - if (stripos($sql, 'LIMIT') !== FALSE) - { - // Remove LIMIT from the SQL - $sql = preg_replace('/\sLIMIT\s+[^a-z]+/i', ' ', $sql); - } - - if (stripos($sql, 'OFFSET') !== FALSE) - { - // Remove OFFSET from the SQL - $sql = preg_replace('/\sOFFSET\s+\d+/i', '', $sql); - } - - // Get the total rows from the last query executed - $result = $this->query - ( - 'SELECT COUNT(*) AS '.$this->escape_column('total_rows').' '. - 'FROM ('.trim($sql).') AS '.$this->escape_table('counted_results') - ); - - // Return the total number of rows from the query - return (int) $result->current()->total_rows; - } - - return FALSE; - } - -} // End Database Class - - -/** - * Sets the code for a Database exception. - */ -class Kohana_Database_Exception extends Kohana_Exception { - - protected $code = E_DATABASE_ERROR; - -} // End Kohana Database Exception diff --git a/kohana/libraries/Database_Expression.php b/kohana/libraries/Database_Expression.php deleted file mode 100644 index 940a6363..00000000 --- a/kohana/libraries/Database_Expression.php +++ /dev/null @@ -1,26 +0,0 @@ -expression = $expression; - } - - public function __toString() - { - return (string) $this->expression; - } - -} // End Database Expr Class \ No newline at end of file diff --git a/kohana/libraries/Encrypt.php b/kohana/libraries/Encrypt.php deleted file mode 100644 index 3d564f99..00000000 --- a/kohana/libraries/Encrypt.php +++ /dev/null @@ -1,164 +0,0 @@ - $size) - { - // Shorten the key to the maximum size - $config['key'] = substr($config['key'], 0, $size); - } - - // Find the initialization vector size - $config['iv_size'] = mcrypt_get_iv_size($config['cipher'], $config['mode']); - - // Cache the config in the object - $this->config = $config; - - Kohana::log('debug', 'Encrypt Library initialized'); - } - - /** - * Encrypts a string and returns an encrypted string that can be decoded. - * - * @param string data to be encrypted - * @return string encrypted data - */ - public function encode($data) - { - // Set the rand type if it has not already been set - if (Encrypt::$rand === NULL) - { - if (KOHANA_IS_WIN) - { - // Windows only supports the system random number generator - Encrypt::$rand = MCRYPT_RAND; - } - else - { - if (defined('MCRYPT_DEV_URANDOM')) - { - // Use /dev/urandom - Encrypt::$rand = MCRYPT_DEV_URANDOM; - } - elseif (defined('MCRYPT_DEV_RANDOM')) - { - // Use /dev/random - Encrypt::$rand = MCRYPT_DEV_RANDOM; - } - else - { - // Use the system random number generator - Encrypt::$rand = MCRYPT_RAND; - } - } - } - - if (Encrypt::$rand === MCRYPT_RAND) - { - // The system random number generator must always be seeded each - // time it is used, or it will not produce true random results - mt_srand(); - } - - // Create a random initialization vector of the proper size for the current cipher - $iv = mcrypt_create_iv($this->config['iv_size'], Encrypt::$rand); - - // Encrypt the data using the configured options and generated iv - $data = mcrypt_encrypt($this->config['cipher'], $this->config['key'], $data, $this->config['mode'], $iv); - - // Use base64 encoding to convert to a string - return base64_encode($iv.$data); - } - - /** - * Decrypts an encoded string back to its original value. - * - * @param string encoded string to be decrypted - * @return string decrypted data - */ - public function decode($data) - { - // Convert the data back to binary - $data = base64_decode($data); - - // Extract the initialization vector from the data - $iv = substr($data, 0, $this->config['iv_size']); - - // Remove the iv from the data - $data = substr($data, $this->config['iv_size']); - - // Return the decrypted data, trimming the \0 padding bytes from the end of the data - return rtrim(mcrypt_decrypt($this->config['cipher'], $this->config['key'], $data, $this->config['mode'], $iv), "\0"); - } - -} // End Encrypt diff --git a/kohana/libraries/Event_Observer.php b/kohana/libraries/Event_Observer.php deleted file mode 100644 index 086c8a23..00000000 --- a/kohana/libraries/Event_Observer.php +++ /dev/null @@ -1,70 +0,0 @@ -update($caller); - } - - /** - * Updates the observer subject with a new caller. - * - * @chainable - * @param object Event_Subject - * @return object - */ - public function update(SplSubject $caller) - { - if ( ! ($caller instanceof Event_Subject)) - throw new Kohana_Exception('event.invalid_subject', get_class($caller), get_class($this)); - - // Update the caller - $this->caller = $caller; - - return $this; - } - - /** - * Detaches this observer from the subject. - * - * @chainable - * @return object - */ - public function remove() - { - // Detach this observer from the caller - $this->caller->detach($this); - - return $this; - } - - /** - * Notify the observer of a new message. This function must be defined in - * all observers and must take exactly one parameter of any type. - * - * @param mixed message string, object, or array - * @return void - */ - abstract public function notify($message); - -} // End Event Observer \ No newline at end of file diff --git a/kohana/libraries/Event_Subject.php b/kohana/libraries/Event_Subject.php deleted file mode 100644 index d1ccc544..00000000 --- a/kohana/libraries/Event_Subject.php +++ /dev/null @@ -1,67 +0,0 @@ -listeners[spl_object_hash($obj)] = $obj; - - return $this; - } - - /** - * Detach an observer from the object. - * - * @chainable - * @param object Event_Observer - * @return object - */ - public function detach(SplObserver $obj) - { - // Remove the listener - unset($this->listeners[spl_object_hash($obj)]); - - return $this; - } - - /** - * Notify all attached observers of a new message. - * - * @chainable - * @param mixed message string, object, or array - * @return object - */ - public function notify($message) - { - foreach ($this->listeners as $obj) - { - $obj->notify($message); - } - - return $this; - } - -} // End Event Subject \ No newline at end of file diff --git a/kohana/libraries/Image.php b/kohana/libraries/Image.php deleted file mode 100644 index 08c2957c..00000000 --- a/kohana/libraries/Image.php +++ /dev/null @@ -1,460 +0,0 @@ - 'gif', - IMAGETYPE_JPEG => 'jpg', - IMAGETYPE_PNG => 'png', - IMAGETYPE_TIFF_II => 'tiff', - IMAGETYPE_TIFF_MM => 'tiff', - ); - - // Driver instance - protected $driver; - - // Driver actions - protected $actions = array(); - - // Reference to the current image filename - protected $image = ''; - - /** - * Creates a new Image instance and returns it. - * - * @param string filename of image - * @param array non-default configurations - * @return object - */ - public static function factory($image, $config = NULL) - { - return new Image($image, $config); - } - - /** - * Creates a new image editor instance. - * - * @throws Kohana_Exception - * @param string filename of image - * @param array non-default configurations - * @return void - */ - public function __construct($image, $config = NULL) - { - static $check; - - // Make the check exactly once - ($check === NULL) and $check = function_exists('getimagesize'); - - if ($check === FALSE) - throw new Kohana_Exception('image.getimagesize_missing'); - - // Check to make sure the image exists - if ( ! is_file($image)) - throw new Kohana_Exception('image.file_not_found', $image); - - // Disable error reporting, to prevent PHP warnings - $ER = error_reporting(0); - - // Fetch the image size and mime type - $image_info = getimagesize($image); - - // Turn on error reporting again - error_reporting($ER); - - // Make sure that the image is readable and valid - if ( ! is_array($image_info) OR count($image_info) < 3) - throw new Kohana_Exception('image.file_unreadable', $image); - - // Check to make sure the image type is allowed - if ( ! isset(Image::$allowed_types[$image_info[2]])) - throw new Kohana_Exception('image.type_not_allowed', $image); - - // Image has been validated, load it - $this->image = array - ( - 'file' => str_replace('\\', '/', realpath($image)), - 'width' => $image_info[0], - 'height' => $image_info[1], - 'type' => $image_info[2], - 'ext' => Image::$allowed_types[$image_info[2]], - 'mime' => $image_info['mime'] - ); - - // Load configuration - $this->config = (array) $config + Kohana::config('image'); - - // Set driver class name - $driver = 'Image_'.ucfirst($this->config['driver']).'_Driver'; - - // Load the driver - if ( ! Kohana::auto_load($driver)) - throw new Kohana_Exception('core.driver_not_found', $this->config['driver'], get_class($this)); - - // Initialize the driver - $this->driver = new $driver($this->config['params']); - - // Validate the driver - if ( ! ($this->driver instanceof Image_Driver)) - throw new Kohana_Exception('core.driver_implements', $this->config['driver'], get_class($this), 'Image_Driver'); - } - - /** - * Handles retrieval of pre-save image properties - * - * @param string property name - * @return mixed - */ - public function __get($property) - { - if (isset($this->image[$property])) - { - return $this->image[$property]; - } - else - { - throw new Kohana_Exception('core.invalid_property', $property, get_class($this)); - } - } - - /** - * Resize an image to a specific width and height. By default, Kohana will - * maintain the aspect ratio using the width as the master dimension. If you - * wish to use height as master dim, set $image->master_dim = Image::HEIGHT - * This method is chainable. - * - * @throws Kohana_Exception - * @param integer width - * @param integer height - * @param integer one of: Image::NONE, Image::AUTO, Image::WIDTH, Image::HEIGHT - * @return object - */ - public function resize($width, $height, $master = NULL) - { - if ( ! $this->valid_size('width', $width)) - throw new Kohana_Exception('image.invalid_width', $width); - - if ( ! $this->valid_size('height', $height)) - throw new Kohana_Exception('image.invalid_height', $height); - - if (empty($width) AND empty($height)) - throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__); - - if ($master === NULL) - { - // Maintain the aspect ratio by default - $master = Image::AUTO; - } - elseif ( ! $this->valid_size('master', $master)) - throw new Kohana_Exception('image.invalid_master'); - - $this->actions['resize'] = array - ( - 'width' => $width, - 'height' => $height, - 'master' => $master, - ); - - return $this; - } - - /** - * Crop an image to a specific width and height. You may also set the top - * and left offset. - * This method is chainable. - * - * @throws Kohana_Exception - * @param integer width - * @param integer height - * @param integer top offset, pixel value or one of: top, center, bottom - * @param integer left offset, pixel value or one of: left, center, right - * @return object - */ - public function crop($width, $height, $top = 'center', $left = 'center') - { - if ( ! $this->valid_size('width', $width)) - throw new Kohana_Exception('image.invalid_width', $width); - - if ( ! $this->valid_size('height', $height)) - throw new Kohana_Exception('image.invalid_height', $height); - - if ( ! $this->valid_size('top', $top)) - throw new Kohana_Exception('image.invalid_top', $top); - - if ( ! $this->valid_size('left', $left)) - throw new Kohana_Exception('image.invalid_left', $left); - - if (empty($width) AND empty($height)) - throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__); - - $this->actions['crop'] = array - ( - 'width' => $width, - 'height' => $height, - 'top' => $top, - 'left' => $left, - ); - - return $this; - } - - /** - * Allows rotation of an image by 180 degrees clockwise or counter clockwise. - * - * @param integer degrees - * @return object - */ - public function rotate($degrees) - { - $degrees = (int) $degrees; - - if ($degrees > 180) - { - do - { - // Keep subtracting full circles until the degrees have normalized - $degrees -= 360; - } - while($degrees > 180); - } - - if ($degrees < -180) - { - do - { - // Keep adding full circles until the degrees have normalized - $degrees += 360; - } - while($degrees < -180); - } - - $this->actions['rotate'] = $degrees; - - return $this; - } - - /** - * Overlay a second image on top of this one. - * - * @throws Kohana_Exception - * @param string $overlay_file path to an image file - * @param integer $x x offset for the overlay - * @param integer $y y offset for the overlay - * @param integer $transparency transparency percent - */ - public function composite($overlay_file, $x, $y, $transparency) - { - $image_info = getimagesize($overlay_file); - - // Check to make sure the image type is allowed - if ( ! isset(Image::$allowed_types[$image_info[2]])) - throw new Kohana_Exception('image.type_not_allowed', $overlay_file); - - $this->actions['composite'] = array - ( - 'overlay_file' => $overlay_file, - 'mime' => $image_info['mime'], - 'x' => $x, - 'y' => $y, - 'transparency' => $transparency - ); - - return $this; - } - - /** - * Flip an image horizontally or vertically. - * - * @throws Kohana_Exception - * @param integer direction - * @return object - */ - public function flip($direction) - { - if ($direction !== Image::HORIZONTAL AND $direction !== Image::VERTICAL) - throw new Kohana_Exception('image.invalid_flip'); - - $this->actions['flip'] = $direction; - - return $this; - } - - /** - * Change the quality of an image. - * - * @param integer quality as a percentage - * @return object - */ - public function quality($amount) - { - $this->actions['quality'] = max(1, min($amount, 100)); - - return $this; - } - - /** - * Sharpen an image. - * - * @param integer amount to sharpen, usually ~20 is ideal - * @return object - */ - public function sharpen($amount) - { - $this->actions['sharpen'] = max(1, min($amount, 100)); - - return $this; - } - - /** - * Save the image to a new image or overwrite this image. - * - * @throws Kohana_Exception - * @param string new image filename - * @param integer permissions for new image - * @param boolean keep or discard image process actions - * @return object - */ - public function save($new_image = FALSE, $chmod = 0644, $keep_actions = FALSE) - { - // If no new image is defined, use the current image - empty($new_image) and $new_image = $this->image['file']; - - // Separate the directory and filename - $dir = pathinfo($new_image, PATHINFO_DIRNAME); - $file = pathinfo($new_image, PATHINFO_BASENAME); - - // Normalize the path - $dir = str_replace('\\', '/', realpath($dir)).'/'; - - if ( ! is_writable($dir)) - throw new Kohana_Exception('image.directory_unwritable', $dir); - - if ($status = $this->driver->process($this->image, $this->actions, $dir, $file)) - { - if ($chmod !== FALSE) - { - // Set permissions - chmod($new_image, $chmod); - } - } - - // Reset actions. Subsequent save() or render() will not apply previous actions. - if ($keep_actions === FALSE) - $this->actions = array(); - - return $status; - } - - /** - * Output the image to the browser. - * - * @param boolean keep or discard image process actions - * @return object - */ - public function render($keep_actions = FALSE) - { - $new_image = $this->image['file']; - - // Separate the directory and filename - $dir = pathinfo($new_image, PATHINFO_DIRNAME); - $file = pathinfo($new_image, PATHINFO_BASENAME); - - // Normalize the path - $dir = str_replace('\\', '/', realpath($dir)).'/'; - - // Process the image with the driver - $status = $this->driver->process($this->image, $this->actions, $dir, $file, $render = TRUE); - - // Reset actions. Subsequent save() or render() will not apply previous actions. - if ($keep_actions === FALSE) - $this->actions = array(); - - return $status; - } - - /** - * Sanitize a given value type. - * - * @param string type of property - * @param mixed property value - * @return boolean - */ - protected function valid_size($type, & $value) - { - if (is_null($value)) - return TRUE; - - if ( ! is_scalar($value)) - return FALSE; - - switch ($type) - { - case 'width': - case 'height': - if (is_string($value) AND ! ctype_digit($value)) - { - // Only numbers and percent signs - if ( ! preg_match('/^[0-9]++%$/D', $value)) - return FALSE; - } - else - { - $value = (int) $value; - } - break; - case 'top': - if (is_string($value) AND ! ctype_digit($value)) - { - if ( ! in_array($value, array('top', 'bottom', 'center'))) - return FALSE; - } - else - { - $value = (int) $value; - } - break; - case 'left': - if (is_string($value) AND ! ctype_digit($value)) - { - if ( ! in_array($value, array('left', 'right', 'center'))) - return FALSE; - } - else - { - $value = (int) $value; - } - break; - case 'master': - if ($value !== Image::NONE AND - $value !== Image::AUTO AND - $value !== Image::WIDTH AND - $value !== Image::HEIGHT) - return FALSE; - break; - } - - return TRUE; - } - -} // End Image \ No newline at end of file diff --git a/kohana/libraries/Input.php b/kohana/libraries/Input.php deleted file mode 100644 index 0e23c800..00000000 --- a/kohana/libraries/Input.php +++ /dev/null @@ -1,452 +0,0 @@ -use_xss_clean = (bool) Kohana::config('core.global_xss_filtering'); - - if (Input::$instance === NULL) - { - // magic_quotes_runtime is enabled - if (get_magic_quotes_runtime()) - { - set_magic_quotes_runtime(0); - Kohana::log('debug', 'Disable magic_quotes_runtime! It is evil and deprecated: http://php.net/magic_quotes'); - } - - // magic_quotes_gpc is enabled - if (get_magic_quotes_gpc()) - { - $this->magic_quotes_gpc = TRUE; - Kohana::log('debug', 'Disable magic_quotes_gpc! It is evil and deprecated: http://php.net/magic_quotes'); - } - - // register_globals is enabled - if (ini_get('register_globals')) - { - if (isset($_REQUEST['GLOBALS'])) - { - // Prevent GLOBALS override attacks - exit('Global variable overload attack.'); - } - - // Destroy the REQUEST global - $_REQUEST = array(); - - // These globals are standard and should not be removed - $preserve = array('GLOBALS', '_REQUEST', '_GET', '_POST', '_FILES', '_COOKIE', '_SERVER', '_ENV', '_SESSION'); - - // This loop has the same effect as disabling register_globals - foreach (array_diff(array_keys($GLOBALS), $preserve) as $key) - { - global $$key; - $$key = NULL; - - // Unset the global variable - unset($GLOBALS[$key], $$key); - } - - // Warn the developer about register globals - Kohana::log('debug', 'Disable register_globals! It is evil and deprecated: http://php.net/register_globals'); - } - - if (is_array($_GET)) - { - foreach ($_GET as $key => $val) - { - // Sanitize $_GET - $_GET[$this->clean_input_keys($key)] = $this->clean_input_data($val); - } - } - else - { - $_GET = array(); - } - - if (is_array($_POST)) - { - foreach ($_POST as $key => $val) - { - // Sanitize $_POST - $_POST[$this->clean_input_keys($key)] = $this->clean_input_data($val); - } - } - else - { - $_POST = array(); - } - - if (is_array($_COOKIE)) - { - foreach ($_COOKIE as $key => $val) - { - // Ignore special attributes in RFC2109 compliant cookies - if ($key == '$Version' OR $key == '$Path' OR $key == '$Domain') - continue; - - // Sanitize $_COOKIE - $_COOKIE[$this->clean_input_keys($key)] = $this->clean_input_data($val); - } - } - else - { - $_COOKIE = array(); - } - - // Create a singleton - Input::$instance = $this; - - Kohana::log('debug', 'Global GET, POST and COOKIE data sanitized'); - } - } - - /** - * Fetch an item from the $_GET array. - * - * @param string key to find - * @param mixed default value - * @param boolean XSS clean the value - * @return mixed - */ - public function get($key = array(), $default = NULL, $xss_clean = FALSE) - { - return $this->search_array($_GET, $key, $default, $xss_clean); - } - - /** - * Fetch an item from the $_POST array. - * - * @param string key to find - * @param mixed default value - * @param boolean XSS clean the value - * @return mixed - */ - public function post($key = array(), $default = NULL, $xss_clean = FALSE) - { - return $this->search_array($_POST, $key, $default, $xss_clean); - } - - /** - * Fetch an item from the $_COOKIE array. - * - * @param string key to find - * @param mixed default value - * @param boolean XSS clean the value - * @return mixed - */ - public function cookie($key = array(), $default = NULL, $xss_clean = FALSE) - { - return $this->search_array($_COOKIE, $key, $default, $xss_clean); - } - - /** - * Fetch an item from the $_SERVER array. - * - * @param string key to find - * @param mixed default value - * @param boolean XSS clean the value - * @return mixed - */ - public function server($key = array(), $default = NULL, $xss_clean = FALSE) - { - return $this->search_array($_SERVER, $key, $default, $xss_clean); - } - - /** - * Fetch an item from a global array. - * - * @param array array to search - * @param string key to find - * @param mixed default value - * @param boolean XSS clean the value - * @return mixed - */ - protected function search_array($array, $key, $default = NULL, $xss_clean = FALSE) - { - if ($key === array()) - return $array; - - if ( ! isset($array[$key])) - return $default; - - // Get the value - $value = $array[$key]; - - if ($this->use_xss_clean === FALSE AND $xss_clean === TRUE) - { - // XSS clean the value - $value = $this->xss_clean($value); - } - - return $value; - } - - /** - * Fetch the IP Address. - * - * @return string - */ - public function ip_address() - { - if ($this->ip_address !== NULL) - return $this->ip_address; - - // Server keys that could contain the client IP address - $keys = array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'); - - foreach ($keys as $key) - { - if ($ip = $this->server($key)) - { - $this->ip_address = $ip; - - // An IP address has been found - break; - } - } - - if ($comma = strrpos($this->ip_address, ',') !== FALSE) - { - $this->ip_address = substr($this->ip_address, $comma + 1); - } - - if ( ! valid::ip($this->ip_address)) - { - // Use an empty IP - $this->ip_address = '0.0.0.0'; - } - - return $this->ip_address; - } - - /** - * Clean cross site scripting exploits from string. - * HTMLPurifier may be used if installed, otherwise defaults to built in method. - * Note - This function should only be used to deal with data upon submission. - * It's not something that should be used for general runtime processing - * since it requires a fair amount of processing overhead. - * - * @param string data to clean - * @param string xss_clean method to use ('htmlpurifier' or defaults to built-in method) - * @return string - */ - public function xss_clean($data, $tool = NULL) - { - if ($tool === NULL) - { - // Use the default tool - $tool = Kohana::config('core.global_xss_filtering'); - } - - if (is_array($data)) - { - foreach ($data as $key => $val) - { - $data[$key] = $this->xss_clean($val, $tool); - } - - return $data; - } - - // Do not clean empty strings - if (trim($data) === '') - return $data; - - if ($tool === TRUE) - { - // NOTE: This is necessary because switch is NOT type-sensative! - $tool = 'default'; - } - - switch ($tool) - { - case 'htmlpurifier': - /** - * @todo License should go here, http://htmlpurifier.org/ - */ - if ( ! class_exists('HTMLPurifier_Config', FALSE)) - { - // Load HTMLPurifier - require Kohana::find_file('vendor', 'htmlpurifier/HTMLPurifier.auto', TRUE); - require 'HTMLPurifier.func.php'; - } - - // Set configuration - $config = HTMLPurifier_Config::createDefault(); - $config->set('HTML', 'TidyLevel', 'none'); // Only XSS cleaning now - - // Run HTMLPurifier - $data = HTMLPurifier($data, $config); - break; - default: - // http://svn.bitflux.ch/repos/public/popoon/trunk/classes/externalinput.php - // +----------------------------------------------------------------------+ - // | Copyright (c) 2001-2006 Bitflux GmbH | - // +----------------------------------------------------------------------+ - // | Licensed under the Apache License, Version 2.0 (the "License"); | - // | you may not use this file except in compliance with the License. | - // | You may obtain a copy of the License at | - // | http://www.apache.org/licenses/LICENSE-2.0 | - // | Unless required by applicable law or agreed to in writing, software | - // | distributed under the License is distributed on an "AS IS" BASIS, | - // | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | - // | implied. See the License for the specific language governing | - // | permissions and limitations under the License. | - // +----------------------------------------------------------------------+ - // | Author: Christian Stocker | - // +----------------------------------------------------------------------+ - // - // Kohana Modifications: - // * Changed double quotes to single quotes, changed indenting and spacing - // * Removed magic_quotes stuff - // * Increased regex readability: - // * Used delimeters that aren't found in the pattern - // * Removed all unneeded escapes - // * Deleted U modifiers and swapped greediness where needed - // * Increased regex speed: - // * Made capturing parentheses non-capturing where possible - // * Removed parentheses where possible - // * Split up alternation alternatives - // * Made some quantifiers possessive - - // Fix &entity\n; - $data = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $data); - $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data); - $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data); - $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8'); - - // Remove any attribute starting with "on" or xmlns - $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data); - - // Remove javascript: and vbscript: protocols - $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data); - $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data); - $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data); - - // Only works in IE: - $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data); - $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data); - $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data); - - // Remove namespaced elements (we do not need them) - $data = preg_replace('#]*+>#i', '', $data); - - do - { - // Remove really unwanted tags - $old_data = $data; - $data = preg_replace('#]*+>#i', '', $data); - } - while ($old_data !== $data); - break; - } - - return $data; - } - - /** - * This is a helper method. It enforces W3C specifications for allowed - * key name strings, to prevent malicious exploitation. - * - * @param string string to clean - * @return string - */ - public function clean_input_keys($str) - { - $chars = PCRE_UNICODE_PROPERTIES ? '\pL' : 'a-zA-Z'; - - if ( ! preg_match('#^['.$chars.'0-9:_.-]++$#uD', $str)) - { - exit('Disallowed key characters in global data.'); - } - - return $str; - } - - /** - * This is a helper method. It escapes data and forces all newline - * characters to "\n". - * - * @param unknown_type string to clean - * @return string - */ - public function clean_input_data($str) - { - if (is_array($str)) - { - $new_array = array(); - foreach ($str as $key => $val) - { - // Recursion! - $new_array[$this->clean_input_keys($key)] = $this->clean_input_data($val); - } - return $new_array; - } - - if ($this->magic_quotes_gpc === TRUE) - { - // Remove annoying magic quotes - $str = stripslashes($str); - } - - if ($this->use_xss_clean === TRUE) - { - $str = $this->xss_clean($str); - } - - if (strpos($str, "\r") !== FALSE) - { - // Standardize newlines - $str = str_replace(array("\r\n", "\r"), "\n", $str); - } - - return $str; - } - -} // End Input Class diff --git a/kohana/libraries/Model.php b/kohana/libraries/Model.php deleted file mode 100644 index 0c9fd8d6..00000000 --- a/kohana/libraries/Model.php +++ /dev/null @@ -1,31 +0,0 @@ -db)) - { - // Load the default database - $this->db = Database::instance($this->db); - } - } - -} // End Model \ No newline at end of file diff --git a/kohana/libraries/ORM.php b/kohana/libraries/ORM.php deleted file mode 100644 index c1048604..00000000 --- a/kohana/libraries/ORM.php +++ /dev/null @@ -1,1431 +0,0 @@ -object_name = strtolower(substr(get_class($this), 0, -6)); - $this->object_plural = inflector::plural($this->object_name); - - if (!isset($this->sorting)) - { - // Default sorting - $this->sorting = array($this->primary_key => 'asc'); - } - - // Initialize database - $this->__initialize(); - - // Clear the object - $this->clear(); - - if (is_object($id)) - { - // Load an object - $this->load_values((array) $id); - } - elseif (!empty($id)) - { - // Find an object - $this->find($id); - } - } - - /** - * Prepares the model database connection, determines the table name, - * and loads column information. - * - * @return void - */ - public function __initialize() - { - if ( ! is_object($this->db)) - { - // Get database instance - $this->db = Database::instance($this->db); - } - - if (empty($this->table_name)) - { - // Table name is the same as the object name - $this->table_name = $this->object_name; - - if ($this->table_names_plural === TRUE) - { - // Make the table name plural - $this->table_name = inflector::plural($this->table_name); - } - } - - if (is_array($this->ignored_columns)) - { - // Make the ignored columns mirrored = mirrored - $this->ignored_columns = array_combine($this->ignored_columns, $this->ignored_columns); - } - - // Load column information - $this->reload_columns(); - } - - /** - * Allows serialization of only the object data and state, to prevent - * "stale" objects being unserialized, which also requires less memory. - * - * @return array - */ - public function __sleep() - { - // Store only information about the object - return array('object_name', 'object', 'changed', 'loaded', 'saved', 'sorting'); - } - - /** - * Prepares the database connection and reloads the object. - * - * @return void - */ - public function __wakeup() - { - // Initialize database - $this->__initialize(); - - if ($this->reload_on_wakeup === TRUE) - { - // Reload the object - $this->reload(); - } - } - - /** - * Handles pass-through to database methods. Calls to query methods - * (query, get, insert, update) are not allowed. Query builder methods - * are chainable. - * - * @param string method name - * @param array method arguments - * @return mixed - */ - public function __call($method, array $args) - { - if (method_exists($this->db, $method)) - { - if (in_array($method, array('query', 'get', 'insert', 'update', 'delete'))) - throw new Kohana_Exception('orm.query_methods_not_allowed'); - - // Method has been applied to the database - $this->db_applied[$method] = $method; - - // Number of arguments passed - $num_args = count($args); - - if ($method === 'select' AND $num_args > 3) - { - // Call select() manually to avoid call_user_func_array - $this->db->select($args); - } - else - { - // We use switch here to manually call the database methods. This is - // done for speed: call_user_func_array can take over 300% longer to - // make calls. Most database methods are 4 arguments or less, so this - // avoids almost any calls to call_user_func_array. - - switch ($num_args) - { - case 0: - if (in_array($method, array('open_paren', 'close_paren', 'enable_cache', 'disable_cache'))) - { - // Should return ORM, not Database - $this->db->$method(); - } - else - { - // Support for things like reset_select, reset_write, list_tables - return $this->db->$method(); - } - break; - case 1: - $this->db->$method($args[0]); - break; - case 2: - $this->db->$method($args[0], $args[1]); - break; - case 3: - $this->db->$method($args[0], $args[1], $args[2]); - break; - case 4: - $this->db->$method($args[0], $args[1], $args[2], $args[3]); - break; - default: - // Here comes the snail... - call_user_func_array(array($this->db, $method), $args); - break; - } - } - - return $this; - } - else - { - throw new Kohana_Exception('core.invalid_method', $method, get_class($this)); - } - } - - /** - * Handles retrieval of all model values, relationships, and metadata. - * - * @param string column name - * @return mixed - */ - public function __get($column) - { - if (array_key_exists($column, $this->object)) - { - return $this->object[$column]; - } - elseif (isset($this->related[$column])) - { - return $this->related[$column]; - } - elseif ($column === 'primary_key_value') - { - return $this->object[$this->primary_key]; - } - elseif ($model = $this->related_object($column)) - { - // This handles the has_one and belongs_to relationships - - if (in_array($model->object_name, $this->belongs_to) OR ! array_key_exists($this->foreign_key($column), $model->object)) - { - // Foreign key lies in this table (this model belongs_to target model) OR an invalid has_one relationship - $where = array($model->table_name.'.'.$model->primary_key => $this->object[$this->foreign_key($column)]); - } - else - { - // Foreign key lies in the target table (this model has_one target model) - $where = array($this->foreign_key($column, $model->table_name) => $this->primary_key_value); - } - - // one<>alias:one relationship - return $this->related[$column] = $model->find($where); - } - elseif (isset($this->has_many[$column])) - { - // Load the "middle" model - $through = ORM::factory(inflector::singular($this->has_many[$column])); - - // Load the "end" model - $model = ORM::factory(inflector::singular($column)); - - // Join ON target model's primary key set to 'through' model's foreign key - // User-defined foreign keys must be defined in the 'through' model - $join_table = $through->table_name; - $join_col1 = $through->foreign_key($model->object_name, $join_table); - $join_col2 = $model->table_name.'.'.$model->primary_key; - - // one<>alias:many relationship - return $this->related[$column] = $model - ->join($join_table, $join_col1, $join_col2) - ->where($through->foreign_key($this->object_name, $join_table), $this->object[$this->primary_key]) - ->find_all(); - } - elseif (in_array($column, $this->has_many)) - { - // one<>many relationship - $model = ORM::factory(inflector::singular($column)); - return $this->related[$column] = $model - ->where($this->foreign_key($column, $model->table_name), $this->object[$this->primary_key]) - ->find_all(); - } - elseif (in_array($column, $this->has_and_belongs_to_many)) - { - // Load the remote model, always singular - $model = ORM::factory(inflector::singular($column)); - - if ($this->has($model, TRUE)) - { - // many<>many relationship - return $this->related[$column] = $model - ->in($model->table_name.'.'.$model->primary_key, $this->changed_relations[$column]) - ->find_all(); - } - else - { - // empty many<>many relationship - return $this->related[$column] = $model - ->where($model->table_name.'.'.$model->primary_key, NULL) - ->find_all(); - } - } - elseif (isset($this->ignored_columns[$column])) - { - return NULL; - } - elseif (in_array($column, array - ( - 'object_name', 'object_plural', // Object - 'primary_key', 'primary_val', 'table_name', 'table_columns', // Table - 'loaded', 'saved', // Status - 'has_one', 'belongs_to', 'has_many', 'has_and_belongs_to_many', 'load_with' // Relationships - ))) - { - // Model meta information - return $this->$column; - } - else - { - throw new Kohana_Exception('core.invalid_property', $column, get_class($this)); - } - } - - /** - * Handles setting of all model values, and tracks changes between values. - * - * @param string column name - * @param mixed column value - * @return void - */ - public function __set($column, $value) - { - if (isset($this->ignored_columns[$column])) - { - return NULL; - } - elseif (isset($this->object[$column]) OR array_key_exists($column, $this->object)) - { - if (isset($this->table_columns[$column])) - { - // Data has changed - $this->changed[$column] = $column; - - // Object is no longer saved - $this->saved = FALSE; - } - - $this->object[$column] = $this->load_type($column, $value); - } - elseif (in_array($column, $this->has_and_belongs_to_many) AND is_array($value)) - { - // Load relations - $model = ORM::factory(inflector::singular($column)); - - if ( ! isset($this->object_relations[$column])) - { - // Load relations - $this->has($model); - } - - // Change the relationships - $this->changed_relations[$column] = $value; - - if (isset($this->related[$column])) - { - // Force a reload of the relationships - unset($this->related[$column]); - } - } - else - { - throw new Kohana_Exception('core.invalid_property', $column, get_class($this)); - } - } - - /** - * Checks if object data is set. - * - * @param string column name - * @return boolean - */ - public function __isset($column) - { - return (isset($this->object[$column]) OR isset($this->related[$column])); - } - - /** - * Unsets object data. - * - * @param string column name - * @return void - */ - public function __unset($column) - { - unset($this->object[$column], $this->changed[$column], $this->related[$column]); - } - - /** - * Displays the primary key of a model when it is converted to a string. - * - * @return string - */ - public function __toString() - { - return (string) $this->object[$this->primary_key]; - } - - /** - * Returns the values of this object as an array. - * - * @return array - */ - public function as_array() - { - $object = array(); - - foreach ($this->object as $key => $val) - { - // Reconstruct the array (calls __get) - $object[$key] = $this->$key; - } - - return $object; - } - - /** - * Binds another one-to-one object to this model. One-to-one objects - * can be nested using 'object1:object2' syntax - * - * @param string $target_path - * @return void - */ - public function with($target_path) - { - if (isset($this->with_applied[$target_path])) - { - // Don't join anything already joined - return $this; - } - - // Split object parts - $objects = explode(':', $target_path); - $target = $this; - foreach ($objects as $object) - { - // Go down the line of objects to find the given target - $parent = $target; - $target = $parent->related_object($object); - - if ( ! $target) - { - // Can't find related object - return $this; - } - } - - $target_name = $object; - - // Pop-off top object to get the parent object (user:photo:tag becomes user:photo - the parent table prefix) - array_pop($objects); - $parent_path = implode(':', $objects); - - if (empty($parent_path)) - { - // Use this table name itself for the parent object - $parent_path = $this->table_name; - } - else - { - if( ! isset($this->with_applied[$parent_path])) - { - // If the parent object hasn't been joined yet, do it first (otherwise LEFT JOINs fail) - $this->with($parent_path); - } - } - - // Add to with_applied to prevent duplicate joins - $this->with_applied[$target_path] = TRUE; - - // Use the keys of the empty object to determine the columns - $select = array_keys($target->object); - foreach ($select as $i => $column) - { - // Add the prefix so that load_result can determine the relationship - $select[$i] = $target_path.'.'.$column.' AS '.$target_path.':'.$column; - } - - - // Select all of the prefixed keys in the object - $this->db->select($select); - - if (in_array($target->object_name, $parent->belongs_to) OR ! isset($target->object[$parent->foreign_key($target_name)])) - { - // Parent belongs_to target, use target's primary key as join column - $join_col1 = $target->foreign_key(TRUE, $target_path); - $join_col2 = $parent->foreign_key($target_name, $parent_path); - } - else - { - // Parent has_one target, use parent's primary key as join column - $join_col2 = $parent->foreign_key(TRUE, $parent_path); - $join_col1 = $parent->foreign_key($target_name, $target_path); - } - - // This allows for models to use different table prefixes (sharing the same database) - $join_table = new Database_Expression($target->db->table_prefix().$target->table_name.' AS '.$this->db->table_prefix().$target_path); - - // Join the related object into the result - $this->db->join($join_table, $join_col1, $join_col2, 'LEFT'); - - return $this; - } - - /** - * Finds and loads a single database row into the object. - * - * @chainable - * @param mixed primary key or an array of clauses - * @return ORM - */ - public function find($id = NULL) - { - if ($id !== NULL) - { - if (is_array($id)) - { - // Search for all clauses - $this->db->where($id); - } - else - { - // Search for a specific column - $this->db->where($this->table_name.'.'.$this->unique_key($id), $id); - } - } - - return $this->load_result(); - } - - /** - * Finds multiple database rows and returns an iterator of the rows found. - * - * @chainable - * @param integer SQL limit - * @param integer SQL offset - * @return ORM_Iterator - */ - public function find_all($limit = NULL, $offset = NULL) - { - if ($limit !== NULL AND ! isset($this->db_applied['limit'])) - { - // Set limit - $this->limit($limit); - } - - if ($offset !== NULL AND ! isset($this->db_applied['offset'])) - { - // Set offset - $this->offset($offset); - } - - return $this->load_result(TRUE); - } - - /** - * Creates a key/value array from all of the objects available. Uses find_all - * to find the objects. - * - * @param string key column - * @param string value column - * @return array - */ - public function select_list($key = NULL, $val = NULL) - { - if ($key === NULL) - { - $key = $this->primary_key; - } - - if ($val === NULL) - { - $val = $this->primary_val; - } - - // Return a select list from the results - return $this->select($key, $val)->find_all()->select_list($key, $val); - } - - /** - * Validates the current object. This method should generally be called - * via the model, after the $_POST Validation object has been created. - * - * @param object Validation array - * @return boolean - */ - public function validate(Validation $array, $save = FALSE) - { - $safe_array = $array->safe_array(); - - if ( ! $array->submitted()) - { - foreach ($safe_array as $key => $value) - { - // Get the value from this object - $value = $this->$key; - - if (is_object($value) AND $value instanceof ORM_Iterator) - { - // Convert the value to an array of primary keys - $value = $value->primary_key_array(); - } - - // Pre-fill data - $array[$key] = $value; - } - } - - // Validate the array - if ($status = $array->validate()) - { - // Grab only set fields (excludes missing data, unlike safe_array) - $fields = $array->as_array(); - - foreach ($fields as $key => $value) - { - if (isset($safe_array[$key])) - { - // Set new data, ignoring any missing fields or fields without rules - $this->$key = $value; - } - } - - if ($save === TRUE OR is_string($save)) - { - // Save this object - $this->save(); - - if (is_string($save)) - { - // Redirect to the saved page - url::redirect($save); - } - } - } - - // Return validation status - return $status; - } - - /** - * Saves the current object. - * - * @chainable - * @return ORM - */ - public function save() - { - if ( ! empty($this->changed)) - { - $data = array(); - foreach ($this->changed as $column) - { - // Compile changed data - $data[$column] = $this->object[$column]; - } - - if ($this->loaded === TRUE) - { - $query = $this->db - ->where($this->primary_key, $this->object[$this->primary_key]) - ->update($this->table_name, $data); - - // Object has been saved - $this->saved = TRUE; - } - else - { - $query = $this->db - ->insert($this->table_name, $data); - - if ($query->count() > 0) - { - if (empty($this->object[$this->primary_key])) - { - // Load the insert id as the primary key - $this->object[$this->primary_key] = $query->insert_id(); - } - - // Object is now loaded and saved - $this->loaded = $this->saved = TRUE; - } - } - - if ($this->saved === TRUE) - { - // All changes have been saved - $this->changed = array(); - } - } - - if ($this->saved === TRUE AND ! empty($this->changed_relations)) - { - foreach ($this->changed_relations as $column => $values) - { - // All values that were added - $added = array_diff($values, $this->object_relations[$column]); - - // All values that were saved - $removed = array_diff($this->object_relations[$column], $values); - - if (empty($added) AND empty($removed)) - { - // No need to bother - continue; - } - - // Clear related columns - unset($this->related[$column]); - - // Load the model - $model = ORM::factory(inflector::singular($column)); - - if (($join_table = array_search($column, $this->has_and_belongs_to_many)) === FALSE) - continue; - - if (is_int($join_table)) - { - // No "through" table, load the default JOIN table - $join_table = $model->join_table($this->table_name); - } - - // Foreign keys for the join table - $object_fk = $this->foreign_key(NULL); - $related_fk = $model->foreign_key(NULL); - - if ( ! empty($added)) - { - foreach ($added as $id) - { - // Insert the new relationship - $this->db->insert($join_table, array - ( - $object_fk => $this->object[$this->primary_key], - $related_fk => $id, - )); - } - } - - if ( ! empty($removed)) - { - $this->db - ->where($object_fk, $this->object[$this->primary_key]) - ->in($related_fk, $removed) - ->delete($join_table); - } - - // Clear all relations for this column - unset($this->object_relations[$column], $this->changed_relations[$column]); - } - } - - return $this; - } - - /** - * Deletes the current object from the database. This does NOT destroy - * relationships that have been created with other objects. - * - * @chainable - * @return ORM - */ - public function delete($id = NULL) - { - if ($id === NULL AND $this->loaded) - { - // Use the the primary key value - $id = $this->object[$this->primary_key]; - } - - // Delete this object - $this->db->where($this->primary_key, $id)->delete($this->table_name); - - return $this->clear(); - } - - /** - * Delete all objects in the associated table. This does NOT destroy - * relationships that have been created with other objects. - * - * @chainable - * @param array ids to delete - * @return ORM - */ - public function delete_all($ids = NULL) - { - if (is_array($ids)) - { - // Delete only given ids - $this->db->in($this->primary_key, $ids); - } - elseif (is_null($ids)) - { - // Delete all records - $this->db->where('1=1'); - } - else - { - // Do nothing - safeguard - return $this; - } - - // Delete all objects - $this->db->delete($this->table_name); - - return $this->clear(); - } - - /** - * Unloads the current object and clears the status. - * - * @chainable - * @return ORM - */ - public function clear() - { - // Create an array with all the columns set to NULL - $columns = array_keys($this->table_columns); - $values = array_combine($columns, array_fill(0, count($columns), NULL)); - - // Replace the current object with an empty one - $this->load_values($values); - - return $this; - } - - /** - * Reloads the current object from the database. - * - * @chainable - * @return ORM - */ - public function reload() - { - return $this->find($this->object[$this->primary_key]); - } - - /** - * Reload column definitions. - * - * @chainable - * @param boolean force reloading - * @return ORM - */ - public function reload_columns($force = FALSE) - { - if ($force === TRUE OR empty($this->table_columns)) - { - if (isset(ORM::$column_cache[$this->object_name])) - { - // Use cached column information - $this->table_columns = ORM::$column_cache[$this->object_name]; - } - else - { - // Load table columns - ORM::$column_cache[$this->object_name] = $this->table_columns = $this->list_fields(); - } - } - - return $this; - } - - /** - * Tests if this object has a relationship to a different model. - * - * @param object related ORM model - * @param boolean check for any relations to given model - * @return boolean - */ - public function has(ORM $model, $any = FALSE) - { - // Determine plural or singular relation name - $related = ($model->table_names_plural === TRUE) ? $model->object_plural : $model->object_name; - - if (($join_table = array_search($related, $this->has_and_belongs_to_many)) === FALSE) - return FALSE; - - if (is_int($join_table)) - { - // No "through" table, load the default JOIN table - $join_table = $model->join_table($this->table_name); - } - - if ( ! isset($this->object_relations[$related])) - { - // Load the object relationships - $this->changed_relations[$related] = $this->object_relations[$related] = $this->load_relations($join_table, $model); - } - - if ( ! $model->empty_primary_key()) - { - // Check if a specific object exists - return in_array($model->primary_key_value, $this->changed_relations[$related]); - } - elseif ($any) - { - // Check if any relations to given model exist - return ! empty($this->changed_relations[$related]); - } - else - { - return FALSE; - } - } - - /** - * Adds a new relationship to between this model and another. - * - * @param object related ORM model - * @return boolean - */ - public function add(ORM $model) - { - if ($this->has($model)) - return TRUE; - - // Get the faked column name - $column = $model->object_plural; - - // Add the new relation to the update - $this->changed_relations[$column][] = $model->primary_key_value; - - if (isset($this->related[$column])) - { - // Force a reload of the relationships - unset($this->related[$column]); - } - - return TRUE; - } - - /** - * Adds a new relationship to between this model and another. - * - * @param object related ORM model - * @return boolean - */ - public function remove(ORM $model) - { - if ( ! $this->has($model)) - return FALSE; - - // Get the faked column name - $column = $model->object_plural; - - if (($key = array_search($model->primary_key_value, $this->changed_relations[$column])) === FALSE) - return FALSE; - - // Remove the relationship - unset($this->changed_relations[$column][$key]); - - if (isset($this->related[$column])) - { - // Force a reload of the relationships - unset($this->related[$column]); - } - - return TRUE; - } - - /** - * Count the number of records in the table. - * - * @return integer - */ - public function count_all() - { - // Return the total number of records in a table - return $this->db->count_records($this->table_name); - } - - /** - * Proxy method to Database list_fields. - * - * @param string table name or NULL to use this table - * @return array - */ - public function list_fields($table = NULL) - { - if ($table === NULL) - { - $table = $this->table_name; - } - - // Proxy to database - return $this->db->list_fields($table); - } - - /** - * Proxy method to Database field_data. - * - * @param string table name - * @return array - */ - public function field_data($table) - { - // Proxy to database - return $this->db->field_data($table); - } - - /** - * Proxy method to Database field_data. - * - * @chainable - * @param string SQL query to clear - * @return ORM - */ - public function clear_cache($sql = NULL) - { - // Proxy to database - $this->db->clear_cache($sql); - - ORM::$column_cache = array(); - - return $this; - } - - /** - * Returns the unique key for a specific value. This method is expected - * to be overloaded in models if the model has other unique columns. - * - * @param mixed unique value - * @return string - */ - public function unique_key($id) - { - return $this->primary_key; - } - - /** - * Determines the name of a foreign key for a specific table. - * - * @param string related table name - * @param string prefix table name (used for JOINs) - * @return string - */ - public function foreign_key($table = NULL, $prefix_table = NULL) - { - if ($table === TRUE) - { - if (is_string($prefix_table)) - { - // Use prefix table name and this table's PK - return $prefix_table.'.'.$this->primary_key; - } - else - { - // Return the name of this table's PK - return $this->table_name.'.'.$this->primary_key; - } - } - - if (is_string($prefix_table)) - { - // Add a period for prefix_table.column support - $prefix_table .= '.'; - } - - if (isset($this->foreign_key[$table])) - { - // Use the defined foreign key name, no magic here! - $foreign_key = $this->foreign_key[$table]; - } - else - { - if ( ! is_string($table) OR ! array_key_exists($table.'_'.$this->primary_key, $this->object)) - { - // Use this table - $table = $this->table_name; - - if (strpos($table, '.') !== FALSE) - { - // Hack around support for PostgreSQL schemas - list ($schema, $table) = explode('.', $table, 2); - } - - if ($this->table_names_plural === TRUE) - { - // Make the key name singular - $table = inflector::singular($table); - } - } - - $foreign_key = $table.'_'.$this->primary_key; - } - - return $prefix_table.$foreign_key; - } - - /** - * This uses alphabetical comparison to choose the name of the table. - * - * Example: The joining table of users and roles would be roles_users, - * because "r" comes before "u". Joining products and categories would - * result in categories_products, because "c" comes before "p". - * - * Example: zoo > zebra > robber > ocean > angel > aardvark - * - * @param string table name - * @return string - */ - public function join_table($table) - { - if ($this->table_name > $table) - { - $table = $table.'_'.$this->table_name; - } - else - { - $table = $this->table_name.'_'.$table; - } - - return $table; - } - - /** - * Returns an ORM model for the given object name; - * - * @param string object name - * @return ORM - */ - protected function related_object($object) - { - if (isset($this->has_one[$object])) - { - $object = ORM::factory($this->has_one[$object]); - } - elseif (isset($this->belongs_to[$object])) - { - $object = ORM::factory($this->belongs_to[$object]); - } - elseif (in_array($object, $this->has_one) OR in_array($object, $this->belongs_to)) - { - $object = ORM::factory($object); - } - else - { - return FALSE; - } - - return $object; - } - - /** - * Loads an array of values into into the current object. - * - * @chainable - * @param array values to load - * @return ORM - */ - public function load_values(array $values) - { - if (array_key_exists($this->primary_key, $values)) - { - // Replace the object and reset the object status - $this->object = $this->changed = $this->related = array(); - - // Set the loaded and saved object status based on the primary key - $this->loaded = $this->saved = ($values[$this->primary_key] !== NULL); - } - - // Related objects - $related = array(); - - foreach ($values as $column => $value) - { - if (strpos($column, ':') === FALSE) - { - if (isset($this->table_columns[$column])) - { - // The type of the value can be determined, convert the value - $value = $this->load_type($column, $value); - } - - $this->object[$column] = $value; - } - else - { - list ($prefix, $column) = explode(':', $column, 2); - - $related[$prefix][$column] = $value; - } - } - - if ( ! empty($related)) - { - foreach ($related as $object => $values) - { - // Load the related objects with the values in the result - $this->related[$object] = $this->related_object($object)->load_values($values); - } - } - - return $this; - } - - /** - * Loads a value according to the types defined by the column metadata. - * - * @param string column name - * @param mixed value to load - * @return mixed - */ - protected function load_type($column, $value) - { - $type = gettype($value); - if ($type == 'object' OR $type == 'array' OR ! isset($this->table_columns[$column])) - return $value; - - // Load column data - $column = $this->table_columns[$column]; - - if ($value === NULL AND ! empty($column['null'])) - return $value; - - if ( ! empty($column['binary']) AND ! empty($column['exact']) AND (int) $column['length'] === 1) - { - // Use boolean for BINARY(1) fields - $column['type'] = 'boolean'; - } - - switch ($column['type']) - { - case 'int': - if ($value === '' AND ! empty($column['null'])) - { - // Forms will only submit strings, so empty integer values must be null - $value = NULL; - } - elseif ((float) $value > PHP_INT_MAX) - { - // This number cannot be represented by a PHP integer, so we convert it to a string - $value = (string) $value; - } - else - { - $value = (int) $value; - } - break; - case 'float': - $value = (float) $value; - break; - case 'boolean': - $value = (bool) $value; - break; - case 'string': - $value = (string) $value; - break; - } - - return $value; - } - - /** - * Loads a database result, either as a new object for this model, or as - * an iterator for multiple rows. - * - * @chainable - * @param boolean return an iterator or load a single row - * @return ORM for single rows - * @return ORM_Iterator for multiple rows - */ - protected function load_result($array = FALSE) - { - if ($array === FALSE) - { - // Only fetch 1 record - $this->db->limit(1); - } - - if ( ! isset($this->db_applied['select'])) - { - // Select all columns by default - $this->db->select($this->table_name.'.*'); - } - - if ( ! empty($this->load_with)) - { - foreach ($this->load_with as $alias => $object) - { - // Join each object into the results - if (is_string($alias)) - { - // Use alias - $this->with($alias); - } - else - { - // Use object - $this->with($object); - } - } - } - - if ( ! isset($this->db_applied['orderby']) AND ! empty($this->sorting)) - { - $sorting = array(); - foreach ($this->sorting as $column => $direction) - { - if (strpos($column, '.') === FALSE) - { - // Keeps sorting working properly when using JOINs on - // tables with columns of the same name - $column = $this->table_name.'.'.$column; - } - - $sorting[$column] = $direction; - } - - // Apply the user-defined sorting - $this->db->orderby($sorting); - } - - // Load the result - $result = $this->db->get($this->table_name); - - if ($array === TRUE) - { - // Return an iterated result - return new ORM_Iterator($this, $result); - } - - if ($result->count() === 1) - { - // Load object values - $this->load_values($result->result(FALSE)->current()); - } - else - { - // Clear the object, nothing was found - $this->clear(); - } - - return $this; - } - - /** - * Return an array of all the primary keys of the related table. - * - * @param string table name - * @param object ORM model to find relations of - * @return array - */ - protected function load_relations($table, ORM $model) - { - // Save the current query chain (otherwise the next call will clash) - $this->db->push(); - - $query = $this->db - ->select($model->foreign_key(NULL).' AS id') - ->from($table) - ->where($this->foreign_key(NULL, $table), $this->object[$this->primary_key]) - ->get() - ->result(TRUE); - - $this->db->pop(); - - $relations = array(); - foreach ($query as $row) - { - $relations[] = $row->id; - } - - return $relations; - } - - /** - * Returns whether or not primary key is empty - * - * @return bool - */ - protected function empty_primary_key() - { - return (empty($this->object[$this->primary_key]) AND $this->object[$this->primary_key] !== '0'); - } - -} // End ORM diff --git a/kohana/libraries/ORM_Iterator.php b/kohana/libraries/ORM_Iterator.php deleted file mode 100644 index 41aa8065..00000000 --- a/kohana/libraries/ORM_Iterator.php +++ /dev/null @@ -1,228 +0,0 @@ -class_name = get_class($model); - $this->primary_key = $model->primary_key; - $this->primary_val = $model->primary_val; - - // Database result - $this->result = $result->result(TRUE); - } - - /** - * Returns an array of the results as ORM objects. - * - * @return array - */ - public function as_array() - { - $array = array(); - - if ($results = $this->result->result_array()) - { - // Import class name - $class = $this->class_name; - - foreach ($results as $obj) - { - $array[] = new $class($obj); - } - } - - return $array; - } - - /** - * Return an array of all of the primary keys for this object. - * - * @return array - */ - public function primary_key_array() - { - $ids = array(); - foreach ($this->result as $row) - { - $ids[] = $row->{$this->primary_key}; - } - return $ids; - } - - /** - * Create a key/value array from the results. - * - * @param string key column - * @param string value column - * @return array - */ - public function select_list($key = NULL, $val = NULL) - { - if ($key === NULL) - { - // Use the default key - $key = $this->primary_key; - } - - if ($val === NULL) - { - // Use the default value - $val = $this->primary_val; - } - - $array = array(); - foreach ($this->result->result_array() as $row) - { - $array[$row->$key] = $row->$val; - } - return $array; - } - - /** - * Return a range of offsets. - * - * @param integer start - * @param integer end - * @return array - */ - public function range($start, $end) - { - // Array of objects - $array = array(); - - if ($this->result->offsetExists($start)) - { - // Import the class name - $class = $this->class_name; - - // Set the end offset - $end = $this->result->offsetExists($end) ? $end : $this->count(); - - for ($i = $start; $i < $end; $i++) - { - // Insert each object in the range - $array[] = new $class($this->result->offsetGet($i)); - } - } - - return $array; - } - - /** - * Countable: count - */ - public function count() - { - return $this->result->count(); - } - - /** - * Iterator: current - */ - public function current() - { - if ($row = $this->result->current()) - { - // Import class name - $class = $this->class_name; - - $row = new $class($row); - } - - return $row; - } - - /** - * Iterator: key - */ - public function key() - { - return $this->result->key(); - } - - /** - * Iterator: next - */ - public function next() - { - return $this->result->next(); - } - - /** - * Iterator: rewind - */ - public function rewind() - { - $this->result->rewind(); - } - - /** - * Iterator: valid - */ - public function valid() - { - return $this->result->valid(); - } - - /** - * ArrayAccess: offsetExists - */ - public function offsetExists($offset) - { - return $this->result->offsetExists($offset); - } - - /** - * ArrayAccess: offsetGet - */ - public function offsetGet($offset) - { - if ($this->result->offsetExists($offset)) - { - // Import class name - $class = $this->class_name; - - return new $class($this->result->offsetGet($offset)); - } - } - - /** - * ArrayAccess: offsetSet - * - * @throws Kohana_Database_Exception - */ - public function offsetSet($offset, $value) - { - throw new Kohana_Database_Exception('database.result_read_only'); - } - - /** - * ArrayAccess: offsetUnset - * - * @throws Kohana_Database_Exception - */ - public function offsetUnset($offset) - { - throw new Kohana_Database_Exception('database.result_read_only'); - } - -} // End ORM Iterator \ No newline at end of file diff --git a/kohana/libraries/ORM_Tree.php b/kohana/libraries/ORM_Tree.php deleted file mode 100644 index cdb09fd0..00000000 --- a/kohana/libraries/ORM_Tree.php +++ /dev/null @@ -1,76 +0,0 @@ -related[$column])) - { - // Load child model - $model = ORM::factory(inflector::singular($this->ORM_Tree_children)); - - if (array_key_exists($this->ORM_Tree_parent_key, $this->object)) - { - // Find children of this parent - $model->where($model->primary_key, $this->object[$this->ORM_Tree_parent_key])->find(); - } - - $this->related[$column] = $model; - } - - return $this->related[$column]; - } - elseif ($column === 'children') - { - if (empty($this->related[$column])) - { - $model = ORM::factory(inflector::singular($this->ORM_Tree_children)); - - if ($this->ORM_Tree_children === $this->table_name) - { - // Load children within this table - $this->related[$column] = $model - ->where($this->ORM_Tree_parent_key, $this->object[$this->primary_key]) - ->find_all(); - } - else - { - // Find first selection of children - $this->related[$column] = $model - ->where($this->foreign_key(), $this->object[$this->primary_key]) - ->where($this->ORM_Tree_parent_key, NULL) - ->find_all(); - } - } - - return $this->related[$column]; - } - - return parent::__get($column); - } - -} // End ORM Tree \ No newline at end of file diff --git a/kohana/libraries/ORM_Versioned.php b/kohana/libraries/ORM_Versioned.php deleted file mode 100644 index 078fe16a..00000000 --- a/kohana/libraries/ORM_Versioned.php +++ /dev/null @@ -1,143 +0,0 @@ -last_version = 1 + ($this->last_version === NULL ? $this->object['version'] : $this->last_version); - $this->__set('version', $this->last_version); - - parent::save(); - - if ($this->saved) - { - $data = array(); - foreach ($this->object as $key => $value) - { - if ($key === 'id') - continue; - - $data[$key] = $value; - } - $data[$this->foreign_key()] = $this->id; - - $this->db->insert($this->table_name.'_versions', $data); - } - - return $this; - } - - /** - * Loads previous version from current object - * - * @chainable - * @return ORM - */ - public function previous() - { - if ( ! $this->loaded) - return $this; - - $this->last_version = ($this->last_version === NULL) ? $this->object['version'] : $this->last_version; - $version = $this->last_version - 1; - - $query = $this->db - ->where($this->foreign_key(), $this->object[$this->primary_key]) - ->where('version', $version) - ->limit(1) - ->get($this->table_name.'_versions'); - - if ($query->count()) - { - $this->load_values($query->result(FALSE)->current()); - } - - return $this; - } - - /** - * Restores the object with data from stored version - * - * @param integer version number you want to restore - * @return ORM - */ - public function restore($version) - { - if ( ! $this->loaded) - return $this; - - $query = $this->db - ->where($this->foreign_key(), $this->object[$this->primary_key]) - ->where('version', $version) - ->limit(1) - ->get($this->table_name.'_versions'); - - if ($query->count()) - { - $row = $query->result(FALSE)->current(); - - foreach ($row as $key => $value) - { - if ($key === $this->primary_key OR $key === $this->foreign_key()) - { - // Do not overwrite the primary key - continue; - } - - if ($key === 'version') - { - // Always use the current version - $value = $this->version; - } - - $this->__set($key, $value); - } - - $this->save(); - } - - return $this; - } - - /** - * Overloads ORM::delete() to delete all versioned entries of current object - * and the object itself - * - * @param integer id of the object you want to delete - * @return ORM - */ - public function delete($id = NULL) - { - if ($id === NULL) - { - // Use the current object id - $id = $this->object[$this->primary_key]; - } - - if ($status = parent::delete($id)) - { - $this->db->where($this->foreign_key(), $id)->delete($this->table_name.'_versions'); - } - - return $status; - } - -} \ No newline at end of file diff --git a/kohana/libraries/Pagination.php b/kohana/libraries/Pagination.php deleted file mode 100644 index a8f7bb19..00000000 --- a/kohana/libraries/Pagination.php +++ /dev/null @@ -1,236 +0,0 @@ -initialize($config); - - Kohana::log('debug', 'Pagination Library initialized'); - } - - /** - * Sets config values. - * - * @throws Kohana_Exception - * @param array configuration settings - * @return void - */ - public function initialize($config = array()) - { - // Load config group - if (isset($config['group'])) - { - // Load and validate config group - if ( ! is_array($group_config = Kohana::config('pagination.'.$config['group']))) - throw new Kohana_Exception('pagination.undefined_group', $config['group']); - - // All pagination config groups inherit default config group - if ($config['group'] !== 'default') - { - // Load and validate default config group - if ( ! is_array($default_config = Kohana::config('pagination.default'))) - throw new Kohana_Exception('pagination.undefined_group', 'default'); - - // Merge config group with default config group - $group_config += $default_config; - } - - // Merge custom config items with config group - $config += $group_config; - } - - // Assign config values to the object - foreach ($config as $key => $value) - { - if (property_exists($this, $key)) - { - $this->$key = $value; - } - } - - // Clean view directory - $this->directory = trim($this->directory, '/').'/'; - - // Build generic URL with page in query string - if ($this->query_string !== '') - { - // Extract current page - $this->current_page = isset($_GET[$this->query_string]) ? (int) $_GET[$this->query_string] : 1; - - // Insert {page} placeholder - $_GET[$this->query_string] = '{page}'; - - // Create full URL - $base_url = ($this->base_url === '') ? Router::$current_uri : $this->base_url; - $this->url = url::site($base_url).'?'.str_replace('%7Bpage%7D', '{page}', http_build_query($_GET)); - - // Reset page number - $_GET[$this->query_string] = $this->current_page; - } - - // Build generic URL with page as URI segment - else - { - // Use current URI if no base_url set - $this->url = ($this->base_url === '') ? Router::$segments : explode('/', trim($this->base_url, '/')); - - // Convert uri 'label' to corresponding integer if needed - if (is_string($this->uri_segment)) - { - if (($key = array_search($this->uri_segment, $this->url)) === FALSE) - { - // If uri 'label' is not found, auto add it to base_url - $this->url[] = $this->uri_segment; - $this->uri_segment = count($this->url) + 1; - } - else - { - $this->uri_segment = $key + 2; - } - } - - // Insert {page} placeholder - $this->url[$this->uri_segment - 1] = '{page}'; - - // Create full URL - $this->url = url::site(implode('/', $this->url)).Router::$query_string; - - // Extract current page - $this->current_page = URI::instance()->segment($this->uri_segment); - } - - // Core pagination values - $this->total_items = (int) max(0, $this->total_items); - $this->items_per_page = (int) max(1, $this->items_per_page); - $this->total_pages = (int) ceil($this->total_items / $this->items_per_page); - $this->current_page = (int) min(max(1, $this->current_page), max(1, $this->total_pages)); - $this->current_first_item = (int) min((($this->current_page - 1) * $this->items_per_page) + 1, $this->total_items); - $this->current_last_item = (int) min($this->current_first_item + $this->items_per_page - 1, $this->total_items); - - // If there is no first/last/previous/next page, relative to the - // current page, value is set to FALSE. Valid page number otherwise. - $this->first_page = ($this->current_page === 1) ? FALSE : 1; - $this->last_page = ($this->current_page >= $this->total_pages) ? FALSE : $this->total_pages; - $this->previous_page = ($this->current_page > 1) ? $this->current_page - 1 : FALSE; - $this->next_page = ($this->current_page < $this->total_pages) ? $this->current_page + 1 : FALSE; - - // SQL values - $this->sql_offset = (int) ($this->current_page - 1) * $this->items_per_page; - $this->sql_limit = sprintf(' LIMIT %d OFFSET %d ', $this->items_per_page, $this->sql_offset); - } - - /** - * Generates the HTML for the chosen pagination style. - * - * @param string pagination style - * @return string pagination html - */ - public function render($style = NULL) - { - // Hide single page pagination - if ($this->auto_hide === TRUE AND $this->total_pages <= 1) - return ''; - - if ($style === NULL) - { - // Use default style - $style = $this->style; - } - - // Return rendered pagination view - return View::factory($this->directory.$style, get_object_vars($this))->render(); - } - - /** - * Magically converts Pagination object to string. - * - * @return string pagination html - */ - public function __toString() - { - return $this->render(); - } - - /** - * Magically gets a pagination variable. - * - * @param string variable key - * @return mixed variable value if the key is found - * @return void if the key is not found - */ - public function __get($key) - { - if (isset($this->$key)) - return $this->$key; - } - - /** - * Adds a secondary interface for accessing properties, e.g. $pagination->total_pages(). - * Note that $pagination->total_pages is the recommended way to access properties. - * - * @param string function name - * @return string - */ - public function __call($func, $args = NULL) - { - return $this->__get($func); - } - -} // End Pagination Class \ No newline at end of file diff --git a/kohana/libraries/Profiler.php b/kohana/libraries/Profiler.php deleted file mode 100644 index 47d82ace..00000000 --- a/kohana/libraries/Profiler.php +++ /dev/null @@ -1,271 +0,0 @@ -show OR (is_array($this->show) AND ! in_array($args[0], $this->show))) - return FALSE; - - // Class name - $class = 'Profiler_'.ucfirst($method); - - $class = new $class(); - - $this->profiles[$args[0]] = $class; - - return $class; - } - - /** - * Disables the profiler for this page only. - * Best used when profiler is autoloaded. - * - * @return void - */ - public function disable() - { - // Removes itself from the event queue - Event::clear('system.display', array($this, 'render')); - } - - /** - * Render the profiler. Output is added to the bottom of the page by default. - * - * @param boolean return the output if TRUE - * @return void|string - */ - public function render($return = FALSE) - { - $start = microtime(TRUE); - - $get = isset($_GET['profiler']) ? explode(',', $_GET['profiler']) : array(); - $this->show = empty($get) ? Kohana::config('profiler.show') : $get; - - Event::run('profiler.run', $this); - - $styles = ''; - foreach ($this->profiles as $profile) - { - $styles .= $profile->styles(); - } - - // Don't display if there's no profiles - if (empty($this->profiles)) - return; - - // Load the profiler view - $data = array - ( - 'profiles' => $this->profiles, - 'styles' => $styles, - 'execution_time' => microtime(TRUE) - $start - ); - $view = new View('kohana_profiler', $data); - - // Return rendered view if $return is TRUE - if ($return == TRUE) - return $view->render(); - - // Add profiler data to the output - if (stripos(Kohana::$output, '') !== FALSE) - { - // Closing body tag was found, insert the profiler data before it - Kohana::$output = str_ireplace('', $view->render().'', Kohana::$output); - } - else - { - // Append the profiler data to the output - Kohana::$output .= $view->render(); - } - } - - /** - * Benchmark times and memory usage from the Benchmark library. - * - * @return void - */ - public function benchmarks() - { - if ( ! $table = $this->table('benchmarks')) - return; - - $table->add_column(); - $table->add_column('kp-column kp-data'); - $table->add_column('kp-column kp-data'); - $table->add_column('kp-column kp-data'); - $table->add_row(array('Benchmarks', 'Time', 'Count', 'Memory'), 'kp-title', 'background-color: #FFE0E0'); - - $benchmarks = Benchmark::get(TRUE); - - // Moves the first benchmark (total execution time) to the end of the array - $benchmarks = array_slice($benchmarks, 1) + array_slice($benchmarks, 0, 1); - - text::alternate(); - foreach ($benchmarks as $name => $benchmark) - { - // Clean unique id from system benchmark names - $name = ucwords(str_replace(array('_', '-'), ' ', str_replace(SYSTEM_BENCHMARK.'_', '', $name))); - - $data = array($name, number_format($benchmark['time'], 3), $benchmark['count'], number_format($benchmark['memory'] / 1024 / 1024, 2).'MB'); - $class = text::alternate('', 'kp-altrow'); - - if ($name == 'Total Execution') - $class = 'kp-totalrow'; - - $table->add_row($data, $class); - } - } - - /** - * Database query benchmarks. - * - * @return void - */ - public function database() - { - if ( ! $table = $this->table('database')) - return; - - $table->add_column(); - $table->add_column('kp-column kp-data'); - $table->add_column('kp-column kp-data'); - $table->add_row(array('Queries', 'Time', 'Rows'), 'kp-title', 'background-color: #E0FFE0'); - - $queries = Database::$benchmarks; - - text::alternate(); - $total_time = $total_rows = 0; - foreach ($queries as $query) - { - $data = array($query['query'], number_format($query['time'], 3), $query['rows']); - $class = text::alternate('', 'kp-altrow'); - $table->add_row($data, $class); - $total_time += $query['time']; - $total_rows += $query['rows']; - } - - $data = array('Total: ' . count($queries), number_format($total_time, 3), $total_rows); - $table->add_row($data, 'kp-totalrow'); - } - - /** - * Session data. - * - * @return void - */ - public function session() - { - if (empty($_SESSION)) return; - - if ( ! $table = $this->table('session')) - return; - - $table->add_column('kp-name'); - $table->add_column(); - $table->add_row(array('Session', 'Value'), 'kp-title', 'background-color: #CCE8FB'); - - text::alternate(); - foreach($_SESSION as $name => $value) - { - if (is_object($value)) - { - $value = get_class($value).' [object]'; - } - - $data = array($name, $value); - $class = text::alternate('', 'kp-altrow'); - $table->add_row($data, $class); - } - } - - /** - * POST data. - * - * @return void - */ - public function post() - { - if (empty($_POST)) return; - - if ( ! $table = $this->table('post')) - return; - - $table->add_column('kp-name'); - $table->add_column(); - $table->add_row(array('POST', 'Value'), 'kp-title', 'background-color: #E0E0FF'); - - text::alternate(); - foreach($_POST as $name => $value) - { - $data = array($name, $value); - $class = text::alternate('', 'kp-altrow'); - $table->add_row($data, $class); - } - } - - /** - * Cookie data. - * - * @return void - */ - public function cookies() - { - if (empty($_COOKIE)) return; - - if ( ! $table = $this->table('cookies')) - return; - - $table->add_column('kp-name'); - $table->add_column(); - $table->add_row(array('Cookies', 'Value'), 'kp-title', 'background-color: #FFF4D7'); - - text::alternate(); - foreach($_COOKIE as $name => $value) - { - $data = array($name, $value); - $class = text::alternate('', 'kp-altrow'); - $table->add_row($data, $class); - } - } -} \ No newline at end of file diff --git a/kohana/libraries/Profiler_Table.php b/kohana/libraries/Profiler_Table.php deleted file mode 100644 index a0058a58..00000000 --- a/kohana/libraries/Profiler_Table.php +++ /dev/null @@ -1,69 +0,0 @@ -columns[] = array('class' => $class, 'style' => $style); - } - - /** - * Add row to table. - * - * @param array data to go in table cells - * @param string CSS class - * @param string CSS style - */ - public function add_row($data, $class = '', $style = '') - { - $this->rows[] = array('data' => $data, 'class' => $class, 'style' => $style); - } - - /** - * Render table. - * - * @return string - */ - public function render() - { - $data['rows'] = $this->rows; - $data['columns'] = $this->columns; - return View::factory('kohana_profiler_table', $data)->render(); - } -} \ No newline at end of file diff --git a/kohana/libraries/Router.php b/kohana/libraries/Router.php deleted file mode 100644 index 6dc9b10c..00000000 --- a/kohana/libraries/Router.php +++ /dev/null @@ -1,304 +0,0 @@ - 1) - { - // Custom routing - Router::$rsegments = Router::routed_uri(Router::$current_uri); - } - - // The routed URI is now complete - Router::$routed_uri = Router::$rsegments; - - // Routed segments will never be empty - Router::$rsegments = explode('/', Router::$rsegments); - - // Prepare to find the controller - $controller_path = ''; - $method_segment = NULL; - - // Paths to search - $paths = Kohana::include_paths(); - - foreach (Router::$rsegments as $key => $segment) - { - // Add the segment to the search path - $controller_path .= $segment; - - $found = FALSE; - foreach ($paths as $dir) - { - // Search within controllers only - $dir .= 'controllers/'; - - if (is_dir($dir.$controller_path) OR is_file($dir.$controller_path.EXT)) - { - // Valid path - $found = TRUE; - - // The controller must be a file that exists with the search path - if ($c = str_replace('\\', '/', realpath($dir.$controller_path.EXT)) - AND is_file($c) AND strpos($c, $dir) === 0) - { - // Set controller name - Router::$controller = $segment; - - // Change controller path - Router::$controller_path = $c; - - // Set the method segment - $method_segment = $key + 1; - - // Stop searching - break; - } - } - } - - if ($found === FALSE) - { - // Maximum depth has been reached, stop searching - break; - } - - // Add another slash - $controller_path .= '/'; - } - - if ($method_segment !== NULL AND isset(Router::$rsegments[$method_segment])) - { - // Set method - Router::$method = Router::$rsegments[$method_segment]; - - if (isset(Router::$rsegments[$method_segment + 1])) - { - // Set arguments - Router::$arguments = array_slice(Router::$rsegments, $method_segment + 1); - } - } - - // Last chance to set routing before a 404 is triggered - Event::run('system.post_routing'); - - if (Router::$controller === NULL) - { - // No controller was found, so no page can be rendered - Event::run('system.404'); - } - } - - /** - * Attempts to determine the current URI using CLI, GET, PATH_INFO, ORIG_PATH_INFO, or PHP_SELF. - * - * @return void - */ - public static function find_uri() - { - if (PHP_SAPI === 'cli') - { - // Command line requires a bit of hacking - if (isset($_SERVER['argv'][1])) - { - Router::$current_uri = $_SERVER['argv'][1]; - - // Remove GET string from segments - if (($query = strpos(Router::$current_uri, '?')) !== FALSE) - { - list (Router::$current_uri, $query) = explode('?', Router::$current_uri, 2); - - // Parse the query string into $_GET - parse_str($query, $_GET); - - // Convert $_GET to UTF-8 - $_GET = utf8::clean($_GET); - } - } - } - elseif (isset($_GET['kohana_uri'])) - { - // Use the URI defined in the query string - Router::$current_uri = $_GET['kohana_uri']; - - // Remove the URI from $_GET - unset($_GET['kohana_uri']); - - // Remove the URI from $_SERVER['QUERY_STRING'] - $_SERVER['QUERY_STRING'] = preg_replace('~\bkohana_uri\b[^&]*+&?~', '', $_SERVER['QUERY_STRING']); - } - elseif (isset($_SERVER['PATH_INFO']) AND $_SERVER['PATH_INFO']) - { - Router::$current_uri = $_SERVER['PATH_INFO']; - } - elseif (isset($_SERVER['ORIG_PATH_INFO']) AND $_SERVER['ORIG_PATH_INFO']) - { - Router::$current_uri = $_SERVER['ORIG_PATH_INFO']; - } - elseif (isset($_SERVER['PHP_SELF']) AND $_SERVER['PHP_SELF']) - { - Router::$current_uri = $_SERVER['PHP_SELF']; - - if (($strpos_fc = strpos(Router::$current_uri, KOHANA)) !== FALSE) - { - // Remove the front controller from the current uri - Router::$current_uri = substr(Router::$current_uri, $strpos_fc + strlen(KOHANA)); - } - } - - // Remove slashes from the start and end of the URI - Router::$current_uri = trim(Router::$current_uri, '/'); - - if (Router::$current_uri !== '') - { - if ($suffix = Kohana::config('core.url_suffix') AND strpos(Router::$current_uri, $suffix) !== FALSE) - { - // Remove the URL suffix - Router::$current_uri = preg_replace('#'.preg_quote($suffix).'$#u', '', Router::$current_uri); - - // Set the URL suffix - Router::$url_suffix = $suffix; - } - - // Reduce multiple slashes into single slashes - Router::$current_uri = preg_replace('#//+#', '/', Router::$current_uri); - } - } - - /** - * Generates routed URI from given URI. - * - * @param string URI to convert - * @return string Routed uri - */ - public static function routed_uri($uri) - { - if (Router::$routes === NULL) - { - // Load routes - Router::$routes = Kohana::config('routes'); - } - - // Prepare variables - $routed_uri = $uri = trim($uri, '/'); - - if (isset(Router::$routes[$uri])) - { - // Literal match, no need for regex - $routed_uri = Router::$routes[$uri]; - } - else - { - // Loop through the routes and see if anything matches - foreach (Router::$routes as $key => $val) - { - if ($key === '_default') continue; - - // Trim slashes - $key = trim($key, '/'); - $val = trim($val, '/'); - - if (preg_match('#^'.$key.'$#u', $uri)) - { - if (strpos($val, '$') !== FALSE) - { - // Use regex routing - $routed_uri = preg_replace('#^'.$key.'$#u', $val, $uri); - } - else - { - // Standard routing - $routed_uri = $val; - } - - // A valid route has been found - break; - } - } - } - - if (isset(Router::$routes[$routed_uri])) - { - // Check for double routing (without regex) - $routed_uri = Router::$routes[$routed_uri]; - } - - return trim($routed_uri, '/'); - } - -} // End Router \ No newline at end of file diff --git a/kohana/libraries/Session.php b/kohana/libraries/Session.php deleted file mode 100644 index e03f5dff..00000000 --- a/kohana/libraries/Session.php +++ /dev/null @@ -1,458 +0,0 @@ -input = Input::instance(); - - // This part only needs to be run once - if (Session::$instance === NULL) - { - // Load config - Session::$config = Kohana::config('session'); - - // Makes a mirrored array, eg: foo=foo - Session::$protect = array_combine(Session::$protect, Session::$protect); - - // Configure garbage collection - ini_set('session.gc_probability', (int) Session::$config['gc_probability']); - ini_set('session.gc_divisor', 100); - ini_set('session.gc_maxlifetime', (Session::$config['expiration'] == 0) ? 86400 : Session::$config['expiration']); - - // Create a new session - $this->create(); - - if (Session::$config['regenerate'] > 0 AND ($_SESSION['total_hits'] % Session::$config['regenerate']) === 0) - { - // Regenerate session id and update session cookie - $this->regenerate(); - } - else - { - // Always update session cookie to keep the session alive - cookie::set(Session::$config['name'], $_SESSION['session_id'], Session::$config['expiration']); - } - - // Close the session just before sending the headers, so that - // the session cookie(s) can be written. - Event::add('system.send_headers', array($this, 'write_close')); - - // Make sure that sessions are closed before exiting - register_shutdown_function(array($this, 'write_close')); - - // Singleton instance - Session::$instance = $this; - } - - Kohana::log('debug', 'Session Library initialized'); - } - - /** - * Get the session id. - * - * @return string - */ - public function id() - { - return $_SESSION['session_id']; - } - - /** - * Create a new session. - * - * @param array variables to set after creation - * @return void - */ - public function create($vars = NULL) - { - // Destroy any current sessions - $this->destroy(); - - if (Session::$config['driver'] !== 'native') - { - // Set driver name - $driver = 'Session_'.ucfirst(Session::$config['driver']).'_Driver'; - - // Load the driver - if ( ! Kohana::auto_load($driver)) - throw new Kohana_Exception('core.driver_not_found', Session::$config['driver'], get_class($this)); - - // Initialize the driver - Session::$driver = new $driver(); - - // Validate the driver - if ( ! (Session::$driver instanceof Session_Driver)) - throw new Kohana_Exception('core.driver_implements', Session::$config['driver'], get_class($this), 'Session_Driver'); - - // Register non-native driver as the session handler - session_set_save_handler - ( - array(Session::$driver, 'open'), - array(Session::$driver, 'close'), - array(Session::$driver, 'read'), - array(Session::$driver, 'write'), - array(Session::$driver, 'destroy'), - array(Session::$driver, 'gc') - ); - } - - // Validate the session name - if ( ! preg_match('~^(?=.*[a-z])[a-z0-9_]++$~iD', Session::$config['name'])) - throw new Kohana_Exception('session.invalid_session_name', Session::$config['name']); - - // Name the session, this will also be the name of the cookie - session_name(Session::$config['name']); - - // Set the session cookie parameters - session_set_cookie_params - ( - Session::$config['expiration'], - Kohana::config('cookie.path'), - Kohana::config('cookie.domain'), - Kohana::config('cookie.secure'), - Kohana::config('cookie.httponly') - ); - - // Start the session! - session_start(); - - // Put session_id in the session variable - $_SESSION['session_id'] = session_id(); - - // Set defaults - if ( ! isset($_SESSION['_kf_flash_'])) - { - $_SESSION['total_hits'] = 0; - $_SESSION['_kf_flash_'] = array(); - - $_SESSION['user_agent'] = Kohana::$user_agent; - $_SESSION['ip_address'] = $this->input->ip_address(); - } - - // Set up flash variables - Session::$flash =& $_SESSION['_kf_flash_']; - - // Increase total hits - $_SESSION['total_hits'] += 1; - - // Validate data only on hits after one - if ($_SESSION['total_hits'] > 1) - { - // Validate the session - foreach (Session::$config['validate'] as $valid) - { - switch ($valid) - { - // Check user agent for consistency - case 'user_agent': - if ($_SESSION[$valid] !== Kohana::$user_agent) - return $this->create(); - break; - - // Check ip address for consistency - case 'ip_address': - if ($_SESSION[$valid] !== $this->input->$valid()) - return $this->create(); - break; - - // Check expiration time to prevent users from manually modifying it - case 'expiration': - if (time() - $_SESSION['last_activity'] > ini_get('session.gc_maxlifetime')) - return $this->create(); - break; - } - } - } - - // Expire flash keys - $this->expire_flash(); - - // Update last activity - $_SESSION['last_activity'] = time(); - - // Set the new data - Session::set($vars); - } - - /** - * Regenerates the global session id. - * - * @return void - */ - public function regenerate() - { - if (Session::$config['driver'] === 'native') - { - // Generate a new session id - // Note: also sets a new session cookie with the updated id - session_regenerate_id(TRUE); - - // Update session with new id - $_SESSION['session_id'] = session_id(); - } - else - { - // Pass the regenerating off to the driver in case it wants to do anything special - $_SESSION['session_id'] = Session::$driver->regenerate(); - } - - // Get the session name - $name = session_name(); - - if (isset($_COOKIE[$name])) - { - // Change the cookie value to match the new session id to prevent "lag" - $_COOKIE[$name] = $_SESSION['session_id']; - } - } - - /** - * Destroys the current session. - * - * @return void - */ - public function destroy() - { - if (session_id() !== '') - { - // Get the session name - $name = session_name(); - - // Destroy the session - session_destroy(); - - // Re-initialize the array - $_SESSION = array(); - - // Delete the session cookie - cookie::delete($name); - } - } - - /** - * Runs the system.session_write event, then calls session_write_close. - * - * @return void - */ - public function write_close() - { - static $run; - - if ($run === NULL) - { - $run = TRUE; - - // Run the events that depend on the session being open - Event::run('system.session_write'); - - // Expire flash keys - $this->expire_flash(); - - // Close the session - session_write_close(); - } - } - - /** - * Set a session variable. - * - * @param string|array key, or array of values - * @param mixed value (if keys is not an array) - * @return void - */ - public function set($keys, $val = FALSE) - { - if (empty($keys)) - return FALSE; - - if ( ! is_array($keys)) - { - $keys = array($keys => $val); - } - - foreach ($keys as $key => $val) - { - if (isset(Session::$protect[$key])) - continue; - - // Set the key - $_SESSION[$key] = $val; - } - } - - /** - * Set a flash variable. - * - * @param string|array key, or array of values - * @param mixed value (if keys is not an array) - * @return void - */ - public function set_flash($keys, $val = FALSE) - { - if (empty($keys)) - return FALSE; - - if ( ! is_array($keys)) - { - $keys = array($keys => $val); - } - - foreach ($keys as $key => $val) - { - if ($key == FALSE) - continue; - - Session::$flash[$key] = 'new'; - Session::set($key, $val); - } - } - - /** - * Freshen one, multiple or all flash variables. - * - * @param string variable key(s) - * @return void - */ - public function keep_flash($keys = NULL) - { - $keys = ($keys === NULL) ? array_keys(Session::$flash) : func_get_args(); - - foreach ($keys as $key) - { - if (isset(Session::$flash[$key])) - { - Session::$flash[$key] = 'new'; - } - } - } - - /** - * Expires old flash data and removes it from the session. - * - * @return void - */ - public function expire_flash() - { - static $run; - - // Method can only be run once - if ($run === TRUE) - return; - - if ( ! empty(Session::$flash)) - { - foreach (Session::$flash as $key => $state) - { - if ($state === 'old') - { - // Flash has expired - unset(Session::$flash[$key], $_SESSION[$key]); - } - else - { - // Flash will expire - Session::$flash[$key] = 'old'; - } - } - } - - // Method has been run - $run = TRUE; - } - - /** - * Get a variable. Access to sub-arrays is supported with key.subkey. - * - * @param string variable key - * @param mixed default value returned if variable does not exist - * @return mixed Variable data if key specified, otherwise array containing all session data. - */ - public function get($key = FALSE, $default = FALSE) - { - if (empty($key)) - return $_SESSION; - - $result = isset($_SESSION[$key]) ? $_SESSION[$key] : Kohana::key_string($_SESSION, $key); - - return ($result === NULL) ? $default : $result; - } - - /** - * Get a variable, and delete it. - * - * @param string variable key - * @param mixed default value returned if variable does not exist - * @return mixed - */ - public function get_once($key, $default = FALSE) - { - $return = Session::get($key, $default); - Session::delete($key); - - return $return; - } - - /** - * Delete one or more variables. - * - * @param string variable key(s) - * @return void - */ - public function delete($keys) - { - $args = func_get_args(); - - foreach ($args as $key) - { - if (isset(Session::$protect[$key])) - continue; - - // Unset the key - unset($_SESSION[$key]); - } - } - -} // End Session Class diff --git a/kohana/libraries/URI.php b/kohana/libraries/URI.php deleted file mode 100644 index d9ccdcf7..00000000 --- a/kohana/libraries/URI.php +++ /dev/null @@ -1,279 +0,0 @@ -build_array(URI::$segments, $offset, $associative); - } - - /** - * Returns an array containing all the re-routed URI segments. - * - * @param integer rsegment offset - * @param boolean return an associative array - * @return array - */ - public function rsegment_array($offset = 0, $associative = FALSE) - { - return $this->build_array(URI::$rsegments, $offset, $associative); - } - - /** - * Returns an array containing all the URI arguments. - * - * @param integer segment offset - * @param boolean return an associative array - * @return array - */ - public function argument_array($offset = 0, $associative = FALSE) - { - return $this->build_array(URI::$arguments, $offset, $associative); - } - - /** - * Creates a simple or associative array from an array and an offset. - * Used as a helper for (r)segment_array and argument_array. - * - * @param array array to rebuild - * @param integer offset to start from - * @param boolean create an associative array - * @return array - */ - public function build_array($array, $offset = 0, $associative = FALSE) - { - // Prevent the keys from being improperly indexed - array_unshift($array, 0); - - // Slice the array, preserving the keys - $array = array_slice($array, $offset + 1, count($array) - 1, TRUE); - - if ($associative === FALSE) - return $array; - - $associative = array(); - $pairs = array_chunk($array, 2); - - foreach ($pairs as $pair) - { - // Add the key/value pair to the associative array - $associative[$pair[0]] = isset($pair[1]) ? $pair[1] : ''; - } - - return $associative; - } - - /** - * Returns the complete URI as a string. - * - * @return string - */ - public function string() - { - return URI::$current_uri; - } - - /** - * Magic method for converting an object to a string. - * - * @return string - */ - public function __toString() - { - return URI::$current_uri; - } - - /** - * Returns the total number of URI segments. - * - * @return integer - */ - public function total_segments() - { - return count(URI::$segments); - } - - /** - * Returns the total number of re-routed URI segments. - * - * @return integer - */ - public function total_rsegments() - { - return count(URI::$rsegments); - } - - /** - * Returns the total number of URI arguments. - * - * @return integer - */ - public function total_arguments() - { - return count(URI::$arguments); - } - - /** - * Returns the last URI segment. - * - * @param mixed default value returned if segment does not exist - * @return string - */ - public function last_segment($default = FALSE) - { - if (($end = $this->total_segments()) < 1) - return $default; - - return URI::$segments[$end - 1]; - } - - /** - * Returns the last re-routed URI segment. - * - * @param mixed default value returned if segment does not exist - * @return string - */ - public function last_rsegment($default = FALSE) - { - if (($end = $this->total_segments()) < 1) - return $default; - - return URI::$rsegments[$end - 1]; - } - - /** - * Returns the path to the current controller (not including the actual - * controller), as a web path. - * - * @param boolean return a full url, or only the path specifically - * @return string - */ - public function controller_path($full = TRUE) - { - return ($full) ? url::site(URI::$controller_path) : URI::$controller_path; - } - - /** - * Returns the current controller, as a web path. - * - * @param boolean return a full url, or only the controller specifically - * @return string - */ - public function controller($full = TRUE) - { - return ($full) ? url::site(URI::$controller_path.URI::$controller) : URI::$controller; - } - - /** - * Returns the current method, as a web path. - * - * @param boolean return a full url, or only the method specifically - * @return string - */ - public function method($full = TRUE) - { - return ($full) ? url::site(URI::$controller_path.URI::$controller.'/'.URI::$method) : URI::$method; - } - -} // End URI Class diff --git a/kohana/libraries/Validation.php b/kohana/libraries/Validation.php deleted file mode 100644 index 5a48bfc5..00000000 --- a/kohana/libraries/Validation.php +++ /dev/null @@ -1,826 +0,0 @@ -submitted = ! empty($array); - - parent::__construct($array, ArrayObject::ARRAY_AS_PROPS | ArrayObject::STD_PROP_LIST); - } - - /** - * Magic clone method, clears errors and messages. - * - * @return void - */ - public function __clone() - { - $this->errors = array(); - $this->messages = array(); - } - - /** - * Create a copy of the current validation rules and change the array. - * - * @chainable - * @param array new array to validate - * @return Validation - */ - public function copy(array $array) - { - $copy = clone $this; - - $copy->exchangeArray($array); - - return $copy; - } - - /** - * Test if the data has been submitted. - * - * @return boolean - */ - public function submitted($value = NULL) - { - if (is_bool($value)) - { - $this->submitted = $value; - } - - return $this->submitted; - } - - /** - * Returns an array of all the field names that have filters, rules, or callbacks. - * - * @return array - */ - public function field_names() - { - // All the fields that are being validated - $fields = array_keys(array_merge - ( - $this->pre_filters, - $this->rules, - $this->callbacks, - $this->post_filters - )); - - // Remove wildcard fields - $fields = array_diff($fields, array('*')); - - return $fields; - } - - /** - * Returns the array values of the current object. - * - * @return array - */ - public function as_array() - { - return $this->getArrayCopy(); - } - - /** - * Returns the ArrayObject values, removing all inputs without rules. - * To choose specific inputs, list the field name as arguments. - * - * @param boolean return only fields with filters, rules, and callbacks - * @return array - */ - public function safe_array() - { - // Load choices - $choices = func_get_args(); - $choices = empty($choices) ? NULL : array_combine($choices, $choices); - - // Get field names - $fields = $this->field_names(); - - $safe = array(); - foreach ($fields as $field) - { - if ($choices === NULL OR isset($choices[$field])) - { - if (isset($this[$field])) - { - $value = $this[$field]; - - if (is_object($value)) - { - // Convert the value back into an array - $value = $value->getArrayCopy(); - } - } - else - { - // Even if the field is not in this array, it must be set - $value = NULL; - } - - // Add the field to the array - $safe[$field] = $value; - } - } - - return $safe; - } - - /** - * Add additional rules that will forced, even for empty fields. All arguments - * passed will be appended to the list. - * - * @chainable - * @param string rule name - * @return object - */ - public function allow_empty_rules($rules) - { - // Any number of args are supported - $rules = func_get_args(); - - // Merge the allowed rules - $this->empty_rules = array_merge($this->empty_rules, $rules); - - return $this; - } - - /** - * Converts a filter, rule, or callback into a fully-qualified callback array. - * - * @return mixed - */ - protected function callback($callback) - { - if (is_string($callback)) - { - if (strpos($callback, '::') !== FALSE) - { - $callback = explode('::', $callback); - } - elseif (function_exists($callback)) - { - // No need to check if the callback is a method - $callback = $callback; - } - elseif (method_exists($this, $callback)) - { - // The callback exists in Validation - $callback = array($this, $callback); - } - elseif (method_exists('valid', $callback)) - { - // The callback exists in valid:: - $callback = array('valid', $callback); - } - } - - if ( ! is_callable($callback, FALSE)) - { - if (is_array($callback)) - { - if (is_object($callback[0])) - { - // Object instance syntax - $name = get_class($callback[0]).'->'.$callback[1]; - } - else - { - // Static class syntax - $name = $callback[0].'::'.$callback[1]; - } - } - else - { - // Function syntax - $name = $callback; - } - - throw new Kohana_Exception('validation.not_callable', $name); - } - - return $callback; - } - - /** - * Add a pre-filter to one or more inputs. Pre-filters are applied before - * rules or callbacks are executed. - * - * @chainable - * @param callback filter - * @param string fields to apply filter to, use TRUE for all fields - * @return object - */ - public function pre_filter($filter, $field = TRUE) - { - if ($field === TRUE OR $field === '*') - { - // Use wildcard - $fields = array('*'); - } - else - { - // Add the filter to specific inputs - $fields = func_get_args(); - $fields = array_slice($fields, 1); - } - - // Convert to a proper callback - $filter = $this->callback($filter); - - foreach ($fields as $field) - { - // Add the filter to specified field - $this->pre_filters[$field][] = $filter; - } - - return $this; - } - - /** - * Add a post-filter to one or more inputs. Post-filters are applied after - * rules and callbacks have been executed. - * - * @chainable - * @param callback filter - * @param string fields to apply filter to, use TRUE for all fields - * @return object - */ - public function post_filter($filter, $field = TRUE) - { - if ($field === TRUE) - { - // Use wildcard - $fields = array('*'); - } - else - { - // Add the filter to specific inputs - $fields = func_get_args(); - $fields = array_slice($fields, 1); - } - - // Convert to a proper callback - $filter = $this->callback($filter); - - foreach ($fields as $field) - { - // Add the filter to specified field - $this->post_filters[$field][] = $filter; - } - - return $this; - } - - /** - * Add rules to a field. Validation rules may only return TRUE or FALSE and - * can not manipulate the value of a field. - * - * @chainable - * @param string field name - * @param callback rules (one or more arguments) - * @return object - */ - public function add_rules($field, $rules) - { - // Get the rules - $rules = func_get_args(); - $rules = array_slice($rules, 1); - - if ($field === TRUE) - { - // Use wildcard - $field = '*'; - } - - foreach ($rules as $rule) - { - // Arguments for rule - $args = NULL; - - if (is_string($rule)) - { - if (preg_match('/^([^\[]++)\[(.+)\]$/', $rule, $matches)) - { - // Split the rule into the function and args - $rule = $matches[1]; - $args = preg_split('/(?array_fields[$field] = $field; - } - - // Convert to a proper callback - $rule = $this->callback($rule); - - // Add the rule, with args, to the field - $this->rules[$field][] = array($rule, $args); - } - - return $this; - } - - /** - * Add callbacks to a field. Callbacks must accept the Validation object - * and the input name. Callback returns are not processed. - * - * @chainable - * @param string field name - * @param callbacks callbacks (unlimited number) - * @return object - */ - public function add_callbacks($field, $callbacks) - { - // Get all callbacks as an array - $callbacks = func_get_args(); - $callbacks = array_slice($callbacks, 1); - - if ($field === TRUE) - { - // Use wildcard - $field = '*'; - } - - foreach ($callbacks as $callback) - { - // Convert to a proper callback - $callback = $this->callback($callback); - - // Add the callback to specified field - $this->callbacks[$field][] = $callback; - } - - return $this; - } - - /** - * Validate by processing pre-filters, rules, callbacks, and post-filters. - * All fields that have filters, rules, or callbacks will be initialized if - * they are undefined. Validation will only be run if there is data already - * in the array. - * - * @param object Validation object, used only for recursion - * @param object name of field for errors - * @return bool - */ - public function validate($object = NULL, $field_name = NULL) - { - if ($object === NULL) - { - // Use the current object - $object = $this; - } - - // Get all field names - $fields = $this->field_names(); - - // Copy the array from the object, to optimize multiple sets - $array = $this->getArrayCopy(); - - foreach ($fields as $field) - { - if ($field === '*') - { - // Ignore wildcard - continue; - } - - if ( ! isset($array[$field])) - { - if (isset($this->array_fields[$field])) - { - // This field must be an array - $array[$field] = array(); - } - else - { - $array[$field] = NULL; - } - } - } - - // Swap the array back into the object - $this->exchangeArray($array); - - // Get all defined field names - $fields = array_keys($array); - - foreach ($this->pre_filters as $field => $callbacks) - { - foreach ($callbacks as $callback) - { - if ($field === '*') - { - foreach ($fields as $f) - { - $this[$f] = is_array($this[$f]) ? array_map($callback, $this[$f]) : call_user_func($callback, $this[$f]); - } - } - else - { - $this[$field] = is_array($this[$field]) ? array_map($callback, $this[$field]) : call_user_func($callback, $this[$field]); - } - } - } - - if ($this->submitted === FALSE) - return FALSE; - - foreach ($this->rules as $field => $callbacks) - { - foreach ($callbacks as $callback) - { - // Separate the callback and arguments - list ($callback, $args) = $callback; - - // Function or method name of the rule - $rule = is_array($callback) ? $callback[1] : $callback; - - if ($field === '*') - { - foreach ($fields as $f) - { - // Note that continue, instead of break, is used when - // applying rules using a wildcard, so that all fields - // will be validated. - - if (isset($this->errors[$f])) - { - // Prevent other rules from being evaluated if an error has occurred - continue; - } - - if (empty($this[$f]) AND ! in_array($rule, $this->empty_rules)) - { - // This rule does not need to be processed on empty fields - continue; - } - - if ($args === NULL) - { - if ( ! call_user_func($callback, $this[$f])) - { - $this->errors[$f] = $rule; - - // Stop validating this field when an error is found - continue; - } - } - else - { - if ( ! call_user_func($callback, $this[$f], $args)) - { - $this->errors[$f] = $rule; - - // Stop validating this field when an error is found - continue; - } - } - } - } - else - { - if (isset($this->errors[$field])) - { - // Prevent other rules from being evaluated if an error has occurred - break; - } - - if ( ! in_array($rule, $this->empty_rules) AND ! $this->required($this[$field])) - { - // This rule does not need to be processed on empty fields - continue; - } - - if ($args === NULL) - { - if ( ! call_user_func($callback, $this[$field])) - { - $this->errors[$field] = $rule; - - // Stop validating this field when an error is found - break; - } - } - else - { - if ( ! call_user_func($callback, $this[$field], $args)) - { - $this->errors[$field] = $rule; - - // Stop validating this field when an error is found - break; - } - } - } - } - } - - foreach ($this->callbacks as $field => $callbacks) - { - foreach ($callbacks as $callback) - { - if ($field === '*') - { - foreach ($fields as $f) - { - // Note that continue, instead of break, is used when - // applying rules using a wildcard, so that all fields - // will be validated. - - if (isset($this->errors[$f])) - { - // Stop validating this field when an error is found - continue; - } - - call_user_func($callback, $this, $f); - } - } - else - { - if (isset($this->errors[$field])) - { - // Stop validating this field when an error is found - break; - } - - call_user_func($callback, $this, $field); - } - } - } - - foreach ($this->post_filters as $field => $callbacks) - { - foreach ($callbacks as $callback) - { - if ($field === '*') - { - foreach ($fields as $f) - { - $this[$f] = is_array($this[$f]) ? array_map($callback, $this[$f]) : call_user_func($callback, $this[$f]); - } - } - else - { - $this[$field] = is_array($this[$field]) ? array_map($callback, $this[$field]) : call_user_func($callback, $this[$field]); - } - } - } - - // Return TRUE if there are no errors - return $this->errors === array(); - } - - /** - * Add an error to an input. - * - * @chainable - * @param string input name - * @param string unique error name - * @return object - */ - public function add_error($field, $name) - { - $this->errors[$field] = $name; - - return $this; - } - - /** - * Sets or returns the message for an input. - * - * @chainable - * @param string input key - * @param string message to set - * @return string|object - */ - public function message($input = NULL, $message = NULL) - { - if ($message === NULL) - { - if ($input === NULL) - { - $messages = array(); - $keys = array_keys($this->messages); - - foreach ($keys as $input) - { - $messages[] = $this->message($input); - } - - return implode("\n", $messages); - } - - // Return nothing if no message exists - if (empty($this->messages[$input])) - return ''; - - // Return the HTML message string - return $this->messages[$input]; - } - else - { - $this->messages[$input] = $message; - } - - return $this; - } - - /** - * Return the errors array. - * - * @param boolean load errors from a lang file - * @return array - */ - public function errors($file = NULL) - { - if ($file === NULL) - { - return $this->errors; - } - else - { - - $errors = array(); - foreach ($this->errors as $input => $error) - { - // Key for this input error - $key = "$file.$input.$error"; - - if (($errors[$input] = Kohana::lang($key)) === $key) - { - // Get the default error message - $errors[$input] = Kohana::lang("$file.$input.default"); - } - } - - return $errors; - } - } - - /** - * Rule: required. Generates an error if the field has an empty value. - * - * @param mixed input value - * @return bool - */ - public function required($str) - { - if (is_object($str) AND $str instanceof ArrayObject) - { - // Get the array from the ArrayObject - $str = $str->getArrayCopy(); - } - - if (is_array($str)) - { - return ! empty($str); - } - else - { - return ! ($str === '' OR $str === NULL OR $str === FALSE); - } - } - - /** - * Rule: matches. Generates an error if the field does not match one or more - * other fields. - * - * @param mixed input value - * @param array input names to match against - * @return bool - */ - public function matches($str, array $inputs) - { - foreach ($inputs as $key) - { - if ($str !== (isset($this[$key]) ? $this[$key] : NULL)) - return FALSE; - } - - return TRUE; - } - - /** - * Rule: length. Generates an error if the field is too long or too short. - * - * @param mixed input value - * @param array minimum, maximum, or exact length to match - * @return bool - */ - public function length($str, array $length) - { - if ( ! is_string($str)) - return FALSE; - - $size = utf8::strlen($str); - $status = FALSE; - - if (count($length) > 1) - { - list ($min, $max) = $length; - - if ($size >= $min AND $size <= $max) - { - $status = TRUE; - } - } - else - { - $status = ($size === (int) $length[0]); - } - - return $status; - } - - /** - * Rule: depends_on. Generates an error if the field does not depend on one - * or more other fields. - * - * @param mixed field name - * @param array field names to check dependency - * @return bool - */ - public function depends_on($field, array $fields) - { - foreach ($fields as $depends_on) - { - if ( ! isset($this[$depends_on]) OR $this[$depends_on] == NULL) - return FALSE; - } - - return TRUE; - } - - /** - * Rule: chars. Generates an error if the field contains characters outside of the list. - * - * @param string field value - * @param array allowed characters - * @return bool - */ - public function chars($value, array $chars) - { - return ! preg_match('![^'.implode('', $chars).']!u', $value); - } - -} // End Validation diff --git a/kohana/libraries/View.php b/kohana/libraries/View.php deleted file mode 100644 index 2b8471c6..00000000 --- a/kohana/libraries/View.php +++ /dev/null @@ -1,309 +0,0 @@ -set_filename($name, $type); - } - - if (is_array($data) AND ! empty($data)) - { - // Preload data using array_merge, to allow user extensions - $this->kohana_local_data = array_merge($this->kohana_local_data, $data); - } - } - - /** - * Magic method access to test for view property - * - * @param string View property to test for - * @return boolean - */ - public function __isset($key = NULL) - { - return $this->is_set($key); - } - - /** - * Sets the view filename. - * - * @chainable - * @param string view filename - * @param string view file type - * @return object - */ - public function set_filename($name, $type = NULL) - { - if ($type == NULL) - { - // Load the filename and set the content type - $this->kohana_filename = Kohana::find_file('views', $name, TRUE); - $this->kohana_filetype = EXT; - } - else - { - // Check if the filetype is allowed by the configuration - if ( ! in_array($type, Kohana::config('view.allowed_filetypes'))) - throw new Kohana_Exception('core.invalid_filetype', $type); - - // Load the filename and set the content type - $this->kohana_filename = Kohana::find_file('views', $name, TRUE, $type); - $this->kohana_filetype = Kohana::config('mimes.'.$type); - - if ($this->kohana_filetype == NULL) - { - // Use the specified type - $this->kohana_filetype = $type; - } - } - - return $this; - } - - /** - * Sets a view variable. - * - * @param string|array name of variable or an array of variables - * @param mixed value when using a named variable - * @return object - */ - public function set($name, $value = NULL) - { - if (is_array($name)) - { - foreach ($name as $key => $value) - { - $this->__set($key, $value); - } - } - else - { - $this->__set($name, $value); - } - - return $this; - } - - /** - * Checks for a property existence in the view locally or globally. Unlike the built in __isset(), - * this method can take an array of properties to test simultaneously. - * - * @param string $key property name to test for - * @param array $key array of property names to test for - * @return boolean property test result - * @return array associative array of keys and boolean test result - */ - public function is_set( $key = FALSE ) - { - // Setup result; - $result = FALSE; - - // If key is an array - if (is_array($key)) - { - // Set the result to an array - $result = array(); - - // Foreach key - foreach ($key as $property) - { - // Set the result to an associative array - $result[$property] = (array_key_exists($property, $this->kohana_local_data) OR array_key_exists($property, View::$kohana_global_data)) ? TRUE : FALSE; - } - } - else - { - // Otherwise just check one property - $result = (array_key_exists($key, $this->kohana_local_data) OR array_key_exists($key, View::$kohana_global_data)) ? TRUE : FALSE; - } - - // Return the result - return $result; - } - - /** - * Sets a bound variable by reference. - * - * @param string name of variable - * @param mixed variable to assign by reference - * @return object - */ - public function bind($name, & $var) - { - $this->kohana_local_data[$name] =& $var; - - return $this; - } - - /** - * Sets a view global variable. - * - * @param string|array name of variable or an array of variables - * @param mixed value when using a named variable - * @return void - */ - public static function set_global($name, $value = NULL) - { - if (is_array($name)) - { - foreach ($name as $key => $value) - { - View::$kohana_global_data[$key] = $value; - } - } - else - { - View::$kohana_global_data[$name] = $value; - } - } - - /** - * Magically sets a view variable. - * - * @param string variable key - * @param string variable value - * @return void - */ - public function __set($key, $value) - { - $this->kohana_local_data[$key] = $value; - } - - /** - * Magically gets a view variable. - * - * @param string variable key - * @return mixed variable value if the key is found - * @return void if the key is not found - */ - public function &__get($key) - { - if (isset($this->kohana_local_data[$key])) - return $this->kohana_local_data[$key]; - - if (isset(View::$kohana_global_data[$key])) - return View::$kohana_global_data[$key]; - - if (isset($this->$key)) - return $this->$key; - } - - /** - * Magically converts view object to string. - * - * @return string - */ - public function __toString() - { - try - { - return $this->render(); - } - catch (Exception $e) - { - // Display the exception using its internal __toString method - return (string) $e; - } - } - - /** - * Renders a view. - * - * @param boolean set to TRUE to echo the output instead of returning it - * @param callback special renderer to pass the output through - * @return string if print is FALSE - * @return void if print is TRUE - */ - public function render($print = FALSE, $renderer = FALSE) - { - if (empty($this->kohana_filename)) - throw new Kohana_Exception('core.view_set_filename'); - - if (is_string($this->kohana_filetype)) - { - // Merge global and local data, local overrides global with the same name - $data = array_merge(View::$kohana_global_data, $this->kohana_local_data); - - // Load the view in the controller for access to $this - $output = Kohana::$instance->_kohana_load_view($this->kohana_filename, $data); - - if ($renderer !== FALSE AND is_callable($renderer, TRUE)) - { - // Pass the output through the user defined renderer - $output = call_user_func($renderer, $output); - } - - if ($print === TRUE) - { - // Display the output - echo $output; - return; - } - } - else - { - // Set the content type and size - header('Content-Type: '.$this->kohana_filetype[0]); - - if ($print === TRUE) - { - if ($file = fopen($this->kohana_filename, 'rb')) - { - // Display the output - fpassthru($file); - fclose($file); - } - return; - } - - // Fetch the file contents - $output = file_get_contents($this->kohana_filename); - } - - return $output; - } -} // End View \ No newline at end of file diff --git a/kohana/libraries/drivers/Cache.php b/kohana/libraries/drivers/Cache.php deleted file mode 100644 index 7c5e3c31..00000000 --- a/kohana/libraries/drivers/Cache.php +++ /dev/null @@ -1,40 +0,0 @@ -directory = $directory; - } - - /** - * Finds an array of files matching the given id or tag. - * - * @param string cache id or tag - * @param bool search for tags - * @return array of filenames matching the id or tag - */ - public function exists($id, $tag = FALSE) - { - if ($id === TRUE) - { - // Find all the files - return glob($this->directory.'*~*~*'); - } - elseif ($tag === TRUE) - { - // Find all the files that have the tag name - $paths = glob($this->directory.'*~*'.$id.'*~*'); - - // Find all tags matching the given tag - $files = array(); - foreach ($paths as $path) - { - // Split the files - $tags = explode('~', basename($path)); - - // Find valid tags - if (count($tags) !== 3 OR empty($tags[1])) - continue; - - // Split the tags by plus signs, used to separate tags - $tags = explode('+', $tags[1]); - - if (in_array($tag, $tags)) - { - // Add the file to the array, it has the requested tag - $files[] = $path; - } - } - - return $files; - } - else - { - // Find the file matching the given id - return glob($this->directory.$id.'~*'); - } - } - - /** - * Sets a cache item to the given data, tags, and lifetime. - * - * @param string cache id to set - * @param string data in the cache - * @param array cache tags - * @param integer lifetime - * @return bool - */ - public function set($id, $data, array $tags = NULL, $lifetime) - { - // Remove old cache files - $this->delete($id); - - // Cache File driver expects unix timestamp - if ($lifetime !== 0) - { - $lifetime += time(); - } - - if ( ! empty($tags)) - { - // Convert the tags into a string list - $tags = implode('+', $tags); - } - - // Write out a serialized cache - return (bool) file_put_contents($this->directory.$id.'~'.$tags.'~'.$lifetime, serialize($data)); - } - - /** - * Finds an array of ids for a given tag. - * - * @param string tag name - * @return array of ids that match the tag - */ - public function find($tag) - { - // An array will always be returned - $result = array(); - - if ($paths = $this->exists($tag, TRUE)) - { - // Length of directory name - $offset = strlen($this->directory); - - // Find all the files with the given tag - foreach ($paths as $path) - { - // Get the id from the filename - list($id, $junk) = explode('~', basename($path), 2); - - if (($data = $this->get($id)) !== FALSE) - { - // Add the result to the array - $result[$id] = $data; - } - } - } - - return $result; - } - - /** - * Fetches a cache item. This will delete the item if it is expired or if - * the hash does not match the stored hash. - * - * @param string cache id - * @return mixed|NULL - */ - public function get($id) - { - if ($file = $this->exists($id)) - { - // Use the first file - $file = current($file); - - // Validate that the cache has not expired - if ($this->expired($file)) - { - // Remove this cache, it has expired - $this->delete($id); - } - else - { - // Turn off errors while reading the file - $ER = error_reporting(0); - - if (($data = file_get_contents($file)) !== FALSE) - { - // Unserialize the data - $data = unserialize($data); - } - else - { - // Delete the data - unset($data); - } - - // Turn errors back on - error_reporting($ER); - } - } - - // Return NULL if there is no data - return isset($data) ? $data : NULL; - } - - /** - * Deletes a cache item by id or tag - * - * @param string cache id or tag, or TRUE for "all items" - * @param boolean use tags - * @return boolean - */ - public function delete($id, $tag = FALSE) - { - $files = $this->exists($id, $tag); - - if (empty($files)) - return FALSE; - - // Disable all error reporting while deleting - $ER = error_reporting(0); - - foreach ($files as $file) - { - // Remove the cache file - if ( ! unlink($file)) - Kohana::log('error', 'Cache: Unable to delete cache file: '.$file); - } - - // Turn on error reporting again - error_reporting($ER); - - return TRUE; - } - - /** - * Deletes all cache files that are older than the current time. - * - * @return void - */ - public function delete_expired() - { - if ($files = $this->exists(TRUE)) - { - // Disable all error reporting while deleting - $ER = error_reporting(0); - - foreach ($files as $file) - { - if ($this->expired($file)) - { - // The cache file has already expired, delete it - if ( ! unlink($file)) - Kohana::log('error', 'Cache: Unable to delete cache file: '.$file); - } - } - - // Turn on error reporting again - error_reporting($ER); - } - } - - /** - * Check if a cache file has expired by filename. - * - * @param string filename - * @return bool - */ - protected function expired($file) - { - // Get the expiration time - $expires = (int) substr($file, strrpos($file, '~') + 1); - - // Expirations of 0 are "never expire" - return ($expires !== 0 AND $expires <= time()); - } - -} // End Cache File Driver \ No newline at end of file diff --git a/kohana/libraries/drivers/Cache/Memcache.php b/kohana/libraries/drivers/Cache/Memcache.php deleted file mode 100644 index d801de9c..00000000 --- a/kohana/libraries/drivers/Cache/Memcache.php +++ /dev/null @@ -1,191 +0,0 @@ -backend = new Memcache; - $this->flags = Kohana::config('cache_memcache.compression') ? MEMCACHE_COMPRESSED : FALSE; - - $servers = Kohana::config('cache_memcache.servers'); - - foreach ($servers as $server) - { - // Make sure all required keys are set - $server += array('host' => '127.0.0.1', 'port' => 11211, 'persistent' => FALSE); - - // Add the server to the pool - $this->backend->addServer($server['host'], $server['port'], (bool) $server['persistent']) - or Kohana::log('error', 'Cache: Connection failed: '.$server['host']); - } - - // Load tags - self::$tags = $this->backend->get(self::TAGS_KEY); - - if ( ! is_array(self::$tags)) - { - // Create a new tags array - self::$tags = array(); - - // Tags have been created - self::$tags_changed = TRUE; - } - } - - public function __destruct() - { - if (self::$tags_changed === TRUE) - { - // Save the tags - $this->backend->set(self::TAGS_KEY, self::$tags, $this->flags, 0); - - // Tags are now unchanged - self::$tags_changed = FALSE; - } - } - - public function find($tag) - { - if (isset(self::$tags[$tag]) AND $results = $this->backend->get(self::$tags[$tag])) - { - // Return all the found caches - return $results; - } - else - { - // No matching tags - return array(); - } - } - - public function get($id) - { - return (($return = $this->backend->get($id)) === FALSE) ? NULL : $return; - } - - public function set($id, $data, array $tags = NULL, $lifetime) - { - if ( ! empty($tags)) - { - // Tags will be changed - self::$tags_changed = TRUE; - - foreach ($tags as $tag) - { - // Add the id to each tag - self::$tags[$tag][$id] = $id; - } - } - - if ($lifetime !== 0) - { - // Memcache driver expects unix timestamp - $lifetime += time(); - } - - // Set a new value - return $this->backend->set($id, $data, $this->flags, $lifetime); - } - - public function delete($id, $tag = FALSE) - { - // Tags will be changed - self::$tags_changed = TRUE; - - if ($id === TRUE) - { - if ($status = $this->backend->flush()) - { - // Remove all tags, all items have been deleted - self::$tags = array(); - - // We must sleep after flushing, or overwriting will not work! - // @see http://php.net/manual/en/function.memcache-flush.php#81420 - sleep(1); - } - - return $status; - } - elseif ($tag === TRUE) - { - if (isset(self::$tags[$id])) - { - foreach (self::$tags[$id] as $_id) - { - // Delete each id in the tag - $this->backend->delete($_id); - } - - // Delete the tag - unset(self::$tags[$id]); - } - - return TRUE; - } - else - { - foreach (self::$tags as $tag => $_ids) - { - if (isset(self::$tags[$tag][$id])) - { - // Remove the id from the tags - unset(self::$tags[$tag][$id]); - } - } - - return $this->backend->delete($id); - } - } - - public function delete_expired() - { - // Tags will be changed - self::$tags_changed = TRUE; - - foreach (self::$tags as $tag => $_ids) - { - foreach ($_ids as $id) - { - if ( ! $this->backend->get($id)) - { - // This id has disappeared, delete it from the tags - unset(self::$tags[$tag][$id]); - } - } - - if (empty(self::$tags[$tag])) - { - // The tag no longer has any valid ids - unset(self::$tags[$tag]); - } - } - - // Memcache handles garbage collection internally - return TRUE; - } - -} // End Cache Memcache Driver diff --git a/kohana/libraries/drivers/Cache/Sqlite.php b/kohana/libraries/drivers/Cache/Sqlite.php deleted file mode 100644 index 9458d851..00000000 --- a/kohana/libraries/drivers/Cache/Sqlite.php +++ /dev/null @@ -1,257 +0,0 @@ -db = new SQLiteDatabase($filename, '0666', $error); - - // Throw an exception if there's an error - if ( ! empty($error)) - throw new Kohana_Exception('cache.driver_error', sqlite_error_string($error)); - - $query = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'caches'"; - $tables = $this->db->query($query, SQLITE_BOTH, $error); - - // Throw an exception if there's an error - if ( ! empty($error)) - throw new Kohana_Exception('cache.driver_error', sqlite_error_string($error)); - - if ($tables->numRows() == 0) - { - Kohana::log('error', 'Cache: Initializing new SQLite cache database'); - - // Issue a CREATE TABLE command - $this->db->unbufferedQuery(Kohana::config('cache_sqlite.schema')); - } - } - - /** - * Checks if a cache id is already set. - * - * @param string cache id - * @return boolean - */ - public function exists($id) - { - // Find the id that matches - $query = "SELECT id FROM caches WHERE id = '$id'"; - - return ($this->db->query($query)->numRows() > 0); - } - - /** - * Sets a cache item to the given data, tags, and lifetime. - * - * @param string cache id to set - * @param string data in the cache - * @param array cache tags - * @param integer lifetime - * @return bool - */ - public function set($id, $data, array $tags = NULL, $lifetime) - { - // Serialize and escape the data - $data = sqlite_escape_string(serialize($data)); - - if ( ! empty($tags)) - { - // Escape the tags, adding brackets so the tag can be explicitly matched - $tags = sqlite_escape_string('<'.implode('>,<', $tags).'>'); - } - - // Cache Sqlite driver expects unix timestamp - if ($lifetime !== 0) - { - $lifetime += time(); - } - - $query = $this->exists($id) - ? "UPDATE caches SET tags = '$tags', expiration = '$lifetime', cache = '$data' WHERE id = '$id'" - : "INSERT INTO caches VALUES('$id', '$tags', '$lifetime', '$data')"; - - // Run the query - $this->db->unbufferedQuery($query, SQLITE_BOTH, $error); - - if ( ! empty($error)) - { - self::log_error($error); - return FALSE; - } - else - { - return TRUE; - } - } - - /** - * Finds an array of ids for a given tag. - * - * @param string tag name - * @return array of ids that match the tag - */ - public function find($tag) - { - $query = "SELECT id,cache FROM caches WHERE tags LIKE '%<{$tag}>%'"; - $query = $this->db->query($query, SQLITE_BOTH, $error); - - // An array will always be returned - $result = array(); - - if ( ! empty($error)) - { - self::log_error($error); - } - elseif ($query->numRows() > 0) - { - // Disable notices for unserializing - $ER = error_reporting(~E_NOTICE); - - while ($row = $query->fetchObject()) - { - // Add each cache to the array - $result[$row->id] = unserialize($row->cache); - } - - // Turn notices back on - error_reporting($ER); - } - - return $result; - } - - /** - * Fetches a cache item. This will delete the item if it is expired or if - * the hash does not match the stored hash. - * - * @param string cache id - * @return mixed|NULL - */ - public function get($id) - { - $query = "SELECT id, expiration, cache FROM caches WHERE id = '$id' LIMIT 0, 1"; - $query = $this->db->query($query, SQLITE_BOTH, $error); - - if ( ! empty($error)) - { - self::log_error($error); - } - elseif ($cache = $query->fetchObject()) - { - // Make sure the expiration is valid and that the hash matches - if ($cache->expiration != 0 AND $cache->expiration <= time()) - { - // Cache is not valid, delete it now - $this->delete($cache->id); - } - else - { - // Disable notices for unserializing - $ER = error_reporting(~E_NOTICE); - - // Return the valid cache data - $data = $cache->cache; - - // Turn notices back on - error_reporting($ER); - } - } - - // No valid cache found - return NULL; - } - - /** - * Deletes a cache item by id or tag - * - * @param string cache id or tag, or TRUE for "all items" - * @param bool delete a tag - * @return bool - */ - public function delete($id, $tag = FALSE) - { - if ($id === TRUE) - { - // Delete all caches - $where = '1'; - } - elseif ($tag === TRUE) - { - // Delete by tag - $where = "tags LIKE '%<{$id}>%'"; - } - else - { - // Delete by id - $where = "id = '$id'"; - } - - $this->db->unbufferedQuery('DELETE FROM caches WHERE '.$where, SQLITE_BOTH, $error); - - if ( ! empty($error)) - { - self::log_error($error); - return FALSE; - } - else - { - return TRUE; - } - } - - /** - * Deletes all cache files that are older than the current time. - */ - public function delete_expired() - { - // Delete all expired caches - $query = 'DELETE FROM caches WHERE expiration != 0 AND expiration <= '.time(); - - $this->db->unbufferedQuery($query); - - return TRUE; - } - -} // End Cache SQLite Driver \ No newline at end of file diff --git a/kohana/libraries/drivers/Cache/Xcache.php b/kohana/libraries/drivers/Cache/Xcache.php deleted file mode 100644 index 6254bbb6..00000000 --- a/kohana/libraries/drivers/Cache/Xcache.php +++ /dev/null @@ -1,119 +0,0 @@ -auth(); - $result = TRUE; - for ($i = 0, $max = xcache_count(XC_TYPE_VAR); $i < $max; $i++) - { - if (xcache_clear_cache(XC_TYPE_VAR, $i) !== NULL) - { - $result = FALSE; - break; - } - } - - // Undo the login - $this->auth(TRUE); - return $result; - } - - return TRUE; - } - - public function delete_expired() - { - return TRUE; - } - - private function auth($reverse = FALSE) - { - static $backup = array(); - - $keys = array('PHP_AUTH_USER', 'PHP_AUTH_PW'); - - foreach ($keys as $key) - { - if ($reverse) - { - if (isset($backup[$key])) - { - $_SERVER[$key] = $backup[$key]; - unset($backup[$key]); - } - else - { - unset($_SERVER[$key]); - } - } - else - { - $value = getenv($key); - - if ( ! empty($value)) - { - $backup[$key] = $value; - } - - $_SERVER[$key] = Kohana::config('cache_xcache.'.$key); - } - } - } - -} // End Cache Xcache Driver diff --git a/kohana/libraries/drivers/Captcha.php b/kohana/libraries/drivers/Captcha.php deleted file mode 100644 index a4343e19..00000000 --- a/kohana/libraries/drivers/Captcha.php +++ /dev/null @@ -1,227 +0,0 @@ -response = $this->generate_challenge(); - - // Store the correct Captcha response in a session - Event::add('system.post_controller', array($this, 'update_response_session')); - } - - /** - * Generate a new Captcha challenge. - * - * @return string the challenge answer - */ - abstract public function generate_challenge(); - - /** - * Output the Captcha challenge. - * - * @param boolean html output - * @return mixed the rendered Captcha (e.g. an image, riddle, etc.) - */ - abstract public function render($html); - - /** - * Stores the response for the current Captcha challenge in a session so it is available - * on the next page load for Captcha::valid(). This method is called after controller - * execution (in the system.post_controller event) in order not to overwrite itself too soon. - * - * @return void - */ - public function update_response_session() - { - Session::instance()->set('captcha_response', sha1(strtoupper($this->response))); - } - - /** - * Validates a Captcha response from a user. - * - * @param string captcha response - * @return boolean - */ - public function valid($response) - { - return (sha1(strtoupper($response)) === Session::instance()->get('captcha_response')); - } - - /** - * Returns the image type. - * - * @param string filename - * @return string|FALSE image type ("png", "gif" or "jpeg") - */ - public function image_type($filename) - { - switch (strtolower(substr(strrchr($filename, '.'), 1))) - { - case 'png': - return 'png'; - - case 'gif': - return 'gif'; - - case 'jpg': - case 'jpeg': - // Return "jpeg" and not "jpg" because of the GD2 function names - return 'jpeg'; - - default: - return FALSE; - } - } - - /** - * Creates an image resource with the dimensions specified in config. - * If a background image is supplied, the image dimensions are used. - * - * @throws Kohana_Exception if no GD2 support - * @param string path to the background image file - * @return void - */ - public function image_create($background = NULL) - { - // Check for GD2 support - if ( ! function_exists('imagegd2')) - throw new Kohana_Exception('captcha.requires_GD2'); - - // Create a new image (black) - $this->image = imagecreatetruecolor(Captcha::$config['width'], Captcha::$config['height']); - - // Use a background image - if ( ! empty($background)) - { - // Create the image using the right function for the filetype - $function = 'imagecreatefrom'.$this->image_type($background); - $this->background_image = $function($background); - - // Resize the image if needed - if (imagesx($this->background_image) !== Captcha::$config['width'] - OR imagesy($this->background_image) !== Captcha::$config['height']) - { - imagecopyresampled - ( - $this->image, $this->background_image, 0, 0, 0, 0, - Captcha::$config['width'], Captcha::$config['height'], - imagesx($this->background_image), imagesy($this->background_image) - ); - } - - // Free up resources - imagedestroy($this->background_image); - } - } - - /** - * Fills the background with a gradient. - * - * @param resource gd image color identifier for start color - * @param resource gd image color identifier for end color - * @param string direction: 'horizontal' or 'vertical', 'random' by default - * @return void - */ - public function image_gradient($color1, $color2, $direction = NULL) - { - $directions = array('horizontal', 'vertical'); - - // Pick a random direction if needed - if ( ! in_array($direction, $directions)) - { - $direction = $directions[array_rand($directions)]; - - // Switch colors - if (mt_rand(0, 1) === 1) - { - $temp = $color1; - $color1 = $color2; - $color2 = $temp; - } - } - - // Extract RGB values - $color1 = imagecolorsforindex($this->image, $color1); - $color2 = imagecolorsforindex($this->image, $color2); - - // Preparations for the gradient loop - $steps = ($direction === 'horizontal') ? Captcha::$config['width'] : Captcha::$config['height']; - - $r1 = ($color1['red'] - $color2['red']) / $steps; - $g1 = ($color1['green'] - $color2['green']) / $steps; - $b1 = ($color1['blue'] - $color2['blue']) / $steps; - - if ($direction === 'horizontal') - { - $x1 =& $i; - $y1 = 0; - $x2 =& $i; - $y2 = Captcha::$config['height']; - } - else - { - $x1 = 0; - $y1 =& $i; - $x2 = Captcha::$config['width']; - $y2 =& $i; - } - - // Execute the gradient loop - for ($i = 0; $i <= $steps; $i++) - { - $r2 = $color1['red'] - floor($i * $r1); - $g2 = $color1['green'] - floor($i * $g1); - $b2 = $color1['blue'] - floor($i * $b1); - $color = imagecolorallocate($this->image, $r2, $g2, $b2); - - imageline($this->image, $x1, $y1, $x2, $y2, $color); - } - } - - /** - * Returns the img html element or outputs the image to the browser. - * - * @param boolean html output - * @return mixed html string or void - */ - public function image_render($html) - { - // Output html element - if ($html) - return 'Captcha'; - - // Send the correct HTTP header - header('Content-Type: image/'.$this->image_type); - - // Pick the correct output function - $function = 'image'.$this->image_type; - $function($this->image); - - // Free up resources - imagedestroy($this->image); - } - -} // End Captcha Driver \ No newline at end of file diff --git a/kohana/libraries/drivers/Captcha/Alpha.php b/kohana/libraries/drivers/Captcha/Alpha.php deleted file mode 100644 index b3a9c9d7..00000000 --- a/kohana/libraries/drivers/Captcha/Alpha.php +++ /dev/null @@ -1,92 +0,0 @@ -image - $this->image_create(Captcha::$config['background']); - - // Add a random gradient - if (empty(Captcha::$config['background'])) - { - $color1 = imagecolorallocate($this->image, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100)); - $color2 = imagecolorallocate($this->image, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100)); - $this->image_gradient($color1, $color2); - } - - // Add a few random circles - for ($i = 0, $count = mt_rand(10, Captcha::$config['complexity'] * 3); $i < $count; $i++) - { - $color = imagecolorallocatealpha($this->image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255), mt_rand(80, 120)); - $size = mt_rand(5, Captcha::$config['height'] / 3); - imagefilledellipse($this->image, mt_rand(0, Captcha::$config['width']), mt_rand(0, Captcha::$config['height']), $size, $size, $color); - } - - // Calculate character font-size and spacing - $default_size = min(Captcha::$config['width'], Captcha::$config['height'] * 2) / strlen($this->response); - $spacing = (int) (Captcha::$config['width'] * 0.9 / strlen($this->response)); - - // Background alphabetic character attributes - $color_limit = mt_rand(96, 160); - $chars = 'ABEFGJKLPQRTVY'; - - // Draw each Captcha character with varying attributes - for ($i = 0, $strlen = strlen($this->response); $i < $strlen; $i++) - { - // Use different fonts if available - $font = Captcha::$config['fontpath'].Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])]; - - $angle = mt_rand(-40, 20); - // Scale the character size on image height - $size = $default_size / 10 * mt_rand(8, 12); - $box = imageftbbox($size, $angle, $font, $this->response[$i]); - - // Calculate character starting coordinates - $x = $spacing / 4 + $i * $spacing; - $y = Captcha::$config['height'] / 2 + ($box[2] - $box[5]) / 4; - - // Draw captcha text character - // Allocate random color, size and rotation attributes to text - $color = imagecolorallocate($this->image, mt_rand(150, 255), mt_rand(200, 255), mt_rand(0, 255)); - - // Write text character to image - imagefttext($this->image, $size, $angle, $x, $y, $color, $font, $this->response[$i]); - - // Draw "ghost" alphabetic character - $text_color = imagecolorallocatealpha($this->image, mt_rand($color_limit + 8, 255), mt_rand($color_limit + 8, 255), mt_rand($color_limit + 8, 255), mt_rand(70, 120)); - $char = substr($chars, mt_rand(0, 14), 1); - imagettftext($this->image, $size * 2, mt_rand(-45, 45), ($x - (mt_rand(5, 10))), ($y + (mt_rand(5, 10))), $text_color, $font, $char); - } - - // Output - return $this->image_render($html); - } - -} // End Captcha Alpha Driver Class \ No newline at end of file diff --git a/kohana/libraries/drivers/Captcha/Basic.php b/kohana/libraries/drivers/Captcha/Basic.php deleted file mode 100644 index d212e72c..00000000 --- a/kohana/libraries/drivers/Captcha/Basic.php +++ /dev/null @@ -1,81 +0,0 @@ -image - $this->image_create(Captcha::$config['background']); - - // Add a random gradient - if (empty(Captcha::$config['background'])) - { - $color1 = imagecolorallocate($this->image, mt_rand(200, 255), mt_rand(200, 255), mt_rand(150, 255)); - $color2 = imagecolorallocate($this->image, mt_rand(200, 255), mt_rand(200, 255), mt_rand(150, 255)); - $this->image_gradient($color1, $color2); - } - - // Add a few random lines - for ($i = 0, $count = mt_rand(5, Captcha::$config['complexity'] * 4); $i < $count; $i++) - { - $color = imagecolorallocatealpha($this->image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(100, 255), mt_rand(50, 120)); - imageline($this->image, mt_rand(0, Captcha::$config['width']), 0, mt_rand(0, Captcha::$config['width']), Captcha::$config['height'], $color); - } - - // Calculate character font-size and spacing - $default_size = min(Captcha::$config['width'], Captcha::$config['height'] * 2) / (strlen($this->response) + 1); - $spacing = (int) (Captcha::$config['width'] * 0.9 / strlen($this->response)); - - // Draw each Captcha character with varying attributes - for ($i = 0, $strlen = strlen($this->response); $i < $strlen; $i++) - { - // Use different fonts if available - $font = Captcha::$config['fontpath'].Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])]; - - // Allocate random color, size and rotation attributes to text - $color = imagecolorallocate($this->image, mt_rand(0, 150), mt_rand(0, 150), mt_rand(0, 150)); - $angle = mt_rand(-40, 20); - - // Scale the character size on image height - $size = $default_size / 10 * mt_rand(8, 12); - $box = imageftbbox($size, $angle, $font, $this->response[$i]); - - // Calculate character starting coordinates - $x = $spacing / 4 + $i * $spacing; - $y = Captcha::$config['height'] / 2 + ($box[2] - $box[5]) / 4; - - // Write text character to image - imagefttext($this->image, $size, $angle, $x, $y, $color, $font, $this->response[$i]); - } - - // Output - return $this->image_render($html); - } - -} // End Captcha Basic Driver Class \ No newline at end of file diff --git a/kohana/libraries/drivers/Captcha/Black.php b/kohana/libraries/drivers/Captcha/Black.php deleted file mode 100644 index 6a2e2226..00000000 --- a/kohana/libraries/drivers/Captcha/Black.php +++ /dev/null @@ -1,72 +0,0 @@ -image_create(Captcha::$config['background']); - - // Add random white/gray arcs, amount depends on complexity setting - $count = (Captcha::$config['width'] + Captcha::$config['height']) / 2; - $count = $count / 5 * min(10, Captcha::$config['complexity']); - for ($i = 0; $i < $count; $i++) - { - imagesetthickness($this->image, mt_rand(1, 2)); - $color = imagecolorallocatealpha($this->image, 255, 255, 255, mt_rand(0, 120)); - imagearc($this->image, mt_rand(-Captcha::$config['width'], Captcha::$config['width']), mt_rand(-Captcha::$config['height'], Captcha::$config['height']), mt_rand(-Captcha::$config['width'], Captcha::$config['width']), mt_rand(-Captcha::$config['height'], Captcha::$config['height']), mt_rand(0, 360), mt_rand(0, 360), $color); - } - - // Use different fonts if available - $font = Captcha::$config['fontpath'].Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])]; - - // Draw the character's white shadows - $size = (int) min(Captcha::$config['height'] / 2, Captcha::$config['width'] * 0.8 / strlen($this->response)); - $angle = mt_rand(-15 + strlen($this->response), 15 - strlen($this->response)); - $x = mt_rand(1, Captcha::$config['width'] * 0.9 - $size * strlen($this->response)); - $y = ((Captcha::$config['height'] - $size) / 2) + $size; - $color = imagecolorallocate($this->image, 255, 255, 255); - imagefttext($this->image, $size, $angle, $x + 1, $y + 1, $color, $font, $this->response); - - // Add more shadows for lower complexities - (Captcha::$config['complexity'] < 10) and imagefttext($this->image, $size, $angle, $x - 1, $y - 1, $color, $font , $this->response); - (Captcha::$config['complexity'] < 8) and imagefttext($this->image, $size, $angle, $x - 2, $y + 2, $color, $font , $this->response); - (Captcha::$config['complexity'] < 6) and imagefttext($this->image, $size, $angle, $x + 2, $y - 2, $color, $font , $this->response); - (Captcha::$config['complexity'] < 4) and imagefttext($this->image, $size, $angle, $x + 3, $y + 3, $color, $font , $this->response); - (Captcha::$config['complexity'] < 2) and imagefttext($this->image, $size, $angle, $x - 3, $y - 3, $color, $font , $this->response); - - // Finally draw the foreground characters - $color = imagecolorallocate($this->image, 0, 0, 0); - imagefttext($this->image, $size, $angle, $x, $y, $color, $font, $this->response); - - // Output - return $this->image_render($html); - } - -} // End Captcha Black Driver Class \ No newline at end of file diff --git a/kohana/libraries/drivers/Captcha/Math.php b/kohana/libraries/drivers/Captcha/Math.php deleted file mode 100644 index 4ac20248..00000000 --- a/kohana/libraries/drivers/Captcha/Math.php +++ /dev/null @@ -1,61 +0,0 @@ -math_exercice = implode(' + ', $numbers).' = '; - - // Return the answer - return array_sum($numbers); - } - - /** - * Outputs the Captcha riddle. - * - * @param boolean html output - * @return mixed - */ - public function render($html) - { - return $this->math_exercice; - } - -} // End Captcha Math Driver Class \ No newline at end of file diff --git a/kohana/libraries/drivers/Captcha/Riddle.php b/kohana/libraries/drivers/Captcha/Riddle.php deleted file mode 100644 index 765eeaad..00000000 --- a/kohana/libraries/drivers/Captcha/Riddle.php +++ /dev/null @@ -1,47 +0,0 @@ -riddle = $riddle[0]; - - // Return the answer - return $riddle[1]; - } - - /** - * Outputs the Captcha riddle. - * - * @param boolean html output - * @return mixed - */ - public function render($html) - { - return $this->riddle; - } - -} // End Captcha Riddle Driver Class \ No newline at end of file diff --git a/kohana/libraries/drivers/Captcha/Word.php b/kohana/libraries/drivers/Captcha/Word.php deleted file mode 100644 index 856bd9b4..00000000 --- a/kohana/libraries/drivers/Captcha/Word.php +++ /dev/null @@ -1,37 +0,0 @@ -escape_table($table).' WHERE '.implode(' ', $where); - } - - /** - * Builds an UPDATE query. - * - * @param string table name - * @param array key => value pairs - * @param array where clause - * @return string - */ - public function update($table, $values, $where) - { - foreach ($values as $key => $val) - { - $valstr[] = $this->escape_column($key).' = '.$val; - } - return 'UPDATE '.$this->escape_table($table).' SET '.implode(', ', $valstr).' WHERE '.implode(' ',$where); - } - - /** - * Set the charset using 'SET NAMES '. - * - * @param string character set to use - */ - public function set_charset($charset) - { - throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); - } - - /** - * Wrap the tablename in backticks, has support for: table.field syntax. - * - * @param string table name - * @return string - */ - abstract public function escape_table($table); - - /** - * Escape a column/field name, has support for special commands. - * - * @param string column name - * @return string - */ - abstract public function escape_column($column); - - /** - * Builds a WHERE portion of a query. - * - * @param mixed key - * @param string value - * @param string type - * @param int number of where clauses - * @param boolean escape the value - * @return string - */ - public function where($key, $value, $type, $num_wheres, $quote) - { - $prefix = ($num_wheres == 0) ? '' : $type; - - if ($quote === -1) - { - $value = ''; - } - else - { - if ($value === NULL) - { - if ( ! $this->has_operator($key)) - { - $key .= ' IS'; - } - - $value = ' NULL'; - } - elseif (is_bool($value)) - { - if ( ! $this->has_operator($key)) - { - $key .= ' ='; - } - - $value = ($value == TRUE) ? ' 1' : ' 0'; - } - else - { - if ( ! $this->has_operator($key) AND ! empty($key)) - { - $key = $this->escape_column($key).' ='; - } - else - { - preg_match('/^(.+?)([<>!=]+|\bIS(?:\s+NULL))\s*$/i', $key, $matches); - if (isset($matches[1]) AND isset($matches[2])) - { - $key = $this->escape_column(trim($matches[1])).' '.trim($matches[2]); - } - } - - $value = ' '.(($quote == TRUE) ? $this->escape($value) : $value); - } - } - - return $prefix.$key.$value; - } - - /** - * Builds a LIKE portion of a query. - * - * @param mixed field name - * @param string value to match with field - * @param boolean add wildcards before and after the match - * @param string clause type (AND or OR) - * @param int number of likes - * @return string - */ - public function like($field, $match, $auto, $type, $num_likes) - { - $prefix = ($num_likes == 0) ? '' : $type; - - $match = $this->escape_str($match); - - if ($auto === TRUE) - { - // Add the start and end quotes - $match = '%'.str_replace('%', '\\%', $match).'%'; - } - - return $prefix.' '.$this->escape_column($field).' LIKE \''.$match . '\''; - } - - /** - * Builds a NOT LIKE portion of a query. - * - * @param mixed field name - * @param string value to match with field - * @param string clause type (AND or OR) - * @param int number of likes - * @return string - */ - public function notlike($field, $match, $auto, $type, $num_likes) - { - $prefix = ($num_likes == 0) ? '' : $type; - - $match = $this->escape_str($match); - - if ($auto === TRUE) - { - // Add the start and end quotes - $match = '%'.$match.'%'; - } - - return $prefix.' '.$this->escape_column($field).' NOT LIKE \''.$match.'\''; - } - - /** - * Builds a REGEX portion of a query. - * - * @param string field name - * @param string value to match with field - * @param string clause type (AND or OR) - * @param integer number of regexes - * @return string - */ - public function regex($field, $match, $type, $num_regexs) - { - throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); - } - - /** - * Builds a NOT REGEX portion of a query. - * - * @param string field name - * @param string value to match with field - * @param string clause type (AND or OR) - * @param integer number of regexes - * @return string - */ - public function notregex($field, $match, $type, $num_regexs) - { - throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); - } - - /** - * Builds an INSERT query. - * - * @param string table name - * @param array keys - * @param array values - * @return string - */ - public function insert($table, $keys, $values) - { - // Escape the column names - foreach ($keys as $key => $value) - { - $keys[$key] = $this->escape_column($value); - } - return 'INSERT INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')'; - } - - /** - * Builds a MERGE portion of a query. - * - * @param string table name - * @param array keys - * @param array values - * @return string - */ - public function merge($table, $keys, $values) - { - throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); - } - - /** - * Builds a LIMIT portion of a query. - * - * @param integer limit - * @param integer offset - * @return string - */ - abstract public function limit($limit, $offset = 0); - - /** - * Creates a prepared statement. - * - * @param string SQL query - * @return Database_Stmt - */ - public function stmt_prepare($sql = '') - { - throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); - } - - /** - * Compiles the SELECT statement. - * Generates a query string based on which functions were used. - * Should not be called directly, the get() function calls it. - * - * @param array select query values - * @return string - */ - abstract public function compile_select($database); - - /** - * Determines if the string has an arithmetic operator in it. - * - * @param string string to check - * @return boolean - */ - public function has_operator($str) - { - return (bool) preg_match('/[<>!=]|\sIS(?:\s+NOT\s+)?\b|BETWEEN/i', trim($str)); - } - - /** - * Escapes any input value. - * - * @param mixed value to escape - * @return string - */ - public function escape($value) - { - if ( ! $this->db_config['escape']) - return $value; - - switch (gettype($value)) - { - case 'string': - $value = '\''.$this->escape_str($value).'\''; - break; - case 'boolean': - $value = (int) $value; - break; - case 'double': - // Convert to non-locale aware float to prevent possible commas - $value = sprintf('%F', $value); - break; - default: - $value = ($value === NULL) ? 'NULL' : $value; - break; - } - - return (string) $value; - } - - /** - * Escapes a string for a query. - * - * @param mixed value to escape - * @return string - */ - abstract public function escape_str($str); - - /** - * Lists all tables in the database. - * - * @return array - */ - abstract public function list_tables(); - - /** - * Lists all fields in a table. - * - * @param string table name - * @return array - */ - abstract function list_fields($table); - - /** - * Returns the last database error. - * - * @return string - */ - abstract public function show_error(); - - /** - * Returns field data about a table. - * - * @param string table name - * @return array - */ - abstract public function field_data($table); - - /** - * Fetches SQL type information about a field, in a generic format. - * - * @param string field datatype - * @return array - */ - protected function sql_type($str) - { - static $sql_types; - - if ($sql_types === NULL) - { - // Load SQL data types - $sql_types = Kohana::config('sql_types'); - } - - $str = strtolower(trim($str)); - - if (($open = strpos($str, '(')) !== FALSE) - { - // Find closing bracket - $close = strpos($str, ')', $open) - 1; - - // Find the type without the size - $type = substr($str, 0, $open); - } - else - { - // No length - $type = $str; - } - - empty($sql_types[$type]) and exit - ( - 'Unknown field type: '.$type.'. '. - 'Please report this: http://trac.kohanaphp.com/newticket' - ); - - // Fetch the field definition - $field = $sql_types[$type]; - - switch ($field['type']) - { - case 'string': - case 'float': - if (isset($close)) - { - // Add the length to the field info - $field['length'] = substr($str, $open + 1, $close - $open); - } - break; - case 'int': - // Add unsigned value - $field['unsigned'] = (strpos($str, 'unsigned') !== FALSE); - break; - } - - return $field; - } - - /** - * Clears the internal query cache. - * - * @param string SQL query - */ - public function clear_cache($sql = NULL) - { - if (empty($sql)) - { - $this->query_cache = array(); - } - else - { - unset($this->query_cache[$this->query_hash($sql)]); - } - - Kohana::log('debug', 'Database cache cleared: '.get_class($this)); - } - - /** - * Creates a hash for an SQL query string. Replaces newlines with spaces, - * trims, and hashes. - * - * @param string SQL query - * @return string - */ - protected function query_hash($sql) - { - return sha1(str_replace("\n", ' ', trim($sql))); - } - -} // End Database Driver Interface - -/** - * Database_Result - * - */ -abstract class Database_Result implements ArrayAccess, Iterator, Countable { - - // Result resource, insert id, and SQL - protected $result; - protected $insert_id; - protected $sql; - - // Current and total rows - protected $current_row = 0; - protected $total_rows = 0; - - // Fetch function and return type - protected $fetch_type; - protected $return_type; - - /** - * Returns the SQL used to fetch the result. - * - * @return string - */ - public function sql() - { - return $this->sql; - } - - /** - * Returns the insert id from the result. - * - * @return mixed - */ - public function insert_id() - { - return $this->insert_id; - } - - /** - * Prepares the query result. - * - * @param boolean return rows as objects - * @param mixed type - * @return Database_Result - */ - abstract function result($object = TRUE, $type = FALSE); - - /** - * Builds an array of query results. - * - * @param boolean return rows as objects - * @param mixed type - * @return array - */ - abstract function result_array($object = NULL, $type = FALSE); - - /** - * Gets the fields of an already run query. - * - * @return array - */ - abstract public function list_fields(); - - /** - * Seek to an offset in the results. - * - * @return boolean - */ - abstract public function seek($offset); - - /** - * Countable: count - */ - public function count() - { - return $this->total_rows; - } - - /** - * ArrayAccess: offsetExists - */ - public function offsetExists($offset) - { - if ($this->total_rows > 0) - { - $min = 0; - $max = $this->total_rows - 1; - - return ! ($offset < $min OR $offset > $max); - } - - return FALSE; - } - - /** - * ArrayAccess: offsetGet - */ - public function offsetGet($offset) - { - if ( ! $this->seek($offset)) - return FALSE; - - // Return the row by calling the defined fetching callback - return call_user_func($this->fetch_type, $this->result, $this->return_type); - } - - /** - * ArrayAccess: offsetSet - * - * @throws Kohana_Database_Exception - */ - final public function offsetSet($offset, $value) - { - throw new Kohana_Database_Exception('database.result_read_only'); - } - - /** - * ArrayAccess: offsetUnset - * - * @throws Kohana_Database_Exception - */ - final public function offsetUnset($offset) - { - throw new Kohana_Database_Exception('database.result_read_only'); - } - - /** - * Iterator: current - */ - public function current() - { - return $this->offsetGet($this->current_row); - } - - /** - * Iterator: key - */ - public function key() - { - return $this->current_row; - } - - /** - * Iterator: next - */ - public function next() - { - ++$this->current_row; - return $this; - } - - /** - * Iterator: prev - */ - public function prev() - { - --$this->current_row; - return $this; - } - - /** - * Iterator: rewind - */ - public function rewind() - { - $this->current_row = 0; - return $this; - } - - /** - * Iterator: valid - */ - public function valid() - { - return $this->offsetExists($this->current_row); - } - -} // End Database Result Interface diff --git a/kohana/libraries/drivers/Database/Mssql.php b/kohana/libraries/drivers/Database/Mssql.php deleted file mode 100644 index 6947679a..00000000 --- a/kohana/libraries/drivers/Database/Mssql.php +++ /dev/null @@ -1,462 +0,0 @@ -db_config = $config; - - Kohana::log('debug', 'MSSQL Database Driver Initialized'); - } - - /** - * Closes the database connection. - */ - public function __destruct() - { - is_resource($this->link) and mssql_close($this->link); - } - - /** - * Make the connection - * - * @return return connection - */ - public function connect() - { - // Check if link already exists - if (is_resource($this->link)) - return $this->link; - - // Import the connect variables - extract($this->db_config['connection']); - - // Persistent connections enabled? - $connect = ($this->db_config['persistent'] == TRUE) ? 'mssql_pconnect' : 'mssql_connect'; - - // Build the connection info - $host = isset($host) ? $host : $socket; - - // Windows uses a comma instead of a colon - $port = (isset($port) AND is_string($port)) ? (KOHANA_IS_WIN ? ',' : ':').$port : ''; - - // Make the connection and select the database - if (($this->link = $connect($host.$port, $user, $pass, TRUE)) AND mssql_select_db($database, $this->link)) - { - /* This is being removed so I can use it, will need to come up with a more elegant workaround in the future... - * - if ($charset = $this->db_config['character_set']) - { - $this->set_charset($charset); - } - */ - - // Clear password after successful connect - $this->db_config['connection']['pass'] = NULL; - - return $this->link; - } - - return FALSE; - } - - public function query($sql) - { - // Only cache if it's turned on, and only cache if it's not a write statement - if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET)\b#i', $sql)) - { - $hash = $this->query_hash($sql); - - if ( ! isset($this->query_cache[$hash])) - { - // Set the cached object - $this->query_cache[$hash] = new Mssql_Result(mssql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); - } - else - { - // Rewind cached result - $this->query_cache[$hash]->rewind(); - } - - // Return the cached query - return $this->query_cache[$hash]; - } - - return new Mssql_Result(mssql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); - } - - public function escape_table($table) - { - if (stripos($table, ' AS ') !== FALSE) - { - // Force 'AS' to uppercase - $table = str_ireplace(' AS ', ' AS ', $table); - - // Runs escape_table on both sides of an AS statement - $table = array_map(array($this, __FUNCTION__), explode(' AS ', $table)); - - // Re-create the AS statement - return implode(' AS ', $table); - } - return '['.str_replace('.', '[.]', $table).']'; - } - - public function escape_column($column) - { - if (!$this->db_config['escape']) - return $column; - - if ($column == '*') - return $column; - - // This matches any functions we support to SELECT. - if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches)) - { - if ( count($matches) == 3) - { - return $matches[1].'('.$this->escape_column($matches[2]).')'; - } - else if ( count($matches) == 5) - { - return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]); - } - } - - // This matches any modifiers we support to SELECT. - if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column)) - { - if (stripos($column, ' AS ') !== FALSE) - { - // Force 'AS' to uppercase - $column = str_ireplace(' AS ', ' AS ', $column); - - // Runs escape_column on both sides of an AS statement - $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column)); - - // Re-create the AS statement - return implode(' AS ', $column); - } - - return preg_replace('/[^.*]+/', '[$0]', $column); - } - - $parts = explode(' ', $column); - $column = ''; - - for ($i = 0, $c = count($parts); $i < $c; $i++) - { - // The column is always last - if ($i == ($c - 1)) - { - $column .= preg_replace('/[^.*]+/', '[$0]', $parts[$i]); - } - else // otherwise, it's a modifier - { - $column .= $parts[$i].' '; - } - } - return $column; - } - - /** - * Limit in SQL Server 2000 only uses the keyword - * 'TOP'; 2007 may have an offset keyword, but - * I am unsure - for pagination style limit,offset - * functionality, a fancy query needs to be built. - * - * @param unknown_type $limit - * @return unknown - */ - public function limit($limit, $offset=null) - { - return 'TOP '.$limit; - } - - public function compile_select($database) - { - $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT '; - $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*'; - - if (count($database['from']) > 0) - { - // Escape the tables - $froms = array(); - foreach ($database['from'] as $from) - $froms[] = $this->escape_column($from); - $sql .= "\nFROM "; - $sql .= implode(', ', $froms); - } - - if (count($database['join']) > 0) - { - foreach($database['join'] AS $join) - { - $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions']; - } - } - - if (count($database['where']) > 0) - { - $sql .= "\nWHERE "; - } - - $sql .= implode("\n", $database['where']); - - if (count($database['groupby']) > 0) - { - $sql .= "\nGROUP BY "; - $sql .= implode(', ', $database['groupby']); - } - - if (count($database['having']) > 0) - { - $sql .= "\nHAVING "; - $sql .= implode("\n", $database['having']); - } - - if (count($database['orderby']) > 0) - { - $sql .= "\nORDER BY "; - $sql .= implode(', ', $database['orderby']); - } - - if (is_numeric($database['limit'])) - { - $sql .= "\n"; - $sql .= $this->limit($database['limit']); - } - - return $sql; - } - - public function escape_str($str) - { - if (!$this->db_config['escape']) - return $str; - - is_resource($this->link) or $this->connect(); - //mssql_real_escape_string($str, $this->link); <-- this function doesn't exist - - $characters = array('/\x00/', '/\x1a/', '/\n/', '/\r/', '/\\\/', '/\'/'); - $replace = array('\\\x00', '\\x1a', '\\n', '\\r', '\\\\', "''"); - return preg_replace($characters, $replace, $str); - } - - public function list_tables() - { - $sql = 'SHOW TABLES FROM ['.$this->db_config['connection']['database'].']'; - $result = $this->query($sql)->result(FALSE, MSSQL_ASSOC); - - $retval = array(); - foreach ($result as $row) - { - $retval[] = current($row); - } - - return $retval; - } - - public function show_error() - { - return mssql_get_last_message($this->link); - } - - public function list_fields($table) - { - $result = array(); - - foreach ($this->field_data($table) as $row) - { - // Make an associative array - $result[$row->Field] = $this->sql_type($row->Type); - } - - return $result; - } - - public function field_data($table) - { - $query = $this->query('SHOW COLUMNS FROM '.$this->escape_table($table), $this->link); - - return $query->result_array(TRUE); - } -} - -/** - * MSSQL Result - */ -class Mssql_Result extends Database_Result { - - // Fetch function and return type - protected $fetch_type = 'mssql_fetch_object'; - protected $return_type = MSSQL_ASSOC; - - /** - * Sets up the result variables. - * - * @param resource query result - * @param resource database link - * @param boolean return objects or arrays - * @param string SQL query that was run - */ - public function __construct($result, $link, $object = TRUE, $sql) - { - $this->result = $result; - - // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query - if (is_resource($result)) - { - $this->current_row = 0; - $this->total_rows = mssql_num_rows($this->result); - $this->fetch_type = ($object === TRUE) ? 'mssql_fetch_object' : 'mssql_fetch_array'; - } - elseif (is_bool($result)) - { - if ($result == FALSE) - { - // SQL error - throw new Kohana_Database_Exception('database.error', mssql_get_last_message($link).' - '.$sql); - } - else - { - // Its an DELETE, INSERT, REPLACE, or UPDATE querys - $last_id = mssql_query('SELECT @@IDENTITY AS last_id', $link); - $result = mssql_fetch_assoc($last_id); - $this->insert_id = $result['last_id']; - $this->total_rows = mssql_rows_affected($link); - } - } - - // Set result type - $this->result($object); - - // Store the SQL - $this->sql = $sql; - } - - /** - * Destruct, the cleanup crew! - */ - public function __destruct() - { - if (is_resource($this->result)) - { - mssql_free_result($this->result); - } - } - - public function result($object = TRUE, $type = MSSQL_ASSOC) - { - $this->fetch_type = ((bool) $object) ? 'mssql_fetch_object' : 'mssql_fetch_array'; - - // This check has to be outside the previous statement, because we do not - // know the state of fetch_type when $object = NULL - // NOTE - The class set by $type must be defined before fetching the result, - // autoloading is disabled to save a lot of stupid overhead. - if ($this->fetch_type == 'mssql_fetch_object') - { - $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $this->return_type = $type; - } - - return $this; - } - - public function as_array($object = NULL, $type = MSSQL_ASSOC) - { - return $this->result_array($object, $type); - } - - public function result_array($object = NULL, $type = MSSQL_ASSOC) - { - $rows = array(); - - if (is_string($object)) - { - $fetch = $object; - } - elseif (is_bool($object)) - { - if ($object === TRUE) - { - $fetch = 'mssql_fetch_object'; - - // NOTE - The class set by $type must be defined before fetching the result, - // autoloading is disabled to save a lot of stupid overhead. - $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $fetch = 'mssql_fetch_array'; - } - } - else - { - // Use the default config values - $fetch = $this->fetch_type; - - if ($fetch == 'mssql_fetch_object') - { - $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - } - - if (mssql_num_rows($this->result)) - { - // Reset the pointer location to make sure things work properly - mssql_data_seek($this->result, 0); - - while ($row = $fetch($this->result, $type)) - { - $rows[] = $row; - } - } - - return isset($rows) ? $rows : array(); - } - - public function list_fields() - { - $field_names = array(); - while ($field = mssql_fetch_field($this->result)) - { - $field_names[] = $field->name; - } - - return $field_names; - } - - public function seek($offset) - { - if ( ! $this->offsetExists($offset)) - return FALSE; - - return mssql_data_seek($this->result, $offset); - } - -} // End mssql_Result Class diff --git a/kohana/libraries/drivers/Database/Mysql.php b/kohana/libraries/drivers/Database/Mysql.php deleted file mode 100644 index d5222f50..00000000 --- a/kohana/libraries/drivers/Database/Mysql.php +++ /dev/null @@ -1,496 +0,0 @@ -db_config = $config; - - Kohana::log('debug', 'MySQL Database Driver Initialized'); - } - - /** - * Closes the database connection. - */ - public function __destruct() - { - is_resource($this->link) and mysql_close($this->link); - } - - public function connect() - { - // Check if link already exists - if (is_resource($this->link)) - return $this->link; - - // Import the connect variables - extract($this->db_config['connection']); - - // Persistent connections enabled? - $connect = ($this->db_config['persistent'] == TRUE) ? 'mysql_pconnect' : 'mysql_connect'; - - // Build the connection info - $host = isset($host) ? $host : $socket; - $port = isset($port) ? ':'.$port : ''; - - // Make the connection and select the database - if (($this->link = $connect($host.$port, $user, $pass, TRUE)) AND mysql_select_db($database, $this->link)) - { - if ($charset = $this->db_config['character_set']) - { - $this->set_charset($charset); - } - - // Clear password after successful connect - $this->db_config['connection']['pass'] = NULL; - - return $this->link; - } - - return FALSE; - } - - public function query($sql) - { - // Only cache if it's turned on, and only cache if it's not a write statement - if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET|DELETE|TRUNCATE)\b#i', $sql)) - { - $hash = $this->query_hash($sql); - - if ( ! isset($this->query_cache[$hash])) - { - // Set the cached object - $this->query_cache[$hash] = new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); - } - else - { - // Rewind cached result - $this->query_cache[$hash]->rewind(); - } - - // Return the cached query - return $this->query_cache[$hash]; - } - - return new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); - } - - public function set_charset($charset) - { - $this->query('SET NAMES '.$this->escape_str($charset)); - } - - public function escape_table($table) - { - if (!$this->db_config['escape']) - return $table; - - if (stripos($table, ' AS ') !== FALSE) - { - // Force 'AS' to uppercase - $table = str_ireplace(' AS ', ' AS ', $table); - - // Runs escape_table on both sides of an AS statement - $table = array_map(array($this, __FUNCTION__), explode(' AS ', $table)); - - // Re-create the AS statement - return implode(' AS ', $table); - } - return '`'.str_replace('.', '`.`', $table).'`'; - } - - public function escape_column($column) - { - if (!$this->db_config['escape']) - return $column; - - if ($column == '*') - return $column; - - // This matches any functions we support to SELECT. - if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches)) - { - if ( count($matches) == 3) - { - return $matches[1].'('.$this->escape_column($matches[2]).')'; - } - else if ( count($matches) == 5) - { - return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]); - } - } - - // This matches any modifiers we support to SELECT. - if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column)) - { - if (stripos($column, ' AS ') !== FALSE) - { - // Force 'AS' to uppercase - $column = str_ireplace(' AS ', ' AS ', $column); - - // Runs escape_column on both sides of an AS statement - $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column)); - - // Re-create the AS statement - return implode(' AS ', $column); - } - - return preg_replace('/[^.*]+/', '`$0`', $column); - } - - $parts = explode(' ', $column); - $column = ''; - - for ($i = 0, $c = count($parts); $i < $c; $i++) - { - // The column is always last - if ($i == ($c - 1)) - { - $column .= preg_replace('/[^.*]+/', '`$0`', $parts[$i]); - } - else // otherwise, it's a modifier - { - $column .= $parts[$i].' '; - } - } - return $column; - } - - public function regex($field, $match, $type, $num_regexs) - { - $prefix = ($num_regexs == 0) ? '' : $type; - - return $prefix.' '.$this->escape_column($field).' REGEXP \''.$this->escape_str($match).'\''; - } - - public function notregex($field, $match, $type, $num_regexs) - { - $prefix = $num_regexs == 0 ? '' : $type; - - return $prefix.' '.$this->escape_column($field).' NOT REGEXP \''.$this->escape_str($match) . '\''; - } - - public function merge($table, $keys, $values) - { - // Escape the column names - foreach ($keys as $key => $value) - { - $keys[$key] = $this->escape_column($value); - } - return 'REPLACE INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')'; - } - - public function limit($limit, $offset = 0) - { - return 'LIMIT '.$offset.', '.$limit; - } - - public function compile_select($database) - { - $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT '; - $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*'; - - if (count($database['from']) > 0) - { - // Escape the tables - $froms = array(); - foreach ($database['from'] as $from) - { - $froms[] = $this->escape_column($from); - } - $sql .= "\nFROM ("; - $sql .= implode(', ', $froms).")"; - } - - if (count($database['join']) > 0) - { - foreach($database['join'] AS $join) - { - $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions']; - } - } - - if (count($database['where']) > 0) - { - $sql .= "\nWHERE "; - } - - $sql .= implode("\n", $database['where']); - - if (count($database['groupby']) > 0) - { - $sql .= "\nGROUP BY "; - $sql .= implode(', ', $database['groupby']); - } - - if (count($database['having']) > 0) - { - $sql .= "\nHAVING "; - $sql .= implode("\n", $database['having']); - } - - if (count($database['orderby']) > 0) - { - $sql .= "\nORDER BY "; - $sql .= implode(', ', $database['orderby']); - } - - if (is_numeric($database['limit'])) - { - $sql .= "\n"; - $sql .= $this->limit($database['limit'], $database['offset']); - } - - return $sql; - } - - public function escape_str($str) - { - if (!$this->db_config['escape']) - return $str; - - is_resource($this->link) or $this->connect(); - - return mysql_real_escape_string($str, $this->link); - } - - public function list_tables() - { - $tables = array(); - - if ($query = $this->query('SHOW TABLES FROM '.$this->escape_table($this->db_config['connection']['database']))) - { - foreach ($query->result(FALSE) as $row) - { - $tables[] = current($row); - } - } - - return $tables; - } - - public function show_error() - { - return mysql_error($this->link); - } - - public function list_fields($table) - { - $result = NULL; - - foreach ($this->field_data($table) as $row) - { - // Make an associative array - $result[$row->Field] = $this->sql_type($row->Type); - - if ($row->Key === 'PRI' AND $row->Extra === 'auto_increment') - { - // For sequenced (AUTO_INCREMENT) tables - $result[$row->Field]['sequenced'] = TRUE; - } - - if ($row->Null === 'YES') - { - // Set NULL status - $result[$row->Field]['null'] = TRUE; - } - } - - if (!isset($result)) - throw new Kohana_Database_Exception('database.table_not_found', $table); - - return $result; - } - - public function field_data($table) - { - $result = $this->query('SHOW COLUMNS FROM '.$this->escape_table($table)); - - return $result->result_array(TRUE); - } - -} // End Database_Mysql_Driver Class - -/** - * MySQL Result - */ -class Mysql_Result extends Database_Result { - - // Fetch function and return type - protected $fetch_type = 'mysql_fetch_object'; - protected $return_type = MYSQL_ASSOC; - - /** - * Sets up the result variables. - * - * @param resource query result - * @param resource database link - * @param boolean return objects or arrays - * @param string SQL query that was run - */ - public function __construct($result, $link, $object = TRUE, $sql) - { - $this->result = $result; - - // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query - if (is_resource($result)) - { - $this->current_row = 0; - $this->total_rows = mysql_num_rows($this->result); - $this->fetch_type = ($object === TRUE) ? 'mysql_fetch_object' : 'mysql_fetch_array'; - } - elseif (is_bool($result)) - { - if ($result == FALSE) - { - // SQL error - throw new Kohana_Database_Exception('database.error', mysql_error($link).' - '.$sql); - } - else - { - // Its an DELETE, INSERT, REPLACE, or UPDATE query - $this->insert_id = mysql_insert_id($link); - $this->total_rows = mysql_affected_rows($link); - } - } - - // Set result type - $this->result($object); - - // Store the SQL - $this->sql = $sql; - } - - /** - * Destruct, the cleanup crew! - */ - public function __destruct() - { - if (is_resource($this->result)) - { - mysql_free_result($this->result); - } - } - - public function result($object = TRUE, $type = MYSQL_ASSOC) - { - $this->fetch_type = ((bool) $object) ? 'mysql_fetch_object' : 'mysql_fetch_array'; - - // This check has to be outside the previous statement, because we do not - // know the state of fetch_type when $object = NULL - // NOTE - The class set by $type must be defined before fetching the result, - // autoloading is disabled to save a lot of stupid overhead. - if ($this->fetch_type == 'mysql_fetch_object' AND $object === TRUE) - { - $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $this->return_type = $type; - } - - return $this; - } - - public function as_array($object = NULL, $type = MYSQL_ASSOC) - { - return $this->result_array($object, $type); - } - - public function result_array($object = NULL, $type = MYSQL_ASSOC) - { - $rows = array(); - - if (is_string($object)) - { - $fetch = $object; - } - elseif (is_bool($object)) - { - if ($object === TRUE) - { - $fetch = 'mysql_fetch_object'; - - $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $fetch = 'mysql_fetch_array'; - } - } - else - { - // Use the default config values - $fetch = $this->fetch_type; - - if ($fetch == 'mysql_fetch_object') - { - $type = (is_string($this->return_type) AND Kohana::auto_load($this->return_type)) ? $this->return_type : 'stdClass'; - } - } - - if (mysql_num_rows($this->result)) - { - // Reset the pointer location to make sure things work properly - mysql_data_seek($this->result, 0); - - while ($row = $fetch($this->result, $type)) - { - $rows[] = $row; - } - } - - return isset($rows) ? $rows : array(); - } - - public function list_fields() - { - $field_names = array(); - while ($field = mysql_fetch_field($this->result)) - { - $field_names[] = $field->name; - } - - return $field_names; - } - - public function seek($offset) - { - if ($this->offsetExists($offset) AND mysql_data_seek($this->result, $offset)) - { - // Set the current row to the offset - $this->current_row = $offset; - - return TRUE; - } - else - { - return FALSE; - } - } - -} // End Mysql_Result Class diff --git a/kohana/libraries/drivers/Database/Mysqli.php b/kohana/libraries/drivers/Database/Mysqli.php deleted file mode 100644 index 0dd9f05c..00000000 --- a/kohana/libraries/drivers/Database/Mysqli.php +++ /dev/null @@ -1,358 +0,0 @@ -db_config = $config; - - Kohana::log('debug', 'MySQLi Database Driver Initialized'); - } - - /** - * Closes the database connection. - */ - public function __destruct() - { - is_object($this->link) and $this->link->close(); - } - - public function connect() - { - // Check if link already exists - if (is_object($this->link)) - return $this->link; - - // Import the connect variables - extract($this->db_config['connection']); - - // Build the connection info - $host = isset($host) ? $host : $socket; - - // Make the connection and select the database - if ($this->link = new mysqli($host, $user, $pass, $database, $port)) - { - if ($charset = $this->db_config['character_set']) - { - $this->set_charset($charset); - } - - // Clear password after successful connect - $this->db_config['connection']['pass'] = NULL; - - return $this->link; - } - - return FALSE; - } - - public function query($sql) - { - // Only cache if it's turned on, and only cache if it's not a write statement - if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET|DELETE|TRUNCATE)\b#i', $sql)) - { - $hash = $this->query_hash($sql); - - if ( ! isset($this->query_cache[$hash])) - { - // Set the cached object - $this->query_cache[$hash] = new Kohana_Mysqli_Result($this->link, $this->db_config['object'], $sql); - } - else - { - // Rewind cached result - $this->query_cache[$hash]->rewind(); - } - - // Return the cached query - return $this->query_cache[$hash]; - } - - return new Kohana_Mysqli_Result($this->link, $this->db_config['object'], $sql); - } - - public function set_charset($charset) - { - if ($this->link->set_charset($charset) === FALSE) - throw new Kohana_Database_Exception('database.error', $this->show_error()); - } - - public function escape_str($str) - { - if (!$this->db_config['escape']) - return $str; - - is_object($this->link) or $this->connect(); - - return $this->link->real_escape_string($str); - } - - public function show_error() - { - return $this->link->error; - } - -} // End Database_Mysqli_Driver Class - -/** - * MySQLi Result - */ -class Kohana_Mysqli_Result extends Database_Result { - - // Database connection - protected $link; - - // Data fetching types - protected $fetch_type = 'mysqli_fetch_object'; - protected $return_type = MYSQLI_ASSOC; - - /** - * Sets up the result variables. - * - * @param object database link - * @param boolean return objects or arrays - * @param string SQL query that was run - */ - public function __construct($link, $object = TRUE, $sql) - { - $this->link = $link; - - if ( ! $this->link->multi_query($sql)) - { - // SQL error - throw new Kohana_Database_Exception('database.error', $this->link->error.' - '.$sql); - } - else - { - $this->result = $this->link->store_result(); - - // If the query is an object, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query - if (is_object($this->result)) - { - $this->current_row = 0; - $this->total_rows = $this->result->num_rows; - $this->fetch_type = ($object === TRUE) ? 'fetch_object' : 'fetch_array'; - } - elseif ($this->link->error) - { - // SQL error - throw new Kohana_Database_Exception('database.error', $this->link->error.' - '.$sql); - } - else - { - // Its an DELETE, INSERT, REPLACE, or UPDATE query - $this->insert_id = $this->link->insert_id; - $this->total_rows = $this->link->affected_rows; - } - } - - // Set result type - $this->result($object); - - // Store the SQL - $this->sql = $sql; - } - - /** - * Magic __destruct function, frees the result. - */ - public function __destruct() - { - if (is_object($this->result)) - { - $this->result->free_result(); - - // this is kinda useless, but needs to be done to avoid the "Commands out of sync; you - // can't run this command now" error. Basically, we get all results after the first one - // (the one we actually need) and free them. - if (is_resource($this->link) AND $this->link->more_results()) - { - do - { - if ($result = $this->link->store_result()) - { - $result->free_result(); - } - } while ($this->link->next_result()); - } - } - } - - public function result($object = TRUE, $type = MYSQLI_ASSOC) - { - $this->fetch_type = ((bool) $object) ? 'fetch_object' : 'fetch_array'; - - // This check has to be outside the previous statement, because we do not - // know the state of fetch_type when $object = NULL - // NOTE - The class set by $type must be defined before fetching the result, - // autoloading is disabled to save a lot of stupid overhead. - if ($this->fetch_type == 'fetch_object') - { - $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $this->return_type = $type; - } - - return $this; - } - - public function as_array($object = NULL, $type = MYSQLI_ASSOC) - { - return $this->result_array($object, $type); - } - - public function result_array($object = NULL, $type = MYSQLI_ASSOC) - { - $rows = array(); - - if (is_string($object)) - { - $fetch = $object; - } - elseif (is_bool($object)) - { - if ($object === TRUE) - { - $fetch = 'fetch_object'; - - // NOTE - The class set by $type must be defined before fetching the result, - // autoloading is disabled to save a lot of stupid overhead. - $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $fetch = 'fetch_array'; - } - } - else - { - // Use the default config values - $fetch = $this->fetch_type; - - if ($fetch == 'fetch_object') - { - $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - } - - if ($this->result->num_rows) - { - // Reset the pointer location to make sure things work properly - $this->result->data_seek(0); - - while ($row = $this->result->$fetch($type)) - { - $rows[] = $row; - } - } - - return isset($rows) ? $rows : array(); - } - - public function list_fields() - { - $field_names = array(); - while ($field = $this->result->fetch_field()) - { - $field_names[] = $field->name; - } - - return $field_names; - } - - public function seek($offset) - { - if ($this->offsetExists($offset) AND $this->result->data_seek($offset)) - { - // Set the current row to the offset - $this->current_row = $offset; - - return TRUE; - } - - return FALSE; - } - - public function offsetGet($offset) - { - if ( ! $this->seek($offset)) - return FALSE; - - // Return the row - $fetch = $this->fetch_type; - return $this->result->$fetch($this->return_type); - } - -} // End Mysqli_Result Class - -/** - * MySQLi Prepared Statement (experimental) - */ -class Kohana_Mysqli_Statement { - - protected $link = NULL; - protected $stmt; - protected $var_names = array(); - protected $var_values = array(); - - public function __construct($sql, $link) - { - $this->link = $link; - - $this->stmt = $this->link->prepare($sql); - - return $this; - } - - public function __destruct() - { - $this->stmt->close(); - } - - // Sets the bind parameters - public function bind_params($param_types, $params) - { - $this->var_names = array_keys($params); - $this->var_values = array_values($params); - call_user_func_array(array($this->stmt, 'bind_param'), array_merge($param_types, $var_names)); - - return $this; - } - - public function bind_result($params) - { - call_user_func_array(array($this->stmt, 'bind_result'), $params); - } - - // Runs the statement - public function execute() - { - foreach ($this->var_names as $key => $name) - { - $$name = $this->var_values[$key]; - } - $this->stmt->execute(); - return $this->stmt; - } -} diff --git a/kohana/libraries/drivers/Database/Pdosqlite.php b/kohana/libraries/drivers/Database/Pdosqlite.php deleted file mode 100644 index c2d1bb21..00000000 --- a/kohana/libraries/drivers/Database/Pdosqlite.php +++ /dev/null @@ -1,486 +0,0 @@ - - */ - -class Database_Pdosqlite_Driver extends Database_Driver { - - // Database connection link - protected $link; - protected $db_config; - - /* - * Constructor: __construct - * Sets up the config for the class. - * - * Parameters: - * config - database configuration - * - */ - public function __construct($config) - { - $this->db_config = $config; - - Kohana::log('debug', 'PDO:Sqlite Database Driver Initialized'); - } - - public function connect() - { - // Import the connect variables - extract($this->db_config['connection']); - - try - { - $this->link = new PDO('sqlite:'.$socket.$database, $user, $pass, - array(PDO::ATTR_PERSISTENT => $this->db_config['persistent'])); - - $this->link->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); - //$this->link->query('PRAGMA count_changes=1;'); - - if ($charset = $this->db_config['character_set']) - { - $this->set_charset($charset); - } - } - catch (PDOException $e) - { - throw new Kohana_Database_Exception('database.error', $e->getMessage()); - } - - // Clear password after successful connect - $this->db_config['connection']['pass'] = NULL; - - return $this->link; - } - - public function query($sql) - { - try - { - $sth = $this->link->prepare($sql); - } - catch (PDOException $e) - { - throw new Kohana_Database_Exception('database.error', $e->getMessage()); - } - return new Pdosqlite_Result($sth, $this->link, $this->db_config['object'], $sql); - } - - public function set_charset($charset) - { - $this->link->query('PRAGMA encoding = '.$this->escape_str($charset)); - } - - public function escape_table($table) - { - if ( ! $this->db_config['escape']) - return $table; - - return '`'.str_replace('.', '`.`', $table).'`'; - } - - public function escape_column($column) - { - if ( ! $this->db_config['escape']) - return $column; - - if ($column == '*') - return $column; - - // This matches any functions we support to SELECT. - if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches)) - { - if ( count($matches) == 3) - { - return $matches[1].'('.$this->escape_column($matches[2]).')'; - } - else if ( count($matches) == 5) - { - return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]); - } - } - - // This matches any modifiers we support to SELECT. - if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column)) - { - if (stripos($column, ' AS ') !== FALSE) - { - // Force 'AS' to uppercase - $column = str_ireplace(' AS ', ' AS ', $column); - - // Runs escape_column on both sides of an AS statement - $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column)); - - // Re-create the AS statement - return implode(' AS ', $column); - } - - return preg_replace('/[^.*]+/', '`$0`', $column); - } - - $parts = explode(' ', $column); - $column = ''; - - for ($i = 0, $c = count($parts); $i < $c; $i++) - { - // The column is always last - if ($i == ($c - 1)) - { - $column .= preg_replace('/[^.*]+/', '`$0`', $parts[$i]); - } - else // otherwise, it's a modifier - { - $column .= $parts[$i].' '; - } - } - return $column; - } - - public function limit($limit, $offset = 0) - { - return 'LIMIT '.$offset.', '.$limit; - } - - public function compile_select($database) - { - $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT '; - $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*'; - - if (count($database['from']) > 0) - { - $sql .= "\nFROM "; - $sql .= implode(', ', $database['from']); - } - - if (count($database['join']) > 0) - { - foreach($database['join'] AS $join) - { - $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions']; - } - } - - if (count($database['where']) > 0) - { - $sql .= "\nWHERE "; - } - - $sql .= implode("\n", $database['where']); - - if (count($database['groupby']) > 0) - { - $sql .= "\nGROUP BY "; - $sql .= implode(', ', $database['groupby']); - } - - if (count($database['having']) > 0) - { - $sql .= "\nHAVING "; - $sql .= implode("\n", $database['having']); - } - - if (count($database['orderby']) > 0) - { - $sql .= "\nORDER BY "; - $sql .= implode(', ', $database['orderby']); - } - - if (is_numeric($database['limit'])) - { - $sql .= "\n"; - $sql .= $this->limit($database['limit'], $database['offset']); - } - - return $sql; - } - - public function escape_str($str) - { - if ( ! $this->db_config['escape']) - return $str; - - if (function_exists('sqlite_escape_string')) - { - $res = sqlite_escape_string($str); - } - else - { - $res = str_replace("'", "''", $str); - } - return $res; - } - - public function list_tables() - { - $sql = "SELECT `name` FROM `sqlite_master` WHERE `type`='table' ORDER BY `name`;"; - try - { - $result = $this->query($sql)->result(FALSE, PDO::FETCH_ASSOC); - $tables = array(); - foreach ($result as $row) - { - $tables[] = current($row); - } - } - catch (PDOException $e) - { - throw new Kohana_Database_Exception('database.error', $e->getMessage()); - } - return $tables; - } - - public function show_error() - { - $err = $this->link->errorInfo(); - return isset($err[2]) ? $err[2] : 'Unknown error!'; - } - - public function list_fields($table, $query = FALSE) - { - static $tables; - if (is_object($query)) - { - if (empty($tables[$table])) - { - $tables[$table] = array(); - - foreach ($query->result() as $row) - { - $tables[$table][] = $row->name; - } - } - - return $tables[$table]; - } - else - { - $result = $this->link->query( 'PRAGMA table_info('.$this->escape_table($table).')' ); - - foreach ($result as $row) - { - $tables[$table][$row['name']] = $this->sql_type($row['type']); - } - - return $tables[$table]; - } - } - - public function field_data($table) - { - Kohana::log('error', 'This method is under developing'); - } - /** - * Version number query string - * - * @access public - * @return string - */ - function version() - { - return $this->link->getAttribute(constant("PDO::ATTR_SERVER_VERSION")); - } - -} // End Database_PdoSqlite_Driver Class - -/* - * PDO-sqlite Result - */ -class Pdosqlite_Result extends Database_Result { - - // Data fetching types - protected $fetch_type = PDO::FETCH_OBJ; - protected $return_type = PDO::FETCH_ASSOC; - - /** - * Sets up the result variables. - * - * @param resource query result - * @param resource database link - * @param boolean return objects or arrays - * @param string SQL query that was run - */ - public function __construct($result, $link, $object = TRUE, $sql) - { - if (is_object($result) OR $result = $link->prepare($sql)) - { - // run the query. Return true if success, false otherwise - if( ! $result->execute()) - { - // Throw Kohana Exception with error message. See PDOStatement errorInfo() method - $arr_infos = $result->errorInfo(); - throw new Kohana_Database_Exception('database.error', $arr_infos[2]); - } - - if (preg_match('/^SELECT|PRAGMA|EXPLAIN/i', $sql)) - { - $this->result = $result; - $this->current_row = 0; - - $this->total_rows = $this->sqlite_row_count(); - - $this->fetch_type = ($object === TRUE) ? PDO::FETCH_OBJ : PDO::FETCH_ASSOC; - } - elseif (preg_match('/^DELETE|INSERT|UPDATE/i', $sql)) - { - $this->insert_id = $link->lastInsertId(); - - $this->total_rows = $result->rowCount(); - } - } - else - { - // SQL error - throw new Kohana_Database_Exception('database.error', $link->errorInfo().' - '.$sql); - } - - // Set result type - $this->result($object); - - // Store the SQL - $this->sql = $sql; - } - - private function sqlite_row_count() - { - $count = 0; - while ($this->result->fetch()) - { - $count++; - } - - // The query must be re-fetched now. - $this->result->execute(); - - return $count; - } - - /* - * Destructor: __destruct - * Magic __destruct function, frees the result. - */ - public function __destruct() - { - if (is_object($this->result)) - { - $this->result->closeCursor(); - $this->result = NULL; - } - } - - public function result($object = TRUE, $type = PDO::FETCH_BOTH) - { - $this->fetch_type = (bool) $object ? PDO::FETCH_OBJ : PDO::FETCH_BOTH; - - if ($this->fetch_type == PDO::FETCH_OBJ) - { - $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $this->return_type = $type; - } - - return $this; - } - - public function as_array($object = NULL, $type = PDO::FETCH_ASSOC) - { - return $this->result_array($object, $type); - } - - public function result_array($object = NULL, $type = PDO::FETCH_ASSOC) - { - $rows = array(); - - if (is_string($object)) - { - $fetch = $object; - } - elseif (is_bool($object)) - { - if ($object === TRUE) - { - $fetch = PDO::FETCH_OBJ; - - // NOTE - The class set by $type must be defined before fetching the result, - // autoloading is disabled to save a lot of stupid overhead. - $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $fetch = PDO::FETCH_OBJ; - } - } - else - { - // Use the default config values - $fetch = $this->fetch_type; - - if ($fetch == PDO::FETCH_OBJ) - { - $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - } - try - { - while ($row = $this->result->fetch($fetch)) - { - $rows[] = $row; - } - } - catch(PDOException $e) - { - throw new Kohana_Database_Exception('database.error', $e->getMessage()); - return FALSE; - } - return $rows; - } - - public function list_fields() - { - $field_names = array(); - for ($i = 0, $max = $this->result->columnCount(); $i < $max; $i++) - { - $info = $this->result->getColumnMeta($i); - $field_names[] = $info['name']; - } - return $field_names; - } - - public function seek($offset) - { - // To request a scrollable cursor for your PDOStatement object, you must - // set the PDO::ATTR_CURSOR attribute to PDO::CURSOR_SCROLL when you - // prepare the statement. - Kohana::log('error', get_class($this).' does not support scrollable cursors, '.__FUNCTION__.' call ignored'); - - return FALSE; - } - - public function offsetGet($offset) - { - try - { - return $this->result->fetch($this->fetch_type, PDO::FETCH_ORI_ABS, $offset); - } - catch(PDOException $e) - { - throw new Kohana_Database_Exception('database.error', $e->getMessage()); - } - } - - public function rewind() - { - // Same problem that seek() has, see above. - return $this->seek(0); - } - -} // End PdoSqlite_Result Class \ No newline at end of file diff --git a/kohana/libraries/drivers/Database/Pgsql.php b/kohana/libraries/drivers/Database/Pgsql.php deleted file mode 100644 index c53c8439..00000000 --- a/kohana/libraries/drivers/Database/Pgsql.php +++ /dev/null @@ -1,538 +0,0 @@ -db_config = $config; - - Kohana::log('debug', 'PgSQL Database Driver Initialized'); - } - - public function connect() - { - // Check if link already exists - if (is_resource($this->link)) - return $this->link; - - // Import the connect variables - extract($this->db_config['connection']); - - // Persistent connections enabled? - $connect = ($this->db_config['persistent'] == TRUE) ? 'pg_pconnect' : 'pg_connect'; - - // Build the connection info - $port = isset($port) ? 'port=\''.$port.'\'' : ''; - $host = isset($host) ? 'host=\''.$host.'\' '.$port : ''; // if no host, connect with the socket - - $connection_string = $host.' dbname=\''.$database.'\' user=\''.$user.'\' password=\''.$pass.'\''; - // Make the connection and select the database - if ($this->link = $connect($connection_string)) - { - if ($charset = $this->db_config['character_set']) - { - echo $this->set_charset($charset); - } - - // Clear password after successful connect - $this->db_config['connection']['pass'] = NULL; - - return $this->link; - } - - return FALSE; - } - - public function query($sql) - { - // Only cache if it's turned on, and only cache if it's not a write statement - if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|SET)\b#i', $sql)) - { - $hash = $this->query_hash($sql); - - if ( ! isset($this->query_cache[$hash])) - { - // Set the cached object - $this->query_cache[$hash] = new Pgsql_Result(pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql); - } - else - { - // Rewind cached result - $this->query_cache[$hash]->rewind(); - } - - return $this->query_cache[$hash]; - } - - // Suppress warning triggered when a database error occurs (e.g., a constraint violation) - return new Pgsql_Result(@pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql); - } - - public function set_charset($charset) - { - $this->query('SET client_encoding TO '.pg_escape_string($this->link, $charset)); - } - - public function escape_table($table) - { - if (!$this->db_config['escape']) - return $table; - - return '"'.str_replace('.', '"."', $table).'"'; - } - - public function escape_column($column) - { - if (!$this->db_config['escape']) - return $column; - - if ($column == '*') - return $column; - - // This matches any functions we support to SELECT. - if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches)) - { - if ( count($matches) == 3) - { - return $matches[1].'('.$this->escape_column($matches[2]).')'; - } - else if ( count($matches) == 5) - { - return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]); - } - } - - // This matches any modifiers we support to SELECT. - if ( ! preg_match('/\b(?:all|distinct)\s/i', $column)) - { - if (stripos($column, ' AS ') !== FALSE) - { - // Force 'AS' to uppercase - $column = str_ireplace(' AS ', ' AS ', $column); - - // Runs escape_column on both sides of an AS statement - $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column)); - - // Re-create the AS statement - return implode(' AS ', $column); - } - - return preg_replace('/[^.*]+/', '"$0"', $column); - } - - $parts = explode(' ', $column); - $column = ''; - - for ($i = 0, $c = count($parts); $i < $c; $i++) - { - // The column is always last - if ($i == ($c - 1)) - { - $column .= preg_replace('/[^.*]+/', '"$0"', $parts[$i]); - } - else // otherwise, it's a modifier - { - $column .= $parts[$i].' '; - } - } - return $column; - } - - public function regex($field, $match, $type, $num_regexs) - { - $prefix = ($num_regexs == 0) ? '' : $type; - - return $prefix.' '.$this->escape_column($field).' ~* \''.$this->escape_str($match).'\''; - } - - public function notregex($field, $match, $type, $num_regexs) - { - $prefix = $num_regexs == 0 ? '' : $type; - - return $prefix.' '.$this->escape_column($field).' !~* \''.$this->escape_str($match) . '\''; - } - - public function limit($limit, $offset = 0) - { - return 'LIMIT '.$limit.' OFFSET '.$offset; - } - - public function compile_select($database) - { - $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT '; - $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*'; - - if (count($database['from']) > 0) - { - $sql .= "\nFROM "; - $sql .= implode(', ', $database['from']); - } - - if (count($database['join']) > 0) - { - foreach($database['join'] AS $join) - { - $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions']; - } - } - - if (count($database['where']) > 0) - { - $sql .= "\nWHERE "; - } - - $sql .= implode("\n", $database['where']); - - if (count($database['groupby']) > 0) - { - $sql .= "\nGROUP BY "; - $sql .= implode(', ', $database['groupby']); - } - - if (count($database['having']) > 0) - { - $sql .= "\nHAVING "; - $sql .= implode("\n", $database['having']); - } - - if (count($database['orderby']) > 0) - { - $sql .= "\nORDER BY "; - $sql .= implode(', ', $database['orderby']); - } - - if (is_numeric($database['limit'])) - { - $sql .= "\n"; - $sql .= $this->limit($database['limit'], $database['offset']); - } - - return $sql; - } - - public function escape_str($str) - { - if (!$this->db_config['escape']) - return $str; - - is_resource($this->link) or $this->connect(); - - return pg_escape_string($this->link, $str); - } - - public function list_tables() - { - $sql = 'SELECT table_schema || \'.\' || table_name FROM information_schema.tables WHERE table_schema NOT IN (\'pg_catalog\', \'information_schema\')'; - $result = $this->query($sql)->result(FALSE, PGSQL_ASSOC); - - $retval = array(); - foreach ($result as $row) - { - $retval[] = current($row); - } - - return $retval; - } - - public function show_error() - { - return pg_last_error($this->link); - } - - public function list_fields($table) - { - $result = NULL; - - foreach ($this->field_data($table) as $row) - { - // Make an associative array - $result[$row->column_name] = $this->sql_type($row->data_type); - - if (!strncmp($row->column_default, 'nextval(', 8)) - { - $result[$row->column_name]['sequenced'] = TRUE; - } - - if ($row->is_nullable === 'YES') - { - $result[$row->column_name]['null'] = TRUE; - } - } - - if (!isset($result)) - throw new Kohana_Database_Exception('database.table_not_found', $table); - - return $result; - } - - public function field_data($table) - { - // http://www.postgresql.org/docs/8.3/static/infoschema-columns.html - $result = $this->query(' - SELECT column_name, column_default, is_nullable, data_type, udt_name, - character_maximum_length, numeric_precision, numeric_precision_radix, numeric_scale - FROM information_schema.columns - WHERE table_name = \''. $this->escape_str($table) .'\' - ORDER BY ordinal_position - '); - - return $result->result_array(TRUE); - } - -} // End Database_Pgsql_Driver Class - -/** - * PostgreSQL Result - */ -class Pgsql_Result extends Database_Result { - - // Data fetching types - protected $fetch_type = 'pgsql_fetch_object'; - protected $return_type = PGSQL_ASSOC; - - /** - * Sets up the result variables. - * - * @param resource query result - * @param resource database link - * @param boolean return objects or arrays - * @param string SQL query that was run - */ - public function __construct($result, $link, $object = TRUE, $sql) - { - $this->link = $link; - $this->result = $result; - - // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query - if (is_resource($result)) - { - // Its an DELETE, INSERT, REPLACE, or UPDATE query - if (preg_match('/^(?:delete|insert|replace|update)\b/iD', trim($sql), $matches)) - { - $this->insert_id = (strtolower($matches[0]) == 'insert') ? $this->insert_id() : FALSE; - $this->total_rows = pg_affected_rows($this->result); - } - else - { - $this->current_row = 0; - $this->total_rows = pg_num_rows($this->result); - $this->fetch_type = ($object === TRUE) ? 'pg_fetch_object' : 'pg_fetch_array'; - } - } - else - { - throw new Kohana_Database_Exception('database.error', pg_last_error().' - '.$sql); - } - - // Set result type - $this->result($object); - - // Store the SQL - $this->sql = $sql; - } - - /** - * Magic __destruct function, frees the result. - */ - public function __destruct() - { - if (is_resource($this->result)) - { - pg_free_result($this->result); - } - } - - public function result($object = TRUE, $type = PGSQL_ASSOC) - { - $this->fetch_type = ((bool) $object) ? 'pg_fetch_object' : 'pg_fetch_array'; - - // This check has to be outside the previous statement, because we do not - // know the state of fetch_type when $object = NULL - // NOTE - The class set by $type must be defined before fetching the result, - // autoloading is disabled to save a lot of stupid overhead. - if ($this->fetch_type == 'pg_fetch_object') - { - $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $this->return_type = $type; - } - - return $this; - } - - public function as_array($object = NULL, $type = PGSQL_ASSOC) - { - return $this->result_array($object, $type); - } - - public function result_array($object = NULL, $type = PGSQL_ASSOC) - { - $rows = array(); - - if (is_string($object)) - { - $fetch = $object; - } - elseif (is_bool($object)) - { - if ($object === TRUE) - { - $fetch = 'pg_fetch_object'; - - // NOTE - The class set by $type must be defined before fetching the result, - // autoloading is disabled to save a lot of stupid overhead. - $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - else - { - $fetch = 'pg_fetch_array'; - } - } - else - { - // Use the default config values - $fetch = $this->fetch_type; - - if ($fetch == 'pg_fetch_object') - { - $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; - } - } - - if ($this->total_rows) - { - pg_result_seek($this->result, 0); - - while ($row = $fetch($this->result, NULL, $type)) - { - $rows[] = $row; - } - } - - return $rows; - } - - public function insert_id() - { - if ($this->insert_id === NULL) - { - $query = 'SELECT LASTVAL() AS insert_id'; - - // Disable error reporting for this, just to silence errors on - // tables that have no serial column. - $ER = error_reporting(0); - - $result = pg_query($this->link, $query); - $insert_id = pg_fetch_array($result, NULL, PGSQL_ASSOC); - - $this->insert_id = $insert_id['insert_id']; - - // Reset error reporting - error_reporting($ER); - } - - return $this->insert_id; - } - - public function seek($offset) - { - if ($this->offsetExists($offset) AND pg_result_seek($this->result, $offset)) - { - // Set the current row to the offset - $this->current_row = $offset; - - return TRUE; - } - - return FALSE; - } - - public function list_fields() - { - $field_names = array(); - - $fields = pg_num_fields($this->result); - for ($i = 0; $i < $fields; ++$i) - { - $field_names[] = pg_field_name($this->result, $i); - } - - return $field_names; - } - - /** - * ArrayAccess: offsetGet - */ - public function offsetGet($offset) - { - if ( ! $this->seek($offset)) - return FALSE; - - // Return the row by calling the defined fetching callback - $fetch = $this->fetch_type; - return $fetch($this->result, NULL, $this->return_type); - } - -} // End Pgsql_Result Class - -/** - * PostgreSQL Prepared Statement (experimental) - */ -class Kohana_Pgsql_Statement { - - protected $link = NULL; - protected $stmt; - - public function __construct($sql, $link) - { - $this->link = $link; - - $this->stmt = $this->link->prepare($sql); - - return $this; - } - - public function __destruct() - { - $this->stmt->close(); - } - - // Sets the bind parameters - public function bind_params() - { - $argv = func_get_args(); - return $this; - } - - // sets the statement values to the bound parameters - public function set_vals() - { - return $this; - } - - // Runs the statement - public function execute() - { - return $this; - } -} diff --git a/kohana/libraries/drivers/Image.php b/kohana/libraries/drivers/Image.php deleted file mode 100644 index f89ba953..00000000 --- a/kohana/libraries/drivers/Image.php +++ /dev/null @@ -1,156 +0,0 @@ - $args) - { - if ( ! $this->$func($args)) - return FALSE; - } - - return TRUE; - } - - /** - * Sanitize and normalize a geometry array based on the temporary image - * width and height. Valid properties are: width, height, top, left. - * - * @param array geometry properties - * @return void - */ - protected function sanitize_geometry( & $geometry) - { - list($width, $height) = $this->properties(); - - // Turn off error reporting - $reporting = error_reporting(0); - - // Width and height cannot exceed current image size - $geometry['width'] = min($geometry['width'], $width); - $geometry['height'] = min($geometry['height'], $height); - - // Set standard coordinates if given, otherwise use pixel values - if ($geometry['top'] === 'center') - { - $geometry['top'] = floor(($height / 2) - ($geometry['height'] / 2)); - } - elseif ($geometry['top'] === 'top') - { - $geometry['top'] = 0; - } - elseif ($geometry['top'] === 'bottom') - { - $geometry['top'] = $height - $geometry['height']; - } - - // Set standard coordinates if given, otherwise use pixel values - if ($geometry['left'] === 'center') - { - $geometry['left'] = floor(($width / 2) - ($geometry['width'] / 2)); - } - elseif ($geometry['left'] === 'left') - { - $geometry['left'] = 0; - } - elseif ($geometry['left'] === 'right') - { - $geometry['left'] = $width - $geometry['height']; - } - - // Restore error reporting - error_reporting($reporting); - } - - /** - * Return the current width and height of the temporary image. This is mainly - * needed for sanitizing the geometry. - * - * @return array width, height - */ - abstract protected function properties(); - - /** - * Process an image with a set of actions. - * - * @param string image filename - * @param array actions to execute - * @param string destination directory path - * @param string destination filename - * @return boolean - */ - abstract public function process($image, $actions, $dir, $file); - - /** - * Flip an image. Valid directions are horizontal and vertical. - * - * @param integer direction to flip - * @return boolean - */ - abstract function flip($direction); - - /** - * Crop an image. Valid properties are: width, height, top, left. - * - * @param array new properties - * @return boolean - */ - abstract function crop($properties); - - /** - * Resize an image. Valid properties are: width, height, and master. - * - * @param array new properties - * @return boolean - */ - abstract public function resize($properties); - - /** - * Rotate an image. Valid amounts are -180 to 180. - * - * @param integer amount to rotate - * @return boolean - */ - abstract public function rotate($amount); - - /** - * Sharpen and image. Valid amounts are 1 to 100. - * - * @param integer amount to sharpen - * @return boolean - */ - abstract public function sharpen($amount); - - /** - * Overlay a second image. Valid properties are: overlay_file, mime, x, y and transparency. - * - * @return boolean - */ - abstract public function composite($properties); - -} // End Image Driver \ No newline at end of file diff --git a/kohana/libraries/drivers/Image/GD.php b/kohana/libraries/drivers/Image/GD.php deleted file mode 100644 index be2af4e2..00000000 --- a/kohana/libraries/drivers/Image/GD.php +++ /dev/null @@ -1,401 +0,0 @@ -image = $image; - - // Create the GD image resource - $this->tmp_image = $create($image['file']); - - // Get the quality setting from the actions - $quality = arr::remove('quality', $actions); - - if ($status = $this->execute($actions)) - { - // Prevent the alpha from being lost - imagealphablending($this->tmp_image, TRUE); - imagesavealpha($this->tmp_image, TRUE); - - switch ($save) - { - case 'imagejpeg': - // Default the quality to 95 - ($quality === NULL) and $quality = 95; - break; - case 'imagegif': - // Remove the quality setting, GIF doesn't use it - unset($quality); - break; - case 'imagepng': - // Always use a compression level of 9 for PNGs. This does not - // affect quality, it only increases the level of compression! - $quality = 9; - break; - } - - if ($render === FALSE) - { - // Set the status to the save return value, saving with the quality requested - $status = isset($quality) ? $save($this->tmp_image, $dir.$file, $quality) : $save($this->tmp_image, $dir.$file); - } - else - { - // Output the image directly to the browser - switch ($save) - { - case 'imagejpeg': - header('Content-Type: image/jpeg'); - break; - case 'imagegif': - header('Content-Type: image/gif'); - break; - case 'imagepng': - header('Content-Type: image/png'); - break; - } - - $status = isset($quality) ? $save($this->tmp_image, NULL, $quality) : $save($this->tmp_image); - } - - // Destroy the temporary image - imagedestroy($this->tmp_image); - } - - return $status; - } - - public function flip($direction) - { - // Get the current width and height - $width = imagesx($this->tmp_image); - $height = imagesy($this->tmp_image); - - // Create the flipped image - $flipped = $this->imagecreatetransparent($width, $height); - - if ($direction === Image::HORIZONTAL) - { - for ($x = 0; $x < $width; $x++) - { - $status = imagecopy($flipped, $this->tmp_image, $x, 0, $width - $x - 1, 0, 1, $height); - } - } - elseif ($direction === Image::VERTICAL) - { - for ($y = 0; $y < $height; $y++) - { - $status = imagecopy($flipped, $this->tmp_image, 0, $y, 0, $height - $y - 1, $width, 1); - } - } - else - { - // Do nothing - return TRUE; - } - - if ($status === TRUE) - { - // Swap the new image for the old one - imagedestroy($this->tmp_image); - $this->tmp_image = $flipped; - } - - return $status; - } - - public function crop($properties) - { - // Sanitize the cropping settings - $this->sanitize_geometry($properties); - - // Get the current width and height - $width = imagesx($this->tmp_image); - $height = imagesy($this->tmp_image); - - // Create the temporary image to copy to - $img = $this->imagecreatetransparent($properties['width'], $properties['height']); - - // Execute the crop - if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, $properties['left'], $properties['top'], $width, $height, $width, $height)) - { - // Swap the new image for the old one - imagedestroy($this->tmp_image); - $this->tmp_image = $img; - } - - return $status; - } - - public function resize($properties) - { - // Get the current width and height - $width = imagesx($this->tmp_image); - $height = imagesy($this->tmp_image); - - if (substr($properties['width'], -1) === '%') - { - // Recalculate the percentage to a pixel size - $properties['width'] = round($width * (substr($properties['width'], 0, -1) / 100)); - } - - if (substr($properties['height'], -1) === '%') - { - // Recalculate the percentage to a pixel size - $properties['height'] = round($height * (substr($properties['height'], 0, -1) / 100)); - } - - // Recalculate the width and height, if they are missing - empty($properties['width']) and $properties['width'] = round($width * $properties['height'] / $height); - empty($properties['height']) and $properties['height'] = round($height * $properties['width'] / $width); - - if ($properties['master'] === Image::AUTO) - { - // Change an automatic master dim to the correct type - $properties['master'] = (($width / $properties['width']) > ($height / $properties['height'])) ? Image::WIDTH : Image::HEIGHT; - } - - if (empty($properties['height']) OR $properties['master'] === Image::WIDTH) - { - // Recalculate the height based on the width - $properties['height'] = round($height * $properties['width'] / $width); - } - - if (empty($properties['width']) OR $properties['master'] === Image::HEIGHT) - { - // Recalculate the width based on the height - $properties['width'] = round($width * $properties['height'] / $height); - } - - // Test if we can do a resize without resampling to speed up the final resize - if ($properties['width'] > $width / 2 AND $properties['height'] > $height / 2) - { - // Presize width and height - $pre_width = $width; - $pre_height = $height; - - // The maximum reduction is 10% greater than the final size - $max_reduction_width = round($properties['width'] * 1.1); - $max_reduction_height = round($properties['height'] * 1.1); - - // Reduce the size using an O(2n) algorithm, until it reaches the maximum reduction - while ($pre_width / 2 > $max_reduction_width AND $pre_height / 2 > $max_reduction_height) - { - $pre_width /= 2; - $pre_height /= 2; - } - - // Create the temporary image to copy to - $img = $this->imagecreatetransparent($pre_width, $pre_height); - - if ($status = imagecopyresized($img, $this->tmp_image, 0, 0, 0, 0, $pre_width, $pre_height, $width, $height)) - { - // Swap the new image for the old one - imagedestroy($this->tmp_image); - $this->tmp_image = $img; - } - - // Set the width and height to the presize - $width = $pre_width; - $height = $pre_height; - } - - // Create the temporary image to copy to - $img = $this->imagecreatetransparent($properties['width'], $properties['height']); - - // Execute the resize - if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, 0, 0, $properties['width'], $properties['height'], $width, $height)) - { - // Swap the new image for the old one - imagedestroy($this->tmp_image); - $this->tmp_image = $img; - } - - return $status; - } - - public function rotate($amount) - { - // Use current image to rotate - $img = $this->tmp_image; - - // White, with an alpha of 0 - $transparent = imagecolorallocatealpha($img, 255, 255, 255, 127); - - // Rotate, setting the transparent color - $img = imagerotate($img, 360 - $amount, $transparent, -1); - - // Fill the background with the transparent "color" - imagecolortransparent($img, $transparent); - - // Merge the images - if ($status = imagecopymerge($this->tmp_image, $img, 0, 0, 0, 0, imagesx($this->tmp_image), imagesy($this->tmp_image), 100)) - { - // Prevent the alpha from being lost - imagealphablending($img, TRUE); - imagesavealpha($img, TRUE); - - // Swap the new image for the old one - imagedestroy($this->tmp_image); - $this->tmp_image = $img; - } - - return $status; - } - - public function sharpen($amount) - { - // Make sure that the sharpening function is available - if ( ! function_exists('imageconvolution')) - throw new Kohana_Exception('image.unsupported_method', __FUNCTION__); - - // Amount should be in the range of 18-10 - $amount = round(abs(-18 + ($amount * 0.08)), 2); - - // Gaussian blur matrix - $matrix = array - ( - array(-1, -1, -1), - array(-1, $amount, -1), - array(-1, -1, -1), - ); - - // Perform the sharpen - return imageconvolution($this->tmp_image, $matrix, $amount - 8, 0); - } - - public function composite($properties) - { - switch($properties['mime']) - { - case "image/jpeg": - $overlay_img = imagecreatefromjpeg($properties['overlay_file']); - break; - - case "image/gif": - $overlay_img = imagecreatefromgif($properties['overlay_file']); - break; - - case "image/png": - $overlay_img = imagecreatefrompng($properties['overlay_file']); - break; - } - - imagecopymerge($this->tmp_image, $overlay_img, $properties['x'], $properties['y'], 0, 0, imagesx($overlay_img), imagesy($overlay_img), $properties['transparency']); - imagedestroy($overlay_img); - return TRUE; - } - - protected function properties() - { - return array(imagesx($this->tmp_image), imagesy($this->tmp_image)); - } - - /** - * Returns an image with a transparent background. Used for rotating to - * prevent unfilled backgrounds. - * - * @param integer image width - * @param integer image height - * @return resource - */ - protected function imagecreatetransparent($width, $height) - { - if (self::$blank_png === NULL) - { - // Decode the blank PNG if it has not been done already - self::$blank_png = imagecreatefromstring(base64_decode - ( - 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29'. - 'mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADqSURBVHjaYvz//z/DYAYAAcTEMMgBQAANegcCBN'. - 'CgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQ'. - 'AANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoH'. - 'AgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB'. - '3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAgAEAMpcDTTQWJVEAAAAASUVORK5CYII=' - )); - - // Set the blank PNG width and height - self::$blank_png_width = imagesx(self::$blank_png); - self::$blank_png_height = imagesy(self::$blank_png); - } - - $img = imagecreatetruecolor($width, $height); - - // Resize the blank image - imagecopyresized($img, self::$blank_png, 0, 0, 0, 0, $width, $height, self::$blank_png_width, self::$blank_png_height); - - // Prevent the alpha from being lost - imagealphablending($img, FALSE); - imagesavealpha($img, TRUE); - - return $img; - } - -} // End Image GD Driver \ No newline at end of file diff --git a/kohana/libraries/drivers/Image/GraphicsMagick.php b/kohana/libraries/drivers/Image/GraphicsMagick.php deleted file mode 100644 index a8bc4d9b..00000000 --- a/kohana/libraries/drivers/Image/GraphicsMagick.php +++ /dev/null @@ -1,221 +0,0 @@ -ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : ''; - - // Check to make sure the provided path is correct - if ( ! is_file(realpath($config['directory']).'/gm'.$this->ext)) - throw new Kohana_Exception('image.graphicsmagick.not_found', 'gm'.$this->ext); - - - // Set the installation directory - $this->dir = str_replace('\\', '/', realpath($config['directory'])).'/'; - } - - /** - * Creates a temporary image and executes the given actions. By creating a - * temporary copy of the image before manipulating it, this process is atomic. - */ - public function process($image, $actions, $dir, $file, $render = FALSE) - { - // We only need the filename - $image = $image['file']; - - // Unique temporary filename - $this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.')); - - // Copy the image to the temporary file - copy($image, $this->tmp_image); - - // Quality change is done last - $quality = (int) arr::remove('quality', $actions); - - // Use 95 for the default quality - empty($quality) and $quality = 95; - - // All calls to these will need to be escaped, so do it now - $this->cmd_image = escapeshellarg($this->tmp_image); - $this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file); - - if ($status = $this->execute($actions)) - { - // Use convert to change the image into its final version. This is - // done to allow the file type to change correctly, and to handle - // the quality conversion in the most effective way possible. - if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image)) - { - $this->errors[] = $error; - } - else - { - // Output the image directly to the browser - if ($render !== FALSE) - { - $contents = file_get_contents($this->tmp_image); - switch (substr($file, strrpos($file, '.') + 1)) - { - case 'jpg': - case 'jpeg': - header('Content-Type: image/jpeg'); - break; - case 'gif': - header('Content-Type: image/gif'); - break; - case 'png': - header('Content-Type: image/png'); - break; - } - echo $contents; - } - } - } - - // Remove the temporary image - unlink($this->tmp_image); - $this->tmp_image = ''; - - return $status; - } - - public function crop($prop) - { - // Sanitize and normalize the properties into geometry - $this->sanitize_geometry($prop); - - // Set the IM geometry based on the properties - $geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']); - - if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function flip($dir) - { - // Convert the direction into a GM command - $dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip'; - - if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function resize($prop) - { - switch ($prop['master']) - { - case Image::WIDTH: // Wx - $dim = escapeshellarg($prop['width'].'x'); - break; - case Image::HEIGHT: // xH - $dim = escapeshellarg('x'.$prop['height']); - break; - case Image::AUTO: // WxH - $dim = escapeshellarg($prop['width'].'x'.$prop['height']); - break; - case Image::NONE: // WxH! - $dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!'); - break; - } - - // Use "convert" to change the width and height - if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function rotate($amt) - { - if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function sharpen($amount) - { - // Set the sigma, radius, and amount. The amount formula allows a nice - // spread between 1 and 100 without pixelizing the image badly. - $sigma = 0.5; - $radius = $sigma * 2; - $amount = round(($amount / 80) * 3.14, 2); - - // Convert the amount to an GM command - $sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0'); - - if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function composite($properties) - { - if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' composite').' -geometry ' . escapeshellarg('+'.$properties['x'].'+'.$properties['y']).' -dissolve '.escapeshellarg($properties['transparency']).' '.escapeshellarg($properties['overlay_file']).' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - return TRUE; - } - - protected function properties() - { - return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE); - } - -} // End Image GraphicsMagick Driver \ No newline at end of file diff --git a/kohana/libraries/drivers/Image/ImageMagick.php b/kohana/libraries/drivers/Image/ImageMagick.php deleted file mode 100644 index 4b381fd6..00000000 --- a/kohana/libraries/drivers/Image/ImageMagick.php +++ /dev/null @@ -1,222 +0,0 @@ -ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : ''; - - // Check to make sure the provided path is correct - if ( ! is_file(realpath($config['directory']).'/convert'.$this->ext)) - throw new Kohana_Exception('image.imagemagick.not_found', 'convert'.$this->ext); - - // Set the installation directory - $this->dir = str_replace('\\', '/', realpath($config['directory'])).'/'; - } - - /** - * Creates a temporary image and executes the given actions. By creating a - * temporary copy of the image before manipulating it, this process is atomic. - */ - public function process($image, $actions, $dir, $file, $render = FALSE) - { - // We only need the filename - $image = $image['file']; - - // Unique temporary filename - $this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.')); - - // Copy the image to the temporary file - copy($image, $this->tmp_image); - - // Quality change is done last - $quality = (int) arr::remove('quality', $actions); - - // Use 95 for the default quality - empty($quality) and $quality = 95; - - // All calls to these will need to be escaped, so do it now - $this->cmd_image = escapeshellarg($this->tmp_image); - $this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file); - - if ($status = $this->execute($actions)) - { - // Use convert to change the image into its final version. This is - // done to allow the file type to change correctly, and to handle - // the quality conversion in the most effective way possible. - if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image)) - { - $this->errors[] = $error; - } - else - { - // Output the image directly to the browser - if ($render !== FALSE) - { - $contents = file_get_contents($this->tmp_image); - switch (substr($file, strrpos($file, '.') + 1)) - { - case 'jpg': - case 'jpeg': - header('Content-Type: image/jpeg'); - break; - case 'gif': - header('Content-Type: image/gif'); - break; - case 'png': - header('Content-Type: image/png'); - break; - } - echo $contents; - } - } - } - - // Remove the temporary image - unlink($this->tmp_image); - $this->tmp_image = ''; - - return $status; - } - - public function crop($prop) - { - // Sanitize and normalize the properties into geometry - $this->sanitize_geometry($prop); - - // Set the IM geometry based on the properties - $geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']); - - if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function flip($dir) - { - // Convert the direction into a IM command - $dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip'; - - if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function resize($prop) - { - switch ($prop['master']) - { - case Image::WIDTH: // Wx - $dim = escapeshellarg($prop['width'].'x'); - break; - case Image::HEIGHT: // xH - $dim = escapeshellarg('x'.$prop['height']); - break; - case Image::AUTO: // WxH - $dim = escapeshellarg($prop['width'].'x'.$prop['height']); - break; - case Image::NONE: // WxH! - $dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!'); - break; - } - - // Use "convert" to change the width and height - if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function rotate($amt) - { - if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function sharpen($amount) - { - // Set the sigma, radius, and amount. The amount formula allows a nice - // spread between 1 and 100 without pixelizing the image badly. - $sigma = 0.5; - $radius = $sigma * 2; - $amount = round(($amount / 80) * 3.14, 2); - - // Convert the amount to an IM command - $sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0'); - - if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - - return TRUE; - } - - public function composite($properties) - { - if ($error = exec(escapeshellcmd($this->dir.'composite'.$this->ext).' -geometry ' . escapeshellarg('+'.$properties['x'].'+'.$properties['y']).' -dissolve '.escapeshellarg($properties['transparency']).' '.escapeshellarg($properties['overlay_file']).' '.$this->cmd_image.' '.$this->cmd_image)) - { - $this->errors[] = $error; - return FALSE; - } - return TRUE; - } - - protected function properties() - { - return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE); - } - -} // End Image ImageMagick Driver \ No newline at end of file diff --git a/kohana/libraries/drivers/Session.php b/kohana/libraries/drivers/Session.php deleted file mode 100644 index fb58c8d3..00000000 --- a/kohana/libraries/drivers/Session.php +++ /dev/null @@ -1,70 +0,0 @@ - 'apc', - * 'requests' => 10000 - * ); - * Lifetime does not need to be set as it is - * overridden by the session expiration setting. - * - * $Id: Cache.php 3769 2008-12-15 00:48:56Z zombor $ - * - * @package Core - * @author Kohana Team - * @copyright (c) 2007-2008 Kohana Team - * @license http://kohanaphp.com/license.html - */ -class Session_Cache_Driver implements Session_Driver { - - protected $cache; - protected $encrypt; - - public function __construct() - { - // Load Encrypt library - if (Kohana::config('session.encryption')) - { - $this->encrypt = new Encrypt; - } - - Kohana::log('debug', 'Session Cache Driver Initialized'); - } - - public function open($path, $name) - { - $config = Kohana::config('session.storage'); - - if (empty($config)) - { - // Load the default group - $config = Kohana::config('cache.default'); - } - elseif (is_string($config)) - { - $name = $config; - - // Test the config group name - if (($config = Kohana::config('cache.'.$config)) === NULL) - throw new Kohana_Exception('cache.undefined_group', $name); - } - - $config['lifetime'] = (Kohana::config('session.expiration') == 0) ? 86400 : Kohana::config('session.expiration'); - $this->cache = new Cache($config); - - return is_object($this->cache); - } - - public function close() - { - return TRUE; - } - - public function read($id) - { - $id = 'session_'.$id; - if ($data = $this->cache->get($id)) - { - return Kohana::config('session.encryption') ? $this->encrypt->decode($data) : $data; - } - - // Return value must be string, NOT a boolean - return ''; - } - - public function write($id, $data) - { - $id = 'session_'.$id; - $data = Kohana::config('session.encryption') ? $this->encrypt->encode($data) : $data; - - return $this->cache->set($id, $data); - } - - public function destroy($id) - { - $id = 'session_'.$id; - return $this->cache->delete($id); - } - - public function regenerate() - { - session_regenerate_id(TRUE); - - // Return new session id - return session_id(); - } - - public function gc($maxlifetime) - { - // Just return, caches are automatically cleaned up - return TRUE; - } - -} // End Session Cache Driver diff --git a/kohana/libraries/drivers/Session/Cookie.php b/kohana/libraries/drivers/Session/Cookie.php deleted file mode 100644 index 7b791064..00000000 --- a/kohana/libraries/drivers/Session/Cookie.php +++ /dev/null @@ -1,80 +0,0 @@ -cookie_name = Kohana::config('session.name').'_data'; - - if (Kohana::config('session.encryption')) - { - $this->encrypt = Encrypt::instance(); - } - - Kohana::log('debug', 'Session Cookie Driver Initialized'); - } - - public function open($path, $name) - { - return TRUE; - } - - public function close() - { - return TRUE; - } - - public function read($id) - { - $data = (string) cookie::get($this->cookie_name); - - if ($data == '') - return $data; - - return empty($this->encrypt) ? base64_decode($data) : $this->encrypt->decode($data); - } - - public function write($id, $data) - { - $data = empty($this->encrypt) ? base64_encode($data) : $this->encrypt->encode($data); - - if (strlen($data) > 4048) - { - Kohana::log('error', 'Session ('.$id.') data exceeds the 4KB limit, ignoring write.'); - return FALSE; - } - - return cookie::set($this->cookie_name, $data, Kohana::config('session.expiration')); - } - - public function destroy($id) - { - return cookie::delete($this->cookie_name); - } - - public function regenerate() - { - session_regenerate_id(TRUE); - - // Return new id - return session_id(); - } - - public function gc($maxlifetime) - { - return TRUE; - } - -} // End Session Cookie Driver Class \ No newline at end of file diff --git a/kohana/libraries/drivers/Session/Database.php b/kohana/libraries/drivers/Session/Database.php deleted file mode 100644 index b4144ffb..00000000 --- a/kohana/libraries/drivers/Session/Database.php +++ /dev/null @@ -1,163 +0,0 @@ -encrypt = Encrypt::instance(); - } - - if (is_array($config['storage'])) - { - if ( ! empty($config['storage']['group'])) - { - // Set the group name - $this->db = $config['storage']['group']; - } - - if ( ! empty($config['storage']['table'])) - { - // Set the table name - $this->table = $config['storage']['table']; - } - } - - // Load database - $this->db = Database::instance($this->db); - - Kohana::log('debug', 'Session Database Driver Initialized'); - } - - public function open($path, $name) - { - return TRUE; - } - - public function close() - { - return TRUE; - } - - public function read($id) - { - // Load the session - $query = $this->db->from($this->table)->where('session_id', $id)->limit(1)->get()->result(TRUE); - - if ($query->count() === 0) - { - // No current session - $this->session_id = NULL; - - return ''; - } - - // Set the current session id - $this->session_id = $id; - - // Load the data - $data = $query->current()->data; - - return ($this->encrypt === NULL) ? base64_decode($data) : $this->encrypt->decode($data); - } - - public function write($id, $data) - { - $data = array - ( - 'session_id' => $id, - 'last_activity' => time(), - 'data' => ($this->encrypt === NULL) ? base64_encode($data) : $this->encrypt->encode($data) - ); - - if ($this->session_id === NULL) - { - // Insert a new session - $query = $this->db->insert($this->table, $data); - } - elseif ($id === $this->session_id) - { - // Do not update the session_id - unset($data['session_id']); - - // Update the existing session - $query = $this->db->update($this->table, $data, array('session_id' => $id)); - } - else - { - // Update the session and id - $query = $this->db->update($this->table, $data, array('session_id' => $this->session_id)); - - // Set the new session id - $this->session_id = $id; - } - - return (bool) $query->count(); - } - - public function destroy($id) - { - // Delete the requested session - $this->db->delete($this->table, array('session_id' => $id)); - - // Session id is no longer valid - $this->session_id = NULL; - - return TRUE; - } - - public function regenerate() - { - // Generate a new session id - session_regenerate_id(); - - // Return new session id - return session_id(); - } - - public function gc($maxlifetime) - { - // Delete all expired sessions - $query = $this->db->delete($this->table, array('last_activity <' => time() - $maxlifetime)); - - Kohana::log('debug', 'Session garbage collected: '.$query->count().' row(s) deleted.'); - - return TRUE; - } - -} // End Session Database Driver diff --git a/kohana/views/kohana/template.php b/kohana/views/kohana/template.php deleted file mode 100644 index b090fd88..00000000 --- a/kohana/views/kohana/template.php +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - <?php echo html::specialchars($title) ?> - - - - - - -

      - - - - - - \ No newline at end of file diff --git a/kohana/views/kohana_error_disabled.php b/kohana/views/kohana_error_disabled.php deleted file mode 100644 index cd911328..00000000 --- a/kohana/views/kohana_error_disabled.php +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - -<?php echo $error ?> - - -
      -

      -

      -
      - - \ No newline at end of file diff --git a/kohana/views/kohana_error_page.php b/kohana/views/kohana_error_page.php deleted file mode 100644 index 944064cc..00000000 --- a/kohana/views/kohana_error_page.php +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - -<?php echo $error ?> - - - -
      -

      -

      - -

      - -

      - -

      - - -

      -
      - - \ No newline at end of file diff --git a/kohana/views/kohana_errors.css b/kohana/views/kohana_errors.css deleted file mode 100644 index 1341f57d..00000000 --- a/kohana/views/kohana_errors.css +++ /dev/null @@ -1,21 +0,0 @@ -div#framework_error { background:#fff; border:solid 1px #ccc; font-family:sans-serif; color:#111; font-size:14px; line-height:130%; } -div#framework_error h3 { color:#fff; font-size:16px; padding:8px 6px; margin:0 0 8px; background:#f15a00; text-align:center; } -div#framework_error a { color:#228; text-decoration:none; } -div#framework_error a:hover { text-decoration:underline; } -div#framework_error strong { color:#900; } -div#framework_error p { margin:0; padding:4px 6px 10px; } -div#framework_error tt, -div#framework_error pre, -div#framework_error code { font-family:monospace; padding:2px 4px; font-size:12px; color:#333; - white-space:pre-wrap; /* CSS 2.1 */ - white-space:-moz-pre-wrap; /* For Mozilla */ - word-wrap:break-word; /* For IE5.5+ */ -} -div#framework_error tt { font-style:italic; } -div#framework_error tt:before { content:">"; color:#aaa; } -div#framework_error code tt:before { content:""; } -div#framework_error pre, -div#framework_error code { background:#eaeee5; border:solid 0 #D6D8D1; border-width:0 1px 1px 0; } -div#framework_error .block { display:block; text-align:left; } -div#framework_error .stats { padding:4px; background: #eee; border-top:solid 1px #ccc; text-align:center; font-size:10px; color:#888; } -div#framework_error .backtrace { margin:0; padding:0 6px; list-style:none; line-height:12px; } \ No newline at end of file diff --git a/kohana/views/kohana_profiler.php b/kohana/views/kohana_profiler.php deleted file mode 100644 index da77a669..00000000 --- a/kohana/views/kohana_profiler.php +++ /dev/null @@ -1,37 +0,0 @@ - - -
      -render(); -} -?> -

      Profiler executed in s

      -
      \ No newline at end of file diff --git a/kohana/views/kohana_profiler_table.css b/kohana/views/kohana_profiler_table.css deleted file mode 100644 index 6e7601c9..00000000 --- a/kohana/views/kohana_profiler_table.css +++ /dev/null @@ -1,53 +0,0 @@ -#kohana-profiler .kp-table -{ - font-size: 1.0em; - color: #4D6171; - width: 100%; - border-collapse: collapse; - border-top: 1px solid #E5EFF8; - border-right: 1px solid #E5EFF8; - border-left: 1px solid #E5EFF8; - margin-bottom: 10px; -} -#kohana-profiler .kp-table td -{ - background-color: #FFFFFF; - border-bottom: 1px solid #E5EFF8; - padding: 3px; - vertical-align: top; -} -#kohana-profiler .kp-table .kp-title td -{ - font-weight: bold; - background-color: inherit; -} -#kohana-profiler .kp-table .kp-altrow td -{ - background-color: #F7FBFF; -} -#kohana-profiler .kp-table .kp-totalrow td -{ - background-color: #FAFAFA; - border-top: 1px solid #D2DCE5; - font-weight: bold; -} -#kohana-profiler .kp-table .kp-column -{ - width: 100px; - border-left: 1px solid #E5EFF8; - text-align: center; -} -#kohana-profiler .kp-table .kp-data, #kohana-profiler .kp-table .kp-name -{ - background-color: #FAFAFB; - vertical-align: top; -} -#kohana-profiler .kp-table .kp-name -{ - width: 200px; - border-right: 1px solid #E5EFF8; -} -#kohana-profiler .kp-table .kp-altrow .kp-data, #kohana-profiler .kp-table .kp-altrow .kp-name -{ - background-color: #F6F8FB; -} \ No newline at end of file diff --git a/kohana/views/kohana_profiler_table.php b/kohana/views/kohana_profiler_table.php deleted file mode 100644 index b6b46530..00000000 --- a/kohana/views/kohana_profiler_table.php +++ /dev/null @@ -1,25 +0,0 @@ - - - - > - $column) - { - $class = empty($column['class']) ? '' : ' class="'.$column['class'].'"'; - $style = empty($column['style']) ? '' : ' style="'.$column['style'].'"'; - $value = $row['data'][$index]; - $value = (is_array($value) OR is_object($value)) ? '
      '.html::specialchars(print_r($value, TRUE)).'
      ' : html::specialchars($value); - echo '', $value, ''; - } - ?> - - -
      \ No newline at end of file diff --git a/system/KohanaLicense.html b/system/KohanaLicense.html new file mode 100644 index 00000000..de2d5299 --- /dev/null +++ b/system/KohanaLicense.html @@ -0,0 +1,30 @@ + + + + + +Kohana License + + + + +

      Kohana License Agreement

      + +

      This license is a legal agreement between you and the Kohana Software Foundation for the use of Kohana Framework (the "Software"). By obtaining the Software you agree to comply with the terms and conditions of this license.

      + +

      Copyright (c) 2007-2008 Kohana Team
      All rights reserved.

      + +

      Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

      + +
        +
      • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
      • +
      • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
      • +
      • Neither the name of the Kohana nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
      • +
      + +

      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

      + +

      NOTE: This license is modeled after the BSD software license.

      + + + \ No newline at end of file diff --git a/system/config/cache.php b/system/config/cache.php new file mode 100644 index 00000000..ccd3da4d --- /dev/null +++ b/system/config/cache.php @@ -0,0 +1,32 @@ + File cache is fast and reliable, but requires many filesystem lookups. + * > Database cache can be used to cache items remotely, but is slower. + * > Memcache is very high performance, but prevents cache tags from being used. + * + * params - Driver parameters, specific to each driver. + * + * lifetime - Default lifetime of caches in seconds. By default caches are stored for + * thirty minutes. Specific lifetime can also be set when creating a new cache. + * Setting this to 0 will never automatically delete caches. + * + * requests - Average number of cache requests that will processed before all expired + * caches are deleted. This is commonly referred to as "garbage collection". + * Setting this to 0 or a negative number will disable automatic garbage collection. + */ +$config['default'] = array +( + 'driver' => 'file', + 'params' => APPPATH.'cache', + 'lifetime' => 1800, + 'requests' => 1000 +); diff --git a/system/config/cache_memcache.php b/system/config/cache_memcache.php new file mode 100644 index 00000000..43d8f205 --- /dev/null +++ b/system/config/cache_memcache.php @@ -0,0 +1,20 @@ + '127.0.0.1', + 'port' => 11211, + 'persistent' => FALSE, + ) +); + +/** + * Enable cache data compression. + */ +$config['compression'] = FALSE; diff --git a/system/config/cache_sqlite.php b/system/config/cache_sqlite.php new file mode 100644 index 00000000..818b8932 --- /dev/null +++ b/system/config/cache_sqlite.php @@ -0,0 +1,10 @@ + array + ( + 'length' => '13,14,15,16,17,18,19', + 'prefix' => '', + 'luhn' => TRUE + ), + 'american express' => array + ( + 'length' => '15', + 'prefix' => '3[47]', + 'luhn' => TRUE + ), + 'diners club' => array + ( + 'length' => '14,16', + 'prefix' => '36|55|30[0-5]', + 'luhn' => TRUE + ), + 'discover' => array + ( + 'length' => '16', + 'prefix' => '6(?:5|011)', + 'luhn' => TRUE, + ), + 'jcb' => array + ( + 'length' => '15,16', + 'prefix' => '3|1800|2131', + 'luhn' => TRUE + ), + 'maestro' => array + ( + 'length' => '16,18', + 'prefix' => '50(?:20|38)|6(?:304|759)', + 'luhn' => TRUE + ), + 'mastercard' => array + ( + 'length' => '16', + 'prefix' => '5[1-5]', + 'luhn' => TRUE + ), + 'visa' => array + ( + 'length' => '13,16', + 'prefix' => '4', + 'luhn' => TRUE + ), +); \ No newline at end of file diff --git a/system/config/database.php b/system/config/database.php new file mode 100644 index 00000000..6519156a --- /dev/null +++ b/system/config/database.php @@ -0,0 +1,45 @@ + 'mysql://dbuser:secret@localhost/kohana' + * character_set - Database character set + * table_prefix - Database table prefix + * object - Enable or disable object results + * cache - Enable or disable query caching + * escape - Enable automatic query builder escaping + */ +$config['default'] = array +( + 'benchmark' => TRUE, + 'persistent' => FALSE, + 'connection' => array + ( + 'type' => 'mysql', + 'user' => 'dbuser', + 'pass' => 'p@ssw0rd', + 'host' => 'localhost', + 'port' => FALSE, + 'socket' => FALSE, + 'database' => 'kohana' + ), + 'character_set' => 'utf8', + 'table_prefix' => '', + 'object' => TRUE, + 'cache' => FALSE, + 'escape' => TRUE +); \ No newline at end of file diff --git a/system/config/email.php b/system/config/email.php new file mode 100644 index 00000000..c768367c --- /dev/null +++ b/system/config/email.php @@ -0,0 +1,22 @@ + 'K0H@NA+PHP_7hE-SW!FtFraM3w0R|<', + 'mode' => MCRYPT_MODE_NOFB, + 'cipher' => MCRYPT_RIJNDAEL_128 +); diff --git a/system/config/http.php b/system/config/http.php new file mode 100644 index 00000000..3c4a86ac --- /dev/null +++ b/system/config/http.php @@ -0,0 +1,19 @@ + 'children', + 'clothes' => 'clothing', + 'man' => 'men', + 'movie' => 'movies', + 'person' => 'people', + 'woman' => 'women', + 'mouse' => 'mice', + 'goose' => 'geese', + 'ox' => 'oxen', + 'leaf' => 'leaves', + 'course' => 'courses', + 'size' => 'sizes', +); diff --git a/system/config/locale.php b/system/config/locale.php new file mode 100644 index 00000000..3a268820 --- /dev/null +++ b/system/config/locale.php @@ -0,0 +1,16 @@ + array('text/h323'), + '7z' => array('application/x-7z-compressed'), + 'abw' => array('application/x-abiword'), + 'acx' => array('application/internet-property-stream'), + 'ai' => array('application/postscript'), + 'aif' => array('audio/x-aiff'), + 'aifc' => array('audio/x-aiff'), + 'aiff' => array('audio/x-aiff'), + 'asf' => array('video/x-ms-asf'), + 'asr' => array('video/x-ms-asf'), + 'asx' => array('video/x-ms-asf'), + 'atom' => array('application/atom+xml'), + 'avi' => array('video/avi', 'video/msvideo', 'video/x-msvideo'), + 'bin' => array('application/octet-stream','application/macbinary'), + 'bmp' => array('image/bmp'), + 'c' => array('text/x-csrc'), + 'c++' => array('text/x-c++src'), + 'cab' => array('application/x-cab'), + 'cc' => array('text/x-c++src'), + 'cda' => array('application/x-cdf'), + 'class' => array('application/octet-stream'), + 'cpp' => array('text/x-c++src'), + 'cpt' => array('application/mac-compactpro'), + 'csh' => array('text/x-csh'), + 'css' => array('text/css'), + 'csv' => array('text/x-comma-separated-values', 'application/vnd.ms-excel', 'text/comma-separated-values', 'text/csv'), + 'dbk' => array('application/docbook+xml'), + 'dcr' => array('application/x-director'), + 'deb' => array('application/x-debian-package'), + 'diff' => array('text/x-diff'), + 'dir' => array('application/x-director'), + 'divx' => array('video/divx'), + 'dll' => array('application/octet-stream', 'application/x-msdos-program'), + 'dmg' => array('application/x-apple-diskimage'), + 'dms' => array('application/octet-stream'), + 'doc' => array('application/msword'), + 'dvi' => array('application/x-dvi'), + 'dxr' => array('application/x-director'), + 'eml' => array('message/rfc822'), + 'eps' => array('application/postscript'), + 'evy' => array('application/envoy'), + 'exe' => array('application/x-msdos-program', 'application/octet-stream'), + 'fla' => array('application/octet-stream'), + 'flac' => array('application/x-flac'), + 'flc' => array('video/flc'), + 'fli' => array('video/fli'), + 'flv' => array('video/x-flv'), + 'gif' => array('image/gif'), + 'gtar' => array('application/x-gtar'), + 'gz' => array('application/x-gzip'), + 'h' => array('text/x-chdr'), + 'h++' => array('text/x-c++hdr'), + 'hh' => array('text/x-c++hdr'), + 'hpp' => array('text/x-c++hdr'), + 'hqx' => array('application/mac-binhex40'), + 'hs' => array('text/x-haskell'), + 'htm' => array('text/html'), + 'html' => array('text/html'), + 'ico' => array('image/x-icon'), + 'ics' => array('text/calendar'), + 'iii' => array('application/x-iphone'), + 'ins' => array('application/x-internet-signup'), + 'iso' => array('application/x-iso9660-image'), + 'isp' => array('application/x-internet-signup'), + 'jar' => array('application/java-archive'), + 'java' => array('application/x-java-applet'), + 'jpe' => array('image/jpeg', 'image/pjpeg'), + 'jpeg' => array('image/jpeg', 'image/pjpeg'), + 'jpg' => array('image/jpeg', 'image/pjpeg'), + 'js' => array('application/x-javascript'), + 'json' => array('application/json'), + 'latex' => array('application/x-latex'), + 'lha' => array('application/octet-stream'), + 'log' => array('text/plain', 'text/x-log'), + 'lzh' => array('application/octet-stream'), + 'm4a' => array('audio/mpeg'), + 'm4p' => array('video/mp4v-es'), + 'm4v' => array('video/mp4'), + 'man' => array('application/x-troff-man'), + 'mdb' => array('application/x-msaccess'), + 'midi' => array('audio/midi'), + 'mid' => array('audio/midi'), + 'mif' => array('application/vnd.mif'), + 'mka' => array('audio/x-matroska'), + 'mkv' => array('video/x-matroska'), + 'mov' => array('video/quicktime'), + 'movie' => array('video/x-sgi-movie'), + 'mp2' => array('audio/mpeg'), + 'mp3' => array('audio/mpeg'), + 'mp4' => array('application/mp4','audio/mp4','video/mp4'), + 'mpa' => array('video/mpeg'), + 'mpe' => array('video/mpeg'), + 'mpeg' => array('video/mpeg'), + 'mpg' => array('video/mpeg'), + 'mpg4' => array('video/mp4'), + 'mpga' => array('audio/mpeg'), + 'mpp' => array('application/vnd.ms-project'), + 'mpv' => array('video/x-matroska'), + 'mpv2' => array('video/mpeg'), + 'ms' => array('application/x-troff-ms'), + 'msg' => array('application/msoutlook','application/x-msg'), + 'msi' => array('application/x-msi'), + 'nws' => array('message/rfc822'), + 'oda' => array('application/oda'), + 'odb' => array('application/vnd.oasis.opendocument.database'), + 'odc' => array('application/vnd.oasis.opendocument.chart'), + 'odf' => array('application/vnd.oasis.opendocument.forumla'), + 'odg' => array('application/vnd.oasis.opendocument.graphics'), + 'odi' => array('application/vnd.oasis.opendocument.image'), + 'odm' => array('application/vnd.oasis.opendocument.text-master'), + 'odp' => array('application/vnd.oasis.opendocument.presentation'), + 'ods' => array('application/vnd.oasis.opendocument.spreadsheet'), + 'odt' => array('application/vnd.oasis.opendocument.text'), + 'oga' => array('audio/ogg'), + 'ogg' => array('application/ogg'), + 'ogv' => array('video/ogg'), + 'otg' => array('application/vnd.oasis.opendocument.graphics-template'), + 'oth' => array('application/vnd.oasis.opendocument.web'), + 'otp' => array('application/vnd.oasis.opendocument.presentation-template'), + 'ots' => array('application/vnd.oasis.opendocument.spreadsheet-template'), + 'ott' => array('application/vnd.oasis.opendocument.template'), + 'p' => array('text/x-pascal'), + 'pas' => array('text/x-pascal'), + 'patch' => array('text/x-diff'), + 'pbm' => array('image/x-portable-bitmap'), + 'pdf' => array('application/pdf', 'application/x-download'), + 'php' => array('application/x-httpd-php'), + 'php3' => array('application/x-httpd-php'), + 'php4' => array('application/x-httpd-php'), + 'php5' => array('application/x-httpd-php'), + 'phps' => array('application/x-httpd-php-source'), + 'phtml' => array('application/x-httpd-php'), + 'pl' => array('text/x-perl'), + 'pm' => array('text/x-perl'), + 'png' => array('image/png', 'image/x-png'), + 'po' => array('text/x-gettext-translation'), + 'pot' => array('application/vnd.ms-powerpoint'), + 'pps' => array('application/vnd.ms-powerpoint'), + 'ppt' => array('application/powerpoint'), + 'ps' => array('application/postscript'), + 'psd' => array('application/x-photoshop', 'image/x-photoshop'), + 'pub' => array('application/x-mspublisher'), + 'py' => array('text/x-python'), + 'qt' => array('video/quicktime'), + 'ra' => array('audio/x-realaudio'), + 'ram' => array('audio/x-realaudio', 'audio/x-pn-realaudio'), + 'rar' => array('application/rar'), + 'rgb' => array('image/x-rgb'), + 'rm' => array('audio/x-pn-realaudio'), + 'rpm' => array('audio/x-pn-realaudio-plugin', 'application/x-redhat-package-manager'), + 'rss' => array('application/rss+xml'), + 'rtf' => array('text/rtf'), + 'rtx' => array('text/richtext'), + 'rv' => array('video/vnd.rn-realvideo'), + 'sea' => array('application/octet-stream'), + 'sh' => array('text/x-sh'), + 'shtml' => array('text/html'), + 'sit' => array('application/x-stuffit'), + 'smi' => array('application/smil'), + 'smil' => array('application/smil'), + 'so' => array('application/octet-stream'), + 'src' => array('application/x-wais-source'), + 'svg' => array('image/svg+xml'), + 'swf' => array('application/x-shockwave-flash'), + 't' => array('application/x-troff'), + 'tar' => array('application/x-tar'), + 'tcl' => array('text/x-tcl'), + 'tex' => array('application/x-tex'), + 'text' => array('text/plain'), + 'texti' => array('application/x-texinfo'), + 'textinfo' => array('application/x-texinfo'), + 'tgz' => array('application/x-tar'), + 'tif' => array('image/tiff'), + 'tiff' => array('image/tiff'), + 'torrent' => array('application/x-bittorrent'), + 'tr' => array('application/x-troff'), + 'tsv' => array('text/tab-separated-values'), + 'txt' => array('text/plain'), + 'wav' => array('audio/x-wav'), + 'wax' => array('audio/x-ms-wax'), + 'wbxml' => array('application/wbxml'), + 'wm' => array('video/x-ms-wm'), + 'wma' => array('audio/x-ms-wma'), + 'wmd' => array('application/x-ms-wmd'), + 'wmlc' => array('application/wmlc'), + 'wmv' => array('video/x-ms-wmv', 'application/octet-stream'), + 'wmx' => array('video/x-ms-wmx'), + 'wmz' => array('application/x-ms-wmz'), + 'word' => array('application/msword', 'application/octet-stream'), + 'wp5' => array('application/wordperfect5.1'), + 'wpd' => array('application/vnd.wordperfect'), + 'wvx' => array('video/x-ms-wvx'), + 'xbm' => array('image/x-xbitmap'), + 'xcf' => array('image/xcf'), + 'xhtml' => array('application/xhtml+xml'), + 'xht' => array('application/xhtml+xml'), + 'xl' => array('application/excel', 'application/vnd.ms-excel'), + 'xla' => array('application/excel', 'application/vnd.ms-excel'), + 'xlc' => array('application/excel', 'application/vnd.ms-excel'), + 'xlm' => array('application/excel', 'application/vnd.ms-excel'), + 'xls' => array('application/excel', 'application/vnd.ms-excel'), + 'xlt' => array('application/excel', 'application/vnd.ms-excel'), + 'xml' => array('text/xml'), + 'xof' => array('x-world/x-vrml'), + 'xpm' => array('image/x-xpixmap'), + 'xsl' => array('text/xml'), + 'xvid' => array('video/x-xvid'), + 'xwd' => array('image/x-xwindowdump'), + 'z' => array('application/x-compress'), + 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed') +); diff --git a/system/config/pagination.php b/system/config/pagination.php new file mode 100644 index 00000000..808fc315 --- /dev/null +++ b/system/config/pagination.php @@ -0,0 +1,25 @@ + 'pagination', + 'style' => 'classic', + 'uri_segment' => 3, + 'query_string' => '', + 'items_per_page' => 20, + 'auto_hide' => FALSE, +); diff --git a/system/config/profiler.php b/system/config/profiler.php new file mode 100644 index 00000000..98ab5a49 --- /dev/null +++ b/system/config/profiler.php @@ -0,0 +1,8 @@ + array('type' => 'int', 'max' => 127), + 'smallint' => array('type' => 'int', 'max' => 32767), + 'mediumint' => array('type' => 'int', 'max' => 8388607), + 'int' => array('type' => 'int', 'max' => 2147483647), + 'integer' => array('type' => 'int', 'max' => 2147483647), + 'bigint' => array('type' => 'int', 'max' => 9223372036854775807), + 'float' => array('type' => 'float'), + 'float unsigned' => array('type' => 'float', 'min' => 0), + 'boolean' => array('type' => 'boolean'), + 'time' => array('type' => 'string', 'format' => '00:00:00'), + 'time with time zone' => array('type' => 'string'), + 'date' => array('type' => 'string', 'format' => '0000-00-00'), + 'year' => array('type' => 'string', 'format' => '0000'), + 'datetime' => array('type' => 'string', 'format' => '0000-00-00 00:00:00'), + 'timestamp with time zone' => array('type' => 'string'), + 'char' => array('type' => 'string', 'exact' => TRUE), + 'binary' => array('type' => 'string', 'binary' => TRUE, 'exact' => TRUE), + 'varchar' => array('type' => 'string'), + 'varbinary' => array('type' => 'string', 'binary' => TRUE), + 'blob' => array('type' => 'string', 'binary' => TRUE), + 'text' => array('type' => 'string') +); + +// DOUBLE +$config['double'] = $config['double precision'] = $config['decimal'] = $config['real'] = $config['numeric'] = $config['float']; +$config['double unsigned'] = $config['float unsigned']; + +// BIT +$config['bit'] = $config['boolean']; + +// TIMESTAMP +$config['timestamp'] = $config['timestamp without time zone'] = $config['datetime']; + +// ENUM +$config['enum'] = $config['set'] = $config['varchar']; + +// TEXT +$config['tinytext'] = $config['mediumtext'] = $config['longtext'] = $config['text']; + +// BLOB +$config['tinyblob'] = $config['mediumblob'] = $config['longblob'] = $config['clob'] = $config['bytea'] = $config['blob']; + +// CHARACTER +$config['character'] = $config['char']; +$config['character varying'] = $config['varchar']; + +// TIME +$config['time without time zone'] = $config['time']; diff --git a/system/config/upload.php b/system/config/upload.php new file mode 100644 index 00000000..df26a2d2 --- /dev/null +++ b/system/config/upload.php @@ -0,0 +1,17 @@ + 'Windows Vista', + 'windows nt 5.2' => 'Windows 2003', + 'windows nt 5.0' => 'Windows 2000', + 'windows nt 5.1' => 'Windows XP', + 'windows nt 4.0' => 'Windows NT', + 'winnt4.0' => 'Windows NT', + 'winnt 4.0' => 'Windows NT', + 'winnt' => 'Windows NT', + 'windows 98' => 'Windows 98', + 'win98' => 'Windows 98', + 'windows 95' => 'Windows 95', + 'win95' => 'Windows 95', + 'windows' => 'Unknown Windows OS', + 'os x' => 'Mac OS X', + 'intel mac' => 'Intel Mac', + 'ppc mac' => 'PowerPC Mac', + 'powerpc' => 'PowerPC', + 'ppc' => 'PowerPC', + 'cygwin' => 'Cygwin', + 'linux' => 'Linux', + 'debian' => 'Debian', + 'openvms' => 'OpenVMS', + 'sunos' => 'Sun Solaris', + 'amiga' => 'Amiga', + 'beos' => 'BeOS', + 'apachebench' => 'ApacheBench', + 'freebsd' => 'FreeBSD', + 'netbsd' => 'NetBSD', + 'bsdi' => 'BSDi', + 'openbsd' => 'OpenBSD', + 'os/2' => 'OS/2', + 'warp' => 'OS/2', + 'aix' => 'AIX', + 'irix' => 'Irix', + 'osf' => 'DEC OSF', + 'hp-ux' => 'HP-UX', + 'hurd' => 'GNU/Hurd', + 'unix' => 'Unknown Unix OS', +); + +// The order of this array should NOT be changed. Many browsers return +// multiple browser types so we want to identify the sub-type first. +$config['browser'] = array +( + 'Opera' => 'Opera', + 'MSIE' => 'Internet Explorer', + 'Internet Explorer' => 'Internet Explorer', + 'Shiira' => 'Shiira', + 'Firefox' => 'Firefox', + 'Chimera' => 'Chimera', + 'Phoenix' => 'Phoenix', + 'Firebird' => 'Firebird', + 'Camino' => 'Camino', + 'Netscape' => 'Netscape', + 'OmniWeb' => 'OmniWeb', + 'Chrome' => 'Chrome', + 'Safari' => 'Safari', + 'Konqueror' => 'Konqueror', + 'Epiphany' => 'Epiphany', + 'Galeon' => 'Galeon', + 'Mozilla' => 'Mozilla', + 'icab' => 'iCab', + 'lynx' => 'Lynx', + 'links' => 'Links', + 'hotjava' => 'HotJava', + 'amaya' => 'Amaya', + 'IBrowse' => 'IBrowse', +); + +$config['mobile'] = array +( + 'mobileexplorer' => 'Mobile Explorer', + 'openwave' => 'Open Wave', + 'opera mini' => 'Opera Mini', + 'operamini' => 'Opera Mini', + 'elaine' => 'Palm', + 'palmsource' => 'Palm', + 'digital paths' => 'Palm', + 'avantgo' => 'Avantgo', + 'xiino' => 'Xiino', + 'palmscape' => 'Palmscape', + 'nokia' => 'Nokia', + 'ericsson' => 'Ericsson', + 'blackBerry' => 'BlackBerry', + 'motorola' => 'Motorola', + 'iphone' => 'iPhone', + 'android' => 'Android', +); + +// There are hundreds of bots but these are the most common. +$config['robot'] = array +( + 'googlebot' => 'Googlebot', + 'msnbot' => 'MSNBot', + 'slurp' => 'Inktomi Slurp', + 'yahoo' => 'Yahoo', + 'askjeeves' => 'AskJeeves', + 'fastcrawler' => 'FastCrawler', + 'infoseek' => 'InfoSeek Robot 1.0', + 'lycos' => 'Lycos', +); \ No newline at end of file diff --git a/system/config/view.php b/system/config/view.php new file mode 100644 index 00000000..6bed22e0 --- /dev/null +++ b/system/config/view.php @@ -0,0 +1,17 @@ +template = new View($this->template); + + if ($this->auto_render == TRUE) + { + // Render the template immediately after the controller method + Event::add('system.post_controller', array($this, '_render')); + } + } + + /** + * Render the loaded template. + */ + public function _render() + { + if ($this->auto_render == TRUE) + { + // Render the template when the class is destroyed + $this->template->render(TRUE); + } + } + +} // End Template_Controller \ No newline at end of file diff --git a/system/core/Benchmark.php b/system/core/Benchmark.php new file mode 100644 index 00000000..ce230f11 --- /dev/null +++ b/system/core/Benchmark.php @@ -0,0 +1,125 @@ + microtime(TRUE), + 'stop' => FALSE, + 'memory_start' => self::memory_usage(), + 'memory_stop' => FALSE + ); + + array_unshift(self::$marks[$name], $mark); + } + + /** + * Set a benchmark stop point. + * + * @param string benchmark name + * @return void + */ + public static function stop($name) + { + if (isset(self::$marks[$name]) AND self::$marks[$name][0]['stop'] === FALSE) + { + self::$marks[$name][0]['stop'] = microtime(TRUE); + self::$marks[$name][0]['memory_stop'] = self::memory_usage(); + } + } + + /** + * Get the elapsed time between a start and stop. + * + * @param string benchmark name, TRUE for all + * @param integer number of decimal places to count to + * @return array + */ + public static function get($name, $decimals = 4) + { + if ($name === TRUE) + { + $times = array(); + $names = array_keys(self::$marks); + + foreach ($names as $name) + { + // Get each mark recursively + $times[$name] = self::get($name, $decimals); + } + + // Return the array + return $times; + } + + if ( ! isset(self::$marks[$name])) + return FALSE; + + if (self::$marks[$name][0]['stop'] === FALSE) + { + // Stop the benchmark to prevent mis-matched results + self::stop($name); + } + + // Return a string version of the time between the start and stop points + // Properly reading a float requires using number_format or sprintf + $time = $memory = 0; + for ($i = 0; $i < count(self::$marks[$name]); $i++) + { + $time += self::$marks[$name][$i]['stop'] - self::$marks[$name][$i]['start']; + $memory += self::$marks[$name][$i]['memory_stop'] - self::$marks[$name][$i]['memory_start']; + } + + return array + ( + 'time' => number_format($time, $decimals), + 'memory' => $memory, + 'count' => count(self::$marks[$name]) + ); + } + + /** + * Returns the current memory usage. This is only possible if the + * memory_get_usage function is supported in PHP. + * + * @return integer + */ + private static function memory_usage() + { + static $func; + + if ($func === NULL) + { + // Test if memory usage can be seen + $func = function_exists('memory_get_usage'); + } + + return $func ? memory_get_usage() : 0; + } + +} // End Benchmark diff --git a/system/core/Bootstrap.php b/system/core/Bootstrap.php new file mode 100644 index 00000000..edfb086d --- /dev/null +++ b/system/core/Bootstrap.php @@ -0,0 +1,58 @@ + $event_callback) + { + if ($callback === $event_callback) + { + unset(self::$events[$name][$i]); + } + } + } + } + + /** + * Execute all of the callbacks attached to an event. + * + * @param string event name + * @param array data can be processed as Event::$data by the callbacks + * @return void + */ + public static function run($name, & $data = NULL) + { + if ( ! empty(self::$events[$name])) + { + // So callbacks can access Event::$data + self::$data =& $data; + $callbacks = self::get($name); + + foreach ($callbacks as $callback) + { + call_user_func($callback); + } + + // Do this to prevent data from getting 'stuck' + $clear_data = ''; + self::$data =& $clear_data; + } + + // The event has been run! + self::$has_run[$name] = $name; + } + + /** + * Check if a given event has been run. + * + * @param string event name + * @return boolean + */ + public static function has_run($name) + { + return isset(self::$has_run[$name]); + } + +} // End Event \ No newline at end of file diff --git a/system/core/Kohana.php b/system/core/Kohana.php new file mode 100644 index 00000000..ac64b5b8 --- /dev/null +++ b/system/core/Kohana.php @@ -0,0 +1,1744 @@ + 1, + 'alert' => 2, + 'info' => 3, + 'debug' => 4, + ); + + // Internal caches and write status + private static $internal_cache = array(); + private static $write_cache; + private static $internal_cache_path; + + /** + * Sets up the PHP environment. Adds error/exception handling, output + * buffering, and adds an auto-loading method for loading classes. + * + * This method is run immediately when this file is loaded, and is + * benchmarked as environment_setup. + * + * For security, this function also destroys the $_REQUEST global variable. + * Using the proper global (GET, POST, COOKIE, etc) is inherently more secure. + * The recommended way to fetch a global variable is using the Input library. + * @see http://www.php.net/globals + * + * @return void + */ + public static function setup() + { + static $run; + + // This function can only be run once + if ($run === TRUE) + return; + + // Start the environment setup benchmark + Benchmark::start(SYSTEM_BENCHMARK.'_environment_setup'); + + // Define Kohana error constant + define('E_KOHANA', 42); + + // Define 404 error constant + define('E_PAGE_NOT_FOUND', 43); + + // Define database error constant + define('E_DATABASE_ERROR', 44); + + if (self::$cache_lifetime = self::config('core.internal_cache')) + { + // Set the directory to be used for the internal cache + if ( ! self::$internal_cache_path = self::config('core.internal_cache_path')) + { + self::$internal_cache_path = APPPATH.'cache/'; + } + + // Load cached configuration and language files + self::$internal_cache['configuration'] = self::cache('configuration', self::$cache_lifetime); + self::$internal_cache['language'] = self::cache('language', self::$cache_lifetime); + + // Load cached file paths + self::$internal_cache['find_file_paths'] = self::cache('find_file_paths', self::$cache_lifetime); + + // Enable cache saving + Event::add('system.shutdown', array(__CLASS__, 'internal_cache_save')); + } + + // Disable notices and "strict" errors + $ER = error_reporting(~E_NOTICE & ~E_STRICT); + + // Set the user agent + self::$user_agent = ( ! empty($_SERVER['HTTP_USER_AGENT']) ? trim($_SERVER['HTTP_USER_AGENT']) : ''); + + if (function_exists('date_default_timezone_set')) + { + $timezone = self::config('locale.timezone'); + + // Set default timezone, due to increased validation of date settings + // which cause massive amounts of E_NOTICEs to be generated in PHP 5.2+ + date_default_timezone_set(empty($timezone) ? date_default_timezone_get() : $timezone); + } + + // Restore error reporting + error_reporting($ER); + + // Start output buffering + ob_start(array(__CLASS__, 'output_buffer')); + + // Save buffering level + self::$buffer_level = ob_get_level(); + + // Set autoloader + spl_autoload_register(array('Kohana', 'auto_load')); + + // Set error handler + set_error_handler(array('Kohana', 'exception_handler')); + + // Set exception handler + set_exception_handler(array('Kohana', 'exception_handler')); + + // Send default text/html UTF-8 header + header('Content-Type: text/html; charset=UTF-8'); + + // Load locales + $locales = self::config('locale.language'); + + // Make first locale UTF-8 + $locales[0] .= '.UTF-8'; + + // Set locale information + self::$locale = setlocale(LC_ALL, $locales); + + if (self::$configuration['core']['log_threshold'] > 0) + { + // Set the log directory + self::log_directory(self::$configuration['core']['log_directory']); + + // Enable log writing at shutdown + register_shutdown_function(array(__CLASS__, 'log_save')); + } + + // Enable Kohana routing + Event::add('system.routing', array('Router', 'find_uri')); + Event::add('system.routing', array('Router', 'setup')); + + // Enable Kohana controller initialization + Event::add('system.execute', array('Kohana', 'instance')); + + // Enable Kohana 404 pages + Event::add('system.404', array('Kohana', 'show_404')); + + // Enable Kohana output handling + Event::add('system.shutdown', array('Kohana', 'shutdown')); + + if (self::config('core.enable_hooks') === TRUE) + { + // Find all the hook files + $hooks = self::list_files('hooks', TRUE); + + foreach ($hooks as $file) + { + // Load the hook + include $file; + } + } + + // Setup is complete, prevent it from being run again + $run = TRUE; + + // Stop the environment setup routine + Benchmark::stop(SYSTEM_BENCHMARK.'_environment_setup'); + } + + /** + * Loads the controller and initializes it. Runs the pre_controller, + * post_controller_constructor, and post_controller events. Triggers + * a system.404 event when the route cannot be mapped to a controller. + * + * This method is benchmarked as controller_setup and controller_execution. + * + * @return object instance of controller + */ + public static function & instance() + { + if (self::$instance === NULL) + { + Benchmark::start(SYSTEM_BENCHMARK.'_controller_setup'); + + // Include the Controller file + require Router::$controller_path; + + try + { + // Start validation of the controller + $class = new ReflectionClass(ucfirst(Router::$controller).'_Controller'); + } + catch (ReflectionException $e) + { + // Controller does not exist + Event::run('system.404'); + } + + if ($class->isAbstract() OR (IN_PRODUCTION AND $class->getConstant('ALLOW_PRODUCTION') == FALSE)) + { + // Controller is not allowed to run in production + Event::run('system.404'); + } + + // Run system.pre_controller + Event::run('system.pre_controller'); + + // Create a new controller instance + $controller = $class->newInstance(); + + // Controller constructor has been executed + Event::run('system.post_controller_constructor'); + + try + { + // Load the controller method + $method = $class->getMethod(Router::$method); + + // Method exists + if (Router::$method[0] === '_') + { + // Do not allow access to hidden methods + Event::run('system.404'); + } + + if ($method->isProtected() or $method->isPrivate()) + { + // Do not attempt to invoke protected methods + throw new ReflectionException('protected controller method'); + } + + // Default arguments + $arguments = Router::$arguments; + } + catch (ReflectionException $e) + { + // Use __call instead + $method = $class->getMethod('__call'); + + // Use arguments in __call format + $arguments = array(Router::$method, Router::$arguments); + } + + // Stop the controller setup benchmark + Benchmark::stop(SYSTEM_BENCHMARK.'_controller_setup'); + + // Start the controller execution benchmark + Benchmark::start(SYSTEM_BENCHMARK.'_controller_execution'); + + // Execute the controller method + $method->invokeArgs($controller, $arguments); + + // Controller method has been executed + Event::run('system.post_controller'); + + // Stop the controller execution benchmark + Benchmark::stop(SYSTEM_BENCHMARK.'_controller_execution'); + } + + return self::$instance; + } + + /** + * Get all include paths. APPPATH is the first path, followed by module + * paths in the order they are configured, follow by the SYSPATH. + * + * @param boolean re-process the include paths + * @return array + */ + public static function include_paths($process = FALSE) + { + if ($process === TRUE) + { + self::$include_paths = array(); + + foreach (self::$configuration['core']['modules'] as $path) + { + if ($path = str_replace('\\', '/', realpath($path))) + { + // Add a valid path + self::$include_paths[] = $path.'/'; + } + } + + // Add APPPATH after all modules + self::$include_paths[] = APPPATH; + + // Add SYSPATH as the last path + self::$include_paths[] = SYSPATH; + } + + return self::$include_paths; + } + + /** + * Get a config item or group. + * + * @param string item name + * @param boolean force a forward slash (/) at the end of the item + * @param boolean is the item required? + * @return mixed + */ + public static function config($key, $slash = FALSE, $required = TRUE) + { + if (self::$configuration === NULL) + { + // Load core configuration + self::$configuration['core'] = self::config_load('core'); + + // Re-parse the include paths + self::include_paths(TRUE); + } + + // Get the group name from the key + $group = explode('.', $key, 2); + $group = $group[0]; + + if ( ! isset(self::$configuration[$group])) + { + // Load the configuration group + self::$configuration[$group] = self::config_load($group, $required); + } + + // Get the value of the key string + $value = self::key_string(self::$configuration, $key); + + if ($slash === TRUE AND is_string($value) AND $value !== '') + { + // Force the value to end with "/" + $value = rtrim($value, '/').'/'; + } + + return $value; + } + + /** + * Sets a configuration item, if allowed. + * + * @param string config key string + * @param string config value + * @return boolean + */ + public static function config_set($key, $value) + { + // Do this to make sure that the config array is already loaded + self::config($key); + + if (substr($key, 0, 7) === 'routes.') + { + // Routes cannot contain sub keys due to possible dots in regex + $keys = explode('.', $key, 2); + } + else + { + // Convert dot-noted key string to an array + $keys = explode('.', $key); + } + + // Used for recursion + $conf =& self::$configuration; + $last = count($keys) - 1; + + foreach ($keys as $i => $k) + { + if ($i === $last) + { + $conf[$k] = $value; + } + else + { + $conf =& $conf[$k]; + } + } + + if ($key === 'core.modules') + { + // Reprocess the include paths + self::include_paths(TRUE); + } + + return TRUE; + } + + /** + * Load a config file. + * + * @param string config filename, without extension + * @param boolean is the file required? + * @return array + */ + public static function config_load($name, $required = TRUE) + { + if ($name === 'core') + { + // Load the application configuration file + require APPPATH.'config/config'.EXT; + + if ( ! isset($config['site_domain'])) + { + // Invalid config file + die('Your Kohana application configuration file is not valid.'); + } + + return $config; + } + + if (isset(self::$internal_cache['configuration'][$name])) + return self::$internal_cache['configuration'][$name]; + + // Load matching configs + $configuration = array(); + + if ($files = self::find_file('config', $name, $required)) + { + foreach ($files as $file) + { + require $file; + + if (isset($config) AND is_array($config)) + { + // Merge in configuration + $configuration = array_merge($configuration, $config); + } + } + } + + if ( ! isset(self::$write_cache['configuration'])) + { + // Cache has changed + self::$write_cache['configuration'] = TRUE; + } + + return self::$internal_cache['configuration'][$name] = $configuration; + } + + /** + * Clears a config group from the cached configuration. + * + * @param string config group + * @return void + */ + public static function config_clear($group) + { + // Remove the group from config + unset(self::$configuration[$group], self::$internal_cache['configuration'][$group]); + + if ( ! isset(self::$write_cache['configuration'])) + { + // Cache has changed + self::$write_cache['configuration'] = TRUE; + } + } + + /** + * Add a new message to the log. + * + * @param string type of message + * @param string message text + * @return void + */ + public static function log($type, $message) + { + if (self::$log_levels[$type] <= self::$configuration['core']['log_threshold']) + { + $message = array(date('Y-m-d H:i:s P'), $type, $message); + + // Run the system.log event + Event::run('system.log', $message); + + self::$log[] = $message; + } + } + + /** + * Save all currently logged messages. + * + * @return void + */ + public static function log_save() + { + if (empty(self::$log) OR self::$configuration['core']['log_threshold'] < 1) + return; + + // Filename of the log + $filename = self::log_directory().date('Y-m-d').'.log'.EXT; + + if ( ! is_file($filename)) + { + // Write the SYSPATH checking header + file_put_contents($filename, + ''.PHP_EOL.PHP_EOL); + + // Prevent external writes + chmod($filename, 0644); + } + + // Messages to write + $messages = array(); + + do + { + // Load the next mess + list ($date, $type, $text) = array_shift(self::$log); + + // Add a new message line + $messages[] = $date.' --- '.$type.': '.$text; + } + while ( ! empty(self::$log)); + + // Write messages to log file + file_put_contents($filename, implode(PHP_EOL, $messages).PHP_EOL, FILE_APPEND); + } + + /** + * Get or set the logging directory. + * + * @param string new log directory + * @return string + */ + public static function log_directory($dir = NULL) + { + static $directory; + + if ( ! empty($dir)) + { + // Get the directory path + $dir = realpath($dir); + + if (is_dir($dir) AND is_writable($dir)) + { + // Change the log directory + $directory = str_replace('\\', '/', $dir).'/'; + } + else + { + // Log directory is invalid + throw new Kohana_Exception('core.log_dir_unwritable', $dir); + } + } + + return $directory; + } + + /** + * Load data from a simple cache file. This should only be used internally, + * and is NOT a replacement for the Cache library. + * + * @param string unique name of cache + * @param integer expiration in seconds + * @return mixed + */ + public static function cache($name, $lifetime) + { + if ($lifetime > 0) + { + $path = self::$internal_cache_path.'kohana_'.$name; + + if (is_file($path)) + { + // Check the file modification time + if ((time() - filemtime($path)) < $lifetime) + { + // Cache is valid + return unserialize(file_get_contents($path)); + } + else + { + // Cache is invalid, delete it + unlink($path); + } + } + } + + // No cache found + return NULL; + } + + /** + * Save data to a simple cache file. This should only be used internally, and + * is NOT a replacement for the Cache library. + * + * @param string cache name + * @param mixed data to cache + * @param integer expiration in seconds + * @return boolean + */ + public static function cache_save($name, $data, $lifetime) + { + if ($lifetime < 1) + return FALSE; + + $path = self::$internal_cache_path.'kohana_'.$name; + + if ($data === NULL) + { + // Delete cache + return (is_file($path) and unlink($path)); + } + else + { + // Write data to cache file + return (bool) file_put_contents($path, serialize($data)); + } + } + + /** + * Kohana output handler. + * + * @param string current output buffer + * @return string + */ + public static function output_buffer($output) + { + if ( ! Event::has_run('system.send_headers')) + { + // Run the send_headers event, specifically for cookies being set + Event::run('system.send_headers'); + } + + // Set final output + self::$output = $output; + + // Set and return the final output + return $output; + } + + /** + * Closes all open output buffers, either by flushing or cleaning all + * open buffers, including the Kohana output buffer. + * + * @param boolean disable to clear buffers, rather than flushing + * @return void + */ + public static function close_buffers($flush = TRUE) + { + if (ob_get_level() >= self::$buffer_level) + { + // Set the close function + $close = ($flush === TRUE) ? 'ob_end_flush' : 'ob_end_clean'; + + while (ob_get_level() > self::$buffer_level) + { + // Flush or clean the buffer + $close(); + } + + // This will flush the Kohana buffer, which sets self::$output + ob_end_clean(); + + // Reset the buffer level + self::$buffer_level = ob_get_level(); + } + } + + /** + * Triggers the shutdown of Kohana by closing the output buffer, runs the system.display event. + * + * @return void + */ + public static function shutdown() + { + // Close output buffers + self::close_buffers(TRUE); + + // Run the output event + Event::run('system.display', self::$output); + + // Render the final output + self::render(self::$output); + } + + /** + * Inserts global Kohana variables into the generated output and prints it. + * + * @param string final output that will displayed + * @return void + */ + public static function render($output) + { + if (self::config('core.render_stats') === TRUE) + { + // Fetch memory usage in MB + $memory = function_exists('memory_get_usage') ? (memory_get_usage() / 1024 / 1024) : 0; + + // Fetch benchmark for page execution time + $benchmark = Benchmark::get(SYSTEM_BENCHMARK.'_total_execution'); + + // Replace the global template variables + $output = str_replace( + array + ( + '{kohana_version}', + '{kohana_codename}', + '{execution_time}', + '{memory_usage}', + '{included_files}', + ), + array + ( + KOHANA_VERSION, + KOHANA_CODENAME, + $benchmark['time'], + number_format($memory, 2).'MB', + count(get_included_files()), + ), + $output + ); + } + + if ($level = self::config('core.output_compression') AND ini_get('output_handler') !== 'ob_gzhandler' AND (int) ini_get('zlib.output_compression') === 0) + { + if ($level < 1 OR $level > 9) + { + // Normalize the level to be an integer between 1 and 9. This + // step must be done to prevent gzencode from triggering an error + $level = max(1, min($level, 9)); + } + + if (stripos(@$_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) + { + $compress = 'gzip'; + } + elseif (stripos(@$_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== FALSE) + { + $compress = 'deflate'; + } + } + + if (isset($compress) AND $level > 0) + { + switch ($compress) + { + case 'gzip': + // Compress output using gzip + $output = gzencode($output, $level); + break; + case 'deflate': + // Compress output using zlib (HTTP deflate) + $output = gzdeflate($output, $level); + break; + } + + // This header must be sent with compressed content to prevent + // browser caches from breaking + header('Vary: Accept-Encoding'); + + // Send the content encoding header + header('Content-Encoding: '.$compress); + + // Sending Content-Length in CGI can result in unexpected behavior + if (stripos(PHP_SAPI, 'cgi') === FALSE) + { + header('Content-Length: '.strlen($output)); + } + } + + echo $output; + } + + /** + * Displays a 404 page. + * + * @throws Kohana_404_Exception + * @param string URI of page + * @param string custom template + * @return void + */ + public static function show_404($page = FALSE, $template = FALSE) + { + throw new Kohana_404_Exception($page, $template); + } + + /** + * Dual-purpose PHP error and exception handler. Uses the kohana_error_page + * view to display the message. + * + * @param integer|object exception object or error code + * @param string error message + * @param string filename + * @param integer line number + * @return void + */ + public static function exception_handler($exception, $message = NULL, $file = NULL, $line = NULL) + { + try + { + // PHP errors have 5 args, always + $PHP_ERROR = (func_num_args() === 5); + + // Test to see if errors should be displayed + if ($PHP_ERROR AND (error_reporting() & $exception) === 0) + return; + + // This is useful for hooks to determine if a page has an error + self::$has_error = TRUE; + + // Error handling will use exactly 5 args, every time + if ($PHP_ERROR) + { + $code = $exception; + $type = 'PHP Error'; + $template = 'kohana_error_page'; + } + else + { + $code = $exception->getCode(); + $type = get_class($exception); + $message = $exception->getMessage(); + $file = $exception->getFile(); + $line = $exception->getLine(); + $template = ($exception instanceof Kohana_Exception) ? $exception->getTemplate() : 'kohana_error_page'; + } + + if (is_numeric($code)) + { + $codes = self::lang('errors'); + + if ( ! empty($codes[$code])) + { + list($level, $error, $description) = $codes[$code]; + } + else + { + $level = 1; + $error = $PHP_ERROR ? 'Unknown Error' : get_class($exception); + $description = ''; + } + } + else + { + // Custom error message, this will never be logged + $level = 5; + $error = $code; + $description = ''; + } + + // Remove the DOCROOT from the path, as a security precaution + $file = str_replace('\\', '/', realpath($file)); + $file = preg_replace('|^'.preg_quote(DOCROOT).'|', '', $file); + + if ($level <= self::$configuration['core']['log_threshold']) + { + // Log the error + self::log('error', self::lang('core.uncaught_exception', $type, $message, $file, $line)); + } + + if ($PHP_ERROR) + { + $description = self::lang('errors.'.E_RECOVERABLE_ERROR); + $description = is_array($description) ? $description[2] : ''; + + if ( ! headers_sent()) + { + // Send the 500 header + header('HTTP/1.1 500 Internal Server Error'); + } + } + else + { + if (method_exists($exception, 'sendHeaders') AND ! headers_sent()) + { + // Send the headers if they have not already been sent + $exception->sendHeaders(); + } + } + + while (ob_get_level() > self::$buffer_level) + { + // Close open buffers + ob_end_clean(); + } + + // Test if display_errors is on + if (self::$configuration['core']['display_errors'] === TRUE) + { + if ( ! IN_PRODUCTION AND $line != FALSE) + { + // Remove the first entry of debug_backtrace(), it is the exception_handler call + $trace = $PHP_ERROR ? array_slice(debug_backtrace(), 1) : $exception->getTrace(); + + // Beautify backtrace + $trace = self::backtrace($trace); + } + + // Load the error + require self::find_file('views', empty($template) ? 'kohana_error_page' : $template); + } + else + { + // Get the i18n messages + $error = self::lang('core.generic_error'); + $message = self::lang('core.errors_disabled', url::site(), url::site(Router::$current_uri)); + + // Load the errors_disabled view + require self::find_file('views', 'kohana_error_disabled'); + } + + if ( ! Event::has_run('system.shutdown')) + { + // Run the shutdown even to ensure a clean exit + Event::run('system.shutdown'); + } + + // Turn off error reporting + error_reporting(0); + exit; + } + catch (Exception $e) + { + if (IN_PRODUCTION) + { + die('Fatal Error'); + } + else + { + die('Fatal Error: '.$e->getMessage().' File: '.$e->getFile().' Line: '.$e->getLine()); + } + } + } + + /** + * Provides class auto-loading. + * + * @throws Kohana_Exception + * @param string name of class + * @return bool + */ + public static function auto_load($class) + { + if (class_exists($class, FALSE)) + return TRUE; + + if (($suffix = strrpos($class, '_')) > 0) + { + // Find the class suffix + $suffix = substr($class, $suffix + 1); + } + else + { + // No suffix + $suffix = FALSE; + } + + if ($suffix === 'Core') + { + $type = 'libraries'; + $file = substr($class, 0, -5); + } + elseif ($suffix === 'Controller') + { + $type = 'controllers'; + // Lowercase filename + $file = strtolower(substr($class, 0, -11)); + } + elseif ($suffix === 'Model') + { + $type = 'models'; + // Lowercase filename + $file = strtolower(substr($class, 0, -6)); + } + elseif ($suffix === 'Driver') + { + $type = 'libraries/drivers'; + $file = str_replace('_', '/', substr($class, 0, -7)); + } + else + { + // This could be either a library or a helper, but libraries must + // always be capitalized, so we check if the first character is + // uppercase. If it is, we are loading a library, not a helper. + $type = ($class[0] < 'a') ? 'libraries' : 'helpers'; + $file = $class; + } + + if ($filename = self::find_file($type, $file)) + { + // Load the class + require $filename; + } + else + { + // The class could not be found + return FALSE; + } + + if ($filename = self::find_file($type, self::$configuration['core']['extension_prefix'].$class)) + { + // Load the class extension + require $filename; + } + elseif ($suffix !== 'Core' AND class_exists($class.'_Core', FALSE)) + { + // Class extension to be evaluated + $extension = 'class '.$class.' extends '.$class.'_Core { }'; + + // Start class analysis + $core = new ReflectionClass($class.'_Core'); + + if ($core->isAbstract()) + { + // Make the extension abstract + $extension = 'abstract '.$extension; + } + + // Transparent class extensions are handled using eval. This is + // a disgusting hack, but it gets the job done. + eval($extension); + } + + return TRUE; + } + + /** + * Find a resource file in a given directory. Files will be located according + * to the order of the include paths. Configuration and i18n files will be + * returned in reverse order. + * + * @throws Kohana_Exception if file is required and not found + * @param string directory to search in + * @param string filename to look for (without extension) + * @param boolean file required + * @param string file extension + * @return array if the type is config, i18n or l10n + * @return string if the file is found + * @return FALSE if the file is not found + */ + public static function find_file($directory, $filename, $required = FALSE, $ext = FALSE) + { + // NOTE: This test MUST be not be a strict comparison (===), or empty + // extensions will be allowed! + if ($ext == '') + { + // Use the default extension + $ext = EXT; + } + else + { + // Add a period before the extension + $ext = '.'.$ext; + } + + // Search path + $search = $directory.'/'.$filename.$ext; + + if (isset(self::$internal_cache['find_file_paths'][$search])) + return self::$internal_cache['find_file_paths'][$search]; + + // Load include paths + $paths = self::$include_paths; + + // Nothing found, yet + $found = NULL; + + if ($directory === 'config' OR $directory === 'i18n') + { + // Search in reverse, for merging + $paths = array_reverse($paths); + + foreach ($paths as $path) + { + if (is_file($path.$search)) + { + // A matching file has been found + $found[] = $path.$search; + } + } + } + else + { + foreach ($paths as $path) + { + if (is_file($path.$search)) + { + // A matching file has been found + $found = $path.$search; + + // Stop searching + break; + } + } + } + + if ($found === NULL) + { + if ($required === TRUE) + { + // Directory i18n key + $directory = 'core.'.inflector::singular($directory); + + // If the file is required, throw an exception + throw new Kohana_Exception('core.resource_not_found', self::lang($directory), $filename); + } + else + { + // Nothing was found, return FALSE + $found = FALSE; + } + } + + if ( ! isset(self::$write_cache['find_file_paths'])) + { + // Write cache at shutdown + self::$write_cache['find_file_paths'] = TRUE; + } + + return self::$internal_cache['find_file_paths'][$search] = $found; + } + + /** + * Lists all files and directories in a resource path. + * + * @param string directory to search + * @param boolean list all files to the maximum depth? + * @param string full path to search (used for recursion, *never* set this manually) + * @return array filenames and directories + */ + public static function list_files($directory, $recursive = FALSE, $path = FALSE) + { + $files = array(); + + if ($path === FALSE) + { + $paths = array_reverse(self::include_paths()); + + foreach ($paths as $path) + { + // Recursively get and merge all files + $files = array_merge($files, self::list_files($directory, $recursive, $path.$directory)); + } + } + else + { + $path = rtrim($path, '/').'/'; + + if (is_readable($path)) + { + $items = (array) glob($path.'*'); + + if ( ! empty($items)) + { + foreach ($items as $index => $item) + { + $files[] = $item = str_replace('\\', '/', $item); + + // Handle recursion + if (is_dir($item) AND $recursive == TRUE) + { + // Filename should only be the basename + $item = pathinfo($item, PATHINFO_BASENAME); + + // Append sub-directory search + $files = array_merge($files, self::list_files($directory, TRUE, $path.$item)); + } + } + } + } + } + + return $files; + } + + /** + * Fetch an i18n language item. + * + * @param string language key to fetch + * @param array additional information to insert into the line + * @return string i18n language string, or the requested key if the i18n item is not found + */ + public static function lang($key, $args = array()) + { + // Extract the main group from the key + $group = explode('.', $key, 2); + $group = $group[0]; + + // Get locale name + $locale = self::config('locale.language.0'); + + if ( ! isset(self::$internal_cache['language'][$locale][$group])) + { + // Messages for this group + $messages = array(); + + if ($files = self::find_file('i18n', $locale.'/'.$group)) + { + foreach ($files as $file) + { + include $file; + + // Merge in configuration + if ( ! empty($lang) AND is_array($lang)) + { + foreach ($lang as $k => $v) + { + $messages[$k] = $v; + } + } + } + } + + if ( ! isset(self::$write_cache['language'])) + { + // Write language cache + self::$write_cache['language'] = TRUE; + } + + self::$internal_cache['language'][$locale][$group] = $messages; + } + + // Get the line from cache + $line = self::key_string(self::$internal_cache['language'][$locale], $key); + + if ($line === NULL) + { + self::log('error', 'Missing i18n entry '.$key.' for language '.$locale); + + // Return the key string as fallback + return $key; + } + + if (is_string($line) AND func_num_args() > 1) + { + $args = array_slice(func_get_args(), 1); + + // Add the arguments into the line + $line = vsprintf($line, is_array($args[0]) ? $args[0] : $args); + } + + return $line; + } + + /** + * Returns the value of a key, defined by a 'dot-noted' string, from an array. + * + * @param array array to search + * @param string dot-noted string: foo.bar.baz + * @return string if the key is found + * @return void if the key is not found + */ + public static function key_string($array, $keys) + { + if (empty($array)) + return NULL; + + // Prepare for loop + $keys = explode('.', $keys); + + do + { + // Get the next key + $key = array_shift($keys); + + if (isset($array[$key])) + { + if (is_array($array[$key]) AND ! empty($keys)) + { + // Dig down to prepare the next loop + $array = $array[$key]; + } + else + { + // Requested key was found + return $array[$key]; + } + } + else + { + // Requested key is not set + break; + } + } + while ( ! empty($keys)); + + return NULL; + } + + /** + * Sets values in an array by using a 'dot-noted' string. + * + * @param array array to set keys in (reference) + * @param string dot-noted string: foo.bar.baz + * @return mixed fill value for the key + * @return void + */ + public static function key_string_set( & $array, $keys, $fill = NULL) + { + if (is_object($array) AND ($array instanceof ArrayObject)) + { + // Copy the array + $array_copy = $array->getArrayCopy(); + + // Is an object + $array_object = TRUE; + } + else + { + if ( ! is_array($array)) + { + // Must always be an array + $array = (array) $array; + } + + // Copy is a reference to the array + $array_copy =& $array; + } + + if (empty($keys)) + return $array; + + // Create keys + $keys = explode('.', $keys); + + // Create reference to the array + $row =& $array_copy; + + for ($i = 0, $end = count($keys) - 1; $i <= $end; $i++) + { + // Get the current key + $key = $keys[$i]; + + if ( ! isset($row[$key])) + { + if (isset($keys[$i + 1])) + { + // Make the value an array + $row[$key] = array(); + } + else + { + // Add the fill key + $row[$key] = $fill; + } + } + elseif (isset($keys[$i + 1])) + { + // Make the value an array + $row[$key] = (array) $row[$key]; + } + + // Go down a level, creating a new row reference + $row =& $row[$key]; + } + + if (isset($array_object)) + { + // Swap the array back in + $array->exchangeArray($array_copy); + } + } + + /** + * Retrieves current user agent information: + * keys: browser, version, platform, mobile, robot, referrer, languages, charsets + * tests: is_browser, is_mobile, is_robot, accept_lang, accept_charset + * + * @param string key or test name + * @param string used with "accept" tests: user_agent(accept_lang, en) + * @return array languages and charsets + * @return string all other keys + * @return boolean all tests + */ + public static function user_agent($key = 'agent', $compare = NULL) + { + static $info; + + // Return the raw string + if ($key === 'agent') + return self::$user_agent; + + if ($info === NULL) + { + // Parse the user agent and extract basic information + $agents = self::config('user_agents'); + + foreach ($agents as $type => $data) + { + foreach ($data as $agent => $name) + { + if (stripos(self::$user_agent, $agent) !== FALSE) + { + if ($type === 'browser' AND preg_match('|'.preg_quote($agent).'[^0-9.]*+([0-9.][0-9.a-z]*)|i', self::$user_agent, $match)) + { + // Set the browser version + $info['version'] = $match[1]; + } + + // Set the agent name + $info[$type] = $name; + break; + } + } + } + } + + if (empty($info[$key])) + { + switch ($key) + { + case 'is_robot': + case 'is_browser': + case 'is_mobile': + // A boolean result + $return = ! empty($info[substr($key, 3)]); + break; + case 'languages': + $return = array(); + if ( ! empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) + { + if (preg_match_all('/[-a-z]{2,}/', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])), $matches)) + { + // Found a result + $return = $matches[0]; + } + } + break; + case 'charsets': + $return = array(); + if ( ! empty($_SERVER['HTTP_ACCEPT_CHARSET'])) + { + if (preg_match_all('/[-a-z0-9]{2,}/', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET'])), $matches)) + { + // Found a result + $return = $matches[0]; + } + } + break; + case 'referrer': + if ( ! empty($_SERVER['HTTP_REFERER'])) + { + // Found a result + $return = trim($_SERVER['HTTP_REFERER']); + } + break; + } + + // Cache the return value + isset($return) and $info[$key] = $return; + } + + if ( ! empty($compare)) + { + // The comparison must always be lowercase + $compare = strtolower($compare); + + switch ($key) + { + case 'accept_lang': + // Check if the lange is accepted + return in_array($compare, self::user_agent('languages')); + break; + case 'accept_charset': + // Check if the charset is accepted + return in_array($compare, self::user_agent('charsets')); + break; + default: + // Invalid comparison + return FALSE; + break; + } + } + + // Return the key, if set + return isset($info[$key]) ? $info[$key] : NULL; + } + + /** + * Quick debugging of any variable. Any number of parameters can be set. + * + * @return string + */ + public static function debug() + { + if (func_num_args() === 0) + return; + + // Get params + $params = func_get_args(); + $output = array(); + + foreach ($params as $var) + { + $output[] = '
      ('.gettype($var).') '.html::specialchars(print_r($var, TRUE)).'
      '; + } + + return implode("\n", $output); + } + + /** + * Displays nice backtrace information. + * @see http://php.net/debug_backtrace + * + * @param array backtrace generated by an exception or debug_backtrace + * @return string + */ + public static function backtrace($trace) + { + if ( ! is_array($trace)) + return; + + // Final output + $output = array(); + + foreach ($trace as $entry) + { + $temp = '
    • '; + + if (isset($entry['file'])) + { + $temp .= self::lang('core.error_file_line', preg_replace('!^'.preg_quote(DOCROOT).'!', '', $entry['file']), $entry['line']); + } + + $temp .= '
      ';
      +
      +			if (isset($entry['class']))
      +			{
      +				// Add class and call type
      +				$temp .= $entry['class'].$entry['type'];
      +			}
      +
      +			// Add function
      +			$temp .= $entry['function'].'( ';
      +
      +			// Add function args
      +			if (isset($entry['args']) AND is_array($entry['args']))
      +			{
      +				// Separator starts as nothing
      +				$sep = '';
      +
      +				while ($arg = array_shift($entry['args']))
      +				{
      +					if (is_string($arg) AND is_file($arg))
      +					{
      +						// Remove docroot from filename
      +						$arg = preg_replace('!^'.preg_quote(DOCROOT).'!', '', $arg);
      +					}
      +
      +					$temp .= $sep.html::specialchars(print_r($arg, TRUE));
      +
      +					// Change separator to a comma
      +					$sep = ', ';
      +				}
      +			}
      +
      +			$temp .= ' )
    • '; + + $output[] = $temp; + } + + return '
        '.implode("\n", $output).'
      '; + } + + /** + * Saves the internal caches: configuration, include paths, etc. + * + * @return boolean + */ + public static function internal_cache_save() + { + if ( ! is_array(self::$write_cache)) + return FALSE; + + // Get internal cache names + $caches = array_keys(self::$write_cache); + + // Nothing written + $written = FALSE; + + foreach ($caches as $cache) + { + if (isset(self::$internal_cache[$cache])) + { + // Write the cache file + self::cache_save($cache, self::$internal_cache[$cache], self::$configuration['core']['internal_cache']); + + // A cache has been written + $written = TRUE; + } + } + + return $written; + } + +} // End Kohana + +/** + * Creates a generic i18n exception. + */ +class Kohana_Exception extends Exception { + + // Template file + protected $template = 'kohana_error_page'; + + // Header + protected $header = FALSE; + + // Error code + protected $code = E_KOHANA; + + /** + * Set exception message. + * + * @param string i18n language key for the message + * @param array addition line parameters + */ + public function __construct($error) + { + $args = array_slice(func_get_args(), 1); + + // Fetch the error message + $message = Kohana::lang($error, $args); + + if ($message === $error OR empty($message)) + { + // Unable to locate the message for the error + $message = 'Unknown Exception: '.$error; + } + + // Sets $this->message the proper way + parent::__construct($message); + } + + /** + * Magic method for converting an object to a string. + * + * @return string i18n message + */ + public function __toString() + { + return (string) $this->message; + } + + /** + * Fetch the template name. + * + * @return string + */ + public function getTemplate() + { + return $this->template; + } + + /** + * Sends an Internal Server Error header. + * + * @return void + */ + public function sendHeaders() + { + // Send the 500 header + header('HTTP/1.1 500 Internal Server Error'); + } + +} // End Kohana Exception + +/** + * Creates a custom exception. + */ +class Kohana_User_Exception extends Kohana_Exception { + + /** + * Set exception title and message. + * + * @param string exception title string + * @param string exception message string + * @param string custom error template + */ + public function __construct($title, $message, $template = FALSE) + { + Exception::__construct($message); + + $this->code = $title; + + if ($template !== FALSE) + { + $this->template = $template; + } + } + +} // End Kohana PHP Exception + +/** + * Creates a Page Not Found exception. + */ +class Kohana_404_Exception extends Kohana_Exception { + + protected $code = E_PAGE_NOT_FOUND; + + /** + * Set internal properties. + * + * @param string URL of page + * @param string custom error template + */ + public function __construct($page = FALSE, $template = FALSE) + { + if ($page === FALSE) + { + // Construct the page URI using Router properties + $page = Router::$current_uri.Router::$url_suffix.Router::$query_string; + } + + Exception::__construct(Kohana::lang('core.page_not_found', $page)); + + $this->template = $template; + } + + /** + * Sends "File Not Found" headers, to emulate server behavior. + * + * @return void + */ + public function sendHeaders() + { + // Send the 404 header + header('HTTP/1.1 404 File Not Found'); + } + +} // End Kohana 404 Exception diff --git a/system/core/utf8.php b/system/core/utf8.php new file mode 100644 index 00000000..9f20f421 --- /dev/null +++ b/system/core/utf8.php @@ -0,0 +1,743 @@ +PCRE has not been compiled with UTF-8 support. '. + 'See PCRE Pattern Modifiers '. + 'for more information. This application cannot be run without UTF-8 support.', + E_USER_ERROR + ); +} + +if ( ! extension_loaded('iconv')) +{ + trigger_error + ( + 'The iconv extension is not loaded. '. + 'Without iconv, strings cannot be properly translated to UTF-8 from user input. '. + 'This application cannot be run without UTF-8 support.', + E_USER_ERROR + ); +} + +if (extension_loaded('mbstring') AND (ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING)) +{ + trigger_error + ( + 'The mbstring extension is overloading PHP\'s native string functions. '. + 'Disable this by setting mbstring.func_overload to 0, 1, 4 or 5 in php.ini or a .htaccess file.'. + 'This application cannot be run without UTF-8 support.', + E_USER_ERROR + ); +} + +// Check PCRE support for Unicode properties such as \p and \X. +$ER = error_reporting(0); +define('PCRE_UNICODE_PROPERTIES', (bool) preg_match('/^\pL$/u', 'ñ')); +error_reporting($ER); + +// SERVER_UTF8 ? use mb_* functions : use non-native functions +if (extension_loaded('mbstring')) +{ + mb_internal_encoding('UTF-8'); + define('SERVER_UTF8', TRUE); +} +else +{ + define('SERVER_UTF8', FALSE); +} + +// Convert all global variables to UTF-8. +$_GET = utf8::clean($_GET); +$_POST = utf8::clean($_POST); +$_COOKIE = utf8::clean($_COOKIE); +$_SERVER = utf8::clean($_SERVER); + +if (PHP_SAPI == 'cli') +{ + // Convert command line arguments + $_SERVER['argv'] = utf8::clean($_SERVER['argv']); +} + +final class utf8 { + + // Called methods + static $called = array(); + + /** + * Recursively cleans arrays, objects, and strings. Removes ASCII control + * codes and converts to UTF-8 while silently discarding incompatible + * UTF-8 characters. + * + * @param string string to clean + * @return string + */ + public static function clean($str) + { + if (is_array($str) OR is_object($str)) + { + foreach ($str as $key => $val) + { + // Recursion! + $str[self::clean($key)] = self::clean($val); + } + } + elseif (is_string($str) AND $str !== '') + { + // Remove control characters + $str = self::strip_ascii_ctrl($str); + + if ( ! self::is_ascii($str)) + { + // Disable notices + $ER = error_reporting(~E_NOTICE); + + // iconv is expensive, so it is only used when needed + $str = iconv('UTF-8', 'UTF-8//IGNORE', $str); + + // Turn notices back on + error_reporting($ER); + } + } + + return $str; + } + + /** + * Tests whether a string contains only 7bit ASCII bytes. This is used to + * determine when to use native functions or UTF-8 functions. + * + * @param string string to check + * @return bool + */ + public static function is_ascii($str) + { + return ! preg_match('/[^\x00-\x7F]/S', $str); + } + + /** + * Strips out device control codes in the ASCII range. + * + * @param string string to clean + * @return string + */ + public static function strip_ascii_ctrl($str) + { + return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $str); + } + + /** + * Strips out all non-7bit ASCII bytes. + * + * @param string string to clean + * @return string + */ + public static function strip_non_ascii($str) + { + return preg_replace('/[^\x00-\x7F]+/S', '', $str); + } + + /** + * Replaces special/accented UTF-8 characters by ASCII-7 'equivalents'. + * + * @author Andreas Gohr + * + * @param string string to transliterate + * @param integer -1 lowercase only, +1 uppercase only, 0 both cases + * @return string + */ + public static function transliterate_to_ascii($str, $case = 0) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _transliterate_to_ascii($str, $case); + } + + /** + * Returns the length of the given string. + * @see http://php.net/strlen + * + * @param string string being measured for length + * @return integer + */ + public static function strlen($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strlen($str); + } + + /** + * Finds position of first occurrence of a UTF-8 string. + * @see http://php.net/strlen + * + * @author Harry Fuecks + * + * @param string haystack + * @param string needle + * @param integer offset from which character in haystack to start searching + * @return integer position of needle + * @return boolean FALSE if the needle is not found + */ + public static function strpos($str, $search, $offset = 0) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strpos($str, $search, $offset); + } + + /** + * Finds position of last occurrence of a char in a UTF-8 string. + * @see http://php.net/strrpos + * + * @author Harry Fuecks + * + * @param string haystack + * @param string needle + * @param integer offset from which character in haystack to start searching + * @return integer position of needle + * @return boolean FALSE if the needle is not found + */ + public static function strrpos($str, $search, $offset = 0) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strrpos($str, $search, $offset); + } + + /** + * Returns part of a UTF-8 string. + * @see http://php.net/substr + * + * @author Chris Smith + * + * @param string input string + * @param integer offset + * @param integer length limit + * @return string + */ + public static function substr($str, $offset, $length = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _substr($str, $offset, $length); + } + + /** + * Replaces text within a portion of a UTF-8 string. + * @see http://php.net/substr_replace + * + * @author Harry Fuecks + * + * @param string input string + * @param string replacement string + * @param integer offset + * @return string + */ + public static function substr_replace($str, $replacement, $offset, $length = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _substr_replace($str, $replacement, $offset, $length); + } + + /** + * Makes a UTF-8 string lowercase. + * @see http://php.net/strtolower + * + * @author Andreas Gohr + * + * @param string mixed case string + * @return string + */ + public static function strtolower($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strtolower($str); + } + + /** + * Makes a UTF-8 string uppercase. + * @see http://php.net/strtoupper + * + * @author Andreas Gohr + * + * @param string mixed case string + * @return string + */ + public static function strtoupper($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strtoupper($str); + } + + /** + * Makes a UTF-8 string's first character uppercase. + * @see http://php.net/ucfirst + * + * @author Harry Fuecks + * + * @param string mixed case string + * @return string + */ + public static function ucfirst($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _ucfirst($str); + } + + /** + * Makes the first character of every word in a UTF-8 string uppercase. + * @see http://php.net/ucwords + * + * @author Harry Fuecks + * + * @param string mixed case string + * @return string + */ + public static function ucwords($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _ucwords($str); + } + + /** + * Case-insensitive UTF-8 string comparison. + * @see http://php.net/strcasecmp + * + * @author Harry Fuecks + * + * @param string string to compare + * @param string string to compare + * @return integer less than 0 if str1 is less than str2 + * @return integer greater than 0 if str1 is greater than str2 + * @return integer 0 if they are equal + */ + public static function strcasecmp($str1, $str2) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strcasecmp($str1, $str2); + } + + /** + * Returns a string or an array with all occurrences of search in subject (ignoring case). + * replaced with the given replace value. + * @see http://php.net/str_ireplace + * + * @note It's not fast and gets slower if $search and/or $replace are arrays. + * @author Harry Fuecks + * + * @param string input string + * @param string needle + * @return string matched substring if found + * @return boolean FALSE if the substring was not found + */ + public static function stristr($str, $search) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _stristr($str, $search); + } + + /** + * Finds the length of the initial segment matching mask. + * @see http://php.net/strspn + * + * @author Harry Fuecks + * + * @param string input string + * @param string mask for search + * @param integer start position of the string to examine + * @param integer length of the string to examine + * @return integer length of the initial segment that contains characters in the mask + */ + public static function strspn($str, $mask, $offset = NULL, $length = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strspn($str, $mask, $offset, $length); + } + + /** + * Finds the length of the initial segment not matching mask. + * @see http://php.net/strcspn + * + * @author Harry Fuecks + * + * @param string input string + * @param string mask for search + * @param integer start position of the string to examine + * @param integer length of the string to examine + * @return integer length of the initial segment that contains characters not in the mask + */ + public static function strcspn($str, $mask, $offset = NULL, $length = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strcspn($str, $mask, $offset, $length); + } + + /** + * Pads a UTF-8 string to a certain length with another string. + * @see http://php.net/str_pad + * + * @author Harry Fuecks + * + * @param string input string + * @param integer desired string length after padding + * @param string string to use as padding + * @param string padding type: STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH + * @return string + */ + public static function str_pad($str, $final_str_length, $pad_str = ' ', $pad_type = STR_PAD_RIGHT) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _str_pad($str, $final_str_length, $pad_str, $pad_type); + } + + /** + * Converts a UTF-8 string to an array. + * @see http://php.net/str_split + * + * @author Harry Fuecks + * + * @param string input string + * @param integer maximum length of each chunk + * @return array + */ + public static function str_split($str, $split_length = 1) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _str_split($str, $split_length); + } + + /** + * Reverses a UTF-8 string. + * @see http://php.net/strrev + * + * @author Harry Fuecks + * + * @param string string to be reversed + * @return string + */ + public static function strrev($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _strrev($str); + } + + /** + * Strips whitespace (or other UTF-8 characters) from the beginning and + * end of a string. + * @see http://php.net/trim + * + * @author Andreas Gohr + * + * @param string input string + * @param string string of characters to remove + * @return string + */ + public static function trim($str, $charlist = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _trim($str, $charlist); + } + + /** + * Strips whitespace (or other UTF-8 characters) from the beginning of a string. + * @see http://php.net/ltrim + * + * @author Andreas Gohr + * + * @param string input string + * @param string string of characters to remove + * @return string + */ + public static function ltrim($str, $charlist = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _ltrim($str, $charlist); + } + + /** + * Strips whitespace (or other UTF-8 characters) from the end of a string. + * @see http://php.net/rtrim + * + * @author Andreas Gohr + * + * @param string input string + * @param string string of characters to remove + * @return string + */ + public static function rtrim($str, $charlist = NULL) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _rtrim($str, $charlist); + } + + /** + * Returns the unicode ordinal for a character. + * @see http://php.net/ord + * + * @author Harry Fuecks + * + * @param string UTF-8 encoded character + * @return integer + */ + public static function ord($chr) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _ord($chr); + } + + /** + * Takes an UTF-8 string and returns an array of ints representing the Unicode characters. + * Astral planes are supported i.e. the ints in the output can be > 0xFFFF. + * Occurrances of the BOM are ignored. Surrogates are not allowed. + * + * The Original Code is Mozilla Communicator client code. + * The Initial Developer of the Original Code is Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer. + * Ported to PHP by Henri Sivonen , see http://hsivonen.iki.fi/php-utf8/. + * Slight modifications to fit with phputf8 library by Harry Fuecks . + * + * @param string UTF-8 encoded string + * @return array unicode code points + * @return boolean FALSE if the string is invalid + */ + public static function to_unicode($str) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _to_unicode($str); + } + + /** + * Takes an array of ints representing the Unicode characters and returns a UTF-8 string. + * Astral planes are supported i.e. the ints in the input can be > 0xFFFF. + * Occurrances of the BOM are ignored. Surrogates are not allowed. + * + * The Original Code is Mozilla Communicator client code. + * The Initial Developer of the Original Code is Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer. + * Ported to PHP by Henri Sivonen , see http://hsivonen.iki.fi/php-utf8/. + * Slight modifications to fit with phputf8 library by Harry Fuecks . + * + * @param array unicode code points representing a string + * @return string utf8 string of characters + * @return boolean FALSE if a code point cannot be found + */ + public static function from_unicode($arr) + { + if ( ! isset(self::$called[__FUNCTION__])) + { + require SYSPATH.'core/utf8/'.__FUNCTION__.EXT; + + // Function has been called + self::$called[__FUNCTION__] = TRUE; + } + + return _from_unicode($arr); + } + +} // End utf8 \ No newline at end of file diff --git a/system/core/utf8/from_unicode.php b/system/core/utf8/from_unicode.php new file mode 100644 index 00000000..66c6742d --- /dev/null +++ b/system/core/utf8/from_unicode.php @@ -0,0 +1,68 @@ += 0) AND ($arr[$k] <= 0x007f)) + { + echo chr($arr[$k]); + } + // 2 byte sequence + elseif ($arr[$k] <= 0x07ff) + { + echo chr(0xc0 | ($arr[$k] >> 6)); + echo chr(0x80 | ($arr[$k] & 0x003f)); + } + // Byte order mark (skip) + elseif ($arr[$k] == 0xFEFF) + { + // nop -- zap the BOM + } + // Test for illegal surrogates + elseif ($arr[$k] >= 0xD800 AND $arr[$k] <= 0xDFFF) + { + // Found a surrogate + trigger_error('utf8::from_unicode: Illegal surrogate at index: '.$k.', value: '.$arr[$k], E_USER_WARNING); + return FALSE; + } + // 3 byte sequence + elseif ($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 + elseif ($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)); + } + // Out of range + else + { + trigger_error('utf8::from_unicode: Codepoint out of Unicode range at index: '.$k.', value: '.$arr[$k], E_USER_WARNING); + return FALSE; + } + } + + $result = ob_get_contents(); + ob_end_clean(); + return $result; +} diff --git a/system/core/utf8/ltrim.php b/system/core/utf8/ltrim.php new file mode 100644 index 00000000..556fe07f --- /dev/null +++ b/system/core/utf8/ltrim.php @@ -0,0 +1,22 @@ += 0 AND $ord0 <= 127) + { + return $ord0; + } + + if ( ! isset($chr[1])) + { + trigger_error('Short sequence - at least 2 bytes expected, only 1 seen', E_USER_WARNING); + return FALSE; + } + + $ord1 = ord($chr[1]); + + if ($ord0 >= 192 AND $ord0 <= 223) + { + return ($ord0 - 192) * 64 + ($ord1 - 128); + } + + if ( ! isset($chr[2])) + { + trigger_error('Short sequence - at least 3 bytes expected, only 2 seen', E_USER_WARNING); + return FALSE; + } + + $ord2 = ord($chr[2]); + + if ($ord0 >= 224 AND $ord0 <= 239) + { + return ($ord0 - 224) * 4096 + ($ord1 - 128) * 64 + ($ord2 - 128); + } + + if ( ! isset($chr[3])) + { + trigger_error('Short sequence - at least 4 bytes expected, only 3 seen', E_USER_WARNING); + return FALSE; + } + + $ord3 = ord($chr[3]); + + if ($ord0 >= 240 AND $ord0 <= 247) + { + return ($ord0 - 240) * 262144 + ($ord1 - 128) * 4096 + ($ord2-128) * 64 + ($ord3 - 128); + } + + if ( ! isset($chr[4])) + { + trigger_error('Short sequence - at least 5 bytes expected, only 4 seen', E_USER_WARNING); + return FALSE; + } + + $ord4 = ord($chr[4]); + + if ($ord0 >= 248 AND $ord0 <= 251) + { + return ($ord0 - 248) * 16777216 + ($ord1-128) * 262144 + ($ord2 - 128) * 4096 + ($ord3 - 128) * 64 + ($ord4 - 128); + } + + if ( ! isset($chr[5])) + { + trigger_error('Short sequence - at least 6 bytes expected, only 5 seen', E_USER_WARNING); + return FALSE; + } + + if ($ord0 >= 252 AND $ord0 <= 253) + { + return ($ord0 - 252) * 1073741824 + ($ord1 - 128) * 16777216 + ($ord2 - 128) * 262144 + ($ord3 - 128) * 4096 + ($ord4 - 128) * 64 + (ord($chr[5]) - 128); + } + + if ($ord0 >= 254 AND $ord0 <= 255) + { + trigger_error('Invalid UTF-8 with surrogate ordinal '.$ord0, E_USER_WARNING); + return FALSE; + } +} \ No newline at end of file diff --git a/system/core/utf8/rtrim.php b/system/core/utf8/rtrim.php new file mode 100644 index 00000000..efa0e19d --- /dev/null +++ b/system/core/utf8/rtrim.php @@ -0,0 +1,22 @@ + $val) + { + $str[$key] = utf8::str_ireplace($search, $replace, $val, $count); + } + return $str; + } + + if (is_array($search)) + { + $keys = array_keys($search); + + foreach ($keys as $k) + { + if (is_array($replace)) + { + if (array_key_exists($k, $replace)) + { + $str = utf8::str_ireplace($search[$k], $replace[$k], $str, $count); + } + else + { + $str = utf8::str_ireplace($search[$k], '', $str, $count); + } + } + else + { + $str = utf8::str_ireplace($search[$k], $replace, $str, $count); + } + } + return $str; + } + + $search = utf8::strtolower($search); + $str_lower = utf8::strtolower($str); + + $total_matched_strlen = 0; + $i = 0; + + while (preg_match('/(.*?)'.preg_quote($search, '/').'/s', $str_lower, $matches)) + { + $matched_strlen = strlen($matches[0]); + $str_lower = substr($str_lower, $matched_strlen); + + $offset = $total_matched_strlen + strlen($matches[1]) + ($i * (strlen($replace) - 1)); + $str = substr_replace($str, $replace, $offset, strlen($search)); + + $total_matched_strlen += $matched_strlen; + $i++; + } + + $count += $i; + return $str; +} diff --git a/system/core/utf8/str_pad.php b/system/core/utf8/str_pad.php new file mode 100644 index 00000000..aab4ccc7 --- /dev/null +++ b/system/core/utf8/str_pad.php @@ -0,0 +1,54 @@ +0x0061, 0x03A6=>0x03C6, 0x0162=>0x0163, 0x00C5=>0x00E5, 0x0042=>0x0062, + 0x0139=>0x013A, 0x00C1=>0x00E1, 0x0141=>0x0142, 0x038E=>0x03CD, 0x0100=>0x0101, + 0x0490=>0x0491, 0x0394=>0x03B4, 0x015A=>0x015B, 0x0044=>0x0064, 0x0393=>0x03B3, + 0x00D4=>0x00F4, 0x042A=>0x044A, 0x0419=>0x0439, 0x0112=>0x0113, 0x041C=>0x043C, + 0x015E=>0x015F, 0x0143=>0x0144, 0x00CE=>0x00EE, 0x040E=>0x045E, 0x042F=>0x044F, + 0x039A=>0x03BA, 0x0154=>0x0155, 0x0049=>0x0069, 0x0053=>0x0073, 0x1E1E=>0x1E1F, + 0x0134=>0x0135, 0x0427=>0x0447, 0x03A0=>0x03C0, 0x0418=>0x0438, 0x00D3=>0x00F3, + 0x0420=>0x0440, 0x0404=>0x0454, 0x0415=>0x0435, 0x0429=>0x0449, 0x014A=>0x014B, + 0x0411=>0x0431, 0x0409=>0x0459, 0x1E02=>0x1E03, 0x00D6=>0x00F6, 0x00D9=>0x00F9, + 0x004E=>0x006E, 0x0401=>0x0451, 0x03A4=>0x03C4, 0x0423=>0x0443, 0x015C=>0x015D, + 0x0403=>0x0453, 0x03A8=>0x03C8, 0x0158=>0x0159, 0x0047=>0x0067, 0x00C4=>0x00E4, + 0x0386=>0x03AC, 0x0389=>0x03AE, 0x0166=>0x0167, 0x039E=>0x03BE, 0x0164=>0x0165, + 0x0116=>0x0117, 0x0108=>0x0109, 0x0056=>0x0076, 0x00DE=>0x00FE, 0x0156=>0x0157, + 0x00DA=>0x00FA, 0x1E60=>0x1E61, 0x1E82=>0x1E83, 0x00C2=>0x00E2, 0x0118=>0x0119, + 0x0145=>0x0146, 0x0050=>0x0070, 0x0150=>0x0151, 0x042E=>0x044E, 0x0128=>0x0129, + 0x03A7=>0x03C7, 0x013D=>0x013E, 0x0422=>0x0442, 0x005A=>0x007A, 0x0428=>0x0448, + 0x03A1=>0x03C1, 0x1E80=>0x1E81, 0x016C=>0x016D, 0x00D5=>0x00F5, 0x0055=>0x0075, + 0x0176=>0x0177, 0x00DC=>0x00FC, 0x1E56=>0x1E57, 0x03A3=>0x03C3, 0x041A=>0x043A, + 0x004D=>0x006D, 0x016A=>0x016B, 0x0170=>0x0171, 0x0424=>0x0444, 0x00CC=>0x00EC, + 0x0168=>0x0169, 0x039F=>0x03BF, 0x004B=>0x006B, 0x00D2=>0x00F2, 0x00C0=>0x00E0, + 0x0414=>0x0434, 0x03A9=>0x03C9, 0x1E6A=>0x1E6B, 0x00C3=>0x00E3, 0x042D=>0x044D, + 0x0416=>0x0436, 0x01A0=>0x01A1, 0x010C=>0x010D, 0x011C=>0x011D, 0x00D0=>0x00F0, + 0x013B=>0x013C, 0x040F=>0x045F, 0x040A=>0x045A, 0x00C8=>0x00E8, 0x03A5=>0x03C5, + 0x0046=>0x0066, 0x00DD=>0x00FD, 0x0043=>0x0063, 0x021A=>0x021B, 0x00CA=>0x00EA, + 0x0399=>0x03B9, 0x0179=>0x017A, 0x00CF=>0x00EF, 0x01AF=>0x01B0, 0x0045=>0x0065, + 0x039B=>0x03BB, 0x0398=>0x03B8, 0x039C=>0x03BC, 0x040C=>0x045C, 0x041F=>0x043F, + 0x042C=>0x044C, 0x00DE=>0x00FE, 0x00D0=>0x00F0, 0x1EF2=>0x1EF3, 0x0048=>0x0068, + 0x00CB=>0x00EB, 0x0110=>0x0111, 0x0413=>0x0433, 0x012E=>0x012F, 0x00C6=>0x00E6, + 0x0058=>0x0078, 0x0160=>0x0161, 0x016E=>0x016F, 0x0391=>0x03B1, 0x0407=>0x0457, + 0x0172=>0x0173, 0x0178=>0x00FF, 0x004F=>0x006F, 0x041B=>0x043B, 0x0395=>0x03B5, + 0x0425=>0x0445, 0x0120=>0x0121, 0x017D=>0x017E, 0x017B=>0x017C, 0x0396=>0x03B6, + 0x0392=>0x03B2, 0x0388=>0x03AD, 0x1E84=>0x1E85, 0x0174=>0x0175, 0x0051=>0x0071, + 0x0417=>0x0437, 0x1E0A=>0x1E0B, 0x0147=>0x0148, 0x0104=>0x0105, 0x0408=>0x0458, + 0x014C=>0x014D, 0x00CD=>0x00ED, 0x0059=>0x0079, 0x010A=>0x010B, 0x038F=>0x03CE, + 0x0052=>0x0072, 0x0410=>0x0430, 0x0405=>0x0455, 0x0402=>0x0452, 0x0126=>0x0127, + 0x0136=>0x0137, 0x012A=>0x012B, 0x038A=>0x03AF, 0x042B=>0x044B, 0x004C=>0x006C, + 0x0397=>0x03B7, 0x0124=>0x0125, 0x0218=>0x0219, 0x00DB=>0x00FB, 0x011E=>0x011F, + 0x041E=>0x043E, 0x1E40=>0x1E41, 0x039D=>0x03BD, 0x0106=>0x0107, 0x03AB=>0x03CB, + 0x0426=>0x0446, 0x00DE=>0x00FE, 0x00C7=>0x00E7, 0x03AA=>0x03CA, 0x0421=>0x0441, + 0x0412=>0x0432, 0x010E=>0x010F, 0x00D8=>0x00F8, 0x0057=>0x0077, 0x011A=>0x011B, + 0x0054=>0x0074, 0x004A=>0x006A, 0x040B=>0x045B, 0x0406=>0x0456, 0x0102=>0x0103, + 0x039B=>0x03BB, 0x00D1=>0x00F1, 0x041D=>0x043D, 0x038C=>0x03CC, 0x00C9=>0x00E9, + 0x00D0=>0x00F0, 0x0407=>0x0457, 0x0122=>0x0123, + ); + } + + $uni = utf8::to_unicode($str); + + if ($uni === FALSE) + return FALSE; + + for ($i = 0, $c = count($uni); $i < $c; $i++) + { + if (isset($UTF8_UPPER_TO_LOWER[$uni[$i]])) + { + $uni[$i] = $UTF8_UPPER_TO_LOWER[$uni[$i]]; + } + } + + return utf8::from_unicode($uni); +} \ No newline at end of file diff --git a/system/core/utf8/strtoupper.php b/system/core/utf8/strtoupper.php new file mode 100644 index 00000000..f3ded739 --- /dev/null +++ b/system/core/utf8/strtoupper.php @@ -0,0 +1,84 @@ +0x0041, 0x03C6=>0x03A6, 0x0163=>0x0162, 0x00E5=>0x00C5, 0x0062=>0x0042, + 0x013A=>0x0139, 0x00E1=>0x00C1, 0x0142=>0x0141, 0x03CD=>0x038E, 0x0101=>0x0100, + 0x0491=>0x0490, 0x03B4=>0x0394, 0x015B=>0x015A, 0x0064=>0x0044, 0x03B3=>0x0393, + 0x00F4=>0x00D4, 0x044A=>0x042A, 0x0439=>0x0419, 0x0113=>0x0112, 0x043C=>0x041C, + 0x015F=>0x015E, 0x0144=>0x0143, 0x00EE=>0x00CE, 0x045E=>0x040E, 0x044F=>0x042F, + 0x03BA=>0x039A, 0x0155=>0x0154, 0x0069=>0x0049, 0x0073=>0x0053, 0x1E1F=>0x1E1E, + 0x0135=>0x0134, 0x0447=>0x0427, 0x03C0=>0x03A0, 0x0438=>0x0418, 0x00F3=>0x00D3, + 0x0440=>0x0420, 0x0454=>0x0404, 0x0435=>0x0415, 0x0449=>0x0429, 0x014B=>0x014A, + 0x0431=>0x0411, 0x0459=>0x0409, 0x1E03=>0x1E02, 0x00F6=>0x00D6, 0x00F9=>0x00D9, + 0x006E=>0x004E, 0x0451=>0x0401, 0x03C4=>0x03A4, 0x0443=>0x0423, 0x015D=>0x015C, + 0x0453=>0x0403, 0x03C8=>0x03A8, 0x0159=>0x0158, 0x0067=>0x0047, 0x00E4=>0x00C4, + 0x03AC=>0x0386, 0x03AE=>0x0389, 0x0167=>0x0166, 0x03BE=>0x039E, 0x0165=>0x0164, + 0x0117=>0x0116, 0x0109=>0x0108, 0x0076=>0x0056, 0x00FE=>0x00DE, 0x0157=>0x0156, + 0x00FA=>0x00DA, 0x1E61=>0x1E60, 0x1E83=>0x1E82, 0x00E2=>0x00C2, 0x0119=>0x0118, + 0x0146=>0x0145, 0x0070=>0x0050, 0x0151=>0x0150, 0x044E=>0x042E, 0x0129=>0x0128, + 0x03C7=>0x03A7, 0x013E=>0x013D, 0x0442=>0x0422, 0x007A=>0x005A, 0x0448=>0x0428, + 0x03C1=>0x03A1, 0x1E81=>0x1E80, 0x016D=>0x016C, 0x00F5=>0x00D5, 0x0075=>0x0055, + 0x0177=>0x0176, 0x00FC=>0x00DC, 0x1E57=>0x1E56, 0x03C3=>0x03A3, 0x043A=>0x041A, + 0x006D=>0x004D, 0x016B=>0x016A, 0x0171=>0x0170, 0x0444=>0x0424, 0x00EC=>0x00CC, + 0x0169=>0x0168, 0x03BF=>0x039F, 0x006B=>0x004B, 0x00F2=>0x00D2, 0x00E0=>0x00C0, + 0x0434=>0x0414, 0x03C9=>0x03A9, 0x1E6B=>0x1E6A, 0x00E3=>0x00C3, 0x044D=>0x042D, + 0x0436=>0x0416, 0x01A1=>0x01A0, 0x010D=>0x010C, 0x011D=>0x011C, 0x00F0=>0x00D0, + 0x013C=>0x013B, 0x045F=>0x040F, 0x045A=>0x040A, 0x00E8=>0x00C8, 0x03C5=>0x03A5, + 0x0066=>0x0046, 0x00FD=>0x00DD, 0x0063=>0x0043, 0x021B=>0x021A, 0x00EA=>0x00CA, + 0x03B9=>0x0399, 0x017A=>0x0179, 0x00EF=>0x00CF, 0x01B0=>0x01AF, 0x0065=>0x0045, + 0x03BB=>0x039B, 0x03B8=>0x0398, 0x03BC=>0x039C, 0x045C=>0x040C, 0x043F=>0x041F, + 0x044C=>0x042C, 0x00FE=>0x00DE, 0x00F0=>0x00D0, 0x1EF3=>0x1EF2, 0x0068=>0x0048, + 0x00EB=>0x00CB, 0x0111=>0x0110, 0x0433=>0x0413, 0x012F=>0x012E, 0x00E6=>0x00C6, + 0x0078=>0x0058, 0x0161=>0x0160, 0x016F=>0x016E, 0x03B1=>0x0391, 0x0457=>0x0407, + 0x0173=>0x0172, 0x00FF=>0x0178, 0x006F=>0x004F, 0x043B=>0x041B, 0x03B5=>0x0395, + 0x0445=>0x0425, 0x0121=>0x0120, 0x017E=>0x017D, 0x017C=>0x017B, 0x03B6=>0x0396, + 0x03B2=>0x0392, 0x03AD=>0x0388, 0x1E85=>0x1E84, 0x0175=>0x0174, 0x0071=>0x0051, + 0x0437=>0x0417, 0x1E0B=>0x1E0A, 0x0148=>0x0147, 0x0105=>0x0104, 0x0458=>0x0408, + 0x014D=>0x014C, 0x00ED=>0x00CD, 0x0079=>0x0059, 0x010B=>0x010A, 0x03CE=>0x038F, + 0x0072=>0x0052, 0x0430=>0x0410, 0x0455=>0x0405, 0x0452=>0x0402, 0x0127=>0x0126, + 0x0137=>0x0136, 0x012B=>0x012A, 0x03AF=>0x038A, 0x044B=>0x042B, 0x006C=>0x004C, + 0x03B7=>0x0397, 0x0125=>0x0124, 0x0219=>0x0218, 0x00FB=>0x00DB, 0x011F=>0x011E, + 0x043E=>0x041E, 0x1E41=>0x1E40, 0x03BD=>0x039D, 0x0107=>0x0106, 0x03CB=>0x03AB, + 0x0446=>0x0426, 0x00FE=>0x00DE, 0x00E7=>0x00C7, 0x03CA=>0x03AA, 0x0441=>0x0421, + 0x0432=>0x0412, 0x010F=>0x010E, 0x00F8=>0x00D8, 0x0077=>0x0057, 0x011B=>0x011A, + 0x0074=>0x0054, 0x006A=>0x004A, 0x045B=>0x040B, 0x0456=>0x0406, 0x0103=>0x0102, + 0x03BB=>0x039B, 0x00F1=>0x00D1, 0x043D=>0x041D, 0x03CC=>0x038C, 0x00E9=>0x00C9, + 0x00F0=>0x00D0, 0x0457=>0x0407, 0x0123=>0x0122, + ); + } + + $uni = utf8::to_unicode($str); + + if ($uni === FALSE) + return FALSE; + + for ($i = 0, $c = count($uni); $i < $c; $i++) + { + if (isset($UTF8_LOWER_TO_UPPER[$uni[$i]])) + { + $uni[$i] = $UTF8_LOWER_TO_UPPER[$uni[$i]]; + } + } + + return utf8::from_unicode($uni); +} \ No newline at end of file diff --git a/system/core/utf8/substr.php b/system/core/utf8/substr.php new file mode 100644 index 00000000..daf66b81 --- /dev/null +++ b/system/core/utf8/substr.php @@ -0,0 +1,75 @@ += $strlen OR ($length < 0 AND $length <= $offset - $strlen)) + return ''; + + // Whole string + if ($offset == 0 AND ($length === NULL OR $length >= $strlen)) + return $str; + + // Build regex + $regex = '^'; + + // Create an offset expression + if ($offset > 0) + { + // PCRE repeating quantifiers must be less than 65536, so repeat when necessary + $x = (int) ($offset / 65535); + $y = (int) ($offset % 65535); + $regex .= ($x == 0) ? '' : '(?:.{65535}){'.$x.'}'; + $regex .= ($y == 0) ? '' : '.{'.$y.'}'; + } + + // Create a length expression + if ($length === NULL) + { + $regex .= '(.*)'; // No length set, grab it all + } + // Find length from the left (positive length) + elseif ($length > 0) + { + // Reduce length so that it can't go beyond the end of the string + $length = min($strlen - $offset, $length); + + $x = (int) ($length / 65535); + $y = (int) ($length % 65535); + $regex .= '('; + $regex .= ($x == 0) ? '' : '(?:.{65535}){'.$x.'}'; + $regex .= '.{'.$y.'})'; + } + // Find length from the right (negative length) + else + { + $x = (int) (-$length / 65535); + $y = (int) (-$length % 65535); + $regex .= '(.*)'; + $regex .= ($x == 0) ? '' : '(?:.{65535}){'.$x.'}'; + $regex .= '.{'.$y.'}'; + } + + preg_match('/'.$regex.'/us', $str, $matches); + return $matches[1]; +} \ No newline at end of file diff --git a/system/core/utf8/substr_replace.php b/system/core/utf8/substr_replace.php new file mode 100644 index 00000000..45e2d2a3 --- /dev/null +++ b/system/core/utf8/substr_replace.php @@ -0,0 +1,22 @@ + 0x10FFFF)) + { + 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 UTF-8 cache + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + } + } + else + { + // ((0xC0 & (*in) != 0x80) AND (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; +} \ No newline at end of file diff --git a/system/core/utf8/transliterate_to_ascii.php b/system/core/utf8/transliterate_to_ascii.php new file mode 100644 index 00000000..07461fbb --- /dev/null +++ b/system/core/utf8/transliterate_to_ascii.php @@ -0,0 +1,77 @@ + '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', 'ö' => 'o', 'è' => 'e', 'ŷ' => 'y', 'ą' => 'a', 'ł' => 'l', + 'ų' => 'u', 'ů' => 'u', 'ş' => 's', 'ğ' => 'g', 'ļ' => 'l', 'ƒ' => 'f', 'ž' => 'z', + 'ẃ' => 'w', 'ḃ' => 'b', 'å' => 'a', 'ì' => 'i', 'ï' => 'i', 'ḋ' => 'd', 'ť' => 't', + 'ŗ' => 'r', 'ä' => 'a', 'í' => 'i', 'ŕ' => 'r', 'ê' => 'e', 'ü' => 'u', 'ò' => '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', + ); + } + + $str = str_replace( + array_keys($UTF8_LOWER_ACCENTS), + array_values($UTF8_LOWER_ACCENTS), + $str + ); + } + + if ($case >= 0) + { + if ($UTF8_UPPER_ACCENTS === NULL) + { + $UTF8_UPPER_ACCENTS = array( + 'À' => 'A', 'Ô' => 'O', 'Ď' => 'D', 'Ḟ' => 'F', 'Ë' => 'E', 'Š' => 'S', 'Ơ' => 'O', + 'Ă' => 'A', 'Ř' => 'R', 'Ț' => 'T', 'Ň' => 'N', 'Ā' => 'A', 'Ķ' => 'K', 'Ĕ' => 'E', + 'Ŝ' => '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', 'Ö' => 'O', 'È' => 'E', 'Ŷ' => 'Y', 'Ą' => 'A', 'Ł' => 'L', + 'Ų' => 'U', 'Ů' => 'U', 'Ş' => 'S', 'Ğ' => 'G', 'Ļ' => 'L', 'Ƒ' => 'F', 'Ž' => 'Z', + 'Ẃ' => 'W', 'Ḃ' => 'B', 'Å' => 'A', 'Ì' => 'I', 'Ï' => 'I', 'Ḋ' => 'D', 'Ť' => 'T', + 'Ŗ' => 'R', 'Ä' => 'A', 'Í' => 'I', 'Ŕ' => 'R', 'Ê' => 'E', 'Ü' => 'U', 'Ò' => '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', + ); + } + + $str = str_replace( + array_keys($UTF8_UPPER_ACCENTS), + array_values($UTF8_UPPER_ACCENTS), + $str + ); + } + + return $str; +} \ No newline at end of file diff --git a/system/core/utf8/trim.php b/system/core/utf8/trim.php new file mode 100644 index 00000000..7434102a --- /dev/null +++ b/system/core/utf8/trim.php @@ -0,0 +1,17 @@ + $value) + { + $value = ($keep_keys === TRUE) ? $value : array_values($value); + foreach ($value as $k => $v) + { + $new_array[$k][$key] = $v; + } + } + + return $new_array; + } + + /** + * Removes a key from an array and returns the value. + * + * @param string key to return + * @param array array to work on + * @return mixed value of the requested array key + */ + public static function remove($key, & $array) + { + if ( ! array_key_exists($key, $array)) + return NULL; + + $val = $array[$key]; + unset($array[$key]); + + return $val; + } + + + /** + * Extract one or more keys from an array. Each key given after the first + * argument (the array) will be extracted. Keys that do not exist in the + * search array will be NULL in the extracted data. + * + * @param array array to search + * @param string key name + * @return array + */ + public static function extract(array $search, $keys) + { + // Get the keys, removing the $search array + $keys = array_slice(func_get_args(), 1); + + $found = array(); + foreach ($keys as $key) + { + if (isset($search[$key])) + { + $found[$key] = $search[$key]; + } + else + { + $found[$key] = NULL; + } + } + + return $found; + } + + /** + * Because PHP does not have this function. + * + * @param array array to unshift + * @param string key to unshift + * @param mixed value to unshift + * @return array + */ + public static function unshift_assoc( array & $array, $key, $val) + { + $array = array_reverse($array, TRUE); + $array[$key] = $val; + $array = array_reverse($array, TRUE); + + return $array; + } + + /** + * Because PHP does not have this function, and array_walk_recursive creates + * references in arrays and is not truly recursive. + * + * @param mixed callback to apply to each member of the array + * @param array array to map to + * @return array + */ + public static function map_recursive($callback, array $array) + { + foreach ($array as $key => $val) + { + // Map the callback to the key + $array[$key] = is_array($val) ? arr::map_recursive($callback, $val) : call_user_func($callback, $val); + } + + return $array; + } + + /** + * @param mixed $needle the value to search for + * @param array $haystack an array of values to search in + * @param boolean $sort sort the array now + * @return integer|FALSE the index of the match or FALSE when not found + */ + public static function binary_search($needle, $haystack, $sort = FALSE) + { + if ($sort) + { + sort($haystack); + } + + $high = count($haystack) - 1; + $low = 0; + + while ($low <= $high) + { + $mid = ($low + $high) >> 1; + + if ($haystack[$mid] < $needle) + { + $low = $mid + 1; + } + elseif ($haystack[$mid] > $needle) + { + $high = $mid - 1; + } + else + { + return $mid; + } + } + + return FALSE; + } + + + /** + * Emulates array_merge_recursive, but appends numeric keys and replaces + * associative keys, instead of appending all keys. + * + * @param array any number of arrays + * @return array + */ + public static function merge() + { + $total = func_num_args(); + + $result = array(); + for ($i = 0; $i < $total; $i++) + { + foreach (func_get_arg($i) as $key => $val) + { + if (isset($result[$key])) + { + if (is_array($val)) + { + // Arrays are merged recursively + $result[$key] = arr::merge($result[$key], $val); + } + elseif (is_int($key)) + { + // Indexed arrays are appended + array_push($result, $val); + } + else + { + // Associative arrays are replaced + $result[$key] = $val; + } + } + else + { + // New values are added + $result[$key] = $val; + } + } + } + + return $result; + } + + /** + * Overwrites an array with values from input array(s). + * Non-existing keys will not be appended! + * + * @param array key array + * @param array input array(s) that will overwrite key array values + * @return array + */ + public static function overwrite($array1, $array2) + { + foreach (array_intersect_key($array2, $array1) as $key => $value) + { + $array1[$key] = $value; + } + + if (func_num_args() > 2) + { + foreach (array_slice(func_get_args(), 2) as $array2) + { + foreach (array_intersect_key($array2, $array1) as $key => $value) + { + $array1[$key] = $value; + } + } + } + + return $array1; + } + + /** + * Fill an array with a range of numbers. + * + * @param integer stepping + * @param integer ending number + * @return array + */ + public static function range($step = 10, $max = 100) + { + if ($step < 1) + return array(); + + $array = array(); + for ($i = $step; $i <= $max; $i += $step) + { + $array[$i] = $i; + } + + return $array; + } + + /** + * Recursively convert an array to an object. + * + * @param array array to convert + * @return object + */ + public static function to_object(array $array, $class = 'stdClass') + { + $object = new $class; + + foreach ($array as $key => $value) + { + if (is_array($value)) + { + // Convert the array to an object + $value = arr::to_object($value, $class); + } + + // Add the value to the object + $object->{$key} = $value; + } + + return $object; + } + +} // End arr diff --git a/system/helpers/cookie.php b/system/helpers/cookie.php new file mode 100644 index 00000000..901b6d86 --- /dev/null +++ b/system/helpers/cookie.php @@ -0,0 +1,84 @@ +cookie($name, $default, $xss_clean); + } + + /** + * Nullify and unset a cookie. + * + * @param string cookie name + * @param string URL path + * @param string URL domain + * @return boolean + */ + public static function delete($name, $path = NULL, $domain = NULL) + { + if ( ! isset($_COOKIE[$name])) + return FALSE; + + // Delete the cookie from globals + unset($_COOKIE[$name]); + + // Sets the cookie value to an empty string, and the expiration to 24 hours ago + return cookie::set($name, '', -86400, $path, $domain, FALSE, FALSE); + } + +} // End cookie \ No newline at end of file diff --git a/system/helpers/date.php b/system/helpers/date.php new file mode 100644 index 00000000..7d5a9ab6 --- /dev/null +++ b/system/helpers/date.php @@ -0,0 +1,405 @@ +> 1); + } + + /** + * Converts a DOS timestamp to UNIX format. + * + * @param integer DOS timestamp + * @return integer + */ + public static function dos2unix($timestamp = FALSE) + { + $sec = 2 * ($timestamp & 0x1f); + $min = ($timestamp >> 5) & 0x3f; + $hrs = ($timestamp >> 11) & 0x1f; + $day = ($timestamp >> 16) & 0x1f; + $mon = ($timestamp >> 21) & 0x0f; + $year = ($timestamp >> 25) & 0x7f; + + return mktime($hrs, $min, $sec, $mon, $day, $year + 1980); + } + + /** + * Returns the offset (in seconds) between two time zones. + * @see http://php.net/timezones + * + * @param string timezone that to find the offset of + * @param string|boolean timezone used as the baseline + * @return integer + */ + public static function offset($remote, $local = TRUE) + { + static $offsets; + + // Default values + $remote = (string) $remote; + $local = ($local === TRUE) ? date_default_timezone_get() : (string) $local; + + // Cache key name + $cache = $remote.$local; + + if (empty($offsets[$cache])) + { + // Create timezone objects + $remote = new DateTimeZone($remote); + $local = new DateTimeZone($local); + + // Create date objects from timezones + $time_there = new DateTime('now', $remote); + $time_here = new DateTime('now', $local); + + // Find the offset + $offsets[$cache] = $remote->getOffset($time_there) - $local->getOffset($time_here); + } + + return $offsets[$cache]; + } + + /** + * Number of seconds in a minute, incrementing by a step. + * + * @param integer amount to increment each step by, 1 to 30 + * @param integer start value + * @param integer end value + * @return array A mirrored (foo => foo) array from 1-60. + */ + public static function seconds($step = 1, $start = 0, $end = 60) + { + // Always integer + $step = (int) $step; + + $seconds = array(); + + for ($i = $start; $i < $end; $i += $step) + { + $seconds[$i] = ($i < 10) ? '0'.$i : $i; + } + + return $seconds; + } + + /** + * Number of minutes in an hour, incrementing by a step. + * + * @param integer amount to increment each step by, 1 to 30 + * @return array A mirrored (foo => foo) array from 1-60. + */ + public static function minutes($step = 5) + { + // Because there are the same number of minutes as seconds in this set, + // we choose to re-use seconds(), rather than creating an entirely new + // function. Shhhh, it's cheating! ;) There are several more of these + // in the following methods. + return date::seconds($step); + } + + /** + * Number of hours in a day. + * + * @param integer amount to increment each step by + * @param boolean use 24-hour time + * @param integer the hour to start at + * @return array A mirrored (foo => foo) array from start-12 or start-23. + */ + public static function hours($step = 1, $long = FALSE, $start = NULL) + { + // Default values + $step = (int) $step; + $long = (bool) $long; + $hours = array(); + + // Set the default start if none was specified. + if ($start === NULL) + { + $start = ($long === FALSE) ? 1 : 0; + } + + $hours = array(); + + // 24-hour time has 24 hours, instead of 12 + $size = ($long === TRUE) ? 23 : 12; + + for ($i = $start; $i <= $size; $i += $step) + { + $hours[$i] = $i; + } + + return $hours; + } + + /** + * Returns AM or PM, based on a given hour. + * + * @param integer number of the hour + * @return string + */ + public static function ampm($hour) + { + // Always integer + $hour = (int) $hour; + + return ($hour > 11) ? 'PM' : 'AM'; + } + + /** + * Adjusts a non-24-hour number into a 24-hour number. + * + * @param integer hour to adjust + * @param string AM or PM + * @return string + */ + public static function adjust($hour, $ampm) + { + $hour = (int) $hour; + $ampm = strtolower($ampm); + + switch ($ampm) + { + case 'am': + if ($hour == 12) + $hour = 0; + break; + case 'pm': + if ($hour < 12) + $hour += 12; + break; + } + + return sprintf('%02s', $hour); + } + + /** + * Number of days in month. + * + * @param integer number of month + * @param integer number of year to check month, defaults to the current year + * @return array A mirrored (foo => foo) array of the days. + */ + public static function days($month, $year = FALSE) + { + static $months; + + // Always integers + $month = (int) $month; + $year = (int) $year; + + // Use the current year by default + $year = ($year == FALSE) ? date('Y') : $year; + + // We use caching for months, because time functions are used + if (empty($months[$year][$month])) + { + $months[$year][$month] = array(); + + // Use date to find the number of days in the given month + $total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1; + + for ($i = 1; $i < $total; $i++) + { + $months[$year][$month][$i] = $i; + } + } + + return $months[$year][$month]; + } + + /** + * Number of months in a year + * + * @return array A mirrored (foo => foo) array from 1-12. + */ + public static function months() + { + return date::hours(); + } + + /** + * Returns an array of years between a starting and ending year. + * Uses the current year +/- 5 as the max/min. + * + * @param integer starting year + * @param integer ending year + * @return array + */ + public static function years($start = FALSE, $end = FALSE) + { + // Default values + $start = ($start === FALSE) ? date('Y') - 5 : (int) $start; + $end = ($end === FALSE) ? date('Y') + 5 : (int) $end; + + $years = array(); + + // Add one, so that "less than" works + $end += 1; + + for ($i = $start; $i < $end; $i++) + { + $years[$i] = $i; + } + + return $years; + } + + /** + * Returns time difference between two timestamps, in human readable format. + * + * @param integer timestamp + * @param integer timestamp, defaults to the current time + * @param string formatting string + * @return string|array + */ + public static function timespan($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') + { + // Array with the output formats + $output = preg_split('/[^a-z]+/', strtolower((string) $output)); + + // Invalid output + if (empty($output)) + return FALSE; + + // Make the output values into keys + extract(array_flip($output), EXTR_SKIP); + + // Default values + $time1 = max(0, (int) $time1); + $time2 = empty($time2) ? time() : max(0, (int) $time2); + + // Calculate timespan (seconds) + $timespan = abs($time1 - $time2); + + // All values found using Google Calculator. + // Years and months do not match the formula exactly, due to leap years. + + // Years ago, 60 * 60 * 24 * 365 + isset($years) and $timespan -= 31556926 * ($years = (int) floor($timespan / 31556926)); + + // Months ago, 60 * 60 * 24 * 30 + isset($months) and $timespan -= 2629744 * ($months = (int) floor($timespan / 2629743.83)); + + // Weeks ago, 60 * 60 * 24 * 7 + isset($weeks) and $timespan -= 604800 * ($weeks = (int) floor($timespan / 604800)); + + // Days ago, 60 * 60 * 24 + isset($days) and $timespan -= 86400 * ($days = (int) floor($timespan / 86400)); + + // Hours ago, 60 * 60 + isset($hours) and $timespan -= 3600 * ($hours = (int) floor($timespan / 3600)); + + // Minutes ago, 60 + isset($minutes) and $timespan -= 60 * ($minutes = (int) floor($timespan / 60)); + + // Seconds ago, 1 + isset($seconds) and $seconds = $timespan; + + // Remove the variables that cannot be accessed + unset($timespan, $time1, $time2); + + // Deny access to these variables + $deny = array_flip(array('deny', 'key', 'difference', 'output')); + + // Return the difference + $difference = array(); + foreach ($output as $key) + { + if (isset($$key) AND ! isset($deny[$key])) + { + // Add requested key to the output + $difference[$key] = $$key; + } + } + + // Invalid output formats string + if (empty($difference)) + return FALSE; + + // If only one output format was asked, don't put it in an array + if (count($difference) === 1) + return current($difference); + + // Return array + return $difference; + } + + /** + * Returns time difference between two timestamps, in the format: + * N year, N months, N weeks, N days, N hours, N minutes, and N seconds ago + * + * @param integer timestamp + * @param integer timestamp, defaults to the current time + * @param string formatting string + * @return string + */ + public static function timespan_string($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') + { + if ($difference = date::timespan($time1, $time2, $output) AND is_array($difference)) + { + // Determine the key of the last item in the array + $last = end($difference); + $last = key($difference); + + $span = array(); + foreach ($difference as $name => $amount) + { + if ($amount === 0) + { + // Skip empty amounts + continue; + } + + // Add the amount to the span + $span[] = ($name === $last ? ' and ' : ', ').$amount.' '.($amount === 1 ? inflector::singular($name) : $name); + } + + // If the difference is less than 60 seconds, remove the preceding and. + if (count($span) === 1) + { + $span[0] = ltrim($span[0], 'and '); + } + + // Replace difference by making the span into a string + $difference = trim(implode('', $span), ','); + } + elseif (is_int($difference)) + { + // Single-value return + $difference = $difference.' '.($difference === 1 ? inflector::singular($output) : $output); + } + + return $difference; + } + +} // End date \ No newline at end of file diff --git a/system/helpers/download.php b/system/helpers/download.php new file mode 100644 index 00000000..49fed42c --- /dev/null +++ b/system/helpers/download.php @@ -0,0 +1,105 @@ +setUsername($config['options']['username']); + empty($config['options']['password']) or $connection->setPassword($config['options']['password']); + + if ( ! empty($config['options']['auth'])) + { + // Get the class name and params + list ($class, $params) = arr::callback_string($config['options']['auth']); + + if ($class === 'PopB4Smtp') + { + // Load the PopB4Smtp class manually, due to its odd filename + require Kohana::find_file('vendor', 'swift/Swift/Authenticator/$PopB4Smtp$'); + } + + // Prepare the class name for auto-loading + $class = 'Swift_Authenticator_'.$class; + + // Attach the authenticator + $connection->attachAuthenticator(($params === NULL) ? new $class : new $class($params[0])); + } + + // Set the timeout to 5 seconds + $connection->setTimeout(empty($config['options']['timeout']) ? 5 : (int) $config['options']['timeout']); + break; + case 'sendmail': + // Create a sendmail connection + $connection = new Swift_Connection_Sendmail + ( + empty($config['options']) ? Swift_Connection_Sendmail::AUTO_DETECT : $config['options'] + ); + + // Set the timeout to 5 seconds + $connection->setTimeout(5); + break; + default: + // Use the native connection + $connection = new Swift_Connection_NativeMail($config['options']); + break; + } + + // Create the SwiftMailer instance + return email::$mail = new Swift($connection); + } + + /** + * Send an email message. + * + * @param string|array recipient email (and name), or an array of To, Cc, Bcc names + * @param string|array sender email (and name) + * @param string message subject + * @param string message body + * @param boolean send email as HTML + * @return integer number of emails sent + */ + public static function send($to, $from, $subject, $message, $html = FALSE) + { + // Connect to SwiftMailer + (email::$mail === NULL) and email::connect(); + + // Determine the message type + $html = ($html === TRUE) ? 'text/html' : 'text/plain'; + + // Create the message + $message = new Swift_Message($subject, $message, $html, '8bit', 'utf-8'); + + if (is_string($to)) + { + // Single recipient + $recipients = new Swift_Address($to); + } + elseif (is_array($to)) + { + if (isset($to[0]) AND isset($to[1])) + { + // Create To: address set + $to = array('to' => $to); + } + + // Create a list of recipients + $recipients = new Swift_RecipientList; + + foreach ($to as $method => $set) + { + if ( ! in_array($method, array('to', 'cc', 'bcc'))) + { + // Use To: by default + $method = 'to'; + } + + // Create method name + $method = 'add'.ucfirst($method); + + if (is_array($set)) + { + // Add a recipient with name + $recipients->$method($set[0], $set[1]); + } + else + { + // Add a recipient without name + $recipients->$method($set); + } + } + } + + if (is_string($from)) + { + // From without a name + $from = new Swift_Address($from); + } + elseif (is_array($from)) + { + // From with a name + $from = new Swift_Address($from[0], $from[1]); + } + + return email::$mail->send($message, $recipients, $from); + } + +} // End email \ No newline at end of file diff --git a/system/helpers/expires.php b/system/helpers/expires.php new file mode 100644 index 00000000..c43cc0cc --- /dev/null +++ b/system/helpers/expires.php @@ -0,0 +1,111 @@ + 0) + { + // Re-send headers + header('Last-Modified: '.gmdate('D, d M Y H:i:s T', $mod_time)); + header('Expires: '.gmdate('D, d M Y H:i:s T', time() + $mod_time_diff)); + header('Cache-Control: max-age='.$mod_time_diff); + header('Status: 304 Not Modified', TRUE, 304); + + // Prevent any output + Event::add('system.display', array('expires', 'prevent_output')); + + // Exit to prevent other output + exit; + } + } + + return FALSE; + } + + /** + * Check headers already created to not step on download or Img_lib's feet + * + * @return boolean + */ + public static function check_headers() + { + foreach (headers_list() as $header) + { + if ((session_cache_limiter() == '' AND stripos($header, 'Last-Modified:') === 0) + OR stripos($header, 'Expires:') === 0) + { + return FALSE; + } + } + + return TRUE; + } + + /** + * Prevent any output from being displayed. Executed during system.display. + * + * @return void + */ + public static function prevent_output() + { + Kohana::$output = ''; + } + +} // End expires \ No newline at end of file diff --git a/system/helpers/feed.php b/system/helpers/feed.php new file mode 100644 index 00000000..74bb2f6b --- /dev/null +++ b/system/helpers/feed.php @@ -0,0 +1,122 @@ +channel) ? $feed->xpath('//item') : $feed->entry; + + $i = 0; + $items = array(); + + foreach ($feed as $item) + { + if ($limit > 0 AND $i++ === $limit) + break; + + $items[] = (array) $item; + } + + return $items; + } + + /** + * Creates a feed from the given parameters. + * + * @param array feed information + * @param array items to add to the feed + * @param string define which format to use + * @param string define which encoding to use + * @return string + */ + public static function create($info, $items, $format = 'rss2', $encoding = 'UTF-8') + { + $info += array('title' => 'Generated Feed', 'link' => '', 'generator' => 'KohanaPHP'); + + $feed = ''; + $feed = simplexml_load_string($feed); + + foreach ($info as $name => $value) + { + if (($name === 'pubDate' OR $name === 'lastBuildDate') AND (is_int($value) OR ctype_digit($value))) + { + // Convert timestamps to RFC 822 formatted dates + $value = date(DATE_RFC822, $value); + } + elseif (($name === 'link' OR $name === 'docs') AND strpos($value, '://') === FALSE) + { + // Convert URIs to URLs + $value = url::site($value, 'http'); + } + + // Add the info to the channel + $feed->channel->addChild($name, $value); + } + + foreach ($items as $item) + { + // Add the item to the channel + $row = $feed->channel->addChild('item'); + + foreach ($item as $name => $value) + { + if ($name === 'pubDate' AND (is_int($value) OR ctype_digit($value))) + { + // Convert timestamps to RFC 822 formatted dates + $value = date(DATE_RFC822, $value); + } + elseif (($name === 'link' OR $name === 'guid') AND strpos($value, '://') === FALSE) + { + // Convert URIs to URLs + $value = url::site($value, 'http'); + } + + // Add the info to the row + $row->addChild($name, $value); + } + } + + return $feed->asXML(); + } + +} // End feed \ No newline at end of file diff --git a/system/helpers/file.php b/system/helpers/file.php new file mode 100644 index 00000000..b1b71740 --- /dev/null +++ b/system/helpers/file.php @@ -0,0 +1,186 @@ +'."\n"; + + // Add hidden fields immediate after opening tag + empty($hidden) or $form .= form::hidden($hidden); + + return $form; + } + + /** + * Generates an opening HTML form tag that can be used for uploading files. + * + * @param string form action attribute + * @param array extra attributes + * @param array hidden fields to be created immediately after the form tag + * @return string + */ + public static function open_multipart($action = NULL, $attr = array(), $hidden = array()) + { + // Set multi-part form type + $attr['enctype'] = 'multipart/form-data'; + + return form::open($action, $attr, $hidden); + } + + /** + * Generates a fieldset opening tag. + * + * @param array html attributes + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function open_fieldset($data = NULL, $extra = '') + { + return ''."\n"; + } + + /** + * Generates a fieldset closing tag. + * + * @return string + */ + public static function close_fieldset() + { + return ''."\n"; + } + + /** + * Generates a legend tag for use with a fieldset. + * + * @param string legend text + * @param array HTML attributes + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function legend($text = '', $data = NULL, $extra = '') + { + return ''.$text.''."\n"; + } + + /** + * Generates hidden form fields. + * You can pass a simple key/value string or an associative array with multiple values. + * + * @param string|array input name (string) or key/value pairs (array) + * @param string input value, if using an input name + * @return string + */ + public static function hidden($data, $value = '') + { + if ( ! is_array($data)) + { + $data = array + ( + $data => $value + ); + } + + $input = ''; + foreach ($data as $name => $value) + { + $attr = array + ( + 'type' => 'hidden', + 'name' => $name, + 'value' => $value + ); + + $input .= form::input($attr)."\n"; + } + + return $input; + } + + /** + * Creates an HTML form input tag. Defaults to a text type. + * + * @param string|array input name or an array of HTML attributes + * @param string input value, when using a name + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function input($data, $value = '', $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + // Type and value are required attributes + $data += array + ( + 'type' => 'text', + 'value' => $value + ); + + return ''; + } + + /** + * Creates a HTML form password input tag. + * + * @param string|array input name or an array of HTML attributes + * @param string input value, when using a name + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function password($data, $value = '', $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'password'; + + return form::input($data, $value, $extra); + } + + /** + * Creates an HTML form upload input tag. + * + * @param string|array input name or an array of HTML attributes + * @param string input value, when using a name + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function upload($data, $value = '', $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'file'; + + return form::input($data, $value, $extra); + } + + /** + * Creates an HTML form textarea tag. + * + * @param string|array input name or an array of HTML attributes + * @param string input value, when using a name + * @param string a string to be attached to the end of the attributes + * @param boolean encode existing entities + * @return string + */ + public static function textarea($data, $value = '', $extra = '', $double_encode = TRUE) + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + // Use the value from $data if possible, or use $value + $value = isset($data['value']) ? $data['value'] : $value; + + // Value is not part of the attributes + unset($data['value']); + + return ''.html::specialchars($value, $double_encode).''; + } + + /** + * Creates an HTML form select tag, or "dropdown menu". + * + * @param string|array input name or an array of HTML attributes + * @param array select options, when using a name + * @param string|array option key(s) that should be selected by default + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function dropdown($data, $options = NULL, $selected = NULL, $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + else + { + if (isset($data['options'])) + { + // Use data options + $options = $data['options']; + } + + if (isset($data['selected'])) + { + // Use data selected + $selected = $data['selected']; + } + } + + if (is_array($selected)) + { + // Multi-select box + $data['multiple'] = 'multiple'; + } + else + { + // Single selection (but converted to an array) + $selected = array($selected); + } + + $input = ''."\n"; + foreach ((array) $options as $key => $val) + { + // Key should always be a string + $key = (string) $key; + + if (is_array($val)) + { + $input .= ''."\n"; + foreach ($val as $inner_key => $inner_val) + { + // Inner key should always be a string + $inner_key = (string) $inner_key; + + $sel = in_array($inner_key, $selected) ? ' selected="selected"' : ''; + $input .= ''."\n"; + } + $input .= ''."\n"; + } + else + { + $sel = in_array($key, $selected) ? ' selected="selected"' : ''; + $input .= ''."\n"; + } + } + $input .= ''; + + return $input; + } + + /** + * Creates an HTML form checkbox input tag. + * + * @param string|array input name or an array of HTML attributes + * @param string input value, when using a name + * @param boolean make the checkbox checked by default + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function checkbox($data, $value = '', $checked = FALSE, $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'checkbox'; + + if ($checked == TRUE OR (isset($data['checked']) AND $data['checked'] == TRUE)) + { + $data['checked'] = 'checked'; + } + else + { + unset($data['checked']); + } + + return form::input($data, $value, $extra); + } + + /** + * Creates an HTML form radio input tag. + * + * @param string|array input name or an array of HTML attributes + * @param string input value, when using a name + * @param boolean make the radio selected by default + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function radio($data = '', $value = '', $checked = FALSE, $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'radio'; + + if ($checked == TRUE OR (isset($data['checked']) AND $data['checked'] == TRUE)) + { + $data['checked'] = 'checked'; + } + else + { + unset($data['checked']); + } + + return form::input($data, $value, $extra); + } + + /** + * Creates an HTML form submit input tag. + * + * @param string|array input name or an array of HTML attributes + * @param string input value, when using a name + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function submit($data = '', $value = '', $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + if (empty($data['name'])) + { + // Remove the name if it is empty + unset($data['name']); + } + + $data['type'] = 'submit'; + + return form::input($data, $value, $extra); + } + + /** + * Creates an HTML form button input tag. + * + * @param string|array input name or an array of HTML attributes + * @param string input value, when using a name + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function button($data = '', $value = '', $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + if (empty($data['name'])) + { + // Remove the name if it is empty + unset($data['name']); + } + + if (isset($data['value']) AND empty($value)) + { + $value = arr::remove('value', $data); + } + + return ''.$value.''; + } + + /** + * Closes an open form tag. + * + * @param string string to be attached after the closing tag + * @return string + */ + public static function close($extra = '') + { + return ''."\n".$extra; + } + + /** + * Creates an HTML form label tag. + * + * @param string|array label "for" name or an array of HTML attributes + * @param string label text or HTML + * @param string a string to be attached to the end of the attributes + * @return string + */ + public static function label($data = '', $text = NULL, $extra = '') + { + if ( ! is_array($data)) + { + if (is_string($data)) + { + // Specify the input this label is for + $data = array('for' => $data); + } + else + { + // No input specified + $data = array(); + } + } + + if ($text === NULL AND isset($data['for'])) + { + // Make the text the human-readable input name + $text = ucwords(inflector::humanize($data['for'])); + } + + return ''.$text.''; + } + + /** + * Sorts a key/value array of HTML attributes, putting form attributes first, + * and returns an attribute string. + * + * @param array HTML attributes array + * @return string + */ + public static function attributes($attr, $type = NULL) + { + if (empty($attr)) + return ''; + + if (isset($attr['name']) AND empty($attr['id']) AND strpos($attr['name'], '[') === FALSE) + { + if ($type === NULL AND ! empty($attr['type'])) + { + // Set the type by the attributes + $type = $attr['type']; + } + + switch ($type) + { + case 'text': + case 'textarea': + case 'password': + case 'select': + case 'checkbox': + case 'file': + case 'image': + case 'button': + case 'submit': + // Only specific types of inputs use name to id matching + $attr['id'] = $attr['name']; + break; + } + } + + $order = array + ( + 'action', + 'method', + 'type', + 'id', + 'name', + 'value', + 'src', + 'size', + 'maxlength', + 'rows', + 'cols', + 'accept', + 'tabindex', + 'accesskey', + 'align', + 'alt', + 'title', + 'class', + 'style', + 'selected', + 'checked', + 'readonly', + 'disabled' + ); + + $sorted = array(); + foreach ($order as $key) + { + if (isset($attr[$key])) + { + // Move the attribute to the sorted array + $sorted[$key] = $attr[$key]; + + // Remove the attribute from unsorted array + unset($attr[$key]); + } + } + + // Combine the sorted and unsorted attributes and create an HTML string + return html::attributes(array_merge($sorted, $attr)); + } + +} // End form \ No newline at end of file diff --git a/system/helpers/format.php b/system/helpers/format.php new file mode 100644 index 00000000..fb8a0294 --- /dev/null +++ b/system/helpers/format.php @@ -0,0 +1,66 @@ +=')) + { + $str = htmlspecialchars($str, ENT_QUOTES, 'UTF-8', FALSE); + } + else + { + $str = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&', $str); + $str = str_replace(array('<', '>', '\'', '"'), array('<', '>', ''', '"'), $str); + } + } + + return $str; + } + + /** + * Perform a html::specialchars() with additional URL specific encoding. + * + * @param string string to convert + * @param boolean encode existing entities + * @return string + */ + public static function specialurlencode($str, $double_encode = TRUE) + { + return str_replace(' ', '%20', html::specialchars($str, $double_encode)); + } + + /** + * Create HTML link anchors. + * + * @param string URL or URI string + * @param string link text + * @param array HTML anchor attributes + * @param string non-default protocol, eg: https + * @return string + */ + public static function anchor($uri, $title = NULL, $attributes = NULL, $protocol = NULL) + { + if ($uri === '') + { + $site_url = url::base(FALSE); + } + elseif (strpos($uri, '://') === FALSE AND strpos($uri, '#') !== 0) + { + $site_url = url::site($uri, $protocol); + } + else + { + if (html::$windowed_urls === TRUE AND empty($attributes['target'])) + { + $attributes['target'] = '_blank'; + } + + $site_url = $uri; + } + + return + // Parsed URL + '' + // Title empty? Use the parsed URL + .(($title === NULL) ? $site_url : $title).''; + } + + /** + * Creates an HTML anchor to a file. + * + * @param string name of file to link to + * @param string link text + * @param array HTML anchor attributes + * @param string non-default protocol, eg: ftp + * @return string + */ + public static function file_anchor($file, $title = NULL, $attributes = NULL, $protocol = NULL) + { + return + // Base URL + URI = full URL + '' + // Title empty? Use the filename part of the URI + .(($title === NULL) ? end(explode('/', $file)) : $title) .''; + } + + /** + * Similar to anchor, but with the protocol parameter first. + * + * @param string link protocol + * @param string URI or URL to link to + * @param string link text + * @param array HTML anchor attributes + * @return string + */ + public static function panchor($protocol, $uri, $title = NULL, $attributes = FALSE) + { + return html::anchor($uri, $title, $attributes, $protocol); + } + + /** + * Create an array of anchors from an array of link/title pairs. + * + * @param array link/title pairs + * @return array + */ + public static function anchor_array(array $array) + { + $anchors = array(); + foreach ($array as $link => $title) + { + // Create list of anchors + $anchors[] = html::anchor($link, $title); + } + return $anchors; + } + + /** + * Generates an obfuscated version of an email address. + * + * @param string email address + * @return string + */ + public static function email($email) + { + $safe = ''; + foreach (str_split($email) as $letter) + { + switch (($letter === '@') ? rand(1, 2) : rand(1, 3)) + { + // HTML entity code + case 1: $safe .= '&#'.ord($letter).';'; break; + // Hex character code + case 2: $safe .= '&#x'.dechex(ord($letter)).';'; break; + // Raw (no) encoding + case 3: $safe .= $letter; + } + } + + return $safe; + } + + /** + * Creates an email anchor. + * + * @param string email address to send to + * @param string link text + * @param array HTML anchor attributes + * @return string + */ + public static function mailto($email, $title = NULL, $attributes = NULL) + { + if (empty($email)) + return $title; + + // Remove the subject or other parameters that do not need to be encoded + if (strpos($email, '?') !== FALSE) + { + // Extract the parameters from the email address + list ($email, $params) = explode('?', $email, 2); + + // Make the params into a query string, replacing spaces + $params = '?'.str_replace(' ', '%20', $params); + } + else + { + // No parameters + $params = ''; + } + + // Obfuscate email address + $safe = html::email($email); + + // Title defaults to the encoded email address + empty($title) and $title = $safe; + + // Parse attributes + empty($attributes) or $attributes = html::attributes($attributes); + + // Encoded start of the href="" is a static encoded version of 'mailto:' + return ''.$title.''; + } + + /** + * Generate a "breadcrumb" list of anchors representing the URI. + * + * @param array segments to use as breadcrumbs, defaults to using Router::$segments + * @return string + */ + public static function breadcrumb($segments = NULL) + { + empty($segments) and $segments = Router::$segments; + + $array = array(); + while ($segment = array_pop($segments)) + { + $array[] = html::anchor + ( + // Complete URI for the URL + implode('/', $segments).'/'.$segment, + // Title for the current segment + ucwords(inflector::humanize($segment)) + ); + } + + // Retrun the array of all the segments + return array_reverse($array); + } + + /** + * Creates a meta tag. + * + * @param string|array tag name, or an array of tags + * @param string tag "content" value + * @return string + */ + public static function meta($tag, $value = NULL) + { + if (is_array($tag)) + { + $tags = array(); + foreach ($tag as $t => $v) + { + // Build each tag and add it to the array + $tags[] = html::meta($t, $v); + } + + // Return all of the tags as a string + return implode("\n", $tags); + } + + // Set the meta attribute value + $attr = in_array(strtolower($tag), Kohana::config('http.meta_equiv')) ? 'http-equiv' : 'name'; + + return ''; + } + + /** + * Creates a stylesheet link. + * + * @param string|array filename, or array of filenames to match to array of medias + * @param string|array media type of stylesheet, or array to match filenames + * @param boolean include the index_page in the link + * @return string + */ + public static function stylesheet($style, $media = FALSE, $index = FALSE) + { + return html::link($style, 'stylesheet', 'text/css', '.css', $media, $index); + } + + /** + * Creates a link tag. + * + * @param string|array filename + * @param string|array relationship + * @param string|array mimetype + * @param string specifies suffix of the file + * @param string|array specifies on what device the document will be displayed + * @param boolean include the index_page in the link + * @return string + */ + public static function link($href, $rel, $type, $suffix = FALSE, $media = FALSE, $index = FALSE) + { + $compiled = ''; + + if (is_array($href)) + { + foreach ($href as $_href) + { + $_rel = is_array($rel) ? array_shift($rel) : $rel; + $_type = is_array($type) ? array_shift($type) : $type; + $_media = is_array($media) ? array_shift($media) : $media; + + $compiled .= html::link($_href, $_rel, $_type, $suffix, $_media, $index); + } + } + else + { + if (strpos($href, '://') === FALSE) + { + // Make the URL absolute + $href = url::base($index).$href; + } + + $length = strlen($suffix); + + if ( $length > 0 AND substr_compare($href, $suffix, -$length, $length, FALSE) !== 0) + { + // Add the defined suffix + $href .= $suffix; + } + + $attr = array + ( + 'rel' => $rel, + 'type' => $type, + 'href' => $href, + ); + + if ( ! empty($media)) + { + // Add the media type to the attributes + $attr['media'] = $media; + } + + $compiled = ''; + } + + return $compiled."\n"; + } + + /** + * Creates a script link. + * + * @param string|array filename + * @param boolean include the index_page in the link + * @return string + */ + public static function script($script, $index = FALSE) + { + $compiled = ''; + + if (is_array($script)) + { + foreach ($script as $name) + { + $compiled .= html::script($name, $index); + } + } + else + { + if (strpos($script, '://') === FALSE) + { + // Add the suffix only when it's not already present + $script = url::base((bool) $index).$script; + } + + if (substr_compare($script, '.js', -3, 3, FALSE) !== 0) + { + // Add the javascript suffix + $script .= '.js'; + } + + $compiled = ''; + } + + return $compiled."\n"; + } + + /** + * Creates a image link. + * + * @param string image source, or an array of attributes + * @param string|array image alt attribute, or an array of attributes + * @param boolean include the index_page in the link + * @return string + */ + public static function image($src = NULL, $alt = NULL, $index = FALSE) + { + // Create attribute list + $attributes = is_array($src) ? $src : array('src' => $src); + + if (is_array($alt)) + { + $attributes += $alt; + } + elseif ( ! empty($alt)) + { + // Add alt to attributes + $attributes['alt'] = $alt; + } + + if (strpos($attributes['src'], '://') === FALSE) + { + // Make the src attribute into an absolute URL + $attributes['src'] = url::base($index).$attributes['src']; + } + + return ''; + } + + /** + * Compiles an array of HTML attributes into an attribute string. + * + * @param string|array array of attributes + * @return string + */ + public static function attributes($attrs) + { + if (empty($attrs)) + return ''; + + if (is_string($attrs)) + return ' '.$attrs; + + $compiled = ''; + foreach ($attrs as $key => $val) + { + $compiled .= ' '.$key.'="'.html::specialchars($val).'"'; + } + + return $compiled; + } + +} // End html diff --git a/system/helpers/inflector.php b/system/helpers/inflector.php new file mode 100644 index 00000000..1e4fee23 --- /dev/null +++ b/system/helpers/inflector.php @@ -0,0 +1,193 @@ + 1) + return $str; + + // Cache key name + $key = 'singular_'.$str.$count; + + if (isset(inflector::$cache[$key])) + return inflector::$cache[$key]; + + if (inflector::uncountable($str)) + return inflector::$cache[$key] = $str; + + if (empty(inflector::$irregular)) + { + // Cache irregular words + inflector::$irregular = Kohana::config('inflector.irregular'); + } + + if ($irregular = array_search($str, inflector::$irregular)) + { + $str = $irregular; + } + elseif (preg_match('/[sxz]es$/', $str) OR preg_match('/[^aeioudgkprt]hes$/', $str)) + { + // Remove "es" + $str = substr($str, 0, -2); + } + elseif (preg_match('/[^aeiou]ies$/', $str)) + { + $str = substr($str, 0, -3).'y'; + } + elseif (substr($str, -1) === 's' AND substr($str, -2) !== 'ss') + { + $str = substr($str, 0, -1); + } + + return inflector::$cache[$key] = $str; + } + + /** + * Makes a singular word plural. + * + * @param string word to pluralize + * @return string + */ + public static function plural($str, $count = NULL) + { + // Remove garbage + $str = strtolower(trim($str)); + + if (is_string($count)) + { + // Convert to integer when using a digit string + $count = (int) $count; + } + + // Do nothing with singular + if ($count === 1) + return $str; + + // Cache key name + $key = 'plural_'.$str.$count; + + if (isset(inflector::$cache[$key])) + return inflector::$cache[$key]; + + if (inflector::uncountable($str)) + return inflector::$cache[$key] = $str; + + if (empty(inflector::$irregular)) + { + // Cache irregular words + inflector::$irregular = Kohana::config('inflector.irregular'); + } + + if (isset(inflector::$irregular[$str])) + { + $str = inflector::$irregular[$str]; + } + elseif (preg_match('/[sxz]$/', $str) OR preg_match('/[^aeioudgkprt]h$/', $str)) + { + $str .= 'es'; + } + elseif (preg_match('/[^aeiou]y$/', $str)) + { + // Change "y" to "ies" + $str = substr_replace($str, 'ies', -1); + } + else + { + $str .= 's'; + } + + // Set the cache and return + return inflector::$cache[$key] = $str; + } + + /** + * Makes a phrase camel case. + * + * @param string phrase to camelize + * @return string + */ + public static function camelize($str) + { + $str = 'x'.strtolower(trim($str)); + $str = ucwords(preg_replace('/[\s_]+/', ' ', $str)); + + return substr(str_replace(' ', '', $str), 1); + } + + /** + * Makes a phrase underscored instead of spaced. + * + * @param string phrase to underscore + * @return string + */ + public static function underscore($str) + { + return preg_replace('/\s+/', '_', trim($str)); + } + + /** + * Makes an underscored or dashed phrase human-reable. + * + * @param string phrase to make human-reable + * @return string + */ + public static function humanize($str) + { + return preg_replace('/[_-]+/', ' ', trim($str)); + } + +} // End inflector \ No newline at end of file diff --git a/system/helpers/num.php b/system/helpers/num.php new file mode 100644 index 00000000..3eb5d5ac --- /dev/null +++ b/system/helpers/num.php @@ -0,0 +1,26 @@ + 0); + } + + /** + * Compare the q values for given array of content types and return the one with the highest value. + * If items are found to have the same q value, the first one encountered in the given array wins. + * If all items in the given array have a q value of 0, FALSE is returned. + * + * @param array content types + * @param boolean set to TRUE to disable wildcard checking + * @return mixed string mime type with highest q value, FALSE if none of the given types are accepted + */ + public static function preferred_accept($types, $explicit_check = FALSE) + { + // Initialize + $mime_types = array(); + $max_q = 0; + $preferred = FALSE; + + // Load q values for all given content types + foreach (array_unique($types) as $type) + { + $mime_types[$type] = request::accepts_at_quality($type, $explicit_check); + } + + // Look for the highest q value + foreach ($mime_types as $type => $q) + { + if ($q > $max_q) + { + $max_q = $q; + $preferred = $type; + } + } + + return $preferred; + } + + /** + * Returns quality factor at which the client accepts content type. + * + * @param string content type (e.g. "image/jpg", "jpg") + * @param boolean set to TRUE to disable wildcard checking + * @return integer|float + */ + public static function accepts_at_quality($type = NULL, $explicit_check = FALSE) + { + request::parse_accept_header(); + + // Normalize type + $type = strtolower((string) $type); + + // General content type (e.g. "jpg") + if (strpos($type, '/') === FALSE) + { + // Don't accept anything by default + $q = 0; + + // Look up relevant mime types + foreach ((array) Kohana::config('mimes.'.$type) as $type) + { + $q2 = request::accepts_at_quality($type, $explicit_check); + $q = ($q2 > $q) ? $q2 : $q; + } + + return $q; + } + + // Content type with subtype given (e.g. "image/jpg") + $type = explode('/', $type, 2); + + // Exact match + if (isset(request::$accept_types[$type[0]][$type[1]])) + return request::$accept_types[$type[0]][$type[1]]; + + // Wildcard match (if not checking explicitly) + if ($explicit_check === FALSE AND isset(request::$accept_types[$type[0]]['*'])) + return request::$accept_types[$type[0]]['*']; + + // Catch-all wildcard match (if not checking explicitly) + if ($explicit_check === FALSE AND isset(request::$accept_types['*']['*'])) + return request::$accept_types['*']['*']; + + // Content type not accepted + return 0; + } + + /** + * Parses client's HTTP Accept request header, and builds array structure representing it. + * + * @return void + */ + protected static function parse_accept_header() + { + // Run this function just once + if (request::$accept_types !== NULL) + return; + + // Initialize accept_types array + request::$accept_types = array(); + + // No HTTP Accept header found + if (empty($_SERVER['HTTP_ACCEPT'])) + { + // Accept everything + request::$accept_types['*']['*'] = 1; + return; + } + + // Remove linebreaks and parse the HTTP Accept header + foreach (explode(',', str_replace(array("\r", "\n"), '', $_SERVER['HTTP_ACCEPT'])) as $accept_entry) + { + // Explode each entry in content type and possible quality factor + $accept_entry = explode(';', trim($accept_entry), 2); + + // Explode each content type (e.g. "text/html") + $type = explode('/', $accept_entry[0], 2); + + // Skip invalid content types + if ( ! isset($type[1])) + continue; + + // Assume a default quality factor of 1 if no custom q value found + $q = (isset($accept_entry[1]) AND preg_match('~\bq\s*+=\s*+([.0-9]+)~', $accept_entry[1], $match)) ? (float) $match[1] : 1; + + // Populate accept_types array + if ( ! isset(request::$accept_types[$type[0]][$type[1]]) OR $q > request::$accept_types[$type[0]][$type[1]]) + { + request::$accept_types[$type[0]][$type[1]] = $q; + } + } + } + +} // End request \ No newline at end of file diff --git a/system/helpers/security.php b/system/helpers/security.php new file mode 100644 index 00000000..cd48d2e0 --- /dev/null +++ b/system/helpers/security.php @@ -0,0 +1,47 @@ +xss_clean($str); + } + + /** + * Remove image tags from a string. + * + * @param string string to sanitize + * @return string + */ + public static function strip_image_tags($str) + { + return preg_replace('#\s]*)["\']?[^>]*)?>#is', '$1', $str); + } + + /** + * Remove PHP tags from a string. + * + * @param string string to sanitize + * @return string + */ + public static function encode_php_tags($str) + { + return str_replace(array(''), array('<?', '?>'), $str); + } + +} // End security \ No newline at end of file diff --git a/system/helpers/text.php b/system/helpers/text.php new file mode 100644 index 00000000..d0e573ec --- /dev/null +++ b/system/helpers/text.php @@ -0,0 +1,410 @@ + 1) + { + if (ctype_alpha($str)) + { + // Add a random digit + $str[mt_rand(0, $length - 1)] = chr(mt_rand(48, 57)); + } + elseif (ctype_digit($str)) + { + // Add a random letter + $str[mt_rand(0, $length - 1)] = chr(mt_rand(65, 90)); + } + } + + return $str; + } + + /** + * Reduces multiple slashes in a string to single slashes. + * + * @param string string to reduce slashes of + * @return string + */ + public static function reduce_slashes($str) + { + return preg_replace('#(? $badword) + { + $badwords[$key] = str_replace('\*', '\S*?', preg_quote((string) $badword)); + } + + $regex = '('.implode('|', $badwords).')'; + + if ($replace_partial_words == TRUE) + { + // Just using \b isn't sufficient when we need to replace a badword that already contains word boundaries itself + $regex = '(?<=\b|\s|^)'.$regex.'(?=\b|\s|$)'; + } + + $regex = '!'.$regex.'!ui'; + + if (utf8::strlen($replacement) == 1) + { + $regex .= 'e'; + return preg_replace($regex, 'str_repeat($replacement, utf8::strlen(\'$1\'))', $str); + } + + return preg_replace($regex, $replacement, $str); + } + + /** + * Finds the text that is similar between a set of words. + * + * @param array words to find similar text of + * @return string + */ + public static function similar(array $words) + { + // First word is the word to match against + $word = current($words); + + for ($i = 0, $max = strlen($word); $i < $max; ++$i) + { + foreach ($words as $w) + { + // Once a difference is found, break out of the loops + if ( ! isset($w[$i]) OR $w[$i] !== $word[$i]) + break 2; + } + } + + // Return the similar text + return substr($word, 0, $i); + } + + /** + * Converts text email addresses and anchors into links. + * + * @param string text to auto link + * @return string + */ + public static function auto_link($text) + { + // Auto link emails first to prevent problems with "www.domain.com@example.com" + return text::auto_link_urls(text::auto_link_emails($text)); + } + + /** + * Converts text anchors into links. + * + * @param string text to auto link + * @return string + */ + public static function auto_link_urls($text) + { + // Finds all http/https/ftp/ftps links that are not part of an existing html anchor + if (preg_match_all('~\b(?)(?:ht|f)tps?://\S+(?:/|\b)~i', $text, $matches)) + { + foreach ($matches[0] as $match) + { + // Replace each link with an anchor + $text = str_replace($match, html::anchor($match), $text); + } + } + + // Find all naked www.links.com (without http://) + if (preg_match_all('~\b(?|58;)(?!\.)[-+_a-z0-9.]++(? and
      markup to text. Basically nl2br() on steroids. + * + * @param string subject + * @return string + */ + public static function auto_p($str) + { + // Trim whitespace + if (($str = trim($str)) === '') + return ''; + + // Standardize newlines + $str = str_replace(array("\r\n", "\r"), "\n", $str); + + // Trim whitespace on each line + $str = preg_replace('~^[ \t]+~m', '', $str); + $str = preg_replace('~[ \t]+$~m', '', $str); + + // The following regexes only need to be executed if the string contains html + if ($html_found = (strpos($str, '<') !== FALSE)) + { + // Elements that should not be surrounded by p tags + $no_p = '(?:p|div|h[1-6r]|ul|ol|li|blockquote|d[dlt]|pre|t[dhr]|t(?:able|body|foot|head)|c(?:aption|olgroup)|form|s(?:elect|tyle)|a(?:ddress|rea)|ma(?:p|th))'; + + // Put at least two linebreaks before and after $no_p elements + $str = preg_replace('~^<'.$no_p.'[^>]*+>~im', "\n$0", $str); + $str = preg_replace('~$~im', "$0\n", $str); + } + + // Do the

      magic! + $str = '

      '.trim($str).'

      '; + $str = preg_replace('~\n{2,}~', "

      \n\n

      ", $str); + + // The following regexes only need to be executed if the string contains html + if ($html_found !== FALSE) + { + // Remove p tags around $no_p elements + $str = preg_replace('~

      (?=]*+>)~i', '', $str); + $str = preg_replace('~(]*+>)

      ~i', '$1', $str); + } + + // Convert single linebreaks to
      + $str = preg_replace('~(?\n", $str); + + return $str; + } + + /** + * Returns human readable sizes. + * @see Based on original functions written by: + * @see Aidan Lister: http://aidanlister.com/repos/v/function.size_readable.php + * @see Quentin Zervaas: http://www.phpriot.com/d/code/strings/filesize-format/ + * + * @param integer size in bytes + * @param string a definitive unit + * @param string the return string format + * @param boolean whether to use SI prefixes or IEC + * @return string + */ + public static function bytes($bytes, $force_unit = NULL, $format = NULL, $si = TRUE) + { + // Format string + $format = ($format === NULL) ? '%01.2f %s' : (string) $format; + + // IEC prefixes (binary) + if ($si == FALSE OR strpos($force_unit, 'i') !== FALSE) + { + $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'); + $mod = 1024; + } + // SI prefixes (decimal) + else + { + $units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB'); + $mod = 1000; + } + + // Determine unit to use + if (($power = array_search((string) $force_unit, $units)) === FALSE) + { + $power = ($bytes > 0) ? floor(log($bytes, $mod)) : 0; + } + + return sprintf($format, $bytes / pow($mod, $power), $units[$power]); + } + + /** + * Prevents widow words by inserting a non-breaking space between the last two words. + * @see http://www.shauninman.com/archive/2006/08/22/widont_wordpress_plugin + * + * @param string string to remove widows from + * @return string + */ + public static function widont($str) + { + $str = rtrim($str); + $space = strrpos($str, ' '); + + if ($space !== FALSE) + { + $str = substr($str, 0, $space).' '.substr($str, $space + 1); + } + + return $str; + } + +} // End text \ No newline at end of file diff --git a/system/helpers/upload.php b/system/helpers/upload.php new file mode 100644 index 00000000..422e9e8d --- /dev/null +++ b/system/helpers/upload.php @@ -0,0 +1,162 @@ + 'Refresh', + '300' => 'Multiple Choices', + '301' => 'Moved Permanently', + '302' => 'Found', + '303' => 'See Other', + '304' => 'Not Modified', + '305' => 'Use Proxy', + '307' => 'Temporary Redirect' + ); + + // Validate the method and default to 302 + $method = isset($codes[$method]) ? (string) $method : '302'; + + if ($method === '300') + { + $uri = (array) $uri; + + $output = '
        '; + foreach ($uri as $link) + { + $output .= '
      • '.html::anchor($link).'
      • '; + } + $output .= '
      '; + + // The first URI will be used for the Location header + $uri = $uri[0]; + } + else + { + $output = '

      '.html::anchor($uri).'

      '; + } + + // Run the redirect event + Event::run('system.redirect', $uri); + + if (strpos($uri, '://') === FALSE) + { + // HTTP headers expect absolute URLs + $uri = url::site($uri, request::protocol()); + } + + if ($method === 'refresh') + { + header('Refresh: 0; url='.$uri); + } + else + { + header('HTTP/1.1 '.$method.' '.$codes[$method]); + header('Location: '.$uri); + } + + // We are about to exit, so run the send_headers event + Event::run('system.send_headers'); + + exit('

      '.$method.' - '.$codes[$method].'

      '.$output); + } + +} // End url \ No newline at end of file diff --git a/system/helpers/valid.php b/system/helpers/valid.php new file mode 100644 index 00000000..610076f3 --- /dev/null +++ b/system/helpers/valid.php @@ -0,0 +1,338 @@ += 0; $i -= 2) + { + // Add up every 2nd digit, starting from the right + $checksum += substr($number, $i, 1); + } + + for ($i = $length - 2; $i >= 0; $i -= 2) + { + // Add up every 2nd digit doubled, starting from the right + $double = substr($number, $i, 1) * 2; + + // Subtract 9 from the double where value is greater than 10 + $checksum += ($double >= 10) ? $double - 9 : $double; + } + + // If the checksum is a multiple of 10, the number is valid + return ($checksum % 10 === 0); + } + + /** + * Checks if a phone number is valid. + * + * @param string phone number to check + * @return boolean + */ + public static function phone($number, $lengths = NULL) + { + if ( ! is_array($lengths)) + { + $lengths = array(7,10,11); + } + + // Remove all non-digit characters from the number + $number = preg_replace('/\D+/', '', $number); + + // Check if the number is within range + return in_array(strlen($number), $lengths); + } + + /** + * Tests if a string is a valid date string. + * + * @param string date to check + * @return boolean + */ + public static function date($str) + { + return (strtotime($str) !== FALSE); + } + + /** + * Checks whether a string consists of alphabetical characters only. + * + * @param string input string + * @param boolean trigger UTF-8 compatibility + * @return boolean + */ + public static function alpha($str, $utf8 = FALSE) + { + return ($utf8 === TRUE) + ? (bool) preg_match('/^\pL++$/uD', (string) $str) + : ctype_alpha((string) $str); + } + + /** + * Checks whether a string consists of alphabetical characters and numbers only. + * + * @param string input string + * @param boolean trigger UTF-8 compatibility + * @return boolean + */ + public static function alpha_numeric($str, $utf8 = FALSE) + { + return ($utf8 === TRUE) + ? (bool) preg_match('/^[\pL\pN]++$/uD', (string) $str) + : ctype_alnum((string) $str); + } + + /** + * Checks whether a string consists of alphabetical characters, numbers, underscores and dashes only. + * + * @param string input string + * @param boolean trigger UTF-8 compatibility + * @return boolean + */ + public static function alpha_dash($str, $utf8 = FALSE) + { + return ($utf8 === TRUE) + ? (bool) preg_match('/^[-\pL\pN_]++$/uD', (string) $str) + : (bool) preg_match('/^[-a-z0-9_]++$/iD', (string) $str); + } + + /** + * Checks whether a string consists of digits only (no dots or dashes). + * + * @param string input string + * @param boolean trigger UTF-8 compatibility + * @return boolean + */ + public static function digit($str, $utf8 = FALSE) + { + return ($utf8 === TRUE) + ? (bool) preg_match('/^\pN++$/uD', (string) $str) + : ctype_digit((string) $str); + } + + /** + * Checks whether a string is a valid number (negative and decimal numbers allowed). + * + * @see Uses locale conversion to allow decimal point to be locale specific. + * @see http://www.php.net/manual/en/function.localeconv.php + * + * @param string input string + * @return boolean + */ + public static function numeric($str) + { + // Use localeconv to set the decimal_point value: Usually a comma or period. + $locale = localeconv(); + return (bool) preg_match('/^-?[0-9'.$locale['decimal_point'].']++$/D', (string) $str); + } + + /** + * Checks whether a string is a valid text. Letters, numbers, whitespace, + * dashes, periods, and underscores are allowed. + * + * @param string text to check + * @return boolean + */ + public static function standard_text($str) + { + // pL matches letters + // pN matches numbers + // pZ matches whitespace + // pPc matches underscores + // pPd matches dashes + // pPo matches normal puncuation + return (bool) preg_match('/^[\pL\pN\pZ\p{Pc}\p{Pd}\p{Po}]++$/uD', (string) $str); + } + + /** + * Checks if a string is a proper decimal format. The format array can be + * used to specify a decimal length, or a number and decimal length, eg: + * array(2) would force the number to have 2 decimal places, array(4,2) + * would force the number to have 4 digits and 2 decimal places. + * + * @param string input string + * @param array decimal format: y or x,y + * @return boolean + */ + public static function decimal($str, $format = NULL) + { + // Create the pattern + $pattern = '/^[0-9]%s\.[0-9]%s$/'; + + if ( ! empty($format)) + { + if (count($format) > 1) + { + // Use the format for number and decimal length + $pattern = sprintf($pattern, '{'.$format[0].'}', '{'.$format[1].'}'); + } + elseif (count($format) > 0) + { + // Use the format as decimal length + $pattern = sprintf($pattern, '+', '{'.$format[0].'}'); + } + } + else + { + // No format + $pattern = sprintf($pattern, '+', '+'); + } + + return (bool) preg_match($pattern, (string) $str); + } + +} // End valid \ No newline at end of file diff --git a/system/i18n/en_US/cache.php b/system/i18n/en_US/cache.php new file mode 100644 index 00000000..bef02793 --- /dev/null +++ b/system/i18n/en_US/cache.php @@ -0,0 +1,10 @@ + 'The %s group is not defined in your configuration.', + 'extension_not_loaded' => 'The %s PHP extension must be loaded to use this driver.', + 'unwritable' => 'The configured storage location, %s, is not writable.', + 'resources' => 'Caching of resources is impossible, because resources cannot be serialized.', + 'driver_error' => '%s', +); \ No newline at end of file diff --git a/system/i18n/en_US/calendar.php b/system/i18n/en_US/calendar.php new file mode 100644 index 00000000..21dad220 --- /dev/null +++ b/system/i18n/en_US/calendar.php @@ -0,0 +1,59 @@ + 'Su', + 'mo' => 'Mo', + 'tu' => 'Tu', + 'we' => 'We', + 'th' => 'Th', + 'fr' => 'Fr', + 'sa' => 'Sa', + + // Short day names + 'sun' => 'Sun', + 'mon' => 'Mon', + 'tue' => 'Tue', + 'wed' => 'Wed', + 'thu' => 'Thu', + 'fri' => 'Fri', + 'sat' => 'Sat', + + // Long day names + 'sunday' => 'Sunday', + 'monday' => 'Monday', + 'tuesday' => 'Tuesday', + 'wednesday' => 'Wednesday', + 'thursday' => 'Thursday', + 'friday' => 'Friday', + 'saturday' => 'Saturday', + + // Short month names + 'jan' => 'Jan', + 'feb' => 'Feb', + 'mar' => 'Mar', + 'apr' => 'Apr', + 'may' => 'May', + 'jun' => 'Jun', + 'jul' => 'Jul', + 'aug' => 'Aug', + 'sep' => 'Sep', + 'oct' => 'Oct', + 'nov' => 'Nov', + 'dec' => 'Dec', + + // Long month names + 'january' => 'January', + 'february' => 'February', + 'march' => 'March', + 'april' => 'April', + 'mayl' => 'May', + 'june' => 'June', + 'july' => 'July', + 'august' => 'August', + 'september' => 'September', + 'october' => 'October', + 'november' => 'November', + 'december' => 'December' +); \ No newline at end of file diff --git a/system/i18n/en_US/core.php b/system/i18n/en_US/core.php new file mode 100644 index 00000000..9711b7cd --- /dev/null +++ b/system/i18n/en_US/core.php @@ -0,0 +1,34 @@ + 'There can be only one instance of Kohana per page request', + 'uncaught_exception' => 'Uncaught %s: %s in file %s on line %s', + 'invalid_method' => 'Invalid method %s called in %s', + 'invalid_property' => 'The %s property does not exist in the %s class.', + 'log_dir_unwritable' => 'The log directory is not writable: %s', + 'resource_not_found' => 'The requested %s, %s, could not be found', + 'invalid_filetype' => 'The requested filetype, .%s, is not allowed in your view configuration file', + 'view_set_filename' => 'You must set the the view filename before calling render', + 'no_default_route' => 'Please set a default route in config/routes.php', + 'no_controller' => 'Kohana was not able to determine a controller to process this request: %s', + 'page_not_found' => 'The page you requested, %s, could not be found.', + 'stats_footer' => 'Loaded in {execution_time} seconds, using {memory_usage} of memory. Generated by Kohana v{kohana_version}.', + 'error_file_line' => '%s [%s]:', + 'stack_trace' => 'Stack Trace', + 'generic_error' => 'Unable to Complete Request', + 'errors_disabled' => 'You can go to the home page or try again.', + + // Drivers + 'driver_implements' => 'The %s driver for the %s library must implement the %s interface', + 'driver_not_found' => 'The %s driver for the %s library could not be found', + + // Resource names + 'config' => 'config file', + 'controller' => 'controller', + 'helper' => 'helper', + 'library' => 'library', + 'driver' => 'driver', + 'model' => 'model', + 'view' => 'view', +); diff --git a/system/i18n/en_US/database.php b/system/i18n/en_US/database.php new file mode 100644 index 00000000..172e5c90 --- /dev/null +++ b/system/i18n/en_US/database.php @@ -0,0 +1,15 @@ + 'The %s group is not defined in your configuration.', + 'error' => 'There was an SQL error: %s', + 'connection' => 'There was an error connecting to the database: %s', + 'invalid_dsn' => 'The DSN you supplied is not valid: %s', + 'must_use_set' => 'You must set a SET clause for your query.', + 'must_use_where' => 'You must set a WHERE clause for your query.', + 'must_use_table' => 'You must set a database table for your query.', + 'table_not_found' => 'Table %s does not exist in your database.', + 'not_implemented' => 'The method you called, %s, is not supported by this driver.', + 'result_read_only' => 'Query results are read only.' +); \ No newline at end of file diff --git a/system/i18n/en_US/encrypt.php b/system/i18n/en_US/encrypt.php new file mode 100644 index 00000000..9afd6208 --- /dev/null +++ b/system/i18n/en_US/encrypt.php @@ -0,0 +1,8 @@ + 'The %s group is not defined in your configuration.', + 'requires_mcrypt' => 'To use the Encrypt library, mcrypt must be enabled in your PHP installation', + 'no_encryption_key' => 'To use the Encrypt library, you must set an encryption key in your config file' +); diff --git a/system/i18n/en_US/errors.php b/system/i18n/en_US/errors.php new file mode 100644 index 00000000..83341a2c --- /dev/null +++ b/system/i18n/en_US/errors.php @@ -0,0 +1,16 @@ + array( 1, 'Framework Error', 'Please check the Kohana documentation for information about the following error.'), + E_PAGE_NOT_FOUND => array( 1, 'Page Not Found', 'The requested page was not found. It may have moved, been deleted, or archived.'), + E_DATABASE_ERROR => array( 1, 'Database Error', 'A database error occurred while performing the requested procedure. Please review the database error below for more information.'), + E_RECOVERABLE_ERROR => array( 1, 'Recoverable Error', 'An error was detected which prevented the loading of this page. If this problem persists, please contact the website administrator.'), + E_ERROR => array( 1, 'Fatal Error', ''), + E_USER_ERROR => array( 1, 'Fatal Error', ''), + E_PARSE => array( 1, 'Syntax Error', ''), + E_WARNING => array( 1, 'Warning Message', ''), + E_USER_WARNING => array( 1, 'Warning Message', ''), + E_STRICT => array( 2, 'Strict Mode Error', ''), + E_NOTICE => array( 2, 'Runtime Message', ''), +); \ No newline at end of file diff --git a/system/i18n/en_US/event.php b/system/i18n/en_US/event.php new file mode 100644 index 00000000..282a0a25 --- /dev/null +++ b/system/i18n/en_US/event.php @@ -0,0 +1,7 @@ + 'Attempt to attach invalid subject %s to %s failed: Subjects must extend the Event_Subject class', + 'invalid_observer' => 'Attempt to attach invalid observer %s to %s failed: Observers must extend the Event_Observer class', +); diff --git a/system/i18n/en_US/image.php b/system/i18n/en_US/image.php new file mode 100644 index 00000000..9f184930 --- /dev/null +++ b/system/i18n/en_US/image.php @@ -0,0 +1,33 @@ + 'The Image library requires the getimagesize() PHP function, which is not available in your installation.', + 'unsupported_method' => 'Your configured driver does not support the %s image transformation.', + 'file_not_found' => 'The specified image, %s, was not found. Please verify that images exist by using file_exists() before manipulating them.', + 'type_not_allowed' => 'The specified image, %s, is not an allowed image type.', + 'invalid_width' => 'The width you specified, %s, is not valid.', + 'invalid_height' => 'The height you specified, %s, is not valid.', + 'invalid_dimensions' => 'The dimensions specified for %s are not valid.', + 'invalid_master' => 'The master dimension specified is not valid.', + 'invalid_flip' => 'The flip direction specified is not valid.', + 'directory_unwritable' => 'The specified directory, %s, is not writable.', + + // ImageMagick specific messages + 'imagemagick' => array + ( + 'not_found' => 'The ImageMagick directory specified does not contain a required program, %s.', + ), + + // GraphicsMagick specific messages + 'graphicsmagick' => array + ( + 'not_found' => 'The GraphicsMagick directory specified does not contain a required program, %s.', + ), + + // GD specific messages + 'gd' => array + ( + 'requires_v2' => 'The Image library requires GD2. Please see http://php.net/gd_info for more information.', + ), +); diff --git a/system/i18n/en_US/orm.php b/system/i18n/en_US/orm.php new file mode 100644 index 00000000..3c5720bc --- /dev/null +++ b/system/i18n/en_US/orm.php @@ -0,0 +1,3 @@ + 'Benchmarks', + 'post_data' => 'Post Data', + 'no_post' => 'No post data', + 'session_data' => 'Session Data', + 'no_session' => 'No session data', + 'queries' => 'Database Queries', + 'no_queries' => 'No queries', + 'no_database' => 'Database not loaded', + 'cookie_data' => 'Cookie Data', + 'no_cookie' => 'No cookie data', +); diff --git a/system/i18n/en_US/session.php b/system/i18n/en_US/session.php new file mode 100644 index 00000000..ee781c65 --- /dev/null +++ b/system/i18n/en_US/session.php @@ -0,0 +1,6 @@ + 'The session_name, %s, is invalid. It must contain only alphanumeric characters and underscores. Also at least one letter must be present.', +); \ No newline at end of file diff --git a/system/i18n/en_US/upload.php b/system/i18n/en_US/upload.php new file mode 100644 index 00000000..7f6e216d --- /dev/null +++ b/system/i18n/en_US/upload.php @@ -0,0 +1,6 @@ + 'The upload destination folder, %s, does not appear to be writable.', +); \ No newline at end of file diff --git a/system/i18n/en_US/validation.php b/system/i18n/en_US/validation.php new file mode 100644 index 00000000..d98e548f --- /dev/null +++ b/system/i18n/en_US/validation.php @@ -0,0 +1,41 @@ + 'Invalid validation rule used: %s', + 'i18n_array' => 'The %s i18n key must be an array to be used with the in_lang rule', + 'not_callable' => 'Callback %s used for Validation is not callable', + + // General errors + 'unknown_error' => 'Unknown validation error while validating the %s field.', + 'required' => 'The %s field is required.', + 'min_length' => 'The %s field must be at least %d characters long.', + 'max_length' => 'The %s field must be %d characters or fewer.', + 'exact_length' => 'The %s field must be exactly %d characters.', + 'in_array' => 'The %s field must be selected from the options listed.', + 'matches' => 'The %s field must match the %s field.', + 'valid_url' => 'The %s field must contain a valid URL.', + 'valid_email' => 'The %s field must contain a valid email address.', + 'valid_ip' => 'The %s field must contain a valid IP address.', + 'valid_type' => 'The %s field must only contain %s characters.', + 'range' => 'The %s field must be between specified ranges.', + 'regex' => 'The %s field does not match accepted input.', + 'depends_on' => 'The %s field depends on the %s field.', + + // Upload errors + 'user_aborted' => 'The %s file was aborted during upload.', + 'invalid_type' => 'The %s file is not an allowed file type.', + 'max_size' => 'The %s file you uploaded was too large. The maximum size allowed is %s.', + 'max_width' => 'The %s file you uploaded was too big. The maximum allowed width is %spx.', + 'max_height' => 'The %s file you uploaded was too big. The maximum allowed height is %spx.', + 'min_width' => 'The %s file you uploaded was too small. The minimum allowed width is %spx.', + 'min_height' => 'The %s file you uploaded was too small. The minimum allowed height is %spx.', + + // Field types + 'alpha' => 'alphabetical', + 'alpha_numeric' => 'alphabetical and numeric', + 'alpha_dash' => 'alphabetical, dash, and underscore', + 'digit' => 'digit', + 'numeric' => 'numeric', +); diff --git a/system/libraries/Cache.php b/system/libraries/Cache.php new file mode 100644 index 00000000..8a02a905 --- /dev/null +++ b/system/libraries/Cache.php @@ -0,0 +1,208 @@ +config = $config; + + // Set driver name + $driver = 'Cache_'.ucfirst($this->config['driver']).'_Driver'; + + // Load the driver + if ( ! Kohana::auto_load($driver)) + throw new Kohana_Exception('core.driver_not_found', $this->config['driver'], get_class($this)); + + // Initialize the driver + $this->driver = new $driver($this->config['params']); + + // Validate the driver + if ( ! ($this->driver instanceof Cache_Driver)) + throw new Kohana_Exception('core.driver_implements', $this->config['driver'], get_class($this), 'Cache_Driver'); + + Kohana::log('debug', 'Cache Library initialized'); + + if (Cache::$loaded !== TRUE) + { + $this->config['requests'] = (int) $this->config['requests']; + + if ($this->config['requests'] > 0 AND mt_rand(1, $this->config['requests']) === 1) + { + // Do garbage collection + $this->driver->delete_expired(); + + Kohana::log('debug', 'Cache: Expired caches deleted.'); + } + + // Cache has been loaded once + Cache::$loaded = TRUE; + } + } + + /** + * Fetches a cache by id. NULL is returned when a cache item is not found. + * + * @param string cache id + * @return mixed cached data or NULL + */ + public function get($id) + { + // Sanitize the ID + $id = $this->sanitize_id($id); + + return $this->driver->get($id); + } + + /** + * Fetches all of the caches for a given tag. An empty array will be + * returned when no matching caches are found. + * + * @param string cache tag + * @return array all cache items matching the tag + */ + public function find($tag) + { + return $this->driver->find($tag); + } + + /** + * Set a cache item by id. Tags may also be added and a custom lifetime + * can be set. Non-string data is automatically serialized. + * + * @param string unique cache id + * @param mixed data to cache + * @param array|string tags for this item + * @param integer number of seconds until the cache expires + * @return boolean + */ + function set($id, $data, $tags = NULL, $lifetime = NULL) + { + if (is_resource($data)) + throw new Kohana_Exception('cache.resources'); + + // Sanitize the ID + $id = $this->sanitize_id($id); + + if ($lifetime === NULL) + { + // Get the default lifetime + $lifetime = $this->config['lifetime']; + } + + return $this->driver->set($id, $data, (array) $tags, $lifetime); + } + + /** + * Delete a cache item by id. + * + * @param string cache id + * @return boolean + */ + public function delete($id) + { + // Sanitize the ID + $id = $this->sanitize_id($id); + + return $this->driver->delete($id); + } + + /** + * Delete all cache items with a given tag. + * + * @param string cache tag name + * @return boolean + */ + public function delete_tag($tag) + { + return $this->driver->delete($tag, TRUE); + } + + /** + * Delete ALL cache items items. + * + * @return boolean + */ + public function delete_all() + { + return $this->driver->delete(TRUE); + } + + /** + * Replaces troublesome characters with underscores. + * + * @param string cache id + * @return string + */ + protected function sanitize_id($id) + { + // Change slashes and spaces to underscores + return str_replace(array('/', '\\', ' '), '_', $id); + } + +} // End Cache diff --git a/system/libraries/Controller.php b/system/libraries/Controller.php new file mode 100644 index 00000000..d111f25e --- /dev/null +++ b/system/libraries/Controller.php @@ -0,0 +1,78 @@ +uri = URI::instance(); + + // Input should always be available + $this->input = Input::instance(); + } + + /** + * Handles methods that do not exist. + * + * @param string method name + * @param array arguments + * @return void + */ + public function __call($method, $args) + { + // Default to showing a 404 page + Event::run('system.404'); + } + + /** + * Includes a View within the controller scope. + * + * @param string view filename + * @param array array of view variables + * @return string + */ + public function _kohana_load_view($kohana_view_filename, $kohana_input_data) + { + if ($kohana_view_filename == '') + return; + + // Buffering on + ob_start(); + + // Import the view variables to local namespace + extract($kohana_input_data, EXTR_SKIP); + + // Views are straight HTML pages with embedded PHP, so importing them + // this way insures that $this can be accessed as if the user was in + // the controller, which gives the easiest access to libraries in views + include $kohana_view_filename; + + // Fetch the output and close the buffer + return ob_get_clean(); + } + +} // End Controller Class \ No newline at end of file diff --git a/system/libraries/Database.php b/system/libraries/Database.php new file mode 100644 index 00000000..6267f63a --- /dev/null +++ b/system/libraries/Database.php @@ -0,0 +1,1444 @@ + TRUE, + 'persistent' => FALSE, + 'connection' => '', + 'character_set' => 'utf8', + 'table_prefix' => '', + 'object' => TRUE, + 'cache' => FALSE, + 'escape' => TRUE, + ); + + // Database driver object + protected $driver; + protected $link; + + // Un-compiled parts of the SQL query + protected $select = array(); + protected $set = array(); + protected $from = array(); + protected $join = array(); + protected $where = array(); + protected $orderby = array(); + protected $order = array(); + protected $groupby = array(); + protected $having = array(); + protected $distinct = FALSE; + protected $limit = FALSE; + protected $offset = FALSE; + protected $last_query = ''; + + // Stack of queries for push/pop + protected $query_history = array(); + + /** + * Returns a singleton instance of Database. + * + * @param mixed configuration array or DSN + * @return Database_Core + */ + public static function & instance($name = 'default', $config = NULL) + { + if ( ! isset(Database::$instances[$name])) + { + // Create a new instance + Database::$instances[$name] = new Database($config === NULL ? $name : $config); + } + + return Database::$instances[$name]; + } + + /** + * Returns the name of a given database instance. + * + * @param Database instance of Database + * @return string + */ + public static function instance_name(Database $db) + { + return array_search($db, Database::$instances, TRUE); + } + + /** + * Sets up the database configuration, loads the Database_Driver. + * + * @throws Kohana_Database_Exception + */ + public function __construct($config = array()) + { + if (empty($config)) + { + // Load the default group + $config = Kohana::config('database.default'); + } + elseif (is_array($config) AND count($config) > 0) + { + if ( ! array_key_exists('connection', $config)) + { + $config = array('connection' => $config); + } + } + elseif (is_string($config)) + { + // The config is a DSN string + if (strpos($config, '://') !== FALSE) + { + $config = array('connection' => $config); + } + // The config is a group name + else + { + $name = $config; + + // Test the config group name + if (($config = Kohana::config('database.'.$config)) === NULL) + throw new Kohana_Database_Exception('database.undefined_group', $name); + } + } + + // Merge the default config with the passed config + $this->config = array_merge($this->config, $config); + + if (is_string($this->config['connection'])) + { + // Make sure the connection is valid + if (strpos($this->config['connection'], '://') === FALSE) + throw new Kohana_Database_Exception('database.invalid_dsn', $this->config['connection']); + + // Parse the DSN, creating an array to hold the connection parameters + $db = array + ( + 'type' => FALSE, + 'user' => FALSE, + 'pass' => FALSE, + 'host' => FALSE, + 'port' => FALSE, + 'socket' => FALSE, + 'database' => FALSE + ); + + // Get the protocol and arguments + list ($db['type'], $connection) = explode('://', $this->config['connection'], 2); + + if (strpos($connection, '@') !== FALSE) + { + // Get the username and password + list ($db['pass'], $connection) = explode('@', $connection, 2); + // Check if a password is supplied + $logindata = explode(':', $db['pass'], 2); + $db['pass'] = (count($logindata) > 1) ? $logindata[1] : ''; + $db['user'] = $logindata[0]; + + // Prepare for finding the database + $connection = explode('/', $connection); + + // Find the database name + $db['database'] = array_pop($connection); + + // Reset connection string + $connection = implode('/', $connection); + + // Find the socket + if (preg_match('/^unix\([^)]++\)/', $connection)) + { + // This one is a little hairy: we explode based on the end of + // the socket, removing the 'unix(' from the connection string + list ($db['socket'], $connection) = explode(')', substr($connection, 5), 2); + } + elseif (strpos($connection, ':') !== FALSE) + { + // Fetch the host and port name + list ($db['host'], $db['port']) = explode(':', $connection, 2); + } + else + { + $db['host'] = $connection; + } + } + else + { + // File connection + $connection = explode('/', $connection); + + // Find database file name + $db['database'] = array_pop($connection); + + // Find database directory name + $db['socket'] = implode('/', $connection).'/'; + } + + // Reset the connection array to the database config + $this->config['connection'] = $db; + } + // Set driver name + $driver = 'Database_'.ucfirst($this->config['connection']['type']).'_Driver'; + + // Load the driver + if ( ! Kohana::auto_load($driver)) + throw new Kohana_Database_Exception('core.driver_not_found', $this->config['connection']['type'], get_class($this)); + + // Initialize the driver + $this->driver = new $driver($this->config); + + // Validate the driver + if ( ! ($this->driver instanceof Database_Driver)) + throw new Kohana_Database_Exception('core.driver_implements', $this->config['connection']['type'], get_class($this), 'Database_Driver'); + + Kohana::log('debug', 'Database Library initialized'); + } + + /** + * Simple connect method to get the database queries up and running. + * + * @return void + */ + public function connect() + { + // A link can be a resource or an object + if ( ! is_resource($this->link) AND ! is_object($this->link)) + { + $this->link = $this->driver->connect(); + if ( ! is_resource($this->link) AND ! is_object($this->link)) + throw new Kohana_Database_Exception('database.connection', $this->driver->show_error()); + + // Clear password after successful connect + $this->config['connection']['pass'] = NULL; + } + } + + /** + * Runs a query into the driver and returns the result. + * + * @param string SQL query to execute + * @return Database_Result + */ + public function query($sql = '') + { + if ($sql == '') return FALSE; + + // No link? Connect! + $this->link or $this->connect(); + + // Start the benchmark + $start = microtime(TRUE); + + if (func_num_args() > 1) //if we have more than one argument ($sql) + { + $argv = func_get_args(); + $binds = (is_array(next($argv))) ? current($argv) : array_slice($argv, 1); + } + + // Compile binds if needed + if (isset($binds)) + { + $sql = $this->compile_binds($sql, $binds); + } + + // Fetch the result + $result = $this->driver->query($this->last_query = $sql); + + // Stop the benchmark + $stop = microtime(TRUE); + + if ($this->config['benchmark'] == TRUE) + { + // Benchmark the query + Database::$benchmarks[] = array('query' => $sql, 'time' => $stop - $start, 'rows' => count($result)); + } + + return $result; + } + + /** + * Selects the column names for a database query. + * + * @param string string or array of column names to select + * @return Database_Core This Database object. + */ + public function select($sql = '*') + { + if (func_num_args() > 1) + { + $sql = func_get_args(); + } + elseif (is_string($sql)) + { + $sql = explode(',', $sql); + } + else + { + $sql = (array) $sql; + } + + foreach ($sql as $val) + { + if (($val = trim($val)) === '') continue; + + if (strpos($val, '(') === FALSE AND $val !== '*') + { + if (preg_match('/^DISTINCT\s++(.+)$/i', $val, $matches)) + { + // Only prepend with table prefix if table name is specified + $val = (strpos($matches[1], '.') !== FALSE) ? $this->config['table_prefix'].$matches[1] : $matches[1]; + + $this->distinct = TRUE; + } + else + { + $val = (strpos($val, '.') !== FALSE) ? $this->config['table_prefix'].$val : $val; + } + + $val = $this->driver->escape_column($val); + } + + $this->select[] = $val; + } + + return $this; + } + + /** + * Selects the from table(s) for a database query. + * + * @param string string or array of tables to select + * @return Database_Core This Database object. + */ + public function from($sql) + { + if (func_num_args() > 1) + { + $sql = func_get_args(); + } + elseif (is_string($sql)) + { + $sql = explode(',', $sql); + } + else + { + $sql = array($sql); + } + + foreach ($sql as $val) + { + if (is_string($val)) + { + if (($val = trim($val)) === '') continue; + + // TODO: Temporary solution, this should be moved to database driver (AS is checked for twice) + if (stripos($val, ' AS ') !== FALSE) + { + $val = str_ireplace(' AS ', ' AS ', $val); + + list($table, $alias) = explode(' AS ', $val); + + // Attach prefix to both sides of the AS + $val = $this->config['table_prefix'].$table.' AS '.$this->config['table_prefix'].$alias; + } + else + { + $val = $this->config['table_prefix'].$val; + } + } + + $this->from[] = $val; + } + + return $this; + } + + /** + * Generates the JOIN portion of the query. + * + * @param string table name + * @param string|array where key or array of key => value pairs + * @param string where value + * @param string type of join + * @return Database_Core This Database object. + */ + public function join($table, $key, $value = NULL, $type = '') + { + $join = array(); + + if ( ! empty($type)) + { + $type = strtoupper(trim($type)); + + if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'), TRUE)) + { + $type = ''; + } + else + { + $type .= ' '; + } + } + + $cond = array(); + $keys = is_array($key) ? $key : array($key => $value); + foreach ($keys as $key => $value) + { + $key = (strpos($key, '.') !== FALSE) ? $this->config['table_prefix'].$key : $key; + + if (is_string($value)) + { + // Only escape if it's a string + $value = $this->driver->escape_column($this->config['table_prefix'].$value); + } + + $cond[] = $this->driver->where($key, $value, 'AND ', count($cond), FALSE); + } + + if ( ! is_array($this->join)) + { + $this->join = array(); + } + + if ( ! is_array($table)) + { + $table = array($table); + } + + foreach ($table as $t) + { + if (is_string($t)) + { + // TODO: Temporary solution, this should be moved to database driver (AS is checked for twice) + if (stripos($t, ' AS ') !== FALSE) + { + $t = str_ireplace(' AS ', ' AS ', $t); + + list($table, $alias) = explode(' AS ', $t); + + // Attach prefix to both sides of the AS + $t = $this->config['table_prefix'].$table.' AS '.$this->config['table_prefix'].$alias; + } + else + { + $t = $this->config['table_prefix'].$t; + } + } + + $join['tables'][] = $this->driver->escape_column($t); + } + + $join['conditions'] = '('.trim(implode(' ', $cond)).')'; + $join['type'] = $type; + + $this->join[] = $join; + + return $this; + } + + + /** + * Selects the where(s) for a database query. + * + * @param string|array key name or array of key => value pairs + * @param string value to match with key + * @param boolean disable quoting of WHERE clause + * @return Database_Core This Database object. + */ + public function where($key, $value = NULL, $quote = TRUE) + { + $quote = (func_num_args() < 2 AND ! is_array($key)) ? -1 : $quote; + if (is_object($key)) + { + $keys = array((string) $key => ''); + } + elseif ( ! is_array($key)) + { + $keys = array($key => $value); + } + else + { + $keys = $key; + } + + foreach ($keys as $key => $value) + { + $key = (strpos($key, '.') !== FALSE) ? $this->config['table_prefix'].$key : $key; + $this->where[] = $this->driver->where($key, $value, 'AND ', count($this->where), $quote); + } + + return $this; + } + + /** + * Selects the or where(s) for a database query. + * + * @param string|array key name or array of key => value pairs + * @param string value to match with key + * @param boolean disable quoting of WHERE clause + * @return Database_Core This Database object. + */ + public function orwhere($key, $value = NULL, $quote = TRUE) + { + $quote = (func_num_args() < 2 AND ! is_array($key)) ? -1 : $quote; + if (is_object($key)) + { + $keys = array((string) $key => ''); + } + elseif ( ! is_array($key)) + { + $keys = array($key => $value); + } + else + { + $keys = $key; + } + + foreach ($keys as $key => $value) + { + $key = (strpos($key, '.') !== FALSE) ? $this->config['table_prefix'].$key : $key; + $this->where[] = $this->driver->where($key, $value, 'OR ', count($this->where), $quote); + } + + return $this; + } + + /** + * Selects the like(s) for a database query. + * + * @param string|array field name or array of field => match pairs + * @param string like value to match with field + * @param boolean automatically add starting and ending wildcards + * @return Database_Core This Database object. + */ + public function like($field, $match = '', $auto = TRUE) + { + $fields = is_array($field) ? $field : array($field => $match); + + foreach ($fields as $field => $match) + { + $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; + $this->where[] = $this->driver->like($field, $match, $auto, 'AND ', count($this->where)); + } + + return $this; + } + + /** + * Selects the or like(s) for a database query. + * + * @param string|array field name or array of field => match pairs + * @param string like value to match with field + * @param boolean automatically add starting and ending wildcards + * @return Database_Core This Database object. + */ + public function orlike($field, $match = '', $auto = TRUE) + { + $fields = is_array($field) ? $field : array($field => $match); + + foreach ($fields as $field => $match) + { + $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; + $this->where[] = $this->driver->like($field, $match, $auto, 'OR ', count($this->where)); + } + + return $this; + } + + /** + * Selects the not like(s) for a database query. + * + * @param string|array field name or array of field => match pairs + * @param string like value to match with field + * @param boolean automatically add starting and ending wildcards + * @return Database_Core This Database object. + */ + public function notlike($field, $match = '', $auto = TRUE) + { + $fields = is_array($field) ? $field : array($field => $match); + + foreach ($fields as $field => $match) + { + $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; + $this->where[] = $this->driver->notlike($field, $match, $auto, 'AND ', count($this->where)); + } + + return $this; + } + + /** + * Selects the or not like(s) for a database query. + * + * @param string|array field name or array of field => match pairs + * @param string like value to match with field + * @return Database_Core This Database object. + */ + public function ornotlike($field, $match = '', $auto = TRUE) + { + $fields = is_array($field) ? $field : array($field => $match); + + foreach ($fields as $field => $match) + { + $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; + $this->where[] = $this->driver->notlike($field, $match, $auto, 'OR ', count($this->where)); + } + + return $this; + } + + /** + * Selects the like(s) for a database query. + * + * @param string|array field name or array of field => match pairs + * @param string like value to match with field + * @return Database_Core This Database object. + */ + public function regex($field, $match = '') + { + $fields = is_array($field) ? $field : array($field => $match); + + foreach ($fields as $field => $match) + { + $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; + $this->where[] = $this->driver->regex($field, $match, 'AND ', count($this->where)); + } + + return $this; + } + + /** + * Selects the or like(s) for a database query. + * + * @param string|array field name or array of field => match pairs + * @param string like value to match with field + * @return Database_Core This Database object. + */ + public function orregex($field, $match = '') + { + $fields = is_array($field) ? $field : array($field => $match); + + foreach ($fields as $field => $match) + { + $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; + $this->where[] = $this->driver->regex($field, $match, 'OR ', count($this->where)); + } + + return $this; + } + + /** + * Selects the not regex(s) for a database query. + * + * @param string|array field name or array of field => match pairs + * @param string regex value to match with field + * @return Database_Core This Database object. + */ + public function notregex($field, $match = '') + { + $fields = is_array($field) ? $field : array($field => $match); + + foreach ($fields as $field => $match) + { + $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; + $this->where[] = $this->driver->notregex($field, $match, 'AND ', count($this->where)); + } + + return $this; + } + + /** + * Selects the or not regex(s) for a database query. + * + * @param string|array field name or array of field => match pairs + * @param string regex value to match with field + * @return Database_Core This Database object. + */ + public function ornotregex($field, $match = '') + { + $fields = is_array($field) ? $field : array($field => $match); + + foreach ($fields as $field => $match) + { + $field = (strpos($field, '.') !== FALSE) ? $this->config['table_prefix'].$field : $field; + $this->where[] = $this->driver->notregex($field, $match, 'OR ', count($this->where)); + } + + return $this; + } + + /** + * Chooses the column to group by in a select query. + * + * @param string column name to group by + * @return Database_Core This Database object. + */ + public function groupby($by) + { + if ( ! is_array($by)) + { + $by = explode(',', (string) $by); + } + + foreach ($by as $val) + { + $val = trim($val); + + if ($val != '') + { + // Add the table prefix if we are using table.column names + if(strpos($val, '.')) + { + $val = $this->config['table_prefix'].$val; + } + + $this->groupby[] = $this->driver->escape_column($val); + } + } + + return $this; + } + + /** + * Selects the having(s) for a database query. + * + * @param string|array key name or array of key => value pairs + * @param string value to match with key + * @param boolean disable quoting of WHERE clause + * @return Database_Core This Database object. + */ + public function having($key, $value = '', $quote = TRUE) + { + $this->having[] = $this->driver->where($key, $value, 'AND', count($this->having), TRUE); + return $this; + } + + /** + * Selects the or having(s) for a database query. + * + * @param string|array key name or array of key => value pairs + * @param string value to match with key + * @param boolean disable quoting of WHERE clause + * @return Database_Core This Database object. + */ + public function orhaving($key, $value = '', $quote = TRUE) + { + $this->having[] = $this->driver->where($key, $value, 'OR', count($this->having), TRUE); + return $this; + } + + /** + * Chooses which column(s) to order the select query by. + * + * @param string|array column(s) to order on, can be an array, single column, or comma seperated list of columns + * @param string direction of the order + * @return Database_Core This Database object. + */ + public function orderby($orderby, $direction = NULL) + { + if ( ! is_array($orderby)) + { + $orderby = array($orderby => $direction); + } + + foreach ($orderby as $column => $direction) + { + $direction = strtoupper(trim($direction)); + + // Add a direction if the provided one isn't valid + if ( ! in_array($direction, array('ASC', 'DESC', 'RAND()', 'RANDOM()', 'NULL'))) + { + $direction = 'ASC'; + } + + // Add the table prefix if a table.column was passed + if (strpos($column, '.')) + { + $column = $this->config['table_prefix'].$column; + } + + $this->orderby[] = $this->driver->escape_column($column).' '.$direction; + } + + return $this; + } + + /** + * Selects the limit section of a query. + * + * @param integer number of rows to limit result to + * @param integer offset in result to start returning rows from + * @return Database_Core This Database object. + */ + public function limit($limit, $offset = NULL) + { + $this->limit = (int) $limit; + + if ($offset !== NULL OR ! is_int($this->offset)) + { + $this->offset($offset); + } + + return $this; + } + + /** + * Sets the offset portion of a query. + * + * @param integer offset value + * @return Database_Core This Database object. + */ + public function offset($value) + { + $this->offset = (int) $value; + + return $this; + } + + /** + * Allows key/value pairs to be set for inserting or updating. + * + * @param string|array key name or array of key => value pairs + * @param string value to match with key + * @return Database_Core This Database object. + */ + public function set($key, $value = '') + { + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + foreach ($key as $k => $v) + { + // Add a table prefix if the column includes the table. + if (strpos($k, '.')) + $k = $this->config['table_prefix'].$k; + + $this->set[$k] = $this->driver->escape($v); + } + + return $this; + } + + /** + * Compiles the select statement based on the other functions called and runs the query. + * + * @param string table name + * @param string limit clause + * @param string offset clause + * @return Database_Result + */ + public function get($table = '', $limit = NULL, $offset = NULL) + { + if ($table != '') + { + $this->from($table); + } + + if ( ! is_null($limit)) + { + $this->limit($limit, $offset); + } + + $sql = $this->driver->compile_select(get_object_vars($this)); + + $this->reset_select(); + + $result = $this->query($sql); + + $this->last_query = $sql; + + return $result; + } + + /** + * Compiles the select statement based on the other functions called and runs the query. + * + * @param string table name + * @param array where clause + * @param string limit clause + * @param string offset clause + * @return Database_Core This Database object. + */ + public function getwhere($table = '', $where = NULL, $limit = NULL, $offset = NULL) + { + if ($table != '') + { + $this->from($table); + } + + if ( ! is_null($where)) + { + $this->where($where); + } + + if ( ! is_null($limit)) + { + $this->limit($limit, $offset); + } + + $sql = $this->driver->compile_select(get_object_vars($this)); + + $this->reset_select(); + + $result = $this->query($sql); + + return $result; + } + + /** + * Compiles the select statement based on the other functions called and returns the query string. + * + * @param string table name + * @param string limit clause + * @param string offset clause + * @return string sql string + */ + public function compile($table = '', $limit = NULL, $offset = NULL) + { + if ($table != '') + { + $this->from($table); + } + + if ( ! is_null($limit)) + { + $this->limit($limit, $offset); + } + + $sql = $this->driver->compile_select(get_object_vars($this)); + + $this->reset_select(); + + return $sql; + } + + /** + * Compiles an insert string and runs the query. + * + * @param string table name + * @param array array of key/value pairs to insert + * @return Database_Result Query result + */ + public function insert($table = '', $set = NULL) + { + if ( ! is_null($set)) + { + $this->set($set); + } + + if ($this->set == NULL) + throw new Kohana_Database_Exception('database.must_use_set'); + + if ($table == '') + { + if ( ! isset($this->from[0])) + throw new Kohana_Database_Exception('database.must_use_table'); + + $table = $this->from[0]; + } + + // If caching is enabled, clear the cache before inserting + ($this->config['cache'] === TRUE) and $this->clear_cache(); + + $sql = $this->driver->insert($this->config['table_prefix'].$table, array_keys($this->set), array_values($this->set)); + + $this->reset_write(); + + return $this->query($sql); + } + + /** + * Adds an "IN" condition to the where clause + * + * @param string Name of the column being examined + * @param mixed An array or string to match against + * @param bool Generate a NOT IN clause instead + * @return Database_Core This Database object. + */ + public function in($field, $values, $not = FALSE) + { + if (is_array($values)) + { + $escaped_values = array(); + foreach ($values as $v) + { + if (is_numeric($v)) + { + $escaped_values[] = $v; + } + else + { + $escaped_values[] = "'".$this->driver->escape_str($v)."'"; + } + } + $values = implode(",", $escaped_values); + } + + $where = $this->driver->escape_column(((strpos($field,'.') !== FALSE) ? $this->config['table_prefix'] : ''). $field).' '.($not === TRUE ? 'NOT ' : '').'IN ('.$values.')'; + $this->where[] = $this->driver->where($where, '', 'AND ', count($this->where), -1); + + return $this; + } + + /** + * Adds a "NOT IN" condition to the where clause + * + * @param string Name of the column being examined + * @param mixed An array or string to match against + * @return Database_Core This Database object. + */ + public function notin($field, $values) + { + return $this->in($field, $values, TRUE); + } + + /** + * Compiles a merge string and runs the query. + * + * @param string table name + * @param array array of key/value pairs to merge + * @return Database_Result Query result + */ + public function merge($table = '', $set = NULL) + { + if ( ! is_null($set)) + { + $this->set($set); + } + + if ($this->set == NULL) + throw new Kohana_Database_Exception('database.must_use_set'); + + if ($table == '') + { + if ( ! isset($this->from[0])) + throw new Kohana_Database_Exception('database.must_use_table'); + + $table = $this->from[0]; + } + + $sql = $this->driver->merge($this->config['table_prefix'].$table, array_keys($this->set), array_values($this->set)); + + $this->reset_write(); + return $this->query($sql); + } + + /** + * Compiles an update string and runs the query. + * + * @param string table name + * @param array associative array of update values + * @param array where clause + * @return Database_Result Query result + */ + public function update($table = '', $set = NULL, $where = NULL) + { + if ( is_array($set)) + { + $this->set($set); + } + + if ( ! is_null($where)) + { + $this->where($where); + } + + if ($this->set == FALSE) + throw new Kohana_Database_Exception('database.must_use_set'); + + if ($table == '') + { + if ( ! isset($this->from[0])) + throw new Kohana_Database_Exception('database.must_use_table'); + + $table = $this->from[0]; + } + + $sql = $this->driver->update($this->config['table_prefix'].$table, $this->set, $this->where); + + $this->reset_write(); + return $this->query($sql); + } + + /** + * Compiles a delete string and runs the query. + * + * @param string table name + * @param array where clause + * @return Database_Result Query result + */ + public function delete($table = '', $where = NULL) + { + if ($table == '') + { + if ( ! isset($this->from[0])) + throw new Kohana_Database_Exception('database.must_use_table'); + + $table = $this->from[0]; + } + else + { + $table = $this->config['table_prefix'].$table; + } + + if (! is_null($where)) + { + $this->where($where); + } + + if (count($this->where) < 1) + throw new Kohana_Database_Exception('database.must_use_where'); + + $sql = $this->driver->delete($table, $this->where); + + $this->reset_write(); + return $this->query($sql); + } + + /** + * Returns the last query run. + * + * @return string SQL + */ + public function last_query() + { + return $this->last_query; + } + + /** + * Count query records. + * + * @param string table name + * @param array where clause + * @return integer + */ + public function count_records($table = FALSE, $where = NULL) + { + if (count($this->from) < 1) + { + if ($table == FALSE) + throw new Kohana_Database_Exception('database.must_use_table'); + + $this->from($table); + } + + if ($where !== NULL) + { + $this->where($where); + } + + $query = $this->select('COUNT(*) AS '.$this->escape_column('records_found'))->get()->result(TRUE); + + return (int) $query->current()->records_found; + } + + /** + * Resets all private select variables. + * + * @return void + */ + protected function reset_select() + { + $this->select = array(); + $this->from = array(); + $this->join = array(); + $this->where = array(); + $this->orderby = array(); + $this->groupby = array(); + $this->having = array(); + $this->distinct = FALSE; + $this->limit = FALSE; + $this->offset = FALSE; + } + + /** + * Resets all private insert and update variables. + * + * @return void + */ + protected function reset_write() + { + $this->set = array(); + $this->from = array(); + $this->where = array(); + } + + /** + * Lists all the tables in the current database. + * + * @return array + */ + public function list_tables() + { + $this->link or $this->connect(); + + return $this->driver->list_tables(); + } + + /** + * See if a table exists in the database. + * + * @param string table name + * @param boolean True to attach table prefix + * @return boolean + */ + public function table_exists($table_name, $prefix = TRUE) + { + if ($prefix) + return in_array($this->config['table_prefix'].$table_name, $this->list_tables()); + else + return in_array($table_name, $this->list_tables()); + } + + /** + * Combine a SQL statement with the bind values. Used for safe queries. + * + * @param string query to bind to the values + * @param array array of values to bind to the query + * @return string + */ + public function compile_binds($sql, $binds) + { + foreach ((array) $binds as $val) + { + // If the SQL contains no more bind marks ("?"), we're done. + if (($next_bind_pos = strpos($sql, '?')) === FALSE) + break; + + // Properly escape the bind value. + $val = $this->driver->escape($val); + + // Temporarily replace possible bind marks ("?"), in the bind value itself, with a placeholder. + $val = str_replace('?', '{%B%}', $val); + + // Replace the first bind mark ("?") with its corresponding value. + $sql = substr($sql, 0, $next_bind_pos).$val.substr($sql, $next_bind_pos + 1); + } + + // Restore placeholders. + return str_replace('{%B%}', '?', $sql); + } + + /** + * Get the field data for a database table, along with the field's attributes. + * + * @param string table name + * @return array + */ + public function field_data($table = '') + { + $this->link or $this->connect(); + + return $this->driver->field_data($this->config['table_prefix'].$table); + } + + /** + * Get the field data for a database table, along with the field's attributes. + * + * @param string table name + * @return array + */ + public function list_fields($table = '') + { + $this->link or $this->connect(); + + return $this->driver->list_fields($this->config['table_prefix'].$table); + } + + /** + * Escapes a value for a query. + * + * @param mixed value to escape + * @return string + */ + public function escape($value) + { + return $this->driver->escape($value); + } + + /** + * Escapes a string for a query. + * + * @param string string to escape + * @return string + */ + public function escape_str($str) + { + return $this->driver->escape_str($str); + } + + /** + * Escapes a table name for a query. + * + * @param string string to escape + * @return string + */ + public function escape_table($table) + { + return $this->driver->escape_table($table); + } + + /** + * Escapes a column name for a query. + * + * @param string string to escape + * @return string + */ + public function escape_column($table) + { + return $this->driver->escape_column($table); + } + + /** + * Returns table prefix of current configuration. + * + * @return string + */ + public function table_prefix() + { + return $this->config['table_prefix']; + } + + /** + * Clears the query cache. + * + * @param string|TRUE clear cache by SQL statement or TRUE for last query + * @return Database_Core This Database object. + */ + public function clear_cache($sql = NULL) + { + if ($sql === TRUE) + { + $this->driver->clear_cache($this->last_query); + } + elseif (is_string($sql)) + { + $this->driver->clear_cache($sql); + } + else + { + $this->driver->clear_cache(); + } + + return $this; + } + + /** + * Pushes existing query space onto the query stack. Use push + * and pop to prevent queries from clashing before they are + * executed + * + * @return Database_Core This Databaes object + */ + public function push() + { + array_push($this->query_history, array( + $this->select, + $this->from, + $this->join, + $this->where, + $this->orderby, + $this->order, + $this->groupby, + $this->having, + $this->distinct, + $this->limit, + $this->offset + )); + + $this->reset_select(); + + return $this; + } + + /** + * Pops from query stack into the current query space. + * + * @return Database_Core This Databaes object + */ + public function pop() + { + if (count($this->query_history) == 0) + { + // No history + return $this; + } + + list( + $this->select, + $this->from, + $this->join, + $this->where, + $this->orderby, + $this->order, + $this->groupby, + $this->having, + $this->distinct, + $this->limit, + $this->offset + ) = array_pop($this->query_history); + + return $this; + } + + /** + * Count the number of records in the last query, without LIMIT or OFFSET applied. + * + * @return integer + */ + public function count_last_query() + { + if ($sql = $this->last_query()) + { + if (stripos($sql, 'LIMIT') !== FALSE) + { + // Remove LIMIT from the SQL + $sql = preg_replace('/\sLIMIT\s+[^a-z]+/i', ' ', $sql); + } + + if (stripos($sql, 'OFFSET') !== FALSE) + { + // Remove OFFSET from the SQL + $sql = preg_replace('/\sOFFSET\s+\d+/i', '', $sql); + } + + // Get the total rows from the last query executed + $result = $this->query + ( + 'SELECT COUNT(*) AS '.$this->escape_column('total_rows').' '. + 'FROM ('.trim($sql).') AS '.$this->escape_table('counted_results') + ); + + // Return the total number of rows from the query + return (int) $result->current()->total_rows; + } + + return FALSE; + } + +} // End Database Class + + +/** + * Sets the code for a Database exception. + */ +class Kohana_Database_Exception extends Kohana_Exception { + + protected $code = E_DATABASE_ERROR; + +} // End Kohana Database Exception diff --git a/system/libraries/Database_Expression.php b/system/libraries/Database_Expression.php new file mode 100644 index 00000000..940a6363 --- /dev/null +++ b/system/libraries/Database_Expression.php @@ -0,0 +1,26 @@ +expression = $expression; + } + + public function __toString() + { + return (string) $this->expression; + } + +} // End Database Expr Class \ No newline at end of file diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php new file mode 100644 index 00000000..3d564f99 --- /dev/null +++ b/system/libraries/Encrypt.php @@ -0,0 +1,164 @@ + $size) + { + // Shorten the key to the maximum size + $config['key'] = substr($config['key'], 0, $size); + } + + // Find the initialization vector size + $config['iv_size'] = mcrypt_get_iv_size($config['cipher'], $config['mode']); + + // Cache the config in the object + $this->config = $config; + + Kohana::log('debug', 'Encrypt Library initialized'); + } + + /** + * Encrypts a string and returns an encrypted string that can be decoded. + * + * @param string data to be encrypted + * @return string encrypted data + */ + public function encode($data) + { + // Set the rand type if it has not already been set + if (Encrypt::$rand === NULL) + { + if (KOHANA_IS_WIN) + { + // Windows only supports the system random number generator + Encrypt::$rand = MCRYPT_RAND; + } + else + { + if (defined('MCRYPT_DEV_URANDOM')) + { + // Use /dev/urandom + Encrypt::$rand = MCRYPT_DEV_URANDOM; + } + elseif (defined('MCRYPT_DEV_RANDOM')) + { + // Use /dev/random + Encrypt::$rand = MCRYPT_DEV_RANDOM; + } + else + { + // Use the system random number generator + Encrypt::$rand = MCRYPT_RAND; + } + } + } + + if (Encrypt::$rand === MCRYPT_RAND) + { + // The system random number generator must always be seeded each + // time it is used, or it will not produce true random results + mt_srand(); + } + + // Create a random initialization vector of the proper size for the current cipher + $iv = mcrypt_create_iv($this->config['iv_size'], Encrypt::$rand); + + // Encrypt the data using the configured options and generated iv + $data = mcrypt_encrypt($this->config['cipher'], $this->config['key'], $data, $this->config['mode'], $iv); + + // Use base64 encoding to convert to a string + return base64_encode($iv.$data); + } + + /** + * Decrypts an encoded string back to its original value. + * + * @param string encoded string to be decrypted + * @return string decrypted data + */ + public function decode($data) + { + // Convert the data back to binary + $data = base64_decode($data); + + // Extract the initialization vector from the data + $iv = substr($data, 0, $this->config['iv_size']); + + // Remove the iv from the data + $data = substr($data, $this->config['iv_size']); + + // Return the decrypted data, trimming the \0 padding bytes from the end of the data + return rtrim(mcrypt_decrypt($this->config['cipher'], $this->config['key'], $data, $this->config['mode'], $iv), "\0"); + } + +} // End Encrypt diff --git a/system/libraries/Event_Observer.php b/system/libraries/Event_Observer.php new file mode 100644 index 00000000..086c8a23 --- /dev/null +++ b/system/libraries/Event_Observer.php @@ -0,0 +1,70 @@ +update($caller); + } + + /** + * Updates the observer subject with a new caller. + * + * @chainable + * @param object Event_Subject + * @return object + */ + public function update(SplSubject $caller) + { + if ( ! ($caller instanceof Event_Subject)) + throw new Kohana_Exception('event.invalid_subject', get_class($caller), get_class($this)); + + // Update the caller + $this->caller = $caller; + + return $this; + } + + /** + * Detaches this observer from the subject. + * + * @chainable + * @return object + */ + public function remove() + { + // Detach this observer from the caller + $this->caller->detach($this); + + return $this; + } + + /** + * Notify the observer of a new message. This function must be defined in + * all observers and must take exactly one parameter of any type. + * + * @param mixed message string, object, or array + * @return void + */ + abstract public function notify($message); + +} // End Event Observer \ No newline at end of file diff --git a/system/libraries/Event_Subject.php b/system/libraries/Event_Subject.php new file mode 100644 index 00000000..d1ccc544 --- /dev/null +++ b/system/libraries/Event_Subject.php @@ -0,0 +1,67 @@ +listeners[spl_object_hash($obj)] = $obj; + + return $this; + } + + /** + * Detach an observer from the object. + * + * @chainable + * @param object Event_Observer + * @return object + */ + public function detach(SplObserver $obj) + { + // Remove the listener + unset($this->listeners[spl_object_hash($obj)]); + + return $this; + } + + /** + * Notify all attached observers of a new message. + * + * @chainable + * @param mixed message string, object, or array + * @return object + */ + public function notify($message) + { + foreach ($this->listeners as $obj) + { + $obj->notify($message); + } + + return $this; + } + +} // End Event Subject \ No newline at end of file diff --git a/system/libraries/Image.php b/system/libraries/Image.php new file mode 100644 index 00000000..08c2957c --- /dev/null +++ b/system/libraries/Image.php @@ -0,0 +1,460 @@ + 'gif', + IMAGETYPE_JPEG => 'jpg', + IMAGETYPE_PNG => 'png', + IMAGETYPE_TIFF_II => 'tiff', + IMAGETYPE_TIFF_MM => 'tiff', + ); + + // Driver instance + protected $driver; + + // Driver actions + protected $actions = array(); + + // Reference to the current image filename + protected $image = ''; + + /** + * Creates a new Image instance and returns it. + * + * @param string filename of image + * @param array non-default configurations + * @return object + */ + public static function factory($image, $config = NULL) + { + return new Image($image, $config); + } + + /** + * Creates a new image editor instance. + * + * @throws Kohana_Exception + * @param string filename of image + * @param array non-default configurations + * @return void + */ + public function __construct($image, $config = NULL) + { + static $check; + + // Make the check exactly once + ($check === NULL) and $check = function_exists('getimagesize'); + + if ($check === FALSE) + throw new Kohana_Exception('image.getimagesize_missing'); + + // Check to make sure the image exists + if ( ! is_file($image)) + throw new Kohana_Exception('image.file_not_found', $image); + + // Disable error reporting, to prevent PHP warnings + $ER = error_reporting(0); + + // Fetch the image size and mime type + $image_info = getimagesize($image); + + // Turn on error reporting again + error_reporting($ER); + + // Make sure that the image is readable and valid + if ( ! is_array($image_info) OR count($image_info) < 3) + throw new Kohana_Exception('image.file_unreadable', $image); + + // Check to make sure the image type is allowed + if ( ! isset(Image::$allowed_types[$image_info[2]])) + throw new Kohana_Exception('image.type_not_allowed', $image); + + // Image has been validated, load it + $this->image = array + ( + 'file' => str_replace('\\', '/', realpath($image)), + 'width' => $image_info[0], + 'height' => $image_info[1], + 'type' => $image_info[2], + 'ext' => Image::$allowed_types[$image_info[2]], + 'mime' => $image_info['mime'] + ); + + // Load configuration + $this->config = (array) $config + Kohana::config('image'); + + // Set driver class name + $driver = 'Image_'.ucfirst($this->config['driver']).'_Driver'; + + // Load the driver + if ( ! Kohana::auto_load($driver)) + throw new Kohana_Exception('core.driver_not_found', $this->config['driver'], get_class($this)); + + // Initialize the driver + $this->driver = new $driver($this->config['params']); + + // Validate the driver + if ( ! ($this->driver instanceof Image_Driver)) + throw new Kohana_Exception('core.driver_implements', $this->config['driver'], get_class($this), 'Image_Driver'); + } + + /** + * Handles retrieval of pre-save image properties + * + * @param string property name + * @return mixed + */ + public function __get($property) + { + if (isset($this->image[$property])) + { + return $this->image[$property]; + } + else + { + throw new Kohana_Exception('core.invalid_property', $property, get_class($this)); + } + } + + /** + * Resize an image to a specific width and height. By default, Kohana will + * maintain the aspect ratio using the width as the master dimension. If you + * wish to use height as master dim, set $image->master_dim = Image::HEIGHT + * This method is chainable. + * + * @throws Kohana_Exception + * @param integer width + * @param integer height + * @param integer one of: Image::NONE, Image::AUTO, Image::WIDTH, Image::HEIGHT + * @return object + */ + public function resize($width, $height, $master = NULL) + { + if ( ! $this->valid_size('width', $width)) + throw new Kohana_Exception('image.invalid_width', $width); + + if ( ! $this->valid_size('height', $height)) + throw new Kohana_Exception('image.invalid_height', $height); + + if (empty($width) AND empty($height)) + throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__); + + if ($master === NULL) + { + // Maintain the aspect ratio by default + $master = Image::AUTO; + } + elseif ( ! $this->valid_size('master', $master)) + throw new Kohana_Exception('image.invalid_master'); + + $this->actions['resize'] = array + ( + 'width' => $width, + 'height' => $height, + 'master' => $master, + ); + + return $this; + } + + /** + * Crop an image to a specific width and height. You may also set the top + * and left offset. + * This method is chainable. + * + * @throws Kohana_Exception + * @param integer width + * @param integer height + * @param integer top offset, pixel value or one of: top, center, bottom + * @param integer left offset, pixel value or one of: left, center, right + * @return object + */ + public function crop($width, $height, $top = 'center', $left = 'center') + { + if ( ! $this->valid_size('width', $width)) + throw new Kohana_Exception('image.invalid_width', $width); + + if ( ! $this->valid_size('height', $height)) + throw new Kohana_Exception('image.invalid_height', $height); + + if ( ! $this->valid_size('top', $top)) + throw new Kohana_Exception('image.invalid_top', $top); + + if ( ! $this->valid_size('left', $left)) + throw new Kohana_Exception('image.invalid_left', $left); + + if (empty($width) AND empty($height)) + throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__); + + $this->actions['crop'] = array + ( + 'width' => $width, + 'height' => $height, + 'top' => $top, + 'left' => $left, + ); + + return $this; + } + + /** + * Allows rotation of an image by 180 degrees clockwise or counter clockwise. + * + * @param integer degrees + * @return object + */ + public function rotate($degrees) + { + $degrees = (int) $degrees; + + if ($degrees > 180) + { + do + { + // Keep subtracting full circles until the degrees have normalized + $degrees -= 360; + } + while($degrees > 180); + } + + if ($degrees < -180) + { + do + { + // Keep adding full circles until the degrees have normalized + $degrees += 360; + } + while($degrees < -180); + } + + $this->actions['rotate'] = $degrees; + + return $this; + } + + /** + * Overlay a second image on top of this one. + * + * @throws Kohana_Exception + * @param string $overlay_file path to an image file + * @param integer $x x offset for the overlay + * @param integer $y y offset for the overlay + * @param integer $transparency transparency percent + */ + public function composite($overlay_file, $x, $y, $transparency) + { + $image_info = getimagesize($overlay_file); + + // Check to make sure the image type is allowed + if ( ! isset(Image::$allowed_types[$image_info[2]])) + throw new Kohana_Exception('image.type_not_allowed', $overlay_file); + + $this->actions['composite'] = array + ( + 'overlay_file' => $overlay_file, + 'mime' => $image_info['mime'], + 'x' => $x, + 'y' => $y, + 'transparency' => $transparency + ); + + return $this; + } + + /** + * Flip an image horizontally or vertically. + * + * @throws Kohana_Exception + * @param integer direction + * @return object + */ + public function flip($direction) + { + if ($direction !== Image::HORIZONTAL AND $direction !== Image::VERTICAL) + throw new Kohana_Exception('image.invalid_flip'); + + $this->actions['flip'] = $direction; + + return $this; + } + + /** + * Change the quality of an image. + * + * @param integer quality as a percentage + * @return object + */ + public function quality($amount) + { + $this->actions['quality'] = max(1, min($amount, 100)); + + return $this; + } + + /** + * Sharpen an image. + * + * @param integer amount to sharpen, usually ~20 is ideal + * @return object + */ + public function sharpen($amount) + { + $this->actions['sharpen'] = max(1, min($amount, 100)); + + return $this; + } + + /** + * Save the image to a new image or overwrite this image. + * + * @throws Kohana_Exception + * @param string new image filename + * @param integer permissions for new image + * @param boolean keep or discard image process actions + * @return object + */ + public function save($new_image = FALSE, $chmod = 0644, $keep_actions = FALSE) + { + // If no new image is defined, use the current image + empty($new_image) and $new_image = $this->image['file']; + + // Separate the directory and filename + $dir = pathinfo($new_image, PATHINFO_DIRNAME); + $file = pathinfo($new_image, PATHINFO_BASENAME); + + // Normalize the path + $dir = str_replace('\\', '/', realpath($dir)).'/'; + + if ( ! is_writable($dir)) + throw new Kohana_Exception('image.directory_unwritable', $dir); + + if ($status = $this->driver->process($this->image, $this->actions, $dir, $file)) + { + if ($chmod !== FALSE) + { + // Set permissions + chmod($new_image, $chmod); + } + } + + // Reset actions. Subsequent save() or render() will not apply previous actions. + if ($keep_actions === FALSE) + $this->actions = array(); + + return $status; + } + + /** + * Output the image to the browser. + * + * @param boolean keep or discard image process actions + * @return object + */ + public function render($keep_actions = FALSE) + { + $new_image = $this->image['file']; + + // Separate the directory and filename + $dir = pathinfo($new_image, PATHINFO_DIRNAME); + $file = pathinfo($new_image, PATHINFO_BASENAME); + + // Normalize the path + $dir = str_replace('\\', '/', realpath($dir)).'/'; + + // Process the image with the driver + $status = $this->driver->process($this->image, $this->actions, $dir, $file, $render = TRUE); + + // Reset actions. Subsequent save() or render() will not apply previous actions. + if ($keep_actions === FALSE) + $this->actions = array(); + + return $status; + } + + /** + * Sanitize a given value type. + * + * @param string type of property + * @param mixed property value + * @return boolean + */ + protected function valid_size($type, & $value) + { + if (is_null($value)) + return TRUE; + + if ( ! is_scalar($value)) + return FALSE; + + switch ($type) + { + case 'width': + case 'height': + if (is_string($value) AND ! ctype_digit($value)) + { + // Only numbers and percent signs + if ( ! preg_match('/^[0-9]++%$/D', $value)) + return FALSE; + } + else + { + $value = (int) $value; + } + break; + case 'top': + if (is_string($value) AND ! ctype_digit($value)) + { + if ( ! in_array($value, array('top', 'bottom', 'center'))) + return FALSE; + } + else + { + $value = (int) $value; + } + break; + case 'left': + if (is_string($value) AND ! ctype_digit($value)) + { + if ( ! in_array($value, array('left', 'right', 'center'))) + return FALSE; + } + else + { + $value = (int) $value; + } + break; + case 'master': + if ($value !== Image::NONE AND + $value !== Image::AUTO AND + $value !== Image::WIDTH AND + $value !== Image::HEIGHT) + return FALSE; + break; + } + + return TRUE; + } + +} // End Image \ No newline at end of file diff --git a/system/libraries/Input.php b/system/libraries/Input.php new file mode 100644 index 00000000..0e23c800 --- /dev/null +++ b/system/libraries/Input.php @@ -0,0 +1,452 @@ +use_xss_clean = (bool) Kohana::config('core.global_xss_filtering'); + + if (Input::$instance === NULL) + { + // magic_quotes_runtime is enabled + if (get_magic_quotes_runtime()) + { + set_magic_quotes_runtime(0); + Kohana::log('debug', 'Disable magic_quotes_runtime! It is evil and deprecated: http://php.net/magic_quotes'); + } + + // magic_quotes_gpc is enabled + if (get_magic_quotes_gpc()) + { + $this->magic_quotes_gpc = TRUE; + Kohana::log('debug', 'Disable magic_quotes_gpc! It is evil and deprecated: http://php.net/magic_quotes'); + } + + // register_globals is enabled + if (ini_get('register_globals')) + { + if (isset($_REQUEST['GLOBALS'])) + { + // Prevent GLOBALS override attacks + exit('Global variable overload attack.'); + } + + // Destroy the REQUEST global + $_REQUEST = array(); + + // These globals are standard and should not be removed + $preserve = array('GLOBALS', '_REQUEST', '_GET', '_POST', '_FILES', '_COOKIE', '_SERVER', '_ENV', '_SESSION'); + + // This loop has the same effect as disabling register_globals + foreach (array_diff(array_keys($GLOBALS), $preserve) as $key) + { + global $$key; + $$key = NULL; + + // Unset the global variable + unset($GLOBALS[$key], $$key); + } + + // Warn the developer about register globals + Kohana::log('debug', 'Disable register_globals! It is evil and deprecated: http://php.net/register_globals'); + } + + if (is_array($_GET)) + { + foreach ($_GET as $key => $val) + { + // Sanitize $_GET + $_GET[$this->clean_input_keys($key)] = $this->clean_input_data($val); + } + } + else + { + $_GET = array(); + } + + if (is_array($_POST)) + { + foreach ($_POST as $key => $val) + { + // Sanitize $_POST + $_POST[$this->clean_input_keys($key)] = $this->clean_input_data($val); + } + } + else + { + $_POST = array(); + } + + if (is_array($_COOKIE)) + { + foreach ($_COOKIE as $key => $val) + { + // Ignore special attributes in RFC2109 compliant cookies + if ($key == '$Version' OR $key == '$Path' OR $key == '$Domain') + continue; + + // Sanitize $_COOKIE + $_COOKIE[$this->clean_input_keys($key)] = $this->clean_input_data($val); + } + } + else + { + $_COOKIE = array(); + } + + // Create a singleton + Input::$instance = $this; + + Kohana::log('debug', 'Global GET, POST and COOKIE data sanitized'); + } + } + + /** + * Fetch an item from the $_GET array. + * + * @param string key to find + * @param mixed default value + * @param boolean XSS clean the value + * @return mixed + */ + public function get($key = array(), $default = NULL, $xss_clean = FALSE) + { + return $this->search_array($_GET, $key, $default, $xss_clean); + } + + /** + * Fetch an item from the $_POST array. + * + * @param string key to find + * @param mixed default value + * @param boolean XSS clean the value + * @return mixed + */ + public function post($key = array(), $default = NULL, $xss_clean = FALSE) + { + return $this->search_array($_POST, $key, $default, $xss_clean); + } + + /** + * Fetch an item from the $_COOKIE array. + * + * @param string key to find + * @param mixed default value + * @param boolean XSS clean the value + * @return mixed + */ + public function cookie($key = array(), $default = NULL, $xss_clean = FALSE) + { + return $this->search_array($_COOKIE, $key, $default, $xss_clean); + } + + /** + * Fetch an item from the $_SERVER array. + * + * @param string key to find + * @param mixed default value + * @param boolean XSS clean the value + * @return mixed + */ + public function server($key = array(), $default = NULL, $xss_clean = FALSE) + { + return $this->search_array($_SERVER, $key, $default, $xss_clean); + } + + /** + * Fetch an item from a global array. + * + * @param array array to search + * @param string key to find + * @param mixed default value + * @param boolean XSS clean the value + * @return mixed + */ + protected function search_array($array, $key, $default = NULL, $xss_clean = FALSE) + { + if ($key === array()) + return $array; + + if ( ! isset($array[$key])) + return $default; + + // Get the value + $value = $array[$key]; + + if ($this->use_xss_clean === FALSE AND $xss_clean === TRUE) + { + // XSS clean the value + $value = $this->xss_clean($value); + } + + return $value; + } + + /** + * Fetch the IP Address. + * + * @return string + */ + public function ip_address() + { + if ($this->ip_address !== NULL) + return $this->ip_address; + + // Server keys that could contain the client IP address + $keys = array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'); + + foreach ($keys as $key) + { + if ($ip = $this->server($key)) + { + $this->ip_address = $ip; + + // An IP address has been found + break; + } + } + + if ($comma = strrpos($this->ip_address, ',') !== FALSE) + { + $this->ip_address = substr($this->ip_address, $comma + 1); + } + + if ( ! valid::ip($this->ip_address)) + { + // Use an empty IP + $this->ip_address = '0.0.0.0'; + } + + return $this->ip_address; + } + + /** + * Clean cross site scripting exploits from string. + * HTMLPurifier may be used if installed, otherwise defaults to built in method. + * Note - This function should only be used to deal with data upon submission. + * It's not something that should be used for general runtime processing + * since it requires a fair amount of processing overhead. + * + * @param string data to clean + * @param string xss_clean method to use ('htmlpurifier' or defaults to built-in method) + * @return string + */ + public function xss_clean($data, $tool = NULL) + { + if ($tool === NULL) + { + // Use the default tool + $tool = Kohana::config('core.global_xss_filtering'); + } + + if (is_array($data)) + { + foreach ($data as $key => $val) + { + $data[$key] = $this->xss_clean($val, $tool); + } + + return $data; + } + + // Do not clean empty strings + if (trim($data) === '') + return $data; + + if ($tool === TRUE) + { + // NOTE: This is necessary because switch is NOT type-sensative! + $tool = 'default'; + } + + switch ($tool) + { + case 'htmlpurifier': + /** + * @todo License should go here, http://htmlpurifier.org/ + */ + if ( ! class_exists('HTMLPurifier_Config', FALSE)) + { + // Load HTMLPurifier + require Kohana::find_file('vendor', 'htmlpurifier/HTMLPurifier.auto', TRUE); + require 'HTMLPurifier.func.php'; + } + + // Set configuration + $config = HTMLPurifier_Config::createDefault(); + $config->set('HTML', 'TidyLevel', 'none'); // Only XSS cleaning now + + // Run HTMLPurifier + $data = HTMLPurifier($data, $config); + break; + default: + // http://svn.bitflux.ch/repos/public/popoon/trunk/classes/externalinput.php + // +----------------------------------------------------------------------+ + // | Copyright (c) 2001-2006 Bitflux GmbH | + // +----------------------------------------------------------------------+ + // | Licensed under the Apache License, Version 2.0 (the "License"); | + // | you may not use this file except in compliance with the License. | + // | You may obtain a copy of the License at | + // | http://www.apache.org/licenses/LICENSE-2.0 | + // | Unless required by applicable law or agreed to in writing, software | + // | distributed under the License is distributed on an "AS IS" BASIS, | + // | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | + // | implied. See the License for the specific language governing | + // | permissions and limitations under the License. | + // +----------------------------------------------------------------------+ + // | Author: Christian Stocker | + // +----------------------------------------------------------------------+ + // + // Kohana Modifications: + // * Changed double quotes to single quotes, changed indenting and spacing + // * Removed magic_quotes stuff + // * Increased regex readability: + // * Used delimeters that aren't found in the pattern + // * Removed all unneeded escapes + // * Deleted U modifiers and swapped greediness where needed + // * Increased regex speed: + // * Made capturing parentheses non-capturing where possible + // * Removed parentheses where possible + // * Split up alternation alternatives + // * Made some quantifiers possessive + + // Fix &entity\n; + $data = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $data); + $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data); + $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data); + $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8'); + + // Remove any attribute starting with "on" or xmlns + $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data); + + // Remove javascript: and vbscript: protocols + $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data); + $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data); + $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data); + + // Only works in IE: + $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data); + $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data); + $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data); + + // Remove namespaced elements (we do not need them) + $data = preg_replace('#]*+>#i', '', $data); + + do + { + // Remove really unwanted tags + $old_data = $data; + $data = preg_replace('#]*+>#i', '', $data); + } + while ($old_data !== $data); + break; + } + + return $data; + } + + /** + * This is a helper method. It enforces W3C specifications for allowed + * key name strings, to prevent malicious exploitation. + * + * @param string string to clean + * @return string + */ + public function clean_input_keys($str) + { + $chars = PCRE_UNICODE_PROPERTIES ? '\pL' : 'a-zA-Z'; + + if ( ! preg_match('#^['.$chars.'0-9:_.-]++$#uD', $str)) + { + exit('Disallowed key characters in global data.'); + } + + return $str; + } + + /** + * This is a helper method. It escapes data and forces all newline + * characters to "\n". + * + * @param unknown_type string to clean + * @return string + */ + public function clean_input_data($str) + { + if (is_array($str)) + { + $new_array = array(); + foreach ($str as $key => $val) + { + // Recursion! + $new_array[$this->clean_input_keys($key)] = $this->clean_input_data($val); + } + return $new_array; + } + + if ($this->magic_quotes_gpc === TRUE) + { + // Remove annoying magic quotes + $str = stripslashes($str); + } + + if ($this->use_xss_clean === TRUE) + { + $str = $this->xss_clean($str); + } + + if (strpos($str, "\r") !== FALSE) + { + // Standardize newlines + $str = str_replace(array("\r\n", "\r"), "\n", $str); + } + + return $str; + } + +} // End Input Class diff --git a/system/libraries/Model.php b/system/libraries/Model.php new file mode 100644 index 00000000..0c9fd8d6 --- /dev/null +++ b/system/libraries/Model.php @@ -0,0 +1,31 @@ +db)) + { + // Load the default database + $this->db = Database::instance($this->db); + } + } + +} // End Model \ No newline at end of file diff --git a/system/libraries/ORM.php b/system/libraries/ORM.php new file mode 100644 index 00000000..c1048604 --- /dev/null +++ b/system/libraries/ORM.php @@ -0,0 +1,1431 @@ +object_name = strtolower(substr(get_class($this), 0, -6)); + $this->object_plural = inflector::plural($this->object_name); + + if (!isset($this->sorting)) + { + // Default sorting + $this->sorting = array($this->primary_key => 'asc'); + } + + // Initialize database + $this->__initialize(); + + // Clear the object + $this->clear(); + + if (is_object($id)) + { + // Load an object + $this->load_values((array) $id); + } + elseif (!empty($id)) + { + // Find an object + $this->find($id); + } + } + + /** + * Prepares the model database connection, determines the table name, + * and loads column information. + * + * @return void + */ + public function __initialize() + { + if ( ! is_object($this->db)) + { + // Get database instance + $this->db = Database::instance($this->db); + } + + if (empty($this->table_name)) + { + // Table name is the same as the object name + $this->table_name = $this->object_name; + + if ($this->table_names_plural === TRUE) + { + // Make the table name plural + $this->table_name = inflector::plural($this->table_name); + } + } + + if (is_array($this->ignored_columns)) + { + // Make the ignored columns mirrored = mirrored + $this->ignored_columns = array_combine($this->ignored_columns, $this->ignored_columns); + } + + // Load column information + $this->reload_columns(); + } + + /** + * Allows serialization of only the object data and state, to prevent + * "stale" objects being unserialized, which also requires less memory. + * + * @return array + */ + public function __sleep() + { + // Store only information about the object + return array('object_name', 'object', 'changed', 'loaded', 'saved', 'sorting'); + } + + /** + * Prepares the database connection and reloads the object. + * + * @return void + */ + public function __wakeup() + { + // Initialize database + $this->__initialize(); + + if ($this->reload_on_wakeup === TRUE) + { + // Reload the object + $this->reload(); + } + } + + /** + * Handles pass-through to database methods. Calls to query methods + * (query, get, insert, update) are not allowed. Query builder methods + * are chainable. + * + * @param string method name + * @param array method arguments + * @return mixed + */ + public function __call($method, array $args) + { + if (method_exists($this->db, $method)) + { + if (in_array($method, array('query', 'get', 'insert', 'update', 'delete'))) + throw new Kohana_Exception('orm.query_methods_not_allowed'); + + // Method has been applied to the database + $this->db_applied[$method] = $method; + + // Number of arguments passed + $num_args = count($args); + + if ($method === 'select' AND $num_args > 3) + { + // Call select() manually to avoid call_user_func_array + $this->db->select($args); + } + else + { + // We use switch here to manually call the database methods. This is + // done for speed: call_user_func_array can take over 300% longer to + // make calls. Most database methods are 4 arguments or less, so this + // avoids almost any calls to call_user_func_array. + + switch ($num_args) + { + case 0: + if (in_array($method, array('open_paren', 'close_paren', 'enable_cache', 'disable_cache'))) + { + // Should return ORM, not Database + $this->db->$method(); + } + else + { + // Support for things like reset_select, reset_write, list_tables + return $this->db->$method(); + } + break; + case 1: + $this->db->$method($args[0]); + break; + case 2: + $this->db->$method($args[0], $args[1]); + break; + case 3: + $this->db->$method($args[0], $args[1], $args[2]); + break; + case 4: + $this->db->$method($args[0], $args[1], $args[2], $args[3]); + break; + default: + // Here comes the snail... + call_user_func_array(array($this->db, $method), $args); + break; + } + } + + return $this; + } + else + { + throw new Kohana_Exception('core.invalid_method', $method, get_class($this)); + } + } + + /** + * Handles retrieval of all model values, relationships, and metadata. + * + * @param string column name + * @return mixed + */ + public function __get($column) + { + if (array_key_exists($column, $this->object)) + { + return $this->object[$column]; + } + elseif (isset($this->related[$column])) + { + return $this->related[$column]; + } + elseif ($column === 'primary_key_value') + { + return $this->object[$this->primary_key]; + } + elseif ($model = $this->related_object($column)) + { + // This handles the has_one and belongs_to relationships + + if (in_array($model->object_name, $this->belongs_to) OR ! array_key_exists($this->foreign_key($column), $model->object)) + { + // Foreign key lies in this table (this model belongs_to target model) OR an invalid has_one relationship + $where = array($model->table_name.'.'.$model->primary_key => $this->object[$this->foreign_key($column)]); + } + else + { + // Foreign key lies in the target table (this model has_one target model) + $where = array($this->foreign_key($column, $model->table_name) => $this->primary_key_value); + } + + // one<>alias:one relationship + return $this->related[$column] = $model->find($where); + } + elseif (isset($this->has_many[$column])) + { + // Load the "middle" model + $through = ORM::factory(inflector::singular($this->has_many[$column])); + + // Load the "end" model + $model = ORM::factory(inflector::singular($column)); + + // Join ON target model's primary key set to 'through' model's foreign key + // User-defined foreign keys must be defined in the 'through' model + $join_table = $through->table_name; + $join_col1 = $through->foreign_key($model->object_name, $join_table); + $join_col2 = $model->table_name.'.'.$model->primary_key; + + // one<>alias:many relationship + return $this->related[$column] = $model + ->join($join_table, $join_col1, $join_col2) + ->where($through->foreign_key($this->object_name, $join_table), $this->object[$this->primary_key]) + ->find_all(); + } + elseif (in_array($column, $this->has_many)) + { + // one<>many relationship + $model = ORM::factory(inflector::singular($column)); + return $this->related[$column] = $model + ->where($this->foreign_key($column, $model->table_name), $this->object[$this->primary_key]) + ->find_all(); + } + elseif (in_array($column, $this->has_and_belongs_to_many)) + { + // Load the remote model, always singular + $model = ORM::factory(inflector::singular($column)); + + if ($this->has($model, TRUE)) + { + // many<>many relationship + return $this->related[$column] = $model + ->in($model->table_name.'.'.$model->primary_key, $this->changed_relations[$column]) + ->find_all(); + } + else + { + // empty many<>many relationship + return $this->related[$column] = $model + ->where($model->table_name.'.'.$model->primary_key, NULL) + ->find_all(); + } + } + elseif (isset($this->ignored_columns[$column])) + { + return NULL; + } + elseif (in_array($column, array + ( + 'object_name', 'object_plural', // Object + 'primary_key', 'primary_val', 'table_name', 'table_columns', // Table + 'loaded', 'saved', // Status + 'has_one', 'belongs_to', 'has_many', 'has_and_belongs_to_many', 'load_with' // Relationships + ))) + { + // Model meta information + return $this->$column; + } + else + { + throw new Kohana_Exception('core.invalid_property', $column, get_class($this)); + } + } + + /** + * Handles setting of all model values, and tracks changes between values. + * + * @param string column name + * @param mixed column value + * @return void + */ + public function __set($column, $value) + { + if (isset($this->ignored_columns[$column])) + { + return NULL; + } + elseif (isset($this->object[$column]) OR array_key_exists($column, $this->object)) + { + if (isset($this->table_columns[$column])) + { + // Data has changed + $this->changed[$column] = $column; + + // Object is no longer saved + $this->saved = FALSE; + } + + $this->object[$column] = $this->load_type($column, $value); + } + elseif (in_array($column, $this->has_and_belongs_to_many) AND is_array($value)) + { + // Load relations + $model = ORM::factory(inflector::singular($column)); + + if ( ! isset($this->object_relations[$column])) + { + // Load relations + $this->has($model); + } + + // Change the relationships + $this->changed_relations[$column] = $value; + + if (isset($this->related[$column])) + { + // Force a reload of the relationships + unset($this->related[$column]); + } + } + else + { + throw new Kohana_Exception('core.invalid_property', $column, get_class($this)); + } + } + + /** + * Checks if object data is set. + * + * @param string column name + * @return boolean + */ + public function __isset($column) + { + return (isset($this->object[$column]) OR isset($this->related[$column])); + } + + /** + * Unsets object data. + * + * @param string column name + * @return void + */ + public function __unset($column) + { + unset($this->object[$column], $this->changed[$column], $this->related[$column]); + } + + /** + * Displays the primary key of a model when it is converted to a string. + * + * @return string + */ + public function __toString() + { + return (string) $this->object[$this->primary_key]; + } + + /** + * Returns the values of this object as an array. + * + * @return array + */ + public function as_array() + { + $object = array(); + + foreach ($this->object as $key => $val) + { + // Reconstruct the array (calls __get) + $object[$key] = $this->$key; + } + + return $object; + } + + /** + * Binds another one-to-one object to this model. One-to-one objects + * can be nested using 'object1:object2' syntax + * + * @param string $target_path + * @return void + */ + public function with($target_path) + { + if (isset($this->with_applied[$target_path])) + { + // Don't join anything already joined + return $this; + } + + // Split object parts + $objects = explode(':', $target_path); + $target = $this; + foreach ($objects as $object) + { + // Go down the line of objects to find the given target + $parent = $target; + $target = $parent->related_object($object); + + if ( ! $target) + { + // Can't find related object + return $this; + } + } + + $target_name = $object; + + // Pop-off top object to get the parent object (user:photo:tag becomes user:photo - the parent table prefix) + array_pop($objects); + $parent_path = implode(':', $objects); + + if (empty($parent_path)) + { + // Use this table name itself for the parent object + $parent_path = $this->table_name; + } + else + { + if( ! isset($this->with_applied[$parent_path])) + { + // If the parent object hasn't been joined yet, do it first (otherwise LEFT JOINs fail) + $this->with($parent_path); + } + } + + // Add to with_applied to prevent duplicate joins + $this->with_applied[$target_path] = TRUE; + + // Use the keys of the empty object to determine the columns + $select = array_keys($target->object); + foreach ($select as $i => $column) + { + // Add the prefix so that load_result can determine the relationship + $select[$i] = $target_path.'.'.$column.' AS '.$target_path.':'.$column; + } + + + // Select all of the prefixed keys in the object + $this->db->select($select); + + if (in_array($target->object_name, $parent->belongs_to) OR ! isset($target->object[$parent->foreign_key($target_name)])) + { + // Parent belongs_to target, use target's primary key as join column + $join_col1 = $target->foreign_key(TRUE, $target_path); + $join_col2 = $parent->foreign_key($target_name, $parent_path); + } + else + { + // Parent has_one target, use parent's primary key as join column + $join_col2 = $parent->foreign_key(TRUE, $parent_path); + $join_col1 = $parent->foreign_key($target_name, $target_path); + } + + // This allows for models to use different table prefixes (sharing the same database) + $join_table = new Database_Expression($target->db->table_prefix().$target->table_name.' AS '.$this->db->table_prefix().$target_path); + + // Join the related object into the result + $this->db->join($join_table, $join_col1, $join_col2, 'LEFT'); + + return $this; + } + + /** + * Finds and loads a single database row into the object. + * + * @chainable + * @param mixed primary key or an array of clauses + * @return ORM + */ + public function find($id = NULL) + { + if ($id !== NULL) + { + if (is_array($id)) + { + // Search for all clauses + $this->db->where($id); + } + else + { + // Search for a specific column + $this->db->where($this->table_name.'.'.$this->unique_key($id), $id); + } + } + + return $this->load_result(); + } + + /** + * Finds multiple database rows and returns an iterator of the rows found. + * + * @chainable + * @param integer SQL limit + * @param integer SQL offset + * @return ORM_Iterator + */ + public function find_all($limit = NULL, $offset = NULL) + { + if ($limit !== NULL AND ! isset($this->db_applied['limit'])) + { + // Set limit + $this->limit($limit); + } + + if ($offset !== NULL AND ! isset($this->db_applied['offset'])) + { + // Set offset + $this->offset($offset); + } + + return $this->load_result(TRUE); + } + + /** + * Creates a key/value array from all of the objects available. Uses find_all + * to find the objects. + * + * @param string key column + * @param string value column + * @return array + */ + public function select_list($key = NULL, $val = NULL) + { + if ($key === NULL) + { + $key = $this->primary_key; + } + + if ($val === NULL) + { + $val = $this->primary_val; + } + + // Return a select list from the results + return $this->select($key, $val)->find_all()->select_list($key, $val); + } + + /** + * Validates the current object. This method should generally be called + * via the model, after the $_POST Validation object has been created. + * + * @param object Validation array + * @return boolean + */ + public function validate(Validation $array, $save = FALSE) + { + $safe_array = $array->safe_array(); + + if ( ! $array->submitted()) + { + foreach ($safe_array as $key => $value) + { + // Get the value from this object + $value = $this->$key; + + if (is_object($value) AND $value instanceof ORM_Iterator) + { + // Convert the value to an array of primary keys + $value = $value->primary_key_array(); + } + + // Pre-fill data + $array[$key] = $value; + } + } + + // Validate the array + if ($status = $array->validate()) + { + // Grab only set fields (excludes missing data, unlike safe_array) + $fields = $array->as_array(); + + foreach ($fields as $key => $value) + { + if (isset($safe_array[$key])) + { + // Set new data, ignoring any missing fields or fields without rules + $this->$key = $value; + } + } + + if ($save === TRUE OR is_string($save)) + { + // Save this object + $this->save(); + + if (is_string($save)) + { + // Redirect to the saved page + url::redirect($save); + } + } + } + + // Return validation status + return $status; + } + + /** + * Saves the current object. + * + * @chainable + * @return ORM + */ + public function save() + { + if ( ! empty($this->changed)) + { + $data = array(); + foreach ($this->changed as $column) + { + // Compile changed data + $data[$column] = $this->object[$column]; + } + + if ($this->loaded === TRUE) + { + $query = $this->db + ->where($this->primary_key, $this->object[$this->primary_key]) + ->update($this->table_name, $data); + + // Object has been saved + $this->saved = TRUE; + } + else + { + $query = $this->db + ->insert($this->table_name, $data); + + if ($query->count() > 0) + { + if (empty($this->object[$this->primary_key])) + { + // Load the insert id as the primary key + $this->object[$this->primary_key] = $query->insert_id(); + } + + // Object is now loaded and saved + $this->loaded = $this->saved = TRUE; + } + } + + if ($this->saved === TRUE) + { + // All changes have been saved + $this->changed = array(); + } + } + + if ($this->saved === TRUE AND ! empty($this->changed_relations)) + { + foreach ($this->changed_relations as $column => $values) + { + // All values that were added + $added = array_diff($values, $this->object_relations[$column]); + + // All values that were saved + $removed = array_diff($this->object_relations[$column], $values); + + if (empty($added) AND empty($removed)) + { + // No need to bother + continue; + } + + // Clear related columns + unset($this->related[$column]); + + // Load the model + $model = ORM::factory(inflector::singular($column)); + + if (($join_table = array_search($column, $this->has_and_belongs_to_many)) === FALSE) + continue; + + if (is_int($join_table)) + { + // No "through" table, load the default JOIN table + $join_table = $model->join_table($this->table_name); + } + + // Foreign keys for the join table + $object_fk = $this->foreign_key(NULL); + $related_fk = $model->foreign_key(NULL); + + if ( ! empty($added)) + { + foreach ($added as $id) + { + // Insert the new relationship + $this->db->insert($join_table, array + ( + $object_fk => $this->object[$this->primary_key], + $related_fk => $id, + )); + } + } + + if ( ! empty($removed)) + { + $this->db + ->where($object_fk, $this->object[$this->primary_key]) + ->in($related_fk, $removed) + ->delete($join_table); + } + + // Clear all relations for this column + unset($this->object_relations[$column], $this->changed_relations[$column]); + } + } + + return $this; + } + + /** + * Deletes the current object from the database. This does NOT destroy + * relationships that have been created with other objects. + * + * @chainable + * @return ORM + */ + public function delete($id = NULL) + { + if ($id === NULL AND $this->loaded) + { + // Use the the primary key value + $id = $this->object[$this->primary_key]; + } + + // Delete this object + $this->db->where($this->primary_key, $id)->delete($this->table_name); + + return $this->clear(); + } + + /** + * Delete all objects in the associated table. This does NOT destroy + * relationships that have been created with other objects. + * + * @chainable + * @param array ids to delete + * @return ORM + */ + public function delete_all($ids = NULL) + { + if (is_array($ids)) + { + // Delete only given ids + $this->db->in($this->primary_key, $ids); + } + elseif (is_null($ids)) + { + // Delete all records + $this->db->where('1=1'); + } + else + { + // Do nothing - safeguard + return $this; + } + + // Delete all objects + $this->db->delete($this->table_name); + + return $this->clear(); + } + + /** + * Unloads the current object and clears the status. + * + * @chainable + * @return ORM + */ + public function clear() + { + // Create an array with all the columns set to NULL + $columns = array_keys($this->table_columns); + $values = array_combine($columns, array_fill(0, count($columns), NULL)); + + // Replace the current object with an empty one + $this->load_values($values); + + return $this; + } + + /** + * Reloads the current object from the database. + * + * @chainable + * @return ORM + */ + public function reload() + { + return $this->find($this->object[$this->primary_key]); + } + + /** + * Reload column definitions. + * + * @chainable + * @param boolean force reloading + * @return ORM + */ + public function reload_columns($force = FALSE) + { + if ($force === TRUE OR empty($this->table_columns)) + { + if (isset(ORM::$column_cache[$this->object_name])) + { + // Use cached column information + $this->table_columns = ORM::$column_cache[$this->object_name]; + } + else + { + // Load table columns + ORM::$column_cache[$this->object_name] = $this->table_columns = $this->list_fields(); + } + } + + return $this; + } + + /** + * Tests if this object has a relationship to a different model. + * + * @param object related ORM model + * @param boolean check for any relations to given model + * @return boolean + */ + public function has(ORM $model, $any = FALSE) + { + // Determine plural or singular relation name + $related = ($model->table_names_plural === TRUE) ? $model->object_plural : $model->object_name; + + if (($join_table = array_search($related, $this->has_and_belongs_to_many)) === FALSE) + return FALSE; + + if (is_int($join_table)) + { + // No "through" table, load the default JOIN table + $join_table = $model->join_table($this->table_name); + } + + if ( ! isset($this->object_relations[$related])) + { + // Load the object relationships + $this->changed_relations[$related] = $this->object_relations[$related] = $this->load_relations($join_table, $model); + } + + if ( ! $model->empty_primary_key()) + { + // Check if a specific object exists + return in_array($model->primary_key_value, $this->changed_relations[$related]); + } + elseif ($any) + { + // Check if any relations to given model exist + return ! empty($this->changed_relations[$related]); + } + else + { + return FALSE; + } + } + + /** + * Adds a new relationship to between this model and another. + * + * @param object related ORM model + * @return boolean + */ + public function add(ORM $model) + { + if ($this->has($model)) + return TRUE; + + // Get the faked column name + $column = $model->object_plural; + + // Add the new relation to the update + $this->changed_relations[$column][] = $model->primary_key_value; + + if (isset($this->related[$column])) + { + // Force a reload of the relationships + unset($this->related[$column]); + } + + return TRUE; + } + + /** + * Adds a new relationship to between this model and another. + * + * @param object related ORM model + * @return boolean + */ + public function remove(ORM $model) + { + if ( ! $this->has($model)) + return FALSE; + + // Get the faked column name + $column = $model->object_plural; + + if (($key = array_search($model->primary_key_value, $this->changed_relations[$column])) === FALSE) + return FALSE; + + // Remove the relationship + unset($this->changed_relations[$column][$key]); + + if (isset($this->related[$column])) + { + // Force a reload of the relationships + unset($this->related[$column]); + } + + return TRUE; + } + + /** + * Count the number of records in the table. + * + * @return integer + */ + public function count_all() + { + // Return the total number of records in a table + return $this->db->count_records($this->table_name); + } + + /** + * Proxy method to Database list_fields. + * + * @param string table name or NULL to use this table + * @return array + */ + public function list_fields($table = NULL) + { + if ($table === NULL) + { + $table = $this->table_name; + } + + // Proxy to database + return $this->db->list_fields($table); + } + + /** + * Proxy method to Database field_data. + * + * @param string table name + * @return array + */ + public function field_data($table) + { + // Proxy to database + return $this->db->field_data($table); + } + + /** + * Proxy method to Database field_data. + * + * @chainable + * @param string SQL query to clear + * @return ORM + */ + public function clear_cache($sql = NULL) + { + // Proxy to database + $this->db->clear_cache($sql); + + ORM::$column_cache = array(); + + return $this; + } + + /** + * Returns the unique key for a specific value. This method is expected + * to be overloaded in models if the model has other unique columns. + * + * @param mixed unique value + * @return string + */ + public function unique_key($id) + { + return $this->primary_key; + } + + /** + * Determines the name of a foreign key for a specific table. + * + * @param string related table name + * @param string prefix table name (used for JOINs) + * @return string + */ + public function foreign_key($table = NULL, $prefix_table = NULL) + { + if ($table === TRUE) + { + if (is_string($prefix_table)) + { + // Use prefix table name and this table's PK + return $prefix_table.'.'.$this->primary_key; + } + else + { + // Return the name of this table's PK + return $this->table_name.'.'.$this->primary_key; + } + } + + if (is_string($prefix_table)) + { + // Add a period for prefix_table.column support + $prefix_table .= '.'; + } + + if (isset($this->foreign_key[$table])) + { + // Use the defined foreign key name, no magic here! + $foreign_key = $this->foreign_key[$table]; + } + else + { + if ( ! is_string($table) OR ! array_key_exists($table.'_'.$this->primary_key, $this->object)) + { + // Use this table + $table = $this->table_name; + + if (strpos($table, '.') !== FALSE) + { + // Hack around support for PostgreSQL schemas + list ($schema, $table) = explode('.', $table, 2); + } + + if ($this->table_names_plural === TRUE) + { + // Make the key name singular + $table = inflector::singular($table); + } + } + + $foreign_key = $table.'_'.$this->primary_key; + } + + return $prefix_table.$foreign_key; + } + + /** + * This uses alphabetical comparison to choose the name of the table. + * + * Example: The joining table of users and roles would be roles_users, + * because "r" comes before "u". Joining products and categories would + * result in categories_products, because "c" comes before "p". + * + * Example: zoo > zebra > robber > ocean > angel > aardvark + * + * @param string table name + * @return string + */ + public function join_table($table) + { + if ($this->table_name > $table) + { + $table = $table.'_'.$this->table_name; + } + else + { + $table = $this->table_name.'_'.$table; + } + + return $table; + } + + /** + * Returns an ORM model for the given object name; + * + * @param string object name + * @return ORM + */ + protected function related_object($object) + { + if (isset($this->has_one[$object])) + { + $object = ORM::factory($this->has_one[$object]); + } + elseif (isset($this->belongs_to[$object])) + { + $object = ORM::factory($this->belongs_to[$object]); + } + elseif (in_array($object, $this->has_one) OR in_array($object, $this->belongs_to)) + { + $object = ORM::factory($object); + } + else + { + return FALSE; + } + + return $object; + } + + /** + * Loads an array of values into into the current object. + * + * @chainable + * @param array values to load + * @return ORM + */ + public function load_values(array $values) + { + if (array_key_exists($this->primary_key, $values)) + { + // Replace the object and reset the object status + $this->object = $this->changed = $this->related = array(); + + // Set the loaded and saved object status based on the primary key + $this->loaded = $this->saved = ($values[$this->primary_key] !== NULL); + } + + // Related objects + $related = array(); + + foreach ($values as $column => $value) + { + if (strpos($column, ':') === FALSE) + { + if (isset($this->table_columns[$column])) + { + // The type of the value can be determined, convert the value + $value = $this->load_type($column, $value); + } + + $this->object[$column] = $value; + } + else + { + list ($prefix, $column) = explode(':', $column, 2); + + $related[$prefix][$column] = $value; + } + } + + if ( ! empty($related)) + { + foreach ($related as $object => $values) + { + // Load the related objects with the values in the result + $this->related[$object] = $this->related_object($object)->load_values($values); + } + } + + return $this; + } + + /** + * Loads a value according to the types defined by the column metadata. + * + * @param string column name + * @param mixed value to load + * @return mixed + */ + protected function load_type($column, $value) + { + $type = gettype($value); + if ($type == 'object' OR $type == 'array' OR ! isset($this->table_columns[$column])) + return $value; + + // Load column data + $column = $this->table_columns[$column]; + + if ($value === NULL AND ! empty($column['null'])) + return $value; + + if ( ! empty($column['binary']) AND ! empty($column['exact']) AND (int) $column['length'] === 1) + { + // Use boolean for BINARY(1) fields + $column['type'] = 'boolean'; + } + + switch ($column['type']) + { + case 'int': + if ($value === '' AND ! empty($column['null'])) + { + // Forms will only submit strings, so empty integer values must be null + $value = NULL; + } + elseif ((float) $value > PHP_INT_MAX) + { + // This number cannot be represented by a PHP integer, so we convert it to a string + $value = (string) $value; + } + else + { + $value = (int) $value; + } + break; + case 'float': + $value = (float) $value; + break; + case 'boolean': + $value = (bool) $value; + break; + case 'string': + $value = (string) $value; + break; + } + + return $value; + } + + /** + * Loads a database result, either as a new object for this model, or as + * an iterator for multiple rows. + * + * @chainable + * @param boolean return an iterator or load a single row + * @return ORM for single rows + * @return ORM_Iterator for multiple rows + */ + protected function load_result($array = FALSE) + { + if ($array === FALSE) + { + // Only fetch 1 record + $this->db->limit(1); + } + + if ( ! isset($this->db_applied['select'])) + { + // Select all columns by default + $this->db->select($this->table_name.'.*'); + } + + if ( ! empty($this->load_with)) + { + foreach ($this->load_with as $alias => $object) + { + // Join each object into the results + if (is_string($alias)) + { + // Use alias + $this->with($alias); + } + else + { + // Use object + $this->with($object); + } + } + } + + if ( ! isset($this->db_applied['orderby']) AND ! empty($this->sorting)) + { + $sorting = array(); + foreach ($this->sorting as $column => $direction) + { + if (strpos($column, '.') === FALSE) + { + // Keeps sorting working properly when using JOINs on + // tables with columns of the same name + $column = $this->table_name.'.'.$column; + } + + $sorting[$column] = $direction; + } + + // Apply the user-defined sorting + $this->db->orderby($sorting); + } + + // Load the result + $result = $this->db->get($this->table_name); + + if ($array === TRUE) + { + // Return an iterated result + return new ORM_Iterator($this, $result); + } + + if ($result->count() === 1) + { + // Load object values + $this->load_values($result->result(FALSE)->current()); + } + else + { + // Clear the object, nothing was found + $this->clear(); + } + + return $this; + } + + /** + * Return an array of all the primary keys of the related table. + * + * @param string table name + * @param object ORM model to find relations of + * @return array + */ + protected function load_relations($table, ORM $model) + { + // Save the current query chain (otherwise the next call will clash) + $this->db->push(); + + $query = $this->db + ->select($model->foreign_key(NULL).' AS id') + ->from($table) + ->where($this->foreign_key(NULL, $table), $this->object[$this->primary_key]) + ->get() + ->result(TRUE); + + $this->db->pop(); + + $relations = array(); + foreach ($query as $row) + { + $relations[] = $row->id; + } + + return $relations; + } + + /** + * Returns whether or not primary key is empty + * + * @return bool + */ + protected function empty_primary_key() + { + return (empty($this->object[$this->primary_key]) AND $this->object[$this->primary_key] !== '0'); + } + +} // End ORM diff --git a/system/libraries/ORM_Iterator.php b/system/libraries/ORM_Iterator.php new file mode 100644 index 00000000..41aa8065 --- /dev/null +++ b/system/libraries/ORM_Iterator.php @@ -0,0 +1,228 @@ +class_name = get_class($model); + $this->primary_key = $model->primary_key; + $this->primary_val = $model->primary_val; + + // Database result + $this->result = $result->result(TRUE); + } + + /** + * Returns an array of the results as ORM objects. + * + * @return array + */ + public function as_array() + { + $array = array(); + + if ($results = $this->result->result_array()) + { + // Import class name + $class = $this->class_name; + + foreach ($results as $obj) + { + $array[] = new $class($obj); + } + } + + return $array; + } + + /** + * Return an array of all of the primary keys for this object. + * + * @return array + */ + public function primary_key_array() + { + $ids = array(); + foreach ($this->result as $row) + { + $ids[] = $row->{$this->primary_key}; + } + return $ids; + } + + /** + * Create a key/value array from the results. + * + * @param string key column + * @param string value column + * @return array + */ + public function select_list($key = NULL, $val = NULL) + { + if ($key === NULL) + { + // Use the default key + $key = $this->primary_key; + } + + if ($val === NULL) + { + // Use the default value + $val = $this->primary_val; + } + + $array = array(); + foreach ($this->result->result_array() as $row) + { + $array[$row->$key] = $row->$val; + } + return $array; + } + + /** + * Return a range of offsets. + * + * @param integer start + * @param integer end + * @return array + */ + public function range($start, $end) + { + // Array of objects + $array = array(); + + if ($this->result->offsetExists($start)) + { + // Import the class name + $class = $this->class_name; + + // Set the end offset + $end = $this->result->offsetExists($end) ? $end : $this->count(); + + for ($i = $start; $i < $end; $i++) + { + // Insert each object in the range + $array[] = new $class($this->result->offsetGet($i)); + } + } + + return $array; + } + + /** + * Countable: count + */ + public function count() + { + return $this->result->count(); + } + + /** + * Iterator: current + */ + public function current() + { + if ($row = $this->result->current()) + { + // Import class name + $class = $this->class_name; + + $row = new $class($row); + } + + return $row; + } + + /** + * Iterator: key + */ + public function key() + { + return $this->result->key(); + } + + /** + * Iterator: next + */ + public function next() + { + return $this->result->next(); + } + + /** + * Iterator: rewind + */ + public function rewind() + { + $this->result->rewind(); + } + + /** + * Iterator: valid + */ + public function valid() + { + return $this->result->valid(); + } + + /** + * ArrayAccess: offsetExists + */ + public function offsetExists($offset) + { + return $this->result->offsetExists($offset); + } + + /** + * ArrayAccess: offsetGet + */ + public function offsetGet($offset) + { + if ($this->result->offsetExists($offset)) + { + // Import class name + $class = $this->class_name; + + return new $class($this->result->offsetGet($offset)); + } + } + + /** + * ArrayAccess: offsetSet + * + * @throws Kohana_Database_Exception + */ + public function offsetSet($offset, $value) + { + throw new Kohana_Database_Exception('database.result_read_only'); + } + + /** + * ArrayAccess: offsetUnset + * + * @throws Kohana_Database_Exception + */ + public function offsetUnset($offset) + { + throw new Kohana_Database_Exception('database.result_read_only'); + } + +} // End ORM Iterator \ No newline at end of file diff --git a/system/libraries/ORM_Tree.php b/system/libraries/ORM_Tree.php new file mode 100644 index 00000000..cdb09fd0 --- /dev/null +++ b/system/libraries/ORM_Tree.php @@ -0,0 +1,76 @@ +related[$column])) + { + // Load child model + $model = ORM::factory(inflector::singular($this->ORM_Tree_children)); + + if (array_key_exists($this->ORM_Tree_parent_key, $this->object)) + { + // Find children of this parent + $model->where($model->primary_key, $this->object[$this->ORM_Tree_parent_key])->find(); + } + + $this->related[$column] = $model; + } + + return $this->related[$column]; + } + elseif ($column === 'children') + { + if (empty($this->related[$column])) + { + $model = ORM::factory(inflector::singular($this->ORM_Tree_children)); + + if ($this->ORM_Tree_children === $this->table_name) + { + // Load children within this table + $this->related[$column] = $model + ->where($this->ORM_Tree_parent_key, $this->object[$this->primary_key]) + ->find_all(); + } + else + { + // Find first selection of children + $this->related[$column] = $model + ->where($this->foreign_key(), $this->object[$this->primary_key]) + ->where($this->ORM_Tree_parent_key, NULL) + ->find_all(); + } + } + + return $this->related[$column]; + } + + return parent::__get($column); + } + +} // End ORM Tree \ No newline at end of file diff --git a/system/libraries/ORM_Versioned.php b/system/libraries/ORM_Versioned.php new file mode 100644 index 00000000..078fe16a --- /dev/null +++ b/system/libraries/ORM_Versioned.php @@ -0,0 +1,143 @@ +last_version = 1 + ($this->last_version === NULL ? $this->object['version'] : $this->last_version); + $this->__set('version', $this->last_version); + + parent::save(); + + if ($this->saved) + { + $data = array(); + foreach ($this->object as $key => $value) + { + if ($key === 'id') + continue; + + $data[$key] = $value; + } + $data[$this->foreign_key()] = $this->id; + + $this->db->insert($this->table_name.'_versions', $data); + } + + return $this; + } + + /** + * Loads previous version from current object + * + * @chainable + * @return ORM + */ + public function previous() + { + if ( ! $this->loaded) + return $this; + + $this->last_version = ($this->last_version === NULL) ? $this->object['version'] : $this->last_version; + $version = $this->last_version - 1; + + $query = $this->db + ->where($this->foreign_key(), $this->object[$this->primary_key]) + ->where('version', $version) + ->limit(1) + ->get($this->table_name.'_versions'); + + if ($query->count()) + { + $this->load_values($query->result(FALSE)->current()); + } + + return $this; + } + + /** + * Restores the object with data from stored version + * + * @param integer version number you want to restore + * @return ORM + */ + public function restore($version) + { + if ( ! $this->loaded) + return $this; + + $query = $this->db + ->where($this->foreign_key(), $this->object[$this->primary_key]) + ->where('version', $version) + ->limit(1) + ->get($this->table_name.'_versions'); + + if ($query->count()) + { + $row = $query->result(FALSE)->current(); + + foreach ($row as $key => $value) + { + if ($key === $this->primary_key OR $key === $this->foreign_key()) + { + // Do not overwrite the primary key + continue; + } + + if ($key === 'version') + { + // Always use the current version + $value = $this->version; + } + + $this->__set($key, $value); + } + + $this->save(); + } + + return $this; + } + + /** + * Overloads ORM::delete() to delete all versioned entries of current object + * and the object itself + * + * @param integer id of the object you want to delete + * @return ORM + */ + public function delete($id = NULL) + { + if ($id === NULL) + { + // Use the current object id + $id = $this->object[$this->primary_key]; + } + + if ($status = parent::delete($id)) + { + $this->db->where($this->foreign_key(), $id)->delete($this->table_name.'_versions'); + } + + return $status; + } + +} \ No newline at end of file diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php new file mode 100644 index 00000000..a8f7bb19 --- /dev/null +++ b/system/libraries/Pagination.php @@ -0,0 +1,236 @@ +initialize($config); + + Kohana::log('debug', 'Pagination Library initialized'); + } + + /** + * Sets config values. + * + * @throws Kohana_Exception + * @param array configuration settings + * @return void + */ + public function initialize($config = array()) + { + // Load config group + if (isset($config['group'])) + { + // Load and validate config group + if ( ! is_array($group_config = Kohana::config('pagination.'.$config['group']))) + throw new Kohana_Exception('pagination.undefined_group', $config['group']); + + // All pagination config groups inherit default config group + if ($config['group'] !== 'default') + { + // Load and validate default config group + if ( ! is_array($default_config = Kohana::config('pagination.default'))) + throw new Kohana_Exception('pagination.undefined_group', 'default'); + + // Merge config group with default config group + $group_config += $default_config; + } + + // Merge custom config items with config group + $config += $group_config; + } + + // Assign config values to the object + foreach ($config as $key => $value) + { + if (property_exists($this, $key)) + { + $this->$key = $value; + } + } + + // Clean view directory + $this->directory = trim($this->directory, '/').'/'; + + // Build generic URL with page in query string + if ($this->query_string !== '') + { + // Extract current page + $this->current_page = isset($_GET[$this->query_string]) ? (int) $_GET[$this->query_string] : 1; + + // Insert {page} placeholder + $_GET[$this->query_string] = '{page}'; + + // Create full URL + $base_url = ($this->base_url === '') ? Router::$current_uri : $this->base_url; + $this->url = url::site($base_url).'?'.str_replace('%7Bpage%7D', '{page}', http_build_query($_GET)); + + // Reset page number + $_GET[$this->query_string] = $this->current_page; + } + + // Build generic URL with page as URI segment + else + { + // Use current URI if no base_url set + $this->url = ($this->base_url === '') ? Router::$segments : explode('/', trim($this->base_url, '/')); + + // Convert uri 'label' to corresponding integer if needed + if (is_string($this->uri_segment)) + { + if (($key = array_search($this->uri_segment, $this->url)) === FALSE) + { + // If uri 'label' is not found, auto add it to base_url + $this->url[] = $this->uri_segment; + $this->uri_segment = count($this->url) + 1; + } + else + { + $this->uri_segment = $key + 2; + } + } + + // Insert {page} placeholder + $this->url[$this->uri_segment - 1] = '{page}'; + + // Create full URL + $this->url = url::site(implode('/', $this->url)).Router::$query_string; + + // Extract current page + $this->current_page = URI::instance()->segment($this->uri_segment); + } + + // Core pagination values + $this->total_items = (int) max(0, $this->total_items); + $this->items_per_page = (int) max(1, $this->items_per_page); + $this->total_pages = (int) ceil($this->total_items / $this->items_per_page); + $this->current_page = (int) min(max(1, $this->current_page), max(1, $this->total_pages)); + $this->current_first_item = (int) min((($this->current_page - 1) * $this->items_per_page) + 1, $this->total_items); + $this->current_last_item = (int) min($this->current_first_item + $this->items_per_page - 1, $this->total_items); + + // If there is no first/last/previous/next page, relative to the + // current page, value is set to FALSE. Valid page number otherwise. + $this->first_page = ($this->current_page === 1) ? FALSE : 1; + $this->last_page = ($this->current_page >= $this->total_pages) ? FALSE : $this->total_pages; + $this->previous_page = ($this->current_page > 1) ? $this->current_page - 1 : FALSE; + $this->next_page = ($this->current_page < $this->total_pages) ? $this->current_page + 1 : FALSE; + + // SQL values + $this->sql_offset = (int) ($this->current_page - 1) * $this->items_per_page; + $this->sql_limit = sprintf(' LIMIT %d OFFSET %d ', $this->items_per_page, $this->sql_offset); + } + + /** + * Generates the HTML for the chosen pagination style. + * + * @param string pagination style + * @return string pagination html + */ + public function render($style = NULL) + { + // Hide single page pagination + if ($this->auto_hide === TRUE AND $this->total_pages <= 1) + return ''; + + if ($style === NULL) + { + // Use default style + $style = $this->style; + } + + // Return rendered pagination view + return View::factory($this->directory.$style, get_object_vars($this))->render(); + } + + /** + * Magically converts Pagination object to string. + * + * @return string pagination html + */ + public function __toString() + { + return $this->render(); + } + + /** + * Magically gets a pagination variable. + * + * @param string variable key + * @return mixed variable value if the key is found + * @return void if the key is not found + */ + public function __get($key) + { + if (isset($this->$key)) + return $this->$key; + } + + /** + * Adds a secondary interface for accessing properties, e.g. $pagination->total_pages(). + * Note that $pagination->total_pages is the recommended way to access properties. + * + * @param string function name + * @return string + */ + public function __call($func, $args = NULL) + { + return $this->__get($func); + } + +} // End Pagination Class \ No newline at end of file diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php new file mode 100644 index 00000000..47d82ace --- /dev/null +++ b/system/libraries/Profiler.php @@ -0,0 +1,271 @@ +show OR (is_array($this->show) AND ! in_array($args[0], $this->show))) + return FALSE; + + // Class name + $class = 'Profiler_'.ucfirst($method); + + $class = new $class(); + + $this->profiles[$args[0]] = $class; + + return $class; + } + + /** + * Disables the profiler for this page only. + * Best used when profiler is autoloaded. + * + * @return void + */ + public function disable() + { + // Removes itself from the event queue + Event::clear('system.display', array($this, 'render')); + } + + /** + * Render the profiler. Output is added to the bottom of the page by default. + * + * @param boolean return the output if TRUE + * @return void|string + */ + public function render($return = FALSE) + { + $start = microtime(TRUE); + + $get = isset($_GET['profiler']) ? explode(',', $_GET['profiler']) : array(); + $this->show = empty($get) ? Kohana::config('profiler.show') : $get; + + Event::run('profiler.run', $this); + + $styles = ''; + foreach ($this->profiles as $profile) + { + $styles .= $profile->styles(); + } + + // Don't display if there's no profiles + if (empty($this->profiles)) + return; + + // Load the profiler view + $data = array + ( + 'profiles' => $this->profiles, + 'styles' => $styles, + 'execution_time' => microtime(TRUE) - $start + ); + $view = new View('kohana_profiler', $data); + + // Return rendered view if $return is TRUE + if ($return == TRUE) + return $view->render(); + + // Add profiler data to the output + if (stripos(Kohana::$output, '') !== FALSE) + { + // Closing body tag was found, insert the profiler data before it + Kohana::$output = str_ireplace('', $view->render().'', Kohana::$output); + } + else + { + // Append the profiler data to the output + Kohana::$output .= $view->render(); + } + } + + /** + * Benchmark times and memory usage from the Benchmark library. + * + * @return void + */ + public function benchmarks() + { + if ( ! $table = $this->table('benchmarks')) + return; + + $table->add_column(); + $table->add_column('kp-column kp-data'); + $table->add_column('kp-column kp-data'); + $table->add_column('kp-column kp-data'); + $table->add_row(array('Benchmarks', 'Time', 'Count', 'Memory'), 'kp-title', 'background-color: #FFE0E0'); + + $benchmarks = Benchmark::get(TRUE); + + // Moves the first benchmark (total execution time) to the end of the array + $benchmarks = array_slice($benchmarks, 1) + array_slice($benchmarks, 0, 1); + + text::alternate(); + foreach ($benchmarks as $name => $benchmark) + { + // Clean unique id from system benchmark names + $name = ucwords(str_replace(array('_', '-'), ' ', str_replace(SYSTEM_BENCHMARK.'_', '', $name))); + + $data = array($name, number_format($benchmark['time'], 3), $benchmark['count'], number_format($benchmark['memory'] / 1024 / 1024, 2).'MB'); + $class = text::alternate('', 'kp-altrow'); + + if ($name == 'Total Execution') + $class = 'kp-totalrow'; + + $table->add_row($data, $class); + } + } + + /** + * Database query benchmarks. + * + * @return void + */ + public function database() + { + if ( ! $table = $this->table('database')) + return; + + $table->add_column(); + $table->add_column('kp-column kp-data'); + $table->add_column('kp-column kp-data'); + $table->add_row(array('Queries', 'Time', 'Rows'), 'kp-title', 'background-color: #E0FFE0'); + + $queries = Database::$benchmarks; + + text::alternate(); + $total_time = $total_rows = 0; + foreach ($queries as $query) + { + $data = array($query['query'], number_format($query['time'], 3), $query['rows']); + $class = text::alternate('', 'kp-altrow'); + $table->add_row($data, $class); + $total_time += $query['time']; + $total_rows += $query['rows']; + } + + $data = array('Total: ' . count($queries), number_format($total_time, 3), $total_rows); + $table->add_row($data, 'kp-totalrow'); + } + + /** + * Session data. + * + * @return void + */ + public function session() + { + if (empty($_SESSION)) return; + + if ( ! $table = $this->table('session')) + return; + + $table->add_column('kp-name'); + $table->add_column(); + $table->add_row(array('Session', 'Value'), 'kp-title', 'background-color: #CCE8FB'); + + text::alternate(); + foreach($_SESSION as $name => $value) + { + if (is_object($value)) + { + $value = get_class($value).' [object]'; + } + + $data = array($name, $value); + $class = text::alternate('', 'kp-altrow'); + $table->add_row($data, $class); + } + } + + /** + * POST data. + * + * @return void + */ + public function post() + { + if (empty($_POST)) return; + + if ( ! $table = $this->table('post')) + return; + + $table->add_column('kp-name'); + $table->add_column(); + $table->add_row(array('POST', 'Value'), 'kp-title', 'background-color: #E0E0FF'); + + text::alternate(); + foreach($_POST as $name => $value) + { + $data = array($name, $value); + $class = text::alternate('', 'kp-altrow'); + $table->add_row($data, $class); + } + } + + /** + * Cookie data. + * + * @return void + */ + public function cookies() + { + if (empty($_COOKIE)) return; + + if ( ! $table = $this->table('cookies')) + return; + + $table->add_column('kp-name'); + $table->add_column(); + $table->add_row(array('Cookies', 'Value'), 'kp-title', 'background-color: #FFF4D7'); + + text::alternate(); + foreach($_COOKIE as $name => $value) + { + $data = array($name, $value); + $class = text::alternate('', 'kp-altrow'); + $table->add_row($data, $class); + } + } +} \ No newline at end of file diff --git a/system/libraries/Profiler_Table.php b/system/libraries/Profiler_Table.php new file mode 100644 index 00000000..a0058a58 --- /dev/null +++ b/system/libraries/Profiler_Table.php @@ -0,0 +1,69 @@ +columns[] = array('class' => $class, 'style' => $style); + } + + /** + * Add row to table. + * + * @param array data to go in table cells + * @param string CSS class + * @param string CSS style + */ + public function add_row($data, $class = '', $style = '') + { + $this->rows[] = array('data' => $data, 'class' => $class, 'style' => $style); + } + + /** + * Render table. + * + * @return string + */ + public function render() + { + $data['rows'] = $this->rows; + $data['columns'] = $this->columns; + return View::factory('kohana_profiler_table', $data)->render(); + } +} \ No newline at end of file diff --git a/system/libraries/Router.php b/system/libraries/Router.php new file mode 100644 index 00000000..6dc9b10c --- /dev/null +++ b/system/libraries/Router.php @@ -0,0 +1,304 @@ + 1) + { + // Custom routing + Router::$rsegments = Router::routed_uri(Router::$current_uri); + } + + // The routed URI is now complete + Router::$routed_uri = Router::$rsegments; + + // Routed segments will never be empty + Router::$rsegments = explode('/', Router::$rsegments); + + // Prepare to find the controller + $controller_path = ''; + $method_segment = NULL; + + // Paths to search + $paths = Kohana::include_paths(); + + foreach (Router::$rsegments as $key => $segment) + { + // Add the segment to the search path + $controller_path .= $segment; + + $found = FALSE; + foreach ($paths as $dir) + { + // Search within controllers only + $dir .= 'controllers/'; + + if (is_dir($dir.$controller_path) OR is_file($dir.$controller_path.EXT)) + { + // Valid path + $found = TRUE; + + // The controller must be a file that exists with the search path + if ($c = str_replace('\\', '/', realpath($dir.$controller_path.EXT)) + AND is_file($c) AND strpos($c, $dir) === 0) + { + // Set controller name + Router::$controller = $segment; + + // Change controller path + Router::$controller_path = $c; + + // Set the method segment + $method_segment = $key + 1; + + // Stop searching + break; + } + } + } + + if ($found === FALSE) + { + // Maximum depth has been reached, stop searching + break; + } + + // Add another slash + $controller_path .= '/'; + } + + if ($method_segment !== NULL AND isset(Router::$rsegments[$method_segment])) + { + // Set method + Router::$method = Router::$rsegments[$method_segment]; + + if (isset(Router::$rsegments[$method_segment + 1])) + { + // Set arguments + Router::$arguments = array_slice(Router::$rsegments, $method_segment + 1); + } + } + + // Last chance to set routing before a 404 is triggered + Event::run('system.post_routing'); + + if (Router::$controller === NULL) + { + // No controller was found, so no page can be rendered + Event::run('system.404'); + } + } + + /** + * Attempts to determine the current URI using CLI, GET, PATH_INFO, ORIG_PATH_INFO, or PHP_SELF. + * + * @return void + */ + public static function find_uri() + { + if (PHP_SAPI === 'cli') + { + // Command line requires a bit of hacking + if (isset($_SERVER['argv'][1])) + { + Router::$current_uri = $_SERVER['argv'][1]; + + // Remove GET string from segments + if (($query = strpos(Router::$current_uri, '?')) !== FALSE) + { + list (Router::$current_uri, $query) = explode('?', Router::$current_uri, 2); + + // Parse the query string into $_GET + parse_str($query, $_GET); + + // Convert $_GET to UTF-8 + $_GET = utf8::clean($_GET); + } + } + } + elseif (isset($_GET['kohana_uri'])) + { + // Use the URI defined in the query string + Router::$current_uri = $_GET['kohana_uri']; + + // Remove the URI from $_GET + unset($_GET['kohana_uri']); + + // Remove the URI from $_SERVER['QUERY_STRING'] + $_SERVER['QUERY_STRING'] = preg_replace('~\bkohana_uri\b[^&]*+&?~', '', $_SERVER['QUERY_STRING']); + } + elseif (isset($_SERVER['PATH_INFO']) AND $_SERVER['PATH_INFO']) + { + Router::$current_uri = $_SERVER['PATH_INFO']; + } + elseif (isset($_SERVER['ORIG_PATH_INFO']) AND $_SERVER['ORIG_PATH_INFO']) + { + Router::$current_uri = $_SERVER['ORIG_PATH_INFO']; + } + elseif (isset($_SERVER['PHP_SELF']) AND $_SERVER['PHP_SELF']) + { + Router::$current_uri = $_SERVER['PHP_SELF']; + + if (($strpos_fc = strpos(Router::$current_uri, KOHANA)) !== FALSE) + { + // Remove the front controller from the current uri + Router::$current_uri = substr(Router::$current_uri, $strpos_fc + strlen(KOHANA)); + } + } + + // Remove slashes from the start and end of the URI + Router::$current_uri = trim(Router::$current_uri, '/'); + + if (Router::$current_uri !== '') + { + if ($suffix = Kohana::config('core.url_suffix') AND strpos(Router::$current_uri, $suffix) !== FALSE) + { + // Remove the URL suffix + Router::$current_uri = preg_replace('#'.preg_quote($suffix).'$#u', '', Router::$current_uri); + + // Set the URL suffix + Router::$url_suffix = $suffix; + } + + // Reduce multiple slashes into single slashes + Router::$current_uri = preg_replace('#//+#', '/', Router::$current_uri); + } + } + + /** + * Generates routed URI from given URI. + * + * @param string URI to convert + * @return string Routed uri + */ + public static function routed_uri($uri) + { + if (Router::$routes === NULL) + { + // Load routes + Router::$routes = Kohana::config('routes'); + } + + // Prepare variables + $routed_uri = $uri = trim($uri, '/'); + + if (isset(Router::$routes[$uri])) + { + // Literal match, no need for regex + $routed_uri = Router::$routes[$uri]; + } + else + { + // Loop through the routes and see if anything matches + foreach (Router::$routes as $key => $val) + { + if ($key === '_default') continue; + + // Trim slashes + $key = trim($key, '/'); + $val = trim($val, '/'); + + if (preg_match('#^'.$key.'$#u', $uri)) + { + if (strpos($val, '$') !== FALSE) + { + // Use regex routing + $routed_uri = preg_replace('#^'.$key.'$#u', $val, $uri); + } + else + { + // Standard routing + $routed_uri = $val; + } + + // A valid route has been found + break; + } + } + } + + if (isset(Router::$routes[$routed_uri])) + { + // Check for double routing (without regex) + $routed_uri = Router::$routes[$routed_uri]; + } + + return trim($routed_uri, '/'); + } + +} // End Router \ No newline at end of file diff --git a/system/libraries/Session.php b/system/libraries/Session.php new file mode 100644 index 00000000..e03f5dff --- /dev/null +++ b/system/libraries/Session.php @@ -0,0 +1,458 @@ +input = Input::instance(); + + // This part only needs to be run once + if (Session::$instance === NULL) + { + // Load config + Session::$config = Kohana::config('session'); + + // Makes a mirrored array, eg: foo=foo + Session::$protect = array_combine(Session::$protect, Session::$protect); + + // Configure garbage collection + ini_set('session.gc_probability', (int) Session::$config['gc_probability']); + ini_set('session.gc_divisor', 100); + ini_set('session.gc_maxlifetime', (Session::$config['expiration'] == 0) ? 86400 : Session::$config['expiration']); + + // Create a new session + $this->create(); + + if (Session::$config['regenerate'] > 0 AND ($_SESSION['total_hits'] % Session::$config['regenerate']) === 0) + { + // Regenerate session id and update session cookie + $this->regenerate(); + } + else + { + // Always update session cookie to keep the session alive + cookie::set(Session::$config['name'], $_SESSION['session_id'], Session::$config['expiration']); + } + + // Close the session just before sending the headers, so that + // the session cookie(s) can be written. + Event::add('system.send_headers', array($this, 'write_close')); + + // Make sure that sessions are closed before exiting + register_shutdown_function(array($this, 'write_close')); + + // Singleton instance + Session::$instance = $this; + } + + Kohana::log('debug', 'Session Library initialized'); + } + + /** + * Get the session id. + * + * @return string + */ + public function id() + { + return $_SESSION['session_id']; + } + + /** + * Create a new session. + * + * @param array variables to set after creation + * @return void + */ + public function create($vars = NULL) + { + // Destroy any current sessions + $this->destroy(); + + if (Session::$config['driver'] !== 'native') + { + // Set driver name + $driver = 'Session_'.ucfirst(Session::$config['driver']).'_Driver'; + + // Load the driver + if ( ! Kohana::auto_load($driver)) + throw new Kohana_Exception('core.driver_not_found', Session::$config['driver'], get_class($this)); + + // Initialize the driver + Session::$driver = new $driver(); + + // Validate the driver + if ( ! (Session::$driver instanceof Session_Driver)) + throw new Kohana_Exception('core.driver_implements', Session::$config['driver'], get_class($this), 'Session_Driver'); + + // Register non-native driver as the session handler + session_set_save_handler + ( + array(Session::$driver, 'open'), + array(Session::$driver, 'close'), + array(Session::$driver, 'read'), + array(Session::$driver, 'write'), + array(Session::$driver, 'destroy'), + array(Session::$driver, 'gc') + ); + } + + // Validate the session name + if ( ! preg_match('~^(?=.*[a-z])[a-z0-9_]++$~iD', Session::$config['name'])) + throw new Kohana_Exception('session.invalid_session_name', Session::$config['name']); + + // Name the session, this will also be the name of the cookie + session_name(Session::$config['name']); + + // Set the session cookie parameters + session_set_cookie_params + ( + Session::$config['expiration'], + Kohana::config('cookie.path'), + Kohana::config('cookie.domain'), + Kohana::config('cookie.secure'), + Kohana::config('cookie.httponly') + ); + + // Start the session! + session_start(); + + // Put session_id in the session variable + $_SESSION['session_id'] = session_id(); + + // Set defaults + if ( ! isset($_SESSION['_kf_flash_'])) + { + $_SESSION['total_hits'] = 0; + $_SESSION['_kf_flash_'] = array(); + + $_SESSION['user_agent'] = Kohana::$user_agent; + $_SESSION['ip_address'] = $this->input->ip_address(); + } + + // Set up flash variables + Session::$flash =& $_SESSION['_kf_flash_']; + + // Increase total hits + $_SESSION['total_hits'] += 1; + + // Validate data only on hits after one + if ($_SESSION['total_hits'] > 1) + { + // Validate the session + foreach (Session::$config['validate'] as $valid) + { + switch ($valid) + { + // Check user agent for consistency + case 'user_agent': + if ($_SESSION[$valid] !== Kohana::$user_agent) + return $this->create(); + break; + + // Check ip address for consistency + case 'ip_address': + if ($_SESSION[$valid] !== $this->input->$valid()) + return $this->create(); + break; + + // Check expiration time to prevent users from manually modifying it + case 'expiration': + if (time() - $_SESSION['last_activity'] > ini_get('session.gc_maxlifetime')) + return $this->create(); + break; + } + } + } + + // Expire flash keys + $this->expire_flash(); + + // Update last activity + $_SESSION['last_activity'] = time(); + + // Set the new data + Session::set($vars); + } + + /** + * Regenerates the global session id. + * + * @return void + */ + public function regenerate() + { + if (Session::$config['driver'] === 'native') + { + // Generate a new session id + // Note: also sets a new session cookie with the updated id + session_regenerate_id(TRUE); + + // Update session with new id + $_SESSION['session_id'] = session_id(); + } + else + { + // Pass the regenerating off to the driver in case it wants to do anything special + $_SESSION['session_id'] = Session::$driver->regenerate(); + } + + // Get the session name + $name = session_name(); + + if (isset($_COOKIE[$name])) + { + // Change the cookie value to match the new session id to prevent "lag" + $_COOKIE[$name] = $_SESSION['session_id']; + } + } + + /** + * Destroys the current session. + * + * @return void + */ + public function destroy() + { + if (session_id() !== '') + { + // Get the session name + $name = session_name(); + + // Destroy the session + session_destroy(); + + // Re-initialize the array + $_SESSION = array(); + + // Delete the session cookie + cookie::delete($name); + } + } + + /** + * Runs the system.session_write event, then calls session_write_close. + * + * @return void + */ + public function write_close() + { + static $run; + + if ($run === NULL) + { + $run = TRUE; + + // Run the events that depend on the session being open + Event::run('system.session_write'); + + // Expire flash keys + $this->expire_flash(); + + // Close the session + session_write_close(); + } + } + + /** + * Set a session variable. + * + * @param string|array key, or array of values + * @param mixed value (if keys is not an array) + * @return void + */ + public function set($keys, $val = FALSE) + { + if (empty($keys)) + return FALSE; + + if ( ! is_array($keys)) + { + $keys = array($keys => $val); + } + + foreach ($keys as $key => $val) + { + if (isset(Session::$protect[$key])) + continue; + + // Set the key + $_SESSION[$key] = $val; + } + } + + /** + * Set a flash variable. + * + * @param string|array key, or array of values + * @param mixed value (if keys is not an array) + * @return void + */ + public function set_flash($keys, $val = FALSE) + { + if (empty($keys)) + return FALSE; + + if ( ! is_array($keys)) + { + $keys = array($keys => $val); + } + + foreach ($keys as $key => $val) + { + if ($key == FALSE) + continue; + + Session::$flash[$key] = 'new'; + Session::set($key, $val); + } + } + + /** + * Freshen one, multiple or all flash variables. + * + * @param string variable key(s) + * @return void + */ + public function keep_flash($keys = NULL) + { + $keys = ($keys === NULL) ? array_keys(Session::$flash) : func_get_args(); + + foreach ($keys as $key) + { + if (isset(Session::$flash[$key])) + { + Session::$flash[$key] = 'new'; + } + } + } + + /** + * Expires old flash data and removes it from the session. + * + * @return void + */ + public function expire_flash() + { + static $run; + + // Method can only be run once + if ($run === TRUE) + return; + + if ( ! empty(Session::$flash)) + { + foreach (Session::$flash as $key => $state) + { + if ($state === 'old') + { + // Flash has expired + unset(Session::$flash[$key], $_SESSION[$key]); + } + else + { + // Flash will expire + Session::$flash[$key] = 'old'; + } + } + } + + // Method has been run + $run = TRUE; + } + + /** + * Get a variable. Access to sub-arrays is supported with key.subkey. + * + * @param string variable key + * @param mixed default value returned if variable does not exist + * @return mixed Variable data if key specified, otherwise array containing all session data. + */ + public function get($key = FALSE, $default = FALSE) + { + if (empty($key)) + return $_SESSION; + + $result = isset($_SESSION[$key]) ? $_SESSION[$key] : Kohana::key_string($_SESSION, $key); + + return ($result === NULL) ? $default : $result; + } + + /** + * Get a variable, and delete it. + * + * @param string variable key + * @param mixed default value returned if variable does not exist + * @return mixed + */ + public function get_once($key, $default = FALSE) + { + $return = Session::get($key, $default); + Session::delete($key); + + return $return; + } + + /** + * Delete one or more variables. + * + * @param string variable key(s) + * @return void + */ + public function delete($keys) + { + $args = func_get_args(); + + foreach ($args as $key) + { + if (isset(Session::$protect[$key])) + continue; + + // Unset the key + unset($_SESSION[$key]); + } + } + +} // End Session Class diff --git a/system/libraries/URI.php b/system/libraries/URI.php new file mode 100644 index 00000000..d9ccdcf7 --- /dev/null +++ b/system/libraries/URI.php @@ -0,0 +1,279 @@ +build_array(URI::$segments, $offset, $associative); + } + + /** + * Returns an array containing all the re-routed URI segments. + * + * @param integer rsegment offset + * @param boolean return an associative array + * @return array + */ + public function rsegment_array($offset = 0, $associative = FALSE) + { + return $this->build_array(URI::$rsegments, $offset, $associative); + } + + /** + * Returns an array containing all the URI arguments. + * + * @param integer segment offset + * @param boolean return an associative array + * @return array + */ + public function argument_array($offset = 0, $associative = FALSE) + { + return $this->build_array(URI::$arguments, $offset, $associative); + } + + /** + * Creates a simple or associative array from an array and an offset. + * Used as a helper for (r)segment_array and argument_array. + * + * @param array array to rebuild + * @param integer offset to start from + * @param boolean create an associative array + * @return array + */ + public function build_array($array, $offset = 0, $associative = FALSE) + { + // Prevent the keys from being improperly indexed + array_unshift($array, 0); + + // Slice the array, preserving the keys + $array = array_slice($array, $offset + 1, count($array) - 1, TRUE); + + if ($associative === FALSE) + return $array; + + $associative = array(); + $pairs = array_chunk($array, 2); + + foreach ($pairs as $pair) + { + // Add the key/value pair to the associative array + $associative[$pair[0]] = isset($pair[1]) ? $pair[1] : ''; + } + + return $associative; + } + + /** + * Returns the complete URI as a string. + * + * @return string + */ + public function string() + { + return URI::$current_uri; + } + + /** + * Magic method for converting an object to a string. + * + * @return string + */ + public function __toString() + { + return URI::$current_uri; + } + + /** + * Returns the total number of URI segments. + * + * @return integer + */ + public function total_segments() + { + return count(URI::$segments); + } + + /** + * Returns the total number of re-routed URI segments. + * + * @return integer + */ + public function total_rsegments() + { + return count(URI::$rsegments); + } + + /** + * Returns the total number of URI arguments. + * + * @return integer + */ + public function total_arguments() + { + return count(URI::$arguments); + } + + /** + * Returns the last URI segment. + * + * @param mixed default value returned if segment does not exist + * @return string + */ + public function last_segment($default = FALSE) + { + if (($end = $this->total_segments()) < 1) + return $default; + + return URI::$segments[$end - 1]; + } + + /** + * Returns the last re-routed URI segment. + * + * @param mixed default value returned if segment does not exist + * @return string + */ + public function last_rsegment($default = FALSE) + { + if (($end = $this->total_segments()) < 1) + return $default; + + return URI::$rsegments[$end - 1]; + } + + /** + * Returns the path to the current controller (not including the actual + * controller), as a web path. + * + * @param boolean return a full url, or only the path specifically + * @return string + */ + public function controller_path($full = TRUE) + { + return ($full) ? url::site(URI::$controller_path) : URI::$controller_path; + } + + /** + * Returns the current controller, as a web path. + * + * @param boolean return a full url, or only the controller specifically + * @return string + */ + public function controller($full = TRUE) + { + return ($full) ? url::site(URI::$controller_path.URI::$controller) : URI::$controller; + } + + /** + * Returns the current method, as a web path. + * + * @param boolean return a full url, or only the method specifically + * @return string + */ + public function method($full = TRUE) + { + return ($full) ? url::site(URI::$controller_path.URI::$controller.'/'.URI::$method) : URI::$method; + } + +} // End URI Class diff --git a/system/libraries/Validation.php b/system/libraries/Validation.php new file mode 100644 index 00000000..5a48bfc5 --- /dev/null +++ b/system/libraries/Validation.php @@ -0,0 +1,826 @@ +submitted = ! empty($array); + + parent::__construct($array, ArrayObject::ARRAY_AS_PROPS | ArrayObject::STD_PROP_LIST); + } + + /** + * Magic clone method, clears errors and messages. + * + * @return void + */ + public function __clone() + { + $this->errors = array(); + $this->messages = array(); + } + + /** + * Create a copy of the current validation rules and change the array. + * + * @chainable + * @param array new array to validate + * @return Validation + */ + public function copy(array $array) + { + $copy = clone $this; + + $copy->exchangeArray($array); + + return $copy; + } + + /** + * Test if the data has been submitted. + * + * @return boolean + */ + public function submitted($value = NULL) + { + if (is_bool($value)) + { + $this->submitted = $value; + } + + return $this->submitted; + } + + /** + * Returns an array of all the field names that have filters, rules, or callbacks. + * + * @return array + */ + public function field_names() + { + // All the fields that are being validated + $fields = array_keys(array_merge + ( + $this->pre_filters, + $this->rules, + $this->callbacks, + $this->post_filters + )); + + // Remove wildcard fields + $fields = array_diff($fields, array('*')); + + return $fields; + } + + /** + * Returns the array values of the current object. + * + * @return array + */ + public function as_array() + { + return $this->getArrayCopy(); + } + + /** + * Returns the ArrayObject values, removing all inputs without rules. + * To choose specific inputs, list the field name as arguments. + * + * @param boolean return only fields with filters, rules, and callbacks + * @return array + */ + public function safe_array() + { + // Load choices + $choices = func_get_args(); + $choices = empty($choices) ? NULL : array_combine($choices, $choices); + + // Get field names + $fields = $this->field_names(); + + $safe = array(); + foreach ($fields as $field) + { + if ($choices === NULL OR isset($choices[$field])) + { + if (isset($this[$field])) + { + $value = $this[$field]; + + if (is_object($value)) + { + // Convert the value back into an array + $value = $value->getArrayCopy(); + } + } + else + { + // Even if the field is not in this array, it must be set + $value = NULL; + } + + // Add the field to the array + $safe[$field] = $value; + } + } + + return $safe; + } + + /** + * Add additional rules that will forced, even for empty fields. All arguments + * passed will be appended to the list. + * + * @chainable + * @param string rule name + * @return object + */ + public function allow_empty_rules($rules) + { + // Any number of args are supported + $rules = func_get_args(); + + // Merge the allowed rules + $this->empty_rules = array_merge($this->empty_rules, $rules); + + return $this; + } + + /** + * Converts a filter, rule, or callback into a fully-qualified callback array. + * + * @return mixed + */ + protected function callback($callback) + { + if (is_string($callback)) + { + if (strpos($callback, '::') !== FALSE) + { + $callback = explode('::', $callback); + } + elseif (function_exists($callback)) + { + // No need to check if the callback is a method + $callback = $callback; + } + elseif (method_exists($this, $callback)) + { + // The callback exists in Validation + $callback = array($this, $callback); + } + elseif (method_exists('valid', $callback)) + { + // The callback exists in valid:: + $callback = array('valid', $callback); + } + } + + if ( ! is_callable($callback, FALSE)) + { + if (is_array($callback)) + { + if (is_object($callback[0])) + { + // Object instance syntax + $name = get_class($callback[0]).'->'.$callback[1]; + } + else + { + // Static class syntax + $name = $callback[0].'::'.$callback[1]; + } + } + else + { + // Function syntax + $name = $callback; + } + + throw new Kohana_Exception('validation.not_callable', $name); + } + + return $callback; + } + + /** + * Add a pre-filter to one or more inputs. Pre-filters are applied before + * rules or callbacks are executed. + * + * @chainable + * @param callback filter + * @param string fields to apply filter to, use TRUE for all fields + * @return object + */ + public function pre_filter($filter, $field = TRUE) + { + if ($field === TRUE OR $field === '*') + { + // Use wildcard + $fields = array('*'); + } + else + { + // Add the filter to specific inputs + $fields = func_get_args(); + $fields = array_slice($fields, 1); + } + + // Convert to a proper callback + $filter = $this->callback($filter); + + foreach ($fields as $field) + { + // Add the filter to specified field + $this->pre_filters[$field][] = $filter; + } + + return $this; + } + + /** + * Add a post-filter to one or more inputs. Post-filters are applied after + * rules and callbacks have been executed. + * + * @chainable + * @param callback filter + * @param string fields to apply filter to, use TRUE for all fields + * @return object + */ + public function post_filter($filter, $field = TRUE) + { + if ($field === TRUE) + { + // Use wildcard + $fields = array('*'); + } + else + { + // Add the filter to specific inputs + $fields = func_get_args(); + $fields = array_slice($fields, 1); + } + + // Convert to a proper callback + $filter = $this->callback($filter); + + foreach ($fields as $field) + { + // Add the filter to specified field + $this->post_filters[$field][] = $filter; + } + + return $this; + } + + /** + * Add rules to a field. Validation rules may only return TRUE or FALSE and + * can not manipulate the value of a field. + * + * @chainable + * @param string field name + * @param callback rules (one or more arguments) + * @return object + */ + public function add_rules($field, $rules) + { + // Get the rules + $rules = func_get_args(); + $rules = array_slice($rules, 1); + + if ($field === TRUE) + { + // Use wildcard + $field = '*'; + } + + foreach ($rules as $rule) + { + // Arguments for rule + $args = NULL; + + if (is_string($rule)) + { + if (preg_match('/^([^\[]++)\[(.+)\]$/', $rule, $matches)) + { + // Split the rule into the function and args + $rule = $matches[1]; + $args = preg_split('/(?array_fields[$field] = $field; + } + + // Convert to a proper callback + $rule = $this->callback($rule); + + // Add the rule, with args, to the field + $this->rules[$field][] = array($rule, $args); + } + + return $this; + } + + /** + * Add callbacks to a field. Callbacks must accept the Validation object + * and the input name. Callback returns are not processed. + * + * @chainable + * @param string field name + * @param callbacks callbacks (unlimited number) + * @return object + */ + public function add_callbacks($field, $callbacks) + { + // Get all callbacks as an array + $callbacks = func_get_args(); + $callbacks = array_slice($callbacks, 1); + + if ($field === TRUE) + { + // Use wildcard + $field = '*'; + } + + foreach ($callbacks as $callback) + { + // Convert to a proper callback + $callback = $this->callback($callback); + + // Add the callback to specified field + $this->callbacks[$field][] = $callback; + } + + return $this; + } + + /** + * Validate by processing pre-filters, rules, callbacks, and post-filters. + * All fields that have filters, rules, or callbacks will be initialized if + * they are undefined. Validation will only be run if there is data already + * in the array. + * + * @param object Validation object, used only for recursion + * @param object name of field for errors + * @return bool + */ + public function validate($object = NULL, $field_name = NULL) + { + if ($object === NULL) + { + // Use the current object + $object = $this; + } + + // Get all field names + $fields = $this->field_names(); + + // Copy the array from the object, to optimize multiple sets + $array = $this->getArrayCopy(); + + foreach ($fields as $field) + { + if ($field === '*') + { + // Ignore wildcard + continue; + } + + if ( ! isset($array[$field])) + { + if (isset($this->array_fields[$field])) + { + // This field must be an array + $array[$field] = array(); + } + else + { + $array[$field] = NULL; + } + } + } + + // Swap the array back into the object + $this->exchangeArray($array); + + // Get all defined field names + $fields = array_keys($array); + + foreach ($this->pre_filters as $field => $callbacks) + { + foreach ($callbacks as $callback) + { + if ($field === '*') + { + foreach ($fields as $f) + { + $this[$f] = is_array($this[$f]) ? array_map($callback, $this[$f]) : call_user_func($callback, $this[$f]); + } + } + else + { + $this[$field] = is_array($this[$field]) ? array_map($callback, $this[$field]) : call_user_func($callback, $this[$field]); + } + } + } + + if ($this->submitted === FALSE) + return FALSE; + + foreach ($this->rules as $field => $callbacks) + { + foreach ($callbacks as $callback) + { + // Separate the callback and arguments + list ($callback, $args) = $callback; + + // Function or method name of the rule + $rule = is_array($callback) ? $callback[1] : $callback; + + if ($field === '*') + { + foreach ($fields as $f) + { + // Note that continue, instead of break, is used when + // applying rules using a wildcard, so that all fields + // will be validated. + + if (isset($this->errors[$f])) + { + // Prevent other rules from being evaluated if an error has occurred + continue; + } + + if (empty($this[$f]) AND ! in_array($rule, $this->empty_rules)) + { + // This rule does not need to be processed on empty fields + continue; + } + + if ($args === NULL) + { + if ( ! call_user_func($callback, $this[$f])) + { + $this->errors[$f] = $rule; + + // Stop validating this field when an error is found + continue; + } + } + else + { + if ( ! call_user_func($callback, $this[$f], $args)) + { + $this->errors[$f] = $rule; + + // Stop validating this field when an error is found + continue; + } + } + } + } + else + { + if (isset($this->errors[$field])) + { + // Prevent other rules from being evaluated if an error has occurred + break; + } + + if ( ! in_array($rule, $this->empty_rules) AND ! $this->required($this[$field])) + { + // This rule does not need to be processed on empty fields + continue; + } + + if ($args === NULL) + { + if ( ! call_user_func($callback, $this[$field])) + { + $this->errors[$field] = $rule; + + // Stop validating this field when an error is found + break; + } + } + else + { + if ( ! call_user_func($callback, $this[$field], $args)) + { + $this->errors[$field] = $rule; + + // Stop validating this field when an error is found + break; + } + } + } + } + } + + foreach ($this->callbacks as $field => $callbacks) + { + foreach ($callbacks as $callback) + { + if ($field === '*') + { + foreach ($fields as $f) + { + // Note that continue, instead of break, is used when + // applying rules using a wildcard, so that all fields + // will be validated. + + if (isset($this->errors[$f])) + { + // Stop validating this field when an error is found + continue; + } + + call_user_func($callback, $this, $f); + } + } + else + { + if (isset($this->errors[$field])) + { + // Stop validating this field when an error is found + break; + } + + call_user_func($callback, $this, $field); + } + } + } + + foreach ($this->post_filters as $field => $callbacks) + { + foreach ($callbacks as $callback) + { + if ($field === '*') + { + foreach ($fields as $f) + { + $this[$f] = is_array($this[$f]) ? array_map($callback, $this[$f]) : call_user_func($callback, $this[$f]); + } + } + else + { + $this[$field] = is_array($this[$field]) ? array_map($callback, $this[$field]) : call_user_func($callback, $this[$field]); + } + } + } + + // Return TRUE if there are no errors + return $this->errors === array(); + } + + /** + * Add an error to an input. + * + * @chainable + * @param string input name + * @param string unique error name + * @return object + */ + public function add_error($field, $name) + { + $this->errors[$field] = $name; + + return $this; + } + + /** + * Sets or returns the message for an input. + * + * @chainable + * @param string input key + * @param string message to set + * @return string|object + */ + public function message($input = NULL, $message = NULL) + { + if ($message === NULL) + { + if ($input === NULL) + { + $messages = array(); + $keys = array_keys($this->messages); + + foreach ($keys as $input) + { + $messages[] = $this->message($input); + } + + return implode("\n", $messages); + } + + // Return nothing if no message exists + if (empty($this->messages[$input])) + return ''; + + // Return the HTML message string + return $this->messages[$input]; + } + else + { + $this->messages[$input] = $message; + } + + return $this; + } + + /** + * Return the errors array. + * + * @param boolean load errors from a lang file + * @return array + */ + public function errors($file = NULL) + { + if ($file === NULL) + { + return $this->errors; + } + else + { + + $errors = array(); + foreach ($this->errors as $input => $error) + { + // Key for this input error + $key = "$file.$input.$error"; + + if (($errors[$input] = Kohana::lang($key)) === $key) + { + // Get the default error message + $errors[$input] = Kohana::lang("$file.$input.default"); + } + } + + return $errors; + } + } + + /** + * Rule: required. Generates an error if the field has an empty value. + * + * @param mixed input value + * @return bool + */ + public function required($str) + { + if (is_object($str) AND $str instanceof ArrayObject) + { + // Get the array from the ArrayObject + $str = $str->getArrayCopy(); + } + + if (is_array($str)) + { + return ! empty($str); + } + else + { + return ! ($str === '' OR $str === NULL OR $str === FALSE); + } + } + + /** + * Rule: matches. Generates an error if the field does not match one or more + * other fields. + * + * @param mixed input value + * @param array input names to match against + * @return bool + */ + public function matches($str, array $inputs) + { + foreach ($inputs as $key) + { + if ($str !== (isset($this[$key]) ? $this[$key] : NULL)) + return FALSE; + } + + return TRUE; + } + + /** + * Rule: length. Generates an error if the field is too long or too short. + * + * @param mixed input value + * @param array minimum, maximum, or exact length to match + * @return bool + */ + public function length($str, array $length) + { + if ( ! is_string($str)) + return FALSE; + + $size = utf8::strlen($str); + $status = FALSE; + + if (count($length) > 1) + { + list ($min, $max) = $length; + + if ($size >= $min AND $size <= $max) + { + $status = TRUE; + } + } + else + { + $status = ($size === (int) $length[0]); + } + + return $status; + } + + /** + * Rule: depends_on. Generates an error if the field does not depend on one + * or more other fields. + * + * @param mixed field name + * @param array field names to check dependency + * @return bool + */ + public function depends_on($field, array $fields) + { + foreach ($fields as $depends_on) + { + if ( ! isset($this[$depends_on]) OR $this[$depends_on] == NULL) + return FALSE; + } + + return TRUE; + } + + /** + * Rule: chars. Generates an error if the field contains characters outside of the list. + * + * @param string field value + * @param array allowed characters + * @return bool + */ + public function chars($value, array $chars) + { + return ! preg_match('![^'.implode('', $chars).']!u', $value); + } + +} // End Validation diff --git a/system/libraries/View.php b/system/libraries/View.php new file mode 100644 index 00000000..2b8471c6 --- /dev/null +++ b/system/libraries/View.php @@ -0,0 +1,309 @@ +set_filename($name, $type); + } + + if (is_array($data) AND ! empty($data)) + { + // Preload data using array_merge, to allow user extensions + $this->kohana_local_data = array_merge($this->kohana_local_data, $data); + } + } + + /** + * Magic method access to test for view property + * + * @param string View property to test for + * @return boolean + */ + public function __isset($key = NULL) + { + return $this->is_set($key); + } + + /** + * Sets the view filename. + * + * @chainable + * @param string view filename + * @param string view file type + * @return object + */ + public function set_filename($name, $type = NULL) + { + if ($type == NULL) + { + // Load the filename and set the content type + $this->kohana_filename = Kohana::find_file('views', $name, TRUE); + $this->kohana_filetype = EXT; + } + else + { + // Check if the filetype is allowed by the configuration + if ( ! in_array($type, Kohana::config('view.allowed_filetypes'))) + throw new Kohana_Exception('core.invalid_filetype', $type); + + // Load the filename and set the content type + $this->kohana_filename = Kohana::find_file('views', $name, TRUE, $type); + $this->kohana_filetype = Kohana::config('mimes.'.$type); + + if ($this->kohana_filetype == NULL) + { + // Use the specified type + $this->kohana_filetype = $type; + } + } + + return $this; + } + + /** + * Sets a view variable. + * + * @param string|array name of variable or an array of variables + * @param mixed value when using a named variable + * @return object + */ + public function set($name, $value = NULL) + { + if (is_array($name)) + { + foreach ($name as $key => $value) + { + $this->__set($key, $value); + } + } + else + { + $this->__set($name, $value); + } + + return $this; + } + + /** + * Checks for a property existence in the view locally or globally. Unlike the built in __isset(), + * this method can take an array of properties to test simultaneously. + * + * @param string $key property name to test for + * @param array $key array of property names to test for + * @return boolean property test result + * @return array associative array of keys and boolean test result + */ + public function is_set( $key = FALSE ) + { + // Setup result; + $result = FALSE; + + // If key is an array + if (is_array($key)) + { + // Set the result to an array + $result = array(); + + // Foreach key + foreach ($key as $property) + { + // Set the result to an associative array + $result[$property] = (array_key_exists($property, $this->kohana_local_data) OR array_key_exists($property, View::$kohana_global_data)) ? TRUE : FALSE; + } + } + else + { + // Otherwise just check one property + $result = (array_key_exists($key, $this->kohana_local_data) OR array_key_exists($key, View::$kohana_global_data)) ? TRUE : FALSE; + } + + // Return the result + return $result; + } + + /** + * Sets a bound variable by reference. + * + * @param string name of variable + * @param mixed variable to assign by reference + * @return object + */ + public function bind($name, & $var) + { + $this->kohana_local_data[$name] =& $var; + + return $this; + } + + /** + * Sets a view global variable. + * + * @param string|array name of variable or an array of variables + * @param mixed value when using a named variable + * @return void + */ + public static function set_global($name, $value = NULL) + { + if (is_array($name)) + { + foreach ($name as $key => $value) + { + View::$kohana_global_data[$key] = $value; + } + } + else + { + View::$kohana_global_data[$name] = $value; + } + } + + /** + * Magically sets a view variable. + * + * @param string variable key + * @param string variable value + * @return void + */ + public function __set($key, $value) + { + $this->kohana_local_data[$key] = $value; + } + + /** + * Magically gets a view variable. + * + * @param string variable key + * @return mixed variable value if the key is found + * @return void if the key is not found + */ + public function &__get($key) + { + if (isset($this->kohana_local_data[$key])) + return $this->kohana_local_data[$key]; + + if (isset(View::$kohana_global_data[$key])) + return View::$kohana_global_data[$key]; + + if (isset($this->$key)) + return $this->$key; + } + + /** + * Magically converts view object to string. + * + * @return string + */ + public function __toString() + { + try + { + return $this->render(); + } + catch (Exception $e) + { + // Display the exception using its internal __toString method + return (string) $e; + } + } + + /** + * Renders a view. + * + * @param boolean set to TRUE to echo the output instead of returning it + * @param callback special renderer to pass the output through + * @return string if print is FALSE + * @return void if print is TRUE + */ + public function render($print = FALSE, $renderer = FALSE) + { + if (empty($this->kohana_filename)) + throw new Kohana_Exception('core.view_set_filename'); + + if (is_string($this->kohana_filetype)) + { + // Merge global and local data, local overrides global with the same name + $data = array_merge(View::$kohana_global_data, $this->kohana_local_data); + + // Load the view in the controller for access to $this + $output = Kohana::$instance->_kohana_load_view($this->kohana_filename, $data); + + if ($renderer !== FALSE AND is_callable($renderer, TRUE)) + { + // Pass the output through the user defined renderer + $output = call_user_func($renderer, $output); + } + + if ($print === TRUE) + { + // Display the output + echo $output; + return; + } + } + else + { + // Set the content type and size + header('Content-Type: '.$this->kohana_filetype[0]); + + if ($print === TRUE) + { + if ($file = fopen($this->kohana_filename, 'rb')) + { + // Display the output + fpassthru($file); + fclose($file); + } + return; + } + + // Fetch the file contents + $output = file_get_contents($this->kohana_filename); + } + + return $output; + } +} // End View \ No newline at end of file diff --git a/system/libraries/drivers/Cache.php b/system/libraries/drivers/Cache.php new file mode 100644 index 00000000..7c5e3c31 --- /dev/null +++ b/system/libraries/drivers/Cache.php @@ -0,0 +1,40 @@ +directory = $directory; + } + + /** + * Finds an array of files matching the given id or tag. + * + * @param string cache id or tag + * @param bool search for tags + * @return array of filenames matching the id or tag + */ + public function exists($id, $tag = FALSE) + { + if ($id === TRUE) + { + // Find all the files + return glob($this->directory.'*~*~*'); + } + elseif ($tag === TRUE) + { + // Find all the files that have the tag name + $paths = glob($this->directory.'*~*'.$id.'*~*'); + + // Find all tags matching the given tag + $files = array(); + foreach ($paths as $path) + { + // Split the files + $tags = explode('~', basename($path)); + + // Find valid tags + if (count($tags) !== 3 OR empty($tags[1])) + continue; + + // Split the tags by plus signs, used to separate tags + $tags = explode('+', $tags[1]); + + if (in_array($tag, $tags)) + { + // Add the file to the array, it has the requested tag + $files[] = $path; + } + } + + return $files; + } + else + { + // Find the file matching the given id + return glob($this->directory.$id.'~*'); + } + } + + /** + * Sets a cache item to the given data, tags, and lifetime. + * + * @param string cache id to set + * @param string data in the cache + * @param array cache tags + * @param integer lifetime + * @return bool + */ + public function set($id, $data, array $tags = NULL, $lifetime) + { + // Remove old cache files + $this->delete($id); + + // Cache File driver expects unix timestamp + if ($lifetime !== 0) + { + $lifetime += time(); + } + + if ( ! empty($tags)) + { + // Convert the tags into a string list + $tags = implode('+', $tags); + } + + // Write out a serialized cache + return (bool) file_put_contents($this->directory.$id.'~'.$tags.'~'.$lifetime, serialize($data)); + } + + /** + * Finds an array of ids for a given tag. + * + * @param string tag name + * @return array of ids that match the tag + */ + public function find($tag) + { + // An array will always be returned + $result = array(); + + if ($paths = $this->exists($tag, TRUE)) + { + // Length of directory name + $offset = strlen($this->directory); + + // Find all the files with the given tag + foreach ($paths as $path) + { + // Get the id from the filename + list($id, $junk) = explode('~', basename($path), 2); + + if (($data = $this->get($id)) !== FALSE) + { + // Add the result to the array + $result[$id] = $data; + } + } + } + + return $result; + } + + /** + * Fetches a cache item. This will delete the item if it is expired or if + * the hash does not match the stored hash. + * + * @param string cache id + * @return mixed|NULL + */ + public function get($id) + { + if ($file = $this->exists($id)) + { + // Use the first file + $file = current($file); + + // Validate that the cache has not expired + if ($this->expired($file)) + { + // Remove this cache, it has expired + $this->delete($id); + } + else + { + // Turn off errors while reading the file + $ER = error_reporting(0); + + if (($data = file_get_contents($file)) !== FALSE) + { + // Unserialize the data + $data = unserialize($data); + } + else + { + // Delete the data + unset($data); + } + + // Turn errors back on + error_reporting($ER); + } + } + + // Return NULL if there is no data + return isset($data) ? $data : NULL; + } + + /** + * Deletes a cache item by id or tag + * + * @param string cache id or tag, or TRUE for "all items" + * @param boolean use tags + * @return boolean + */ + public function delete($id, $tag = FALSE) + { + $files = $this->exists($id, $tag); + + if (empty($files)) + return FALSE; + + // Disable all error reporting while deleting + $ER = error_reporting(0); + + foreach ($files as $file) + { + // Remove the cache file + if ( ! unlink($file)) + Kohana::log('error', 'Cache: Unable to delete cache file: '.$file); + } + + // Turn on error reporting again + error_reporting($ER); + + return TRUE; + } + + /** + * Deletes all cache files that are older than the current time. + * + * @return void + */ + public function delete_expired() + { + if ($files = $this->exists(TRUE)) + { + // Disable all error reporting while deleting + $ER = error_reporting(0); + + foreach ($files as $file) + { + if ($this->expired($file)) + { + // The cache file has already expired, delete it + if ( ! unlink($file)) + Kohana::log('error', 'Cache: Unable to delete cache file: '.$file); + } + } + + // Turn on error reporting again + error_reporting($ER); + } + } + + /** + * Check if a cache file has expired by filename. + * + * @param string filename + * @return bool + */ + protected function expired($file) + { + // Get the expiration time + $expires = (int) substr($file, strrpos($file, '~') + 1); + + // Expirations of 0 are "never expire" + return ($expires !== 0 AND $expires <= time()); + } + +} // End Cache File Driver \ No newline at end of file diff --git a/system/libraries/drivers/Cache/Memcache.php b/system/libraries/drivers/Cache/Memcache.php new file mode 100644 index 00000000..d801de9c --- /dev/null +++ b/system/libraries/drivers/Cache/Memcache.php @@ -0,0 +1,191 @@ +backend = new Memcache; + $this->flags = Kohana::config('cache_memcache.compression') ? MEMCACHE_COMPRESSED : FALSE; + + $servers = Kohana::config('cache_memcache.servers'); + + foreach ($servers as $server) + { + // Make sure all required keys are set + $server += array('host' => '127.0.0.1', 'port' => 11211, 'persistent' => FALSE); + + // Add the server to the pool + $this->backend->addServer($server['host'], $server['port'], (bool) $server['persistent']) + or Kohana::log('error', 'Cache: Connection failed: '.$server['host']); + } + + // Load tags + self::$tags = $this->backend->get(self::TAGS_KEY); + + if ( ! is_array(self::$tags)) + { + // Create a new tags array + self::$tags = array(); + + // Tags have been created + self::$tags_changed = TRUE; + } + } + + public function __destruct() + { + if (self::$tags_changed === TRUE) + { + // Save the tags + $this->backend->set(self::TAGS_KEY, self::$tags, $this->flags, 0); + + // Tags are now unchanged + self::$tags_changed = FALSE; + } + } + + public function find($tag) + { + if (isset(self::$tags[$tag]) AND $results = $this->backend->get(self::$tags[$tag])) + { + // Return all the found caches + return $results; + } + else + { + // No matching tags + return array(); + } + } + + public function get($id) + { + return (($return = $this->backend->get($id)) === FALSE) ? NULL : $return; + } + + public function set($id, $data, array $tags = NULL, $lifetime) + { + if ( ! empty($tags)) + { + // Tags will be changed + self::$tags_changed = TRUE; + + foreach ($tags as $tag) + { + // Add the id to each tag + self::$tags[$tag][$id] = $id; + } + } + + if ($lifetime !== 0) + { + // Memcache driver expects unix timestamp + $lifetime += time(); + } + + // Set a new value + return $this->backend->set($id, $data, $this->flags, $lifetime); + } + + public function delete($id, $tag = FALSE) + { + // Tags will be changed + self::$tags_changed = TRUE; + + if ($id === TRUE) + { + if ($status = $this->backend->flush()) + { + // Remove all tags, all items have been deleted + self::$tags = array(); + + // We must sleep after flushing, or overwriting will not work! + // @see http://php.net/manual/en/function.memcache-flush.php#81420 + sleep(1); + } + + return $status; + } + elseif ($tag === TRUE) + { + if (isset(self::$tags[$id])) + { + foreach (self::$tags[$id] as $_id) + { + // Delete each id in the tag + $this->backend->delete($_id); + } + + // Delete the tag + unset(self::$tags[$id]); + } + + return TRUE; + } + else + { + foreach (self::$tags as $tag => $_ids) + { + if (isset(self::$tags[$tag][$id])) + { + // Remove the id from the tags + unset(self::$tags[$tag][$id]); + } + } + + return $this->backend->delete($id); + } + } + + public function delete_expired() + { + // Tags will be changed + self::$tags_changed = TRUE; + + foreach (self::$tags as $tag => $_ids) + { + foreach ($_ids as $id) + { + if ( ! $this->backend->get($id)) + { + // This id has disappeared, delete it from the tags + unset(self::$tags[$tag][$id]); + } + } + + if (empty(self::$tags[$tag])) + { + // The tag no longer has any valid ids + unset(self::$tags[$tag]); + } + } + + // Memcache handles garbage collection internally + return TRUE; + } + +} // End Cache Memcache Driver diff --git a/system/libraries/drivers/Cache/Sqlite.php b/system/libraries/drivers/Cache/Sqlite.php new file mode 100644 index 00000000..9458d851 --- /dev/null +++ b/system/libraries/drivers/Cache/Sqlite.php @@ -0,0 +1,257 @@ +db = new SQLiteDatabase($filename, '0666', $error); + + // Throw an exception if there's an error + if ( ! empty($error)) + throw new Kohana_Exception('cache.driver_error', sqlite_error_string($error)); + + $query = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'caches'"; + $tables = $this->db->query($query, SQLITE_BOTH, $error); + + // Throw an exception if there's an error + if ( ! empty($error)) + throw new Kohana_Exception('cache.driver_error', sqlite_error_string($error)); + + if ($tables->numRows() == 0) + { + Kohana::log('error', 'Cache: Initializing new SQLite cache database'); + + // Issue a CREATE TABLE command + $this->db->unbufferedQuery(Kohana::config('cache_sqlite.schema')); + } + } + + /** + * Checks if a cache id is already set. + * + * @param string cache id + * @return boolean + */ + public function exists($id) + { + // Find the id that matches + $query = "SELECT id FROM caches WHERE id = '$id'"; + + return ($this->db->query($query)->numRows() > 0); + } + + /** + * Sets a cache item to the given data, tags, and lifetime. + * + * @param string cache id to set + * @param string data in the cache + * @param array cache tags + * @param integer lifetime + * @return bool + */ + public function set($id, $data, array $tags = NULL, $lifetime) + { + // Serialize and escape the data + $data = sqlite_escape_string(serialize($data)); + + if ( ! empty($tags)) + { + // Escape the tags, adding brackets so the tag can be explicitly matched + $tags = sqlite_escape_string('<'.implode('>,<', $tags).'>'); + } + + // Cache Sqlite driver expects unix timestamp + if ($lifetime !== 0) + { + $lifetime += time(); + } + + $query = $this->exists($id) + ? "UPDATE caches SET tags = '$tags', expiration = '$lifetime', cache = '$data' WHERE id = '$id'" + : "INSERT INTO caches VALUES('$id', '$tags', '$lifetime', '$data')"; + + // Run the query + $this->db->unbufferedQuery($query, SQLITE_BOTH, $error); + + if ( ! empty($error)) + { + self::log_error($error); + return FALSE; + } + else + { + return TRUE; + } + } + + /** + * Finds an array of ids for a given tag. + * + * @param string tag name + * @return array of ids that match the tag + */ + public function find($tag) + { + $query = "SELECT id,cache FROM caches WHERE tags LIKE '%<{$tag}>%'"; + $query = $this->db->query($query, SQLITE_BOTH, $error); + + // An array will always be returned + $result = array(); + + if ( ! empty($error)) + { + self::log_error($error); + } + elseif ($query->numRows() > 0) + { + // Disable notices for unserializing + $ER = error_reporting(~E_NOTICE); + + while ($row = $query->fetchObject()) + { + // Add each cache to the array + $result[$row->id] = unserialize($row->cache); + } + + // Turn notices back on + error_reporting($ER); + } + + return $result; + } + + /** + * Fetches a cache item. This will delete the item if it is expired or if + * the hash does not match the stored hash. + * + * @param string cache id + * @return mixed|NULL + */ + public function get($id) + { + $query = "SELECT id, expiration, cache FROM caches WHERE id = '$id' LIMIT 0, 1"; + $query = $this->db->query($query, SQLITE_BOTH, $error); + + if ( ! empty($error)) + { + self::log_error($error); + } + elseif ($cache = $query->fetchObject()) + { + // Make sure the expiration is valid and that the hash matches + if ($cache->expiration != 0 AND $cache->expiration <= time()) + { + // Cache is not valid, delete it now + $this->delete($cache->id); + } + else + { + // Disable notices for unserializing + $ER = error_reporting(~E_NOTICE); + + // Return the valid cache data + $data = $cache->cache; + + // Turn notices back on + error_reporting($ER); + } + } + + // No valid cache found + return NULL; + } + + /** + * Deletes a cache item by id or tag + * + * @param string cache id or tag, or TRUE for "all items" + * @param bool delete a tag + * @return bool + */ + public function delete($id, $tag = FALSE) + { + if ($id === TRUE) + { + // Delete all caches + $where = '1'; + } + elseif ($tag === TRUE) + { + // Delete by tag + $where = "tags LIKE '%<{$id}>%'"; + } + else + { + // Delete by id + $where = "id = '$id'"; + } + + $this->db->unbufferedQuery('DELETE FROM caches WHERE '.$where, SQLITE_BOTH, $error); + + if ( ! empty($error)) + { + self::log_error($error); + return FALSE; + } + else + { + return TRUE; + } + } + + /** + * Deletes all cache files that are older than the current time. + */ + public function delete_expired() + { + // Delete all expired caches + $query = 'DELETE FROM caches WHERE expiration != 0 AND expiration <= '.time(); + + $this->db->unbufferedQuery($query); + + return TRUE; + } + +} // End Cache SQLite Driver \ No newline at end of file diff --git a/system/libraries/drivers/Cache/Xcache.php b/system/libraries/drivers/Cache/Xcache.php new file mode 100644 index 00000000..6254bbb6 --- /dev/null +++ b/system/libraries/drivers/Cache/Xcache.php @@ -0,0 +1,119 @@ +auth(); + $result = TRUE; + for ($i = 0, $max = xcache_count(XC_TYPE_VAR); $i < $max; $i++) + { + if (xcache_clear_cache(XC_TYPE_VAR, $i) !== NULL) + { + $result = FALSE; + break; + } + } + + // Undo the login + $this->auth(TRUE); + return $result; + } + + return TRUE; + } + + public function delete_expired() + { + return TRUE; + } + + private function auth($reverse = FALSE) + { + static $backup = array(); + + $keys = array('PHP_AUTH_USER', 'PHP_AUTH_PW'); + + foreach ($keys as $key) + { + if ($reverse) + { + if (isset($backup[$key])) + { + $_SERVER[$key] = $backup[$key]; + unset($backup[$key]); + } + else + { + unset($_SERVER[$key]); + } + } + else + { + $value = getenv($key); + + if ( ! empty($value)) + { + $backup[$key] = $value; + } + + $_SERVER[$key] = Kohana::config('cache_xcache.'.$key); + } + } + } + +} // End Cache Xcache Driver diff --git a/system/libraries/drivers/Captcha.php b/system/libraries/drivers/Captcha.php new file mode 100644 index 00000000..a4343e19 --- /dev/null +++ b/system/libraries/drivers/Captcha.php @@ -0,0 +1,227 @@ +response = $this->generate_challenge(); + + // Store the correct Captcha response in a session + Event::add('system.post_controller', array($this, 'update_response_session')); + } + + /** + * Generate a new Captcha challenge. + * + * @return string the challenge answer + */ + abstract public function generate_challenge(); + + /** + * Output the Captcha challenge. + * + * @param boolean html output + * @return mixed the rendered Captcha (e.g. an image, riddle, etc.) + */ + abstract public function render($html); + + /** + * Stores the response for the current Captcha challenge in a session so it is available + * on the next page load for Captcha::valid(). This method is called after controller + * execution (in the system.post_controller event) in order not to overwrite itself too soon. + * + * @return void + */ + public function update_response_session() + { + Session::instance()->set('captcha_response', sha1(strtoupper($this->response))); + } + + /** + * Validates a Captcha response from a user. + * + * @param string captcha response + * @return boolean + */ + public function valid($response) + { + return (sha1(strtoupper($response)) === Session::instance()->get('captcha_response')); + } + + /** + * Returns the image type. + * + * @param string filename + * @return string|FALSE image type ("png", "gif" or "jpeg") + */ + public function image_type($filename) + { + switch (strtolower(substr(strrchr($filename, '.'), 1))) + { + case 'png': + return 'png'; + + case 'gif': + return 'gif'; + + case 'jpg': + case 'jpeg': + // Return "jpeg" and not "jpg" because of the GD2 function names + return 'jpeg'; + + default: + return FALSE; + } + } + + /** + * Creates an image resource with the dimensions specified in config. + * If a background image is supplied, the image dimensions are used. + * + * @throws Kohana_Exception if no GD2 support + * @param string path to the background image file + * @return void + */ + public function image_create($background = NULL) + { + // Check for GD2 support + if ( ! function_exists('imagegd2')) + throw new Kohana_Exception('captcha.requires_GD2'); + + // Create a new image (black) + $this->image = imagecreatetruecolor(Captcha::$config['width'], Captcha::$config['height']); + + // Use a background image + if ( ! empty($background)) + { + // Create the image using the right function for the filetype + $function = 'imagecreatefrom'.$this->image_type($background); + $this->background_image = $function($background); + + // Resize the image if needed + if (imagesx($this->background_image) !== Captcha::$config['width'] + OR imagesy($this->background_image) !== Captcha::$config['height']) + { + imagecopyresampled + ( + $this->image, $this->background_image, 0, 0, 0, 0, + Captcha::$config['width'], Captcha::$config['height'], + imagesx($this->background_image), imagesy($this->background_image) + ); + } + + // Free up resources + imagedestroy($this->background_image); + } + } + + /** + * Fills the background with a gradient. + * + * @param resource gd image color identifier for start color + * @param resource gd image color identifier for end color + * @param string direction: 'horizontal' or 'vertical', 'random' by default + * @return void + */ + public function image_gradient($color1, $color2, $direction = NULL) + { + $directions = array('horizontal', 'vertical'); + + // Pick a random direction if needed + if ( ! in_array($direction, $directions)) + { + $direction = $directions[array_rand($directions)]; + + // Switch colors + if (mt_rand(0, 1) === 1) + { + $temp = $color1; + $color1 = $color2; + $color2 = $temp; + } + } + + // Extract RGB values + $color1 = imagecolorsforindex($this->image, $color1); + $color2 = imagecolorsforindex($this->image, $color2); + + // Preparations for the gradient loop + $steps = ($direction === 'horizontal') ? Captcha::$config['width'] : Captcha::$config['height']; + + $r1 = ($color1['red'] - $color2['red']) / $steps; + $g1 = ($color1['green'] - $color2['green']) / $steps; + $b1 = ($color1['blue'] - $color2['blue']) / $steps; + + if ($direction === 'horizontal') + { + $x1 =& $i; + $y1 = 0; + $x2 =& $i; + $y2 = Captcha::$config['height']; + } + else + { + $x1 = 0; + $y1 =& $i; + $x2 = Captcha::$config['width']; + $y2 =& $i; + } + + // Execute the gradient loop + for ($i = 0; $i <= $steps; $i++) + { + $r2 = $color1['red'] - floor($i * $r1); + $g2 = $color1['green'] - floor($i * $g1); + $b2 = $color1['blue'] - floor($i * $b1); + $color = imagecolorallocate($this->image, $r2, $g2, $b2); + + imageline($this->image, $x1, $y1, $x2, $y2, $color); + } + } + + /** + * Returns the img html element or outputs the image to the browser. + * + * @param boolean html output + * @return mixed html string or void + */ + public function image_render($html) + { + // Output html element + if ($html) + return 'Captcha'; + + // Send the correct HTTP header + header('Content-Type: image/'.$this->image_type); + + // Pick the correct output function + $function = 'image'.$this->image_type; + $function($this->image); + + // Free up resources + imagedestroy($this->image); + } + +} // End Captcha Driver \ No newline at end of file diff --git a/system/libraries/drivers/Captcha/Alpha.php b/system/libraries/drivers/Captcha/Alpha.php new file mode 100644 index 00000000..b3a9c9d7 --- /dev/null +++ b/system/libraries/drivers/Captcha/Alpha.php @@ -0,0 +1,92 @@ +image + $this->image_create(Captcha::$config['background']); + + // Add a random gradient + if (empty(Captcha::$config['background'])) + { + $color1 = imagecolorallocate($this->image, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100)); + $color2 = imagecolorallocate($this->image, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100)); + $this->image_gradient($color1, $color2); + } + + // Add a few random circles + for ($i = 0, $count = mt_rand(10, Captcha::$config['complexity'] * 3); $i < $count; $i++) + { + $color = imagecolorallocatealpha($this->image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255), mt_rand(80, 120)); + $size = mt_rand(5, Captcha::$config['height'] / 3); + imagefilledellipse($this->image, mt_rand(0, Captcha::$config['width']), mt_rand(0, Captcha::$config['height']), $size, $size, $color); + } + + // Calculate character font-size and spacing + $default_size = min(Captcha::$config['width'], Captcha::$config['height'] * 2) / strlen($this->response); + $spacing = (int) (Captcha::$config['width'] * 0.9 / strlen($this->response)); + + // Background alphabetic character attributes + $color_limit = mt_rand(96, 160); + $chars = 'ABEFGJKLPQRTVY'; + + // Draw each Captcha character with varying attributes + for ($i = 0, $strlen = strlen($this->response); $i < $strlen; $i++) + { + // Use different fonts if available + $font = Captcha::$config['fontpath'].Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])]; + + $angle = mt_rand(-40, 20); + // Scale the character size on image height + $size = $default_size / 10 * mt_rand(8, 12); + $box = imageftbbox($size, $angle, $font, $this->response[$i]); + + // Calculate character starting coordinates + $x = $spacing / 4 + $i * $spacing; + $y = Captcha::$config['height'] / 2 + ($box[2] - $box[5]) / 4; + + // Draw captcha text character + // Allocate random color, size and rotation attributes to text + $color = imagecolorallocate($this->image, mt_rand(150, 255), mt_rand(200, 255), mt_rand(0, 255)); + + // Write text character to image + imagefttext($this->image, $size, $angle, $x, $y, $color, $font, $this->response[$i]); + + // Draw "ghost" alphabetic character + $text_color = imagecolorallocatealpha($this->image, mt_rand($color_limit + 8, 255), mt_rand($color_limit + 8, 255), mt_rand($color_limit + 8, 255), mt_rand(70, 120)); + $char = substr($chars, mt_rand(0, 14), 1); + imagettftext($this->image, $size * 2, mt_rand(-45, 45), ($x - (mt_rand(5, 10))), ($y + (mt_rand(5, 10))), $text_color, $font, $char); + } + + // Output + return $this->image_render($html); + } + +} // End Captcha Alpha Driver Class \ No newline at end of file diff --git a/system/libraries/drivers/Captcha/Basic.php b/system/libraries/drivers/Captcha/Basic.php new file mode 100644 index 00000000..d212e72c --- /dev/null +++ b/system/libraries/drivers/Captcha/Basic.php @@ -0,0 +1,81 @@ +image + $this->image_create(Captcha::$config['background']); + + // Add a random gradient + if (empty(Captcha::$config['background'])) + { + $color1 = imagecolorallocate($this->image, mt_rand(200, 255), mt_rand(200, 255), mt_rand(150, 255)); + $color2 = imagecolorallocate($this->image, mt_rand(200, 255), mt_rand(200, 255), mt_rand(150, 255)); + $this->image_gradient($color1, $color2); + } + + // Add a few random lines + for ($i = 0, $count = mt_rand(5, Captcha::$config['complexity'] * 4); $i < $count; $i++) + { + $color = imagecolorallocatealpha($this->image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(100, 255), mt_rand(50, 120)); + imageline($this->image, mt_rand(0, Captcha::$config['width']), 0, mt_rand(0, Captcha::$config['width']), Captcha::$config['height'], $color); + } + + // Calculate character font-size and spacing + $default_size = min(Captcha::$config['width'], Captcha::$config['height'] * 2) / (strlen($this->response) + 1); + $spacing = (int) (Captcha::$config['width'] * 0.9 / strlen($this->response)); + + // Draw each Captcha character with varying attributes + for ($i = 0, $strlen = strlen($this->response); $i < $strlen; $i++) + { + // Use different fonts if available + $font = Captcha::$config['fontpath'].Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])]; + + // Allocate random color, size and rotation attributes to text + $color = imagecolorallocate($this->image, mt_rand(0, 150), mt_rand(0, 150), mt_rand(0, 150)); + $angle = mt_rand(-40, 20); + + // Scale the character size on image height + $size = $default_size / 10 * mt_rand(8, 12); + $box = imageftbbox($size, $angle, $font, $this->response[$i]); + + // Calculate character starting coordinates + $x = $spacing / 4 + $i * $spacing; + $y = Captcha::$config['height'] / 2 + ($box[2] - $box[5]) / 4; + + // Write text character to image + imagefttext($this->image, $size, $angle, $x, $y, $color, $font, $this->response[$i]); + } + + // Output + return $this->image_render($html); + } + +} // End Captcha Basic Driver Class \ No newline at end of file diff --git a/system/libraries/drivers/Captcha/Black.php b/system/libraries/drivers/Captcha/Black.php new file mode 100644 index 00000000..6a2e2226 --- /dev/null +++ b/system/libraries/drivers/Captcha/Black.php @@ -0,0 +1,72 @@ +image_create(Captcha::$config['background']); + + // Add random white/gray arcs, amount depends on complexity setting + $count = (Captcha::$config['width'] + Captcha::$config['height']) / 2; + $count = $count / 5 * min(10, Captcha::$config['complexity']); + for ($i = 0; $i < $count; $i++) + { + imagesetthickness($this->image, mt_rand(1, 2)); + $color = imagecolorallocatealpha($this->image, 255, 255, 255, mt_rand(0, 120)); + imagearc($this->image, mt_rand(-Captcha::$config['width'], Captcha::$config['width']), mt_rand(-Captcha::$config['height'], Captcha::$config['height']), mt_rand(-Captcha::$config['width'], Captcha::$config['width']), mt_rand(-Captcha::$config['height'], Captcha::$config['height']), mt_rand(0, 360), mt_rand(0, 360), $color); + } + + // Use different fonts if available + $font = Captcha::$config['fontpath'].Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])]; + + // Draw the character's white shadows + $size = (int) min(Captcha::$config['height'] / 2, Captcha::$config['width'] * 0.8 / strlen($this->response)); + $angle = mt_rand(-15 + strlen($this->response), 15 - strlen($this->response)); + $x = mt_rand(1, Captcha::$config['width'] * 0.9 - $size * strlen($this->response)); + $y = ((Captcha::$config['height'] - $size) / 2) + $size; + $color = imagecolorallocate($this->image, 255, 255, 255); + imagefttext($this->image, $size, $angle, $x + 1, $y + 1, $color, $font, $this->response); + + // Add more shadows for lower complexities + (Captcha::$config['complexity'] < 10) and imagefttext($this->image, $size, $angle, $x - 1, $y - 1, $color, $font , $this->response); + (Captcha::$config['complexity'] < 8) and imagefttext($this->image, $size, $angle, $x - 2, $y + 2, $color, $font , $this->response); + (Captcha::$config['complexity'] < 6) and imagefttext($this->image, $size, $angle, $x + 2, $y - 2, $color, $font , $this->response); + (Captcha::$config['complexity'] < 4) and imagefttext($this->image, $size, $angle, $x + 3, $y + 3, $color, $font , $this->response); + (Captcha::$config['complexity'] < 2) and imagefttext($this->image, $size, $angle, $x - 3, $y - 3, $color, $font , $this->response); + + // Finally draw the foreground characters + $color = imagecolorallocate($this->image, 0, 0, 0); + imagefttext($this->image, $size, $angle, $x, $y, $color, $font, $this->response); + + // Output + return $this->image_render($html); + } + +} // End Captcha Black Driver Class \ No newline at end of file diff --git a/system/libraries/drivers/Captcha/Math.php b/system/libraries/drivers/Captcha/Math.php new file mode 100644 index 00000000..4ac20248 --- /dev/null +++ b/system/libraries/drivers/Captcha/Math.php @@ -0,0 +1,61 @@ +math_exercice = implode(' + ', $numbers).' = '; + + // Return the answer + return array_sum($numbers); + } + + /** + * Outputs the Captcha riddle. + * + * @param boolean html output + * @return mixed + */ + public function render($html) + { + return $this->math_exercice; + } + +} // End Captcha Math Driver Class \ No newline at end of file diff --git a/system/libraries/drivers/Captcha/Riddle.php b/system/libraries/drivers/Captcha/Riddle.php new file mode 100644 index 00000000..765eeaad --- /dev/null +++ b/system/libraries/drivers/Captcha/Riddle.php @@ -0,0 +1,47 @@ +riddle = $riddle[0]; + + // Return the answer + return $riddle[1]; + } + + /** + * Outputs the Captcha riddle. + * + * @param boolean html output + * @return mixed + */ + public function render($html) + { + return $this->riddle; + } + +} // End Captcha Riddle Driver Class \ No newline at end of file diff --git a/system/libraries/drivers/Captcha/Word.php b/system/libraries/drivers/Captcha/Word.php new file mode 100644 index 00000000..856bd9b4 --- /dev/null +++ b/system/libraries/drivers/Captcha/Word.php @@ -0,0 +1,37 @@ +escape_table($table).' WHERE '.implode(' ', $where); + } + + /** + * Builds an UPDATE query. + * + * @param string table name + * @param array key => value pairs + * @param array where clause + * @return string + */ + public function update($table, $values, $where) + { + foreach ($values as $key => $val) + { + $valstr[] = $this->escape_column($key).' = '.$val; + } + return 'UPDATE '.$this->escape_table($table).' SET '.implode(', ', $valstr).' WHERE '.implode(' ',$where); + } + + /** + * Set the charset using 'SET NAMES '. + * + * @param string character set to use + */ + public function set_charset($charset) + { + throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); + } + + /** + * Wrap the tablename in backticks, has support for: table.field syntax. + * + * @param string table name + * @return string + */ + abstract public function escape_table($table); + + /** + * Escape a column/field name, has support for special commands. + * + * @param string column name + * @return string + */ + abstract public function escape_column($column); + + /** + * Builds a WHERE portion of a query. + * + * @param mixed key + * @param string value + * @param string type + * @param int number of where clauses + * @param boolean escape the value + * @return string + */ + public function where($key, $value, $type, $num_wheres, $quote) + { + $prefix = ($num_wheres == 0) ? '' : $type; + + if ($quote === -1) + { + $value = ''; + } + else + { + if ($value === NULL) + { + if ( ! $this->has_operator($key)) + { + $key .= ' IS'; + } + + $value = ' NULL'; + } + elseif (is_bool($value)) + { + if ( ! $this->has_operator($key)) + { + $key .= ' ='; + } + + $value = ($value == TRUE) ? ' 1' : ' 0'; + } + else + { + if ( ! $this->has_operator($key) AND ! empty($key)) + { + $key = $this->escape_column($key).' ='; + } + else + { + preg_match('/^(.+?)([<>!=]+|\bIS(?:\s+NULL))\s*$/i', $key, $matches); + if (isset($matches[1]) AND isset($matches[2])) + { + $key = $this->escape_column(trim($matches[1])).' '.trim($matches[2]); + } + } + + $value = ' '.(($quote == TRUE) ? $this->escape($value) : $value); + } + } + + return $prefix.$key.$value; + } + + /** + * Builds a LIKE portion of a query. + * + * @param mixed field name + * @param string value to match with field + * @param boolean add wildcards before and after the match + * @param string clause type (AND or OR) + * @param int number of likes + * @return string + */ + public function like($field, $match, $auto, $type, $num_likes) + { + $prefix = ($num_likes == 0) ? '' : $type; + + $match = $this->escape_str($match); + + if ($auto === TRUE) + { + // Add the start and end quotes + $match = '%'.str_replace('%', '\\%', $match).'%'; + } + + return $prefix.' '.$this->escape_column($field).' LIKE \''.$match . '\''; + } + + /** + * Builds a NOT LIKE portion of a query. + * + * @param mixed field name + * @param string value to match with field + * @param string clause type (AND or OR) + * @param int number of likes + * @return string + */ + public function notlike($field, $match, $auto, $type, $num_likes) + { + $prefix = ($num_likes == 0) ? '' : $type; + + $match = $this->escape_str($match); + + if ($auto === TRUE) + { + // Add the start and end quotes + $match = '%'.$match.'%'; + } + + return $prefix.' '.$this->escape_column($field).' NOT LIKE \''.$match.'\''; + } + + /** + * Builds a REGEX portion of a query. + * + * @param string field name + * @param string value to match with field + * @param string clause type (AND or OR) + * @param integer number of regexes + * @return string + */ + public function regex($field, $match, $type, $num_regexs) + { + throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); + } + + /** + * Builds a NOT REGEX portion of a query. + * + * @param string field name + * @param string value to match with field + * @param string clause type (AND or OR) + * @param integer number of regexes + * @return string + */ + public function notregex($field, $match, $type, $num_regexs) + { + throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); + } + + /** + * Builds an INSERT query. + * + * @param string table name + * @param array keys + * @param array values + * @return string + */ + public function insert($table, $keys, $values) + { + // Escape the column names + foreach ($keys as $key => $value) + { + $keys[$key] = $this->escape_column($value); + } + return 'INSERT INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')'; + } + + /** + * Builds a MERGE portion of a query. + * + * @param string table name + * @param array keys + * @param array values + * @return string + */ + public function merge($table, $keys, $values) + { + throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); + } + + /** + * Builds a LIMIT portion of a query. + * + * @param integer limit + * @param integer offset + * @return string + */ + abstract public function limit($limit, $offset = 0); + + /** + * Creates a prepared statement. + * + * @param string SQL query + * @return Database_Stmt + */ + public function stmt_prepare($sql = '') + { + throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__); + } + + /** + * Compiles the SELECT statement. + * Generates a query string based on which functions were used. + * Should not be called directly, the get() function calls it. + * + * @param array select query values + * @return string + */ + abstract public function compile_select($database); + + /** + * Determines if the string has an arithmetic operator in it. + * + * @param string string to check + * @return boolean + */ + public function has_operator($str) + { + return (bool) preg_match('/[<>!=]|\sIS(?:\s+NOT\s+)?\b|BETWEEN/i', trim($str)); + } + + /** + * Escapes any input value. + * + * @param mixed value to escape + * @return string + */ + public function escape($value) + { + if ( ! $this->db_config['escape']) + return $value; + + switch (gettype($value)) + { + case 'string': + $value = '\''.$this->escape_str($value).'\''; + break; + case 'boolean': + $value = (int) $value; + break; + case 'double': + // Convert to non-locale aware float to prevent possible commas + $value = sprintf('%F', $value); + break; + default: + $value = ($value === NULL) ? 'NULL' : $value; + break; + } + + return (string) $value; + } + + /** + * Escapes a string for a query. + * + * @param mixed value to escape + * @return string + */ + abstract public function escape_str($str); + + /** + * Lists all tables in the database. + * + * @return array + */ + abstract public function list_tables(); + + /** + * Lists all fields in a table. + * + * @param string table name + * @return array + */ + abstract function list_fields($table); + + /** + * Returns the last database error. + * + * @return string + */ + abstract public function show_error(); + + /** + * Returns field data about a table. + * + * @param string table name + * @return array + */ + abstract public function field_data($table); + + /** + * Fetches SQL type information about a field, in a generic format. + * + * @param string field datatype + * @return array + */ + protected function sql_type($str) + { + static $sql_types; + + if ($sql_types === NULL) + { + // Load SQL data types + $sql_types = Kohana::config('sql_types'); + } + + $str = strtolower(trim($str)); + + if (($open = strpos($str, '(')) !== FALSE) + { + // Find closing bracket + $close = strpos($str, ')', $open) - 1; + + // Find the type without the size + $type = substr($str, 0, $open); + } + else + { + // No length + $type = $str; + } + + empty($sql_types[$type]) and exit + ( + 'Unknown field type: '.$type.'. '. + 'Please report this: http://trac.kohanaphp.com/newticket' + ); + + // Fetch the field definition + $field = $sql_types[$type]; + + switch ($field['type']) + { + case 'string': + case 'float': + if (isset($close)) + { + // Add the length to the field info + $field['length'] = substr($str, $open + 1, $close - $open); + } + break; + case 'int': + // Add unsigned value + $field['unsigned'] = (strpos($str, 'unsigned') !== FALSE); + break; + } + + return $field; + } + + /** + * Clears the internal query cache. + * + * @param string SQL query + */ + public function clear_cache($sql = NULL) + { + if (empty($sql)) + { + $this->query_cache = array(); + } + else + { + unset($this->query_cache[$this->query_hash($sql)]); + } + + Kohana::log('debug', 'Database cache cleared: '.get_class($this)); + } + + /** + * Creates a hash for an SQL query string. Replaces newlines with spaces, + * trims, and hashes. + * + * @param string SQL query + * @return string + */ + protected function query_hash($sql) + { + return sha1(str_replace("\n", ' ', trim($sql))); + } + +} // End Database Driver Interface + +/** + * Database_Result + * + */ +abstract class Database_Result implements ArrayAccess, Iterator, Countable { + + // Result resource, insert id, and SQL + protected $result; + protected $insert_id; + protected $sql; + + // Current and total rows + protected $current_row = 0; + protected $total_rows = 0; + + // Fetch function and return type + protected $fetch_type; + protected $return_type; + + /** + * Returns the SQL used to fetch the result. + * + * @return string + */ + public function sql() + { + return $this->sql; + } + + /** + * Returns the insert id from the result. + * + * @return mixed + */ + public function insert_id() + { + return $this->insert_id; + } + + /** + * Prepares the query result. + * + * @param boolean return rows as objects + * @param mixed type + * @return Database_Result + */ + abstract function result($object = TRUE, $type = FALSE); + + /** + * Builds an array of query results. + * + * @param boolean return rows as objects + * @param mixed type + * @return array + */ + abstract function result_array($object = NULL, $type = FALSE); + + /** + * Gets the fields of an already run query. + * + * @return array + */ + abstract public function list_fields(); + + /** + * Seek to an offset in the results. + * + * @return boolean + */ + abstract public function seek($offset); + + /** + * Countable: count + */ + public function count() + { + return $this->total_rows; + } + + /** + * ArrayAccess: offsetExists + */ + public function offsetExists($offset) + { + if ($this->total_rows > 0) + { + $min = 0; + $max = $this->total_rows - 1; + + return ! ($offset < $min OR $offset > $max); + } + + return FALSE; + } + + /** + * ArrayAccess: offsetGet + */ + public function offsetGet($offset) + { + if ( ! $this->seek($offset)) + return FALSE; + + // Return the row by calling the defined fetching callback + return call_user_func($this->fetch_type, $this->result, $this->return_type); + } + + /** + * ArrayAccess: offsetSet + * + * @throws Kohana_Database_Exception + */ + final public function offsetSet($offset, $value) + { + throw new Kohana_Database_Exception('database.result_read_only'); + } + + /** + * ArrayAccess: offsetUnset + * + * @throws Kohana_Database_Exception + */ + final public function offsetUnset($offset) + { + throw new Kohana_Database_Exception('database.result_read_only'); + } + + /** + * Iterator: current + */ + public function current() + { + return $this->offsetGet($this->current_row); + } + + /** + * Iterator: key + */ + public function key() + { + return $this->current_row; + } + + /** + * Iterator: next + */ + public function next() + { + ++$this->current_row; + return $this; + } + + /** + * Iterator: prev + */ + public function prev() + { + --$this->current_row; + return $this; + } + + /** + * Iterator: rewind + */ + public function rewind() + { + $this->current_row = 0; + return $this; + } + + /** + * Iterator: valid + */ + public function valid() + { + return $this->offsetExists($this->current_row); + } + +} // End Database Result Interface diff --git a/system/libraries/drivers/Database/Mssql.php b/system/libraries/drivers/Database/Mssql.php new file mode 100644 index 00000000..6947679a --- /dev/null +++ b/system/libraries/drivers/Database/Mssql.php @@ -0,0 +1,462 @@ +db_config = $config; + + Kohana::log('debug', 'MSSQL Database Driver Initialized'); + } + + /** + * Closes the database connection. + */ + public function __destruct() + { + is_resource($this->link) and mssql_close($this->link); + } + + /** + * Make the connection + * + * @return return connection + */ + public function connect() + { + // Check if link already exists + if (is_resource($this->link)) + return $this->link; + + // Import the connect variables + extract($this->db_config['connection']); + + // Persistent connections enabled? + $connect = ($this->db_config['persistent'] == TRUE) ? 'mssql_pconnect' : 'mssql_connect'; + + // Build the connection info + $host = isset($host) ? $host : $socket; + + // Windows uses a comma instead of a colon + $port = (isset($port) AND is_string($port)) ? (KOHANA_IS_WIN ? ',' : ':').$port : ''; + + // Make the connection and select the database + if (($this->link = $connect($host.$port, $user, $pass, TRUE)) AND mssql_select_db($database, $this->link)) + { + /* This is being removed so I can use it, will need to come up with a more elegant workaround in the future... + * + if ($charset = $this->db_config['character_set']) + { + $this->set_charset($charset); + } + */ + + // Clear password after successful connect + $this->db_config['connection']['pass'] = NULL; + + return $this->link; + } + + return FALSE; + } + + public function query($sql) + { + // Only cache if it's turned on, and only cache if it's not a write statement + if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET)\b#i', $sql)) + { + $hash = $this->query_hash($sql); + + if ( ! isset($this->query_cache[$hash])) + { + // Set the cached object + $this->query_cache[$hash] = new Mssql_Result(mssql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); + } + else + { + // Rewind cached result + $this->query_cache[$hash]->rewind(); + } + + // Return the cached query + return $this->query_cache[$hash]; + } + + return new Mssql_Result(mssql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); + } + + public function escape_table($table) + { + if (stripos($table, ' AS ') !== FALSE) + { + // Force 'AS' to uppercase + $table = str_ireplace(' AS ', ' AS ', $table); + + // Runs escape_table on both sides of an AS statement + $table = array_map(array($this, __FUNCTION__), explode(' AS ', $table)); + + // Re-create the AS statement + return implode(' AS ', $table); + } + return '['.str_replace('.', '[.]', $table).']'; + } + + public function escape_column($column) + { + if (!$this->db_config['escape']) + return $column; + + if ($column == '*') + return $column; + + // This matches any functions we support to SELECT. + if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches)) + { + if ( count($matches) == 3) + { + return $matches[1].'('.$this->escape_column($matches[2]).')'; + } + else if ( count($matches) == 5) + { + return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]); + } + } + + // This matches any modifiers we support to SELECT. + if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column)) + { + if (stripos($column, ' AS ') !== FALSE) + { + // Force 'AS' to uppercase + $column = str_ireplace(' AS ', ' AS ', $column); + + // Runs escape_column on both sides of an AS statement + $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column)); + + // Re-create the AS statement + return implode(' AS ', $column); + } + + return preg_replace('/[^.*]+/', '[$0]', $column); + } + + $parts = explode(' ', $column); + $column = ''; + + for ($i = 0, $c = count($parts); $i < $c; $i++) + { + // The column is always last + if ($i == ($c - 1)) + { + $column .= preg_replace('/[^.*]+/', '[$0]', $parts[$i]); + } + else // otherwise, it's a modifier + { + $column .= $parts[$i].' '; + } + } + return $column; + } + + /** + * Limit in SQL Server 2000 only uses the keyword + * 'TOP'; 2007 may have an offset keyword, but + * I am unsure - for pagination style limit,offset + * functionality, a fancy query needs to be built. + * + * @param unknown_type $limit + * @return unknown + */ + public function limit($limit, $offset=null) + { + return 'TOP '.$limit; + } + + public function compile_select($database) + { + $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT '; + $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*'; + + if (count($database['from']) > 0) + { + // Escape the tables + $froms = array(); + foreach ($database['from'] as $from) + $froms[] = $this->escape_column($from); + $sql .= "\nFROM "; + $sql .= implode(', ', $froms); + } + + if (count($database['join']) > 0) + { + foreach($database['join'] AS $join) + { + $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions']; + } + } + + if (count($database['where']) > 0) + { + $sql .= "\nWHERE "; + } + + $sql .= implode("\n", $database['where']); + + if (count($database['groupby']) > 0) + { + $sql .= "\nGROUP BY "; + $sql .= implode(', ', $database['groupby']); + } + + if (count($database['having']) > 0) + { + $sql .= "\nHAVING "; + $sql .= implode("\n", $database['having']); + } + + if (count($database['orderby']) > 0) + { + $sql .= "\nORDER BY "; + $sql .= implode(', ', $database['orderby']); + } + + if (is_numeric($database['limit'])) + { + $sql .= "\n"; + $sql .= $this->limit($database['limit']); + } + + return $sql; + } + + public function escape_str($str) + { + if (!$this->db_config['escape']) + return $str; + + is_resource($this->link) or $this->connect(); + //mssql_real_escape_string($str, $this->link); <-- this function doesn't exist + + $characters = array('/\x00/', '/\x1a/', '/\n/', '/\r/', '/\\\/', '/\'/'); + $replace = array('\\\x00', '\\x1a', '\\n', '\\r', '\\\\', "''"); + return preg_replace($characters, $replace, $str); + } + + public function list_tables() + { + $sql = 'SHOW TABLES FROM ['.$this->db_config['connection']['database'].']'; + $result = $this->query($sql)->result(FALSE, MSSQL_ASSOC); + + $retval = array(); + foreach ($result as $row) + { + $retval[] = current($row); + } + + return $retval; + } + + public function show_error() + { + return mssql_get_last_message($this->link); + } + + public function list_fields($table) + { + $result = array(); + + foreach ($this->field_data($table) as $row) + { + // Make an associative array + $result[$row->Field] = $this->sql_type($row->Type); + } + + return $result; + } + + public function field_data($table) + { + $query = $this->query('SHOW COLUMNS FROM '.$this->escape_table($table), $this->link); + + return $query->result_array(TRUE); + } +} + +/** + * MSSQL Result + */ +class Mssql_Result extends Database_Result { + + // Fetch function and return type + protected $fetch_type = 'mssql_fetch_object'; + protected $return_type = MSSQL_ASSOC; + + /** + * Sets up the result variables. + * + * @param resource query result + * @param resource database link + * @param boolean return objects or arrays + * @param string SQL query that was run + */ + public function __construct($result, $link, $object = TRUE, $sql) + { + $this->result = $result; + + // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query + if (is_resource($result)) + { + $this->current_row = 0; + $this->total_rows = mssql_num_rows($this->result); + $this->fetch_type = ($object === TRUE) ? 'mssql_fetch_object' : 'mssql_fetch_array'; + } + elseif (is_bool($result)) + { + if ($result == FALSE) + { + // SQL error + throw new Kohana_Database_Exception('database.error', mssql_get_last_message($link).' - '.$sql); + } + else + { + // Its an DELETE, INSERT, REPLACE, or UPDATE querys + $last_id = mssql_query('SELECT @@IDENTITY AS last_id', $link); + $result = mssql_fetch_assoc($last_id); + $this->insert_id = $result['last_id']; + $this->total_rows = mssql_rows_affected($link); + } + } + + // Set result type + $this->result($object); + + // Store the SQL + $this->sql = $sql; + } + + /** + * Destruct, the cleanup crew! + */ + public function __destruct() + { + if (is_resource($this->result)) + { + mssql_free_result($this->result); + } + } + + public function result($object = TRUE, $type = MSSQL_ASSOC) + { + $this->fetch_type = ((bool) $object) ? 'mssql_fetch_object' : 'mssql_fetch_array'; + + // This check has to be outside the previous statement, because we do not + // know the state of fetch_type when $object = NULL + // NOTE - The class set by $type must be defined before fetching the result, + // autoloading is disabled to save a lot of stupid overhead. + if ($this->fetch_type == 'mssql_fetch_object') + { + $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $this->return_type = $type; + } + + return $this; + } + + public function as_array($object = NULL, $type = MSSQL_ASSOC) + { + return $this->result_array($object, $type); + } + + public function result_array($object = NULL, $type = MSSQL_ASSOC) + { + $rows = array(); + + if (is_string($object)) + { + $fetch = $object; + } + elseif (is_bool($object)) + { + if ($object === TRUE) + { + $fetch = 'mssql_fetch_object'; + + // NOTE - The class set by $type must be defined before fetching the result, + // autoloading is disabled to save a lot of stupid overhead. + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $fetch = 'mssql_fetch_array'; + } + } + else + { + // Use the default config values + $fetch = $this->fetch_type; + + if ($fetch == 'mssql_fetch_object') + { + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + } + + if (mssql_num_rows($this->result)) + { + // Reset the pointer location to make sure things work properly + mssql_data_seek($this->result, 0); + + while ($row = $fetch($this->result, $type)) + { + $rows[] = $row; + } + } + + return isset($rows) ? $rows : array(); + } + + public function list_fields() + { + $field_names = array(); + while ($field = mssql_fetch_field($this->result)) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + public function seek($offset) + { + if ( ! $this->offsetExists($offset)) + return FALSE; + + return mssql_data_seek($this->result, $offset); + } + +} // End mssql_Result Class diff --git a/system/libraries/drivers/Database/Mysql.php b/system/libraries/drivers/Database/Mysql.php new file mode 100644 index 00000000..d5222f50 --- /dev/null +++ b/system/libraries/drivers/Database/Mysql.php @@ -0,0 +1,496 @@ +db_config = $config; + + Kohana::log('debug', 'MySQL Database Driver Initialized'); + } + + /** + * Closes the database connection. + */ + public function __destruct() + { + is_resource($this->link) and mysql_close($this->link); + } + + public function connect() + { + // Check if link already exists + if (is_resource($this->link)) + return $this->link; + + // Import the connect variables + extract($this->db_config['connection']); + + // Persistent connections enabled? + $connect = ($this->db_config['persistent'] == TRUE) ? 'mysql_pconnect' : 'mysql_connect'; + + // Build the connection info + $host = isset($host) ? $host : $socket; + $port = isset($port) ? ':'.$port : ''; + + // Make the connection and select the database + if (($this->link = $connect($host.$port, $user, $pass, TRUE)) AND mysql_select_db($database, $this->link)) + { + if ($charset = $this->db_config['character_set']) + { + $this->set_charset($charset); + } + + // Clear password after successful connect + $this->db_config['connection']['pass'] = NULL; + + return $this->link; + } + + return FALSE; + } + + public function query($sql) + { + // Only cache if it's turned on, and only cache if it's not a write statement + if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET|DELETE|TRUNCATE)\b#i', $sql)) + { + $hash = $this->query_hash($sql); + + if ( ! isset($this->query_cache[$hash])) + { + // Set the cached object + $this->query_cache[$hash] = new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); + } + else + { + // Rewind cached result + $this->query_cache[$hash]->rewind(); + } + + // Return the cached query + return $this->query_cache[$hash]; + } + + return new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); + } + + public function set_charset($charset) + { + $this->query('SET NAMES '.$this->escape_str($charset)); + } + + public function escape_table($table) + { + if (!$this->db_config['escape']) + return $table; + + if (stripos($table, ' AS ') !== FALSE) + { + // Force 'AS' to uppercase + $table = str_ireplace(' AS ', ' AS ', $table); + + // Runs escape_table on both sides of an AS statement + $table = array_map(array($this, __FUNCTION__), explode(' AS ', $table)); + + // Re-create the AS statement + return implode(' AS ', $table); + } + return '`'.str_replace('.', '`.`', $table).'`'; + } + + public function escape_column($column) + { + if (!$this->db_config['escape']) + return $column; + + if ($column == '*') + return $column; + + // This matches any functions we support to SELECT. + if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches)) + { + if ( count($matches) == 3) + { + return $matches[1].'('.$this->escape_column($matches[2]).')'; + } + else if ( count($matches) == 5) + { + return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]); + } + } + + // This matches any modifiers we support to SELECT. + if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column)) + { + if (stripos($column, ' AS ') !== FALSE) + { + // Force 'AS' to uppercase + $column = str_ireplace(' AS ', ' AS ', $column); + + // Runs escape_column on both sides of an AS statement + $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column)); + + // Re-create the AS statement + return implode(' AS ', $column); + } + + return preg_replace('/[^.*]+/', '`$0`', $column); + } + + $parts = explode(' ', $column); + $column = ''; + + for ($i = 0, $c = count($parts); $i < $c; $i++) + { + // The column is always last + if ($i == ($c - 1)) + { + $column .= preg_replace('/[^.*]+/', '`$0`', $parts[$i]); + } + else // otherwise, it's a modifier + { + $column .= $parts[$i].' '; + } + } + return $column; + } + + public function regex($field, $match, $type, $num_regexs) + { + $prefix = ($num_regexs == 0) ? '' : $type; + + return $prefix.' '.$this->escape_column($field).' REGEXP \''.$this->escape_str($match).'\''; + } + + public function notregex($field, $match, $type, $num_regexs) + { + $prefix = $num_regexs == 0 ? '' : $type; + + return $prefix.' '.$this->escape_column($field).' NOT REGEXP \''.$this->escape_str($match) . '\''; + } + + public function merge($table, $keys, $values) + { + // Escape the column names + foreach ($keys as $key => $value) + { + $keys[$key] = $this->escape_column($value); + } + return 'REPLACE INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')'; + } + + public function limit($limit, $offset = 0) + { + return 'LIMIT '.$offset.', '.$limit; + } + + public function compile_select($database) + { + $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT '; + $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*'; + + if (count($database['from']) > 0) + { + // Escape the tables + $froms = array(); + foreach ($database['from'] as $from) + { + $froms[] = $this->escape_column($from); + } + $sql .= "\nFROM ("; + $sql .= implode(', ', $froms).")"; + } + + if (count($database['join']) > 0) + { + foreach($database['join'] AS $join) + { + $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions']; + } + } + + if (count($database['where']) > 0) + { + $sql .= "\nWHERE "; + } + + $sql .= implode("\n", $database['where']); + + if (count($database['groupby']) > 0) + { + $sql .= "\nGROUP BY "; + $sql .= implode(', ', $database['groupby']); + } + + if (count($database['having']) > 0) + { + $sql .= "\nHAVING "; + $sql .= implode("\n", $database['having']); + } + + if (count($database['orderby']) > 0) + { + $sql .= "\nORDER BY "; + $sql .= implode(', ', $database['orderby']); + } + + if (is_numeric($database['limit'])) + { + $sql .= "\n"; + $sql .= $this->limit($database['limit'], $database['offset']); + } + + return $sql; + } + + public function escape_str($str) + { + if (!$this->db_config['escape']) + return $str; + + is_resource($this->link) or $this->connect(); + + return mysql_real_escape_string($str, $this->link); + } + + public function list_tables() + { + $tables = array(); + + if ($query = $this->query('SHOW TABLES FROM '.$this->escape_table($this->db_config['connection']['database']))) + { + foreach ($query->result(FALSE) as $row) + { + $tables[] = current($row); + } + } + + return $tables; + } + + public function show_error() + { + return mysql_error($this->link); + } + + public function list_fields($table) + { + $result = NULL; + + foreach ($this->field_data($table) as $row) + { + // Make an associative array + $result[$row->Field] = $this->sql_type($row->Type); + + if ($row->Key === 'PRI' AND $row->Extra === 'auto_increment') + { + // For sequenced (AUTO_INCREMENT) tables + $result[$row->Field]['sequenced'] = TRUE; + } + + if ($row->Null === 'YES') + { + // Set NULL status + $result[$row->Field]['null'] = TRUE; + } + } + + if (!isset($result)) + throw new Kohana_Database_Exception('database.table_not_found', $table); + + return $result; + } + + public function field_data($table) + { + $result = $this->query('SHOW COLUMNS FROM '.$this->escape_table($table)); + + return $result->result_array(TRUE); + } + +} // End Database_Mysql_Driver Class + +/** + * MySQL Result + */ +class Mysql_Result extends Database_Result { + + // Fetch function and return type + protected $fetch_type = 'mysql_fetch_object'; + protected $return_type = MYSQL_ASSOC; + + /** + * Sets up the result variables. + * + * @param resource query result + * @param resource database link + * @param boolean return objects or arrays + * @param string SQL query that was run + */ + public function __construct($result, $link, $object = TRUE, $sql) + { + $this->result = $result; + + // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query + if (is_resource($result)) + { + $this->current_row = 0; + $this->total_rows = mysql_num_rows($this->result); + $this->fetch_type = ($object === TRUE) ? 'mysql_fetch_object' : 'mysql_fetch_array'; + } + elseif (is_bool($result)) + { + if ($result == FALSE) + { + // SQL error + throw new Kohana_Database_Exception('database.error', mysql_error($link).' - '.$sql); + } + else + { + // Its an DELETE, INSERT, REPLACE, or UPDATE query + $this->insert_id = mysql_insert_id($link); + $this->total_rows = mysql_affected_rows($link); + } + } + + // Set result type + $this->result($object); + + // Store the SQL + $this->sql = $sql; + } + + /** + * Destruct, the cleanup crew! + */ + public function __destruct() + { + if (is_resource($this->result)) + { + mysql_free_result($this->result); + } + } + + public function result($object = TRUE, $type = MYSQL_ASSOC) + { + $this->fetch_type = ((bool) $object) ? 'mysql_fetch_object' : 'mysql_fetch_array'; + + // This check has to be outside the previous statement, because we do not + // know the state of fetch_type when $object = NULL + // NOTE - The class set by $type must be defined before fetching the result, + // autoloading is disabled to save a lot of stupid overhead. + if ($this->fetch_type == 'mysql_fetch_object' AND $object === TRUE) + { + $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $this->return_type = $type; + } + + return $this; + } + + public function as_array($object = NULL, $type = MYSQL_ASSOC) + { + return $this->result_array($object, $type); + } + + public function result_array($object = NULL, $type = MYSQL_ASSOC) + { + $rows = array(); + + if (is_string($object)) + { + $fetch = $object; + } + elseif (is_bool($object)) + { + if ($object === TRUE) + { + $fetch = 'mysql_fetch_object'; + + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $fetch = 'mysql_fetch_array'; + } + } + else + { + // Use the default config values + $fetch = $this->fetch_type; + + if ($fetch == 'mysql_fetch_object') + { + $type = (is_string($this->return_type) AND Kohana::auto_load($this->return_type)) ? $this->return_type : 'stdClass'; + } + } + + if (mysql_num_rows($this->result)) + { + // Reset the pointer location to make sure things work properly + mysql_data_seek($this->result, 0); + + while ($row = $fetch($this->result, $type)) + { + $rows[] = $row; + } + } + + return isset($rows) ? $rows : array(); + } + + public function list_fields() + { + $field_names = array(); + while ($field = mysql_fetch_field($this->result)) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + public function seek($offset) + { + if ($this->offsetExists($offset) AND mysql_data_seek($this->result, $offset)) + { + // Set the current row to the offset + $this->current_row = $offset; + + return TRUE; + } + else + { + return FALSE; + } + } + +} // End Mysql_Result Class diff --git a/system/libraries/drivers/Database/Mysqli.php b/system/libraries/drivers/Database/Mysqli.php new file mode 100644 index 00000000..0dd9f05c --- /dev/null +++ b/system/libraries/drivers/Database/Mysqli.php @@ -0,0 +1,358 @@ +db_config = $config; + + Kohana::log('debug', 'MySQLi Database Driver Initialized'); + } + + /** + * Closes the database connection. + */ + public function __destruct() + { + is_object($this->link) and $this->link->close(); + } + + public function connect() + { + // Check if link already exists + if (is_object($this->link)) + return $this->link; + + // Import the connect variables + extract($this->db_config['connection']); + + // Build the connection info + $host = isset($host) ? $host : $socket; + + // Make the connection and select the database + if ($this->link = new mysqli($host, $user, $pass, $database, $port)) + { + if ($charset = $this->db_config['character_set']) + { + $this->set_charset($charset); + } + + // Clear password after successful connect + $this->db_config['connection']['pass'] = NULL; + + return $this->link; + } + + return FALSE; + } + + public function query($sql) + { + // Only cache if it's turned on, and only cache if it's not a write statement + if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET|DELETE|TRUNCATE)\b#i', $sql)) + { + $hash = $this->query_hash($sql); + + if ( ! isset($this->query_cache[$hash])) + { + // Set the cached object + $this->query_cache[$hash] = new Kohana_Mysqli_Result($this->link, $this->db_config['object'], $sql); + } + else + { + // Rewind cached result + $this->query_cache[$hash]->rewind(); + } + + // Return the cached query + return $this->query_cache[$hash]; + } + + return new Kohana_Mysqli_Result($this->link, $this->db_config['object'], $sql); + } + + public function set_charset($charset) + { + if ($this->link->set_charset($charset) === FALSE) + throw new Kohana_Database_Exception('database.error', $this->show_error()); + } + + public function escape_str($str) + { + if (!$this->db_config['escape']) + return $str; + + is_object($this->link) or $this->connect(); + + return $this->link->real_escape_string($str); + } + + public function show_error() + { + return $this->link->error; + } + +} // End Database_Mysqli_Driver Class + +/** + * MySQLi Result + */ +class Kohana_Mysqli_Result extends Database_Result { + + // Database connection + protected $link; + + // Data fetching types + protected $fetch_type = 'mysqli_fetch_object'; + protected $return_type = MYSQLI_ASSOC; + + /** + * Sets up the result variables. + * + * @param object database link + * @param boolean return objects or arrays + * @param string SQL query that was run + */ + public function __construct($link, $object = TRUE, $sql) + { + $this->link = $link; + + if ( ! $this->link->multi_query($sql)) + { + // SQL error + throw new Kohana_Database_Exception('database.error', $this->link->error.' - '.$sql); + } + else + { + $this->result = $this->link->store_result(); + + // If the query is an object, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query + if (is_object($this->result)) + { + $this->current_row = 0; + $this->total_rows = $this->result->num_rows; + $this->fetch_type = ($object === TRUE) ? 'fetch_object' : 'fetch_array'; + } + elseif ($this->link->error) + { + // SQL error + throw new Kohana_Database_Exception('database.error', $this->link->error.' - '.$sql); + } + else + { + // Its an DELETE, INSERT, REPLACE, or UPDATE query + $this->insert_id = $this->link->insert_id; + $this->total_rows = $this->link->affected_rows; + } + } + + // Set result type + $this->result($object); + + // Store the SQL + $this->sql = $sql; + } + + /** + * Magic __destruct function, frees the result. + */ + public function __destruct() + { + if (is_object($this->result)) + { + $this->result->free_result(); + + // this is kinda useless, but needs to be done to avoid the "Commands out of sync; you + // can't run this command now" error. Basically, we get all results after the first one + // (the one we actually need) and free them. + if (is_resource($this->link) AND $this->link->more_results()) + { + do + { + if ($result = $this->link->store_result()) + { + $result->free_result(); + } + } while ($this->link->next_result()); + } + } + } + + public function result($object = TRUE, $type = MYSQLI_ASSOC) + { + $this->fetch_type = ((bool) $object) ? 'fetch_object' : 'fetch_array'; + + // This check has to be outside the previous statement, because we do not + // know the state of fetch_type when $object = NULL + // NOTE - The class set by $type must be defined before fetching the result, + // autoloading is disabled to save a lot of stupid overhead. + if ($this->fetch_type == 'fetch_object') + { + $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $this->return_type = $type; + } + + return $this; + } + + public function as_array($object = NULL, $type = MYSQLI_ASSOC) + { + return $this->result_array($object, $type); + } + + public function result_array($object = NULL, $type = MYSQLI_ASSOC) + { + $rows = array(); + + if (is_string($object)) + { + $fetch = $object; + } + elseif (is_bool($object)) + { + if ($object === TRUE) + { + $fetch = 'fetch_object'; + + // NOTE - The class set by $type must be defined before fetching the result, + // autoloading is disabled to save a lot of stupid overhead. + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $fetch = 'fetch_array'; + } + } + else + { + // Use the default config values + $fetch = $this->fetch_type; + + if ($fetch == 'fetch_object') + { + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + } + + if ($this->result->num_rows) + { + // Reset the pointer location to make sure things work properly + $this->result->data_seek(0); + + while ($row = $this->result->$fetch($type)) + { + $rows[] = $row; + } + } + + return isset($rows) ? $rows : array(); + } + + public function list_fields() + { + $field_names = array(); + while ($field = $this->result->fetch_field()) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + public function seek($offset) + { + if ($this->offsetExists($offset) AND $this->result->data_seek($offset)) + { + // Set the current row to the offset + $this->current_row = $offset; + + return TRUE; + } + + return FALSE; + } + + public function offsetGet($offset) + { + if ( ! $this->seek($offset)) + return FALSE; + + // Return the row + $fetch = $this->fetch_type; + return $this->result->$fetch($this->return_type); + } + +} // End Mysqli_Result Class + +/** + * MySQLi Prepared Statement (experimental) + */ +class Kohana_Mysqli_Statement { + + protected $link = NULL; + protected $stmt; + protected $var_names = array(); + protected $var_values = array(); + + public function __construct($sql, $link) + { + $this->link = $link; + + $this->stmt = $this->link->prepare($sql); + + return $this; + } + + public function __destruct() + { + $this->stmt->close(); + } + + // Sets the bind parameters + public function bind_params($param_types, $params) + { + $this->var_names = array_keys($params); + $this->var_values = array_values($params); + call_user_func_array(array($this->stmt, 'bind_param'), array_merge($param_types, $var_names)); + + return $this; + } + + public function bind_result($params) + { + call_user_func_array(array($this->stmt, 'bind_result'), $params); + } + + // Runs the statement + public function execute() + { + foreach ($this->var_names as $key => $name) + { + $$name = $this->var_values[$key]; + } + $this->stmt->execute(); + return $this->stmt; + } +} diff --git a/system/libraries/drivers/Database/Pdosqlite.php b/system/libraries/drivers/Database/Pdosqlite.php new file mode 100644 index 00000000..c2d1bb21 --- /dev/null +++ b/system/libraries/drivers/Database/Pdosqlite.php @@ -0,0 +1,486 @@ + + */ + +class Database_Pdosqlite_Driver extends Database_Driver { + + // Database connection link + protected $link; + protected $db_config; + + /* + * Constructor: __construct + * Sets up the config for the class. + * + * Parameters: + * config - database configuration + * + */ + public function __construct($config) + { + $this->db_config = $config; + + Kohana::log('debug', 'PDO:Sqlite Database Driver Initialized'); + } + + public function connect() + { + // Import the connect variables + extract($this->db_config['connection']); + + try + { + $this->link = new PDO('sqlite:'.$socket.$database, $user, $pass, + array(PDO::ATTR_PERSISTENT => $this->db_config['persistent'])); + + $this->link->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + //$this->link->query('PRAGMA count_changes=1;'); + + if ($charset = $this->db_config['character_set']) + { + $this->set_charset($charset); + } + } + catch (PDOException $e) + { + throw new Kohana_Database_Exception('database.error', $e->getMessage()); + } + + // Clear password after successful connect + $this->db_config['connection']['pass'] = NULL; + + return $this->link; + } + + public function query($sql) + { + try + { + $sth = $this->link->prepare($sql); + } + catch (PDOException $e) + { + throw new Kohana_Database_Exception('database.error', $e->getMessage()); + } + return new Pdosqlite_Result($sth, $this->link, $this->db_config['object'], $sql); + } + + public function set_charset($charset) + { + $this->link->query('PRAGMA encoding = '.$this->escape_str($charset)); + } + + public function escape_table($table) + { + if ( ! $this->db_config['escape']) + return $table; + + return '`'.str_replace('.', '`.`', $table).'`'; + } + + public function escape_column($column) + { + if ( ! $this->db_config['escape']) + return $column; + + if ($column == '*') + return $column; + + // This matches any functions we support to SELECT. + if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches)) + { + if ( count($matches) == 3) + { + return $matches[1].'('.$this->escape_column($matches[2]).')'; + } + else if ( count($matches) == 5) + { + return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]); + } + } + + // This matches any modifiers we support to SELECT. + if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column)) + { + if (stripos($column, ' AS ') !== FALSE) + { + // Force 'AS' to uppercase + $column = str_ireplace(' AS ', ' AS ', $column); + + // Runs escape_column on both sides of an AS statement + $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column)); + + // Re-create the AS statement + return implode(' AS ', $column); + } + + return preg_replace('/[^.*]+/', '`$0`', $column); + } + + $parts = explode(' ', $column); + $column = ''; + + for ($i = 0, $c = count($parts); $i < $c; $i++) + { + // The column is always last + if ($i == ($c - 1)) + { + $column .= preg_replace('/[^.*]+/', '`$0`', $parts[$i]); + } + else // otherwise, it's a modifier + { + $column .= $parts[$i].' '; + } + } + return $column; + } + + public function limit($limit, $offset = 0) + { + return 'LIMIT '.$offset.', '.$limit; + } + + public function compile_select($database) + { + $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT '; + $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*'; + + if (count($database['from']) > 0) + { + $sql .= "\nFROM "; + $sql .= implode(', ', $database['from']); + } + + if (count($database['join']) > 0) + { + foreach($database['join'] AS $join) + { + $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions']; + } + } + + if (count($database['where']) > 0) + { + $sql .= "\nWHERE "; + } + + $sql .= implode("\n", $database['where']); + + if (count($database['groupby']) > 0) + { + $sql .= "\nGROUP BY "; + $sql .= implode(', ', $database['groupby']); + } + + if (count($database['having']) > 0) + { + $sql .= "\nHAVING "; + $sql .= implode("\n", $database['having']); + } + + if (count($database['orderby']) > 0) + { + $sql .= "\nORDER BY "; + $sql .= implode(', ', $database['orderby']); + } + + if (is_numeric($database['limit'])) + { + $sql .= "\n"; + $sql .= $this->limit($database['limit'], $database['offset']); + } + + return $sql; + } + + public function escape_str($str) + { + if ( ! $this->db_config['escape']) + return $str; + + if (function_exists('sqlite_escape_string')) + { + $res = sqlite_escape_string($str); + } + else + { + $res = str_replace("'", "''", $str); + } + return $res; + } + + public function list_tables() + { + $sql = "SELECT `name` FROM `sqlite_master` WHERE `type`='table' ORDER BY `name`;"; + try + { + $result = $this->query($sql)->result(FALSE, PDO::FETCH_ASSOC); + $tables = array(); + foreach ($result as $row) + { + $tables[] = current($row); + } + } + catch (PDOException $e) + { + throw new Kohana_Database_Exception('database.error', $e->getMessage()); + } + return $tables; + } + + public function show_error() + { + $err = $this->link->errorInfo(); + return isset($err[2]) ? $err[2] : 'Unknown error!'; + } + + public function list_fields($table, $query = FALSE) + { + static $tables; + if (is_object($query)) + { + if (empty($tables[$table])) + { + $tables[$table] = array(); + + foreach ($query->result() as $row) + { + $tables[$table][] = $row->name; + } + } + + return $tables[$table]; + } + else + { + $result = $this->link->query( 'PRAGMA table_info('.$this->escape_table($table).')' ); + + foreach ($result as $row) + { + $tables[$table][$row['name']] = $this->sql_type($row['type']); + } + + return $tables[$table]; + } + } + + public function field_data($table) + { + Kohana::log('error', 'This method is under developing'); + } + /** + * Version number query string + * + * @access public + * @return string + */ + function version() + { + return $this->link->getAttribute(constant("PDO::ATTR_SERVER_VERSION")); + } + +} // End Database_PdoSqlite_Driver Class + +/* + * PDO-sqlite Result + */ +class Pdosqlite_Result extends Database_Result { + + // Data fetching types + protected $fetch_type = PDO::FETCH_OBJ; + protected $return_type = PDO::FETCH_ASSOC; + + /** + * Sets up the result variables. + * + * @param resource query result + * @param resource database link + * @param boolean return objects or arrays + * @param string SQL query that was run + */ + public function __construct($result, $link, $object = TRUE, $sql) + { + if (is_object($result) OR $result = $link->prepare($sql)) + { + // run the query. Return true if success, false otherwise + if( ! $result->execute()) + { + // Throw Kohana Exception with error message. See PDOStatement errorInfo() method + $arr_infos = $result->errorInfo(); + throw new Kohana_Database_Exception('database.error', $arr_infos[2]); + } + + if (preg_match('/^SELECT|PRAGMA|EXPLAIN/i', $sql)) + { + $this->result = $result; + $this->current_row = 0; + + $this->total_rows = $this->sqlite_row_count(); + + $this->fetch_type = ($object === TRUE) ? PDO::FETCH_OBJ : PDO::FETCH_ASSOC; + } + elseif (preg_match('/^DELETE|INSERT|UPDATE/i', $sql)) + { + $this->insert_id = $link->lastInsertId(); + + $this->total_rows = $result->rowCount(); + } + } + else + { + // SQL error + throw new Kohana_Database_Exception('database.error', $link->errorInfo().' - '.$sql); + } + + // Set result type + $this->result($object); + + // Store the SQL + $this->sql = $sql; + } + + private function sqlite_row_count() + { + $count = 0; + while ($this->result->fetch()) + { + $count++; + } + + // The query must be re-fetched now. + $this->result->execute(); + + return $count; + } + + /* + * Destructor: __destruct + * Magic __destruct function, frees the result. + */ + public function __destruct() + { + if (is_object($this->result)) + { + $this->result->closeCursor(); + $this->result = NULL; + } + } + + public function result($object = TRUE, $type = PDO::FETCH_BOTH) + { + $this->fetch_type = (bool) $object ? PDO::FETCH_OBJ : PDO::FETCH_BOTH; + + if ($this->fetch_type == PDO::FETCH_OBJ) + { + $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $this->return_type = $type; + } + + return $this; + } + + public function as_array($object = NULL, $type = PDO::FETCH_ASSOC) + { + return $this->result_array($object, $type); + } + + public function result_array($object = NULL, $type = PDO::FETCH_ASSOC) + { + $rows = array(); + + if (is_string($object)) + { + $fetch = $object; + } + elseif (is_bool($object)) + { + if ($object === TRUE) + { + $fetch = PDO::FETCH_OBJ; + + // NOTE - The class set by $type must be defined before fetching the result, + // autoloading is disabled to save a lot of stupid overhead. + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $fetch = PDO::FETCH_OBJ; + } + } + else + { + // Use the default config values + $fetch = $this->fetch_type; + + if ($fetch == PDO::FETCH_OBJ) + { + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + } + try + { + while ($row = $this->result->fetch($fetch)) + { + $rows[] = $row; + } + } + catch(PDOException $e) + { + throw new Kohana_Database_Exception('database.error', $e->getMessage()); + return FALSE; + } + return $rows; + } + + public function list_fields() + { + $field_names = array(); + for ($i = 0, $max = $this->result->columnCount(); $i < $max; $i++) + { + $info = $this->result->getColumnMeta($i); + $field_names[] = $info['name']; + } + return $field_names; + } + + public function seek($offset) + { + // To request a scrollable cursor for your PDOStatement object, you must + // set the PDO::ATTR_CURSOR attribute to PDO::CURSOR_SCROLL when you + // prepare the statement. + Kohana::log('error', get_class($this).' does not support scrollable cursors, '.__FUNCTION__.' call ignored'); + + return FALSE; + } + + public function offsetGet($offset) + { + try + { + return $this->result->fetch($this->fetch_type, PDO::FETCH_ORI_ABS, $offset); + } + catch(PDOException $e) + { + throw new Kohana_Database_Exception('database.error', $e->getMessage()); + } + } + + public function rewind() + { + // Same problem that seek() has, see above. + return $this->seek(0); + } + +} // End PdoSqlite_Result Class \ No newline at end of file diff --git a/system/libraries/drivers/Database/Pgsql.php b/system/libraries/drivers/Database/Pgsql.php new file mode 100644 index 00000000..c53c8439 --- /dev/null +++ b/system/libraries/drivers/Database/Pgsql.php @@ -0,0 +1,538 @@ +db_config = $config; + + Kohana::log('debug', 'PgSQL Database Driver Initialized'); + } + + public function connect() + { + // Check if link already exists + if (is_resource($this->link)) + return $this->link; + + // Import the connect variables + extract($this->db_config['connection']); + + // Persistent connections enabled? + $connect = ($this->db_config['persistent'] == TRUE) ? 'pg_pconnect' : 'pg_connect'; + + // Build the connection info + $port = isset($port) ? 'port=\''.$port.'\'' : ''; + $host = isset($host) ? 'host=\''.$host.'\' '.$port : ''; // if no host, connect with the socket + + $connection_string = $host.' dbname=\''.$database.'\' user=\''.$user.'\' password=\''.$pass.'\''; + // Make the connection and select the database + if ($this->link = $connect($connection_string)) + { + if ($charset = $this->db_config['character_set']) + { + echo $this->set_charset($charset); + } + + // Clear password after successful connect + $this->db_config['connection']['pass'] = NULL; + + return $this->link; + } + + return FALSE; + } + + public function query($sql) + { + // Only cache if it's turned on, and only cache if it's not a write statement + if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|SET)\b#i', $sql)) + { + $hash = $this->query_hash($sql); + + if ( ! isset($this->query_cache[$hash])) + { + // Set the cached object + $this->query_cache[$hash] = new Pgsql_Result(pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql); + } + else + { + // Rewind cached result + $this->query_cache[$hash]->rewind(); + } + + return $this->query_cache[$hash]; + } + + // Suppress warning triggered when a database error occurs (e.g., a constraint violation) + return new Pgsql_Result(@pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql); + } + + public function set_charset($charset) + { + $this->query('SET client_encoding TO '.pg_escape_string($this->link, $charset)); + } + + public function escape_table($table) + { + if (!$this->db_config['escape']) + return $table; + + return '"'.str_replace('.', '"."', $table).'"'; + } + + public function escape_column($column) + { + if (!$this->db_config['escape']) + return $column; + + if ($column == '*') + return $column; + + // This matches any functions we support to SELECT. + if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches)) + { + if ( count($matches) == 3) + { + return $matches[1].'('.$this->escape_column($matches[2]).')'; + } + else if ( count($matches) == 5) + { + return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]); + } + } + + // This matches any modifiers we support to SELECT. + if ( ! preg_match('/\b(?:all|distinct)\s/i', $column)) + { + if (stripos($column, ' AS ') !== FALSE) + { + // Force 'AS' to uppercase + $column = str_ireplace(' AS ', ' AS ', $column); + + // Runs escape_column on both sides of an AS statement + $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column)); + + // Re-create the AS statement + return implode(' AS ', $column); + } + + return preg_replace('/[^.*]+/', '"$0"', $column); + } + + $parts = explode(' ', $column); + $column = ''; + + for ($i = 0, $c = count($parts); $i < $c; $i++) + { + // The column is always last + if ($i == ($c - 1)) + { + $column .= preg_replace('/[^.*]+/', '"$0"', $parts[$i]); + } + else // otherwise, it's a modifier + { + $column .= $parts[$i].' '; + } + } + return $column; + } + + public function regex($field, $match, $type, $num_regexs) + { + $prefix = ($num_regexs == 0) ? '' : $type; + + return $prefix.' '.$this->escape_column($field).' ~* \''.$this->escape_str($match).'\''; + } + + public function notregex($field, $match, $type, $num_regexs) + { + $prefix = $num_regexs == 0 ? '' : $type; + + return $prefix.' '.$this->escape_column($field).' !~* \''.$this->escape_str($match) . '\''; + } + + public function limit($limit, $offset = 0) + { + return 'LIMIT '.$limit.' OFFSET '.$offset; + } + + public function compile_select($database) + { + $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT '; + $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*'; + + if (count($database['from']) > 0) + { + $sql .= "\nFROM "; + $sql .= implode(', ', $database['from']); + } + + if (count($database['join']) > 0) + { + foreach($database['join'] AS $join) + { + $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions']; + } + } + + if (count($database['where']) > 0) + { + $sql .= "\nWHERE "; + } + + $sql .= implode("\n", $database['where']); + + if (count($database['groupby']) > 0) + { + $sql .= "\nGROUP BY "; + $sql .= implode(', ', $database['groupby']); + } + + if (count($database['having']) > 0) + { + $sql .= "\nHAVING "; + $sql .= implode("\n", $database['having']); + } + + if (count($database['orderby']) > 0) + { + $sql .= "\nORDER BY "; + $sql .= implode(', ', $database['orderby']); + } + + if (is_numeric($database['limit'])) + { + $sql .= "\n"; + $sql .= $this->limit($database['limit'], $database['offset']); + } + + return $sql; + } + + public function escape_str($str) + { + if (!$this->db_config['escape']) + return $str; + + is_resource($this->link) or $this->connect(); + + return pg_escape_string($this->link, $str); + } + + public function list_tables() + { + $sql = 'SELECT table_schema || \'.\' || table_name FROM information_schema.tables WHERE table_schema NOT IN (\'pg_catalog\', \'information_schema\')'; + $result = $this->query($sql)->result(FALSE, PGSQL_ASSOC); + + $retval = array(); + foreach ($result as $row) + { + $retval[] = current($row); + } + + return $retval; + } + + public function show_error() + { + return pg_last_error($this->link); + } + + public function list_fields($table) + { + $result = NULL; + + foreach ($this->field_data($table) as $row) + { + // Make an associative array + $result[$row->column_name] = $this->sql_type($row->data_type); + + if (!strncmp($row->column_default, 'nextval(', 8)) + { + $result[$row->column_name]['sequenced'] = TRUE; + } + + if ($row->is_nullable === 'YES') + { + $result[$row->column_name]['null'] = TRUE; + } + } + + if (!isset($result)) + throw new Kohana_Database_Exception('database.table_not_found', $table); + + return $result; + } + + public function field_data($table) + { + // http://www.postgresql.org/docs/8.3/static/infoschema-columns.html + $result = $this->query(' + SELECT column_name, column_default, is_nullable, data_type, udt_name, + character_maximum_length, numeric_precision, numeric_precision_radix, numeric_scale + FROM information_schema.columns + WHERE table_name = \''. $this->escape_str($table) .'\' + ORDER BY ordinal_position + '); + + return $result->result_array(TRUE); + } + +} // End Database_Pgsql_Driver Class + +/** + * PostgreSQL Result + */ +class Pgsql_Result extends Database_Result { + + // Data fetching types + protected $fetch_type = 'pgsql_fetch_object'; + protected $return_type = PGSQL_ASSOC; + + /** + * Sets up the result variables. + * + * @param resource query result + * @param resource database link + * @param boolean return objects or arrays + * @param string SQL query that was run + */ + public function __construct($result, $link, $object = TRUE, $sql) + { + $this->link = $link; + $this->result = $result; + + // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query + if (is_resource($result)) + { + // Its an DELETE, INSERT, REPLACE, or UPDATE query + if (preg_match('/^(?:delete|insert|replace|update)\b/iD', trim($sql), $matches)) + { + $this->insert_id = (strtolower($matches[0]) == 'insert') ? $this->insert_id() : FALSE; + $this->total_rows = pg_affected_rows($this->result); + } + else + { + $this->current_row = 0; + $this->total_rows = pg_num_rows($this->result); + $this->fetch_type = ($object === TRUE) ? 'pg_fetch_object' : 'pg_fetch_array'; + } + } + else + { + throw new Kohana_Database_Exception('database.error', pg_last_error().' - '.$sql); + } + + // Set result type + $this->result($object); + + // Store the SQL + $this->sql = $sql; + } + + /** + * Magic __destruct function, frees the result. + */ + public function __destruct() + { + if (is_resource($this->result)) + { + pg_free_result($this->result); + } + } + + public function result($object = TRUE, $type = PGSQL_ASSOC) + { + $this->fetch_type = ((bool) $object) ? 'pg_fetch_object' : 'pg_fetch_array'; + + // This check has to be outside the previous statement, because we do not + // know the state of fetch_type when $object = NULL + // NOTE - The class set by $type must be defined before fetching the result, + // autoloading is disabled to save a lot of stupid overhead. + if ($this->fetch_type == 'pg_fetch_object') + { + $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $this->return_type = $type; + } + + return $this; + } + + public function as_array($object = NULL, $type = PGSQL_ASSOC) + { + return $this->result_array($object, $type); + } + + public function result_array($object = NULL, $type = PGSQL_ASSOC) + { + $rows = array(); + + if (is_string($object)) + { + $fetch = $object; + } + elseif (is_bool($object)) + { + if ($object === TRUE) + { + $fetch = 'pg_fetch_object'; + + // NOTE - The class set by $type must be defined before fetching the result, + // autoloading is disabled to save a lot of stupid overhead. + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $fetch = 'pg_fetch_array'; + } + } + else + { + // Use the default config values + $fetch = $this->fetch_type; + + if ($fetch == 'pg_fetch_object') + { + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + } + + if ($this->total_rows) + { + pg_result_seek($this->result, 0); + + while ($row = $fetch($this->result, NULL, $type)) + { + $rows[] = $row; + } + } + + return $rows; + } + + public function insert_id() + { + if ($this->insert_id === NULL) + { + $query = 'SELECT LASTVAL() AS insert_id'; + + // Disable error reporting for this, just to silence errors on + // tables that have no serial column. + $ER = error_reporting(0); + + $result = pg_query($this->link, $query); + $insert_id = pg_fetch_array($result, NULL, PGSQL_ASSOC); + + $this->insert_id = $insert_id['insert_id']; + + // Reset error reporting + error_reporting($ER); + } + + return $this->insert_id; + } + + public function seek($offset) + { + if ($this->offsetExists($offset) AND pg_result_seek($this->result, $offset)) + { + // Set the current row to the offset + $this->current_row = $offset; + + return TRUE; + } + + return FALSE; + } + + public function list_fields() + { + $field_names = array(); + + $fields = pg_num_fields($this->result); + for ($i = 0; $i < $fields; ++$i) + { + $field_names[] = pg_field_name($this->result, $i); + } + + return $field_names; + } + + /** + * ArrayAccess: offsetGet + */ + public function offsetGet($offset) + { + if ( ! $this->seek($offset)) + return FALSE; + + // Return the row by calling the defined fetching callback + $fetch = $this->fetch_type; + return $fetch($this->result, NULL, $this->return_type); + } + +} // End Pgsql_Result Class + +/** + * PostgreSQL Prepared Statement (experimental) + */ +class Kohana_Pgsql_Statement { + + protected $link = NULL; + protected $stmt; + + public function __construct($sql, $link) + { + $this->link = $link; + + $this->stmt = $this->link->prepare($sql); + + return $this; + } + + public function __destruct() + { + $this->stmt->close(); + } + + // Sets the bind parameters + public function bind_params() + { + $argv = func_get_args(); + return $this; + } + + // sets the statement values to the bound parameters + public function set_vals() + { + return $this; + } + + // Runs the statement + public function execute() + { + return $this; + } +} diff --git a/system/libraries/drivers/Image.php b/system/libraries/drivers/Image.php new file mode 100644 index 00000000..f89ba953 --- /dev/null +++ b/system/libraries/drivers/Image.php @@ -0,0 +1,156 @@ + $args) + { + if ( ! $this->$func($args)) + return FALSE; + } + + return TRUE; + } + + /** + * Sanitize and normalize a geometry array based on the temporary image + * width and height. Valid properties are: width, height, top, left. + * + * @param array geometry properties + * @return void + */ + protected function sanitize_geometry( & $geometry) + { + list($width, $height) = $this->properties(); + + // Turn off error reporting + $reporting = error_reporting(0); + + // Width and height cannot exceed current image size + $geometry['width'] = min($geometry['width'], $width); + $geometry['height'] = min($geometry['height'], $height); + + // Set standard coordinates if given, otherwise use pixel values + if ($geometry['top'] === 'center') + { + $geometry['top'] = floor(($height / 2) - ($geometry['height'] / 2)); + } + elseif ($geometry['top'] === 'top') + { + $geometry['top'] = 0; + } + elseif ($geometry['top'] === 'bottom') + { + $geometry['top'] = $height - $geometry['height']; + } + + // Set standard coordinates if given, otherwise use pixel values + if ($geometry['left'] === 'center') + { + $geometry['left'] = floor(($width / 2) - ($geometry['width'] / 2)); + } + elseif ($geometry['left'] === 'left') + { + $geometry['left'] = 0; + } + elseif ($geometry['left'] === 'right') + { + $geometry['left'] = $width - $geometry['height']; + } + + // Restore error reporting + error_reporting($reporting); + } + + /** + * Return the current width and height of the temporary image. This is mainly + * needed for sanitizing the geometry. + * + * @return array width, height + */ + abstract protected function properties(); + + /** + * Process an image with a set of actions. + * + * @param string image filename + * @param array actions to execute + * @param string destination directory path + * @param string destination filename + * @return boolean + */ + abstract public function process($image, $actions, $dir, $file); + + /** + * Flip an image. Valid directions are horizontal and vertical. + * + * @param integer direction to flip + * @return boolean + */ + abstract function flip($direction); + + /** + * Crop an image. Valid properties are: width, height, top, left. + * + * @param array new properties + * @return boolean + */ + abstract function crop($properties); + + /** + * Resize an image. Valid properties are: width, height, and master. + * + * @param array new properties + * @return boolean + */ + abstract public function resize($properties); + + /** + * Rotate an image. Valid amounts are -180 to 180. + * + * @param integer amount to rotate + * @return boolean + */ + abstract public function rotate($amount); + + /** + * Sharpen and image. Valid amounts are 1 to 100. + * + * @param integer amount to sharpen + * @return boolean + */ + abstract public function sharpen($amount); + + /** + * Overlay a second image. Valid properties are: overlay_file, mime, x, y and transparency. + * + * @return boolean + */ + abstract public function composite($properties); + +} // End Image Driver \ No newline at end of file diff --git a/system/libraries/drivers/Image/GD.php b/system/libraries/drivers/Image/GD.php new file mode 100644 index 00000000..be2af4e2 --- /dev/null +++ b/system/libraries/drivers/Image/GD.php @@ -0,0 +1,401 @@ +image = $image; + + // Create the GD image resource + $this->tmp_image = $create($image['file']); + + // Get the quality setting from the actions + $quality = arr::remove('quality', $actions); + + if ($status = $this->execute($actions)) + { + // Prevent the alpha from being lost + imagealphablending($this->tmp_image, TRUE); + imagesavealpha($this->tmp_image, TRUE); + + switch ($save) + { + case 'imagejpeg': + // Default the quality to 95 + ($quality === NULL) and $quality = 95; + break; + case 'imagegif': + // Remove the quality setting, GIF doesn't use it + unset($quality); + break; + case 'imagepng': + // Always use a compression level of 9 for PNGs. This does not + // affect quality, it only increases the level of compression! + $quality = 9; + break; + } + + if ($render === FALSE) + { + // Set the status to the save return value, saving with the quality requested + $status = isset($quality) ? $save($this->tmp_image, $dir.$file, $quality) : $save($this->tmp_image, $dir.$file); + } + else + { + // Output the image directly to the browser + switch ($save) + { + case 'imagejpeg': + header('Content-Type: image/jpeg'); + break; + case 'imagegif': + header('Content-Type: image/gif'); + break; + case 'imagepng': + header('Content-Type: image/png'); + break; + } + + $status = isset($quality) ? $save($this->tmp_image, NULL, $quality) : $save($this->tmp_image); + } + + // Destroy the temporary image + imagedestroy($this->tmp_image); + } + + return $status; + } + + public function flip($direction) + { + // Get the current width and height + $width = imagesx($this->tmp_image); + $height = imagesy($this->tmp_image); + + // Create the flipped image + $flipped = $this->imagecreatetransparent($width, $height); + + if ($direction === Image::HORIZONTAL) + { + for ($x = 0; $x < $width; $x++) + { + $status = imagecopy($flipped, $this->tmp_image, $x, 0, $width - $x - 1, 0, 1, $height); + } + } + elseif ($direction === Image::VERTICAL) + { + for ($y = 0; $y < $height; $y++) + { + $status = imagecopy($flipped, $this->tmp_image, 0, $y, 0, $height - $y - 1, $width, 1); + } + } + else + { + // Do nothing + return TRUE; + } + + if ($status === TRUE) + { + // Swap the new image for the old one + imagedestroy($this->tmp_image); + $this->tmp_image = $flipped; + } + + return $status; + } + + public function crop($properties) + { + // Sanitize the cropping settings + $this->sanitize_geometry($properties); + + // Get the current width and height + $width = imagesx($this->tmp_image); + $height = imagesy($this->tmp_image); + + // Create the temporary image to copy to + $img = $this->imagecreatetransparent($properties['width'], $properties['height']); + + // Execute the crop + if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, $properties['left'], $properties['top'], $width, $height, $width, $height)) + { + // Swap the new image for the old one + imagedestroy($this->tmp_image); + $this->tmp_image = $img; + } + + return $status; + } + + public function resize($properties) + { + // Get the current width and height + $width = imagesx($this->tmp_image); + $height = imagesy($this->tmp_image); + + if (substr($properties['width'], -1) === '%') + { + // Recalculate the percentage to a pixel size + $properties['width'] = round($width * (substr($properties['width'], 0, -1) / 100)); + } + + if (substr($properties['height'], -1) === '%') + { + // Recalculate the percentage to a pixel size + $properties['height'] = round($height * (substr($properties['height'], 0, -1) / 100)); + } + + // Recalculate the width and height, if they are missing + empty($properties['width']) and $properties['width'] = round($width * $properties['height'] / $height); + empty($properties['height']) and $properties['height'] = round($height * $properties['width'] / $width); + + if ($properties['master'] === Image::AUTO) + { + // Change an automatic master dim to the correct type + $properties['master'] = (($width / $properties['width']) > ($height / $properties['height'])) ? Image::WIDTH : Image::HEIGHT; + } + + if (empty($properties['height']) OR $properties['master'] === Image::WIDTH) + { + // Recalculate the height based on the width + $properties['height'] = round($height * $properties['width'] / $width); + } + + if (empty($properties['width']) OR $properties['master'] === Image::HEIGHT) + { + // Recalculate the width based on the height + $properties['width'] = round($width * $properties['height'] / $height); + } + + // Test if we can do a resize without resampling to speed up the final resize + if ($properties['width'] > $width / 2 AND $properties['height'] > $height / 2) + { + // Presize width and height + $pre_width = $width; + $pre_height = $height; + + // The maximum reduction is 10% greater than the final size + $max_reduction_width = round($properties['width'] * 1.1); + $max_reduction_height = round($properties['height'] * 1.1); + + // Reduce the size using an O(2n) algorithm, until it reaches the maximum reduction + while ($pre_width / 2 > $max_reduction_width AND $pre_height / 2 > $max_reduction_height) + { + $pre_width /= 2; + $pre_height /= 2; + } + + // Create the temporary image to copy to + $img = $this->imagecreatetransparent($pre_width, $pre_height); + + if ($status = imagecopyresized($img, $this->tmp_image, 0, 0, 0, 0, $pre_width, $pre_height, $width, $height)) + { + // Swap the new image for the old one + imagedestroy($this->tmp_image); + $this->tmp_image = $img; + } + + // Set the width and height to the presize + $width = $pre_width; + $height = $pre_height; + } + + // Create the temporary image to copy to + $img = $this->imagecreatetransparent($properties['width'], $properties['height']); + + // Execute the resize + if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, 0, 0, $properties['width'], $properties['height'], $width, $height)) + { + // Swap the new image for the old one + imagedestroy($this->tmp_image); + $this->tmp_image = $img; + } + + return $status; + } + + public function rotate($amount) + { + // Use current image to rotate + $img = $this->tmp_image; + + // White, with an alpha of 0 + $transparent = imagecolorallocatealpha($img, 255, 255, 255, 127); + + // Rotate, setting the transparent color + $img = imagerotate($img, 360 - $amount, $transparent, -1); + + // Fill the background with the transparent "color" + imagecolortransparent($img, $transparent); + + // Merge the images + if ($status = imagecopymerge($this->tmp_image, $img, 0, 0, 0, 0, imagesx($this->tmp_image), imagesy($this->tmp_image), 100)) + { + // Prevent the alpha from being lost + imagealphablending($img, TRUE); + imagesavealpha($img, TRUE); + + // Swap the new image for the old one + imagedestroy($this->tmp_image); + $this->tmp_image = $img; + } + + return $status; + } + + public function sharpen($amount) + { + // Make sure that the sharpening function is available + if ( ! function_exists('imageconvolution')) + throw new Kohana_Exception('image.unsupported_method', __FUNCTION__); + + // Amount should be in the range of 18-10 + $amount = round(abs(-18 + ($amount * 0.08)), 2); + + // Gaussian blur matrix + $matrix = array + ( + array(-1, -1, -1), + array(-1, $amount, -1), + array(-1, -1, -1), + ); + + // Perform the sharpen + return imageconvolution($this->tmp_image, $matrix, $amount - 8, 0); + } + + public function composite($properties) + { + switch($properties['mime']) + { + case "image/jpeg": + $overlay_img = imagecreatefromjpeg($properties['overlay_file']); + break; + + case "image/gif": + $overlay_img = imagecreatefromgif($properties['overlay_file']); + break; + + case "image/png": + $overlay_img = imagecreatefrompng($properties['overlay_file']); + break; + } + + imagecopymerge($this->tmp_image, $overlay_img, $properties['x'], $properties['y'], 0, 0, imagesx($overlay_img), imagesy($overlay_img), $properties['transparency']); + imagedestroy($overlay_img); + return TRUE; + } + + protected function properties() + { + return array(imagesx($this->tmp_image), imagesy($this->tmp_image)); + } + + /** + * Returns an image with a transparent background. Used for rotating to + * prevent unfilled backgrounds. + * + * @param integer image width + * @param integer image height + * @return resource + */ + protected function imagecreatetransparent($width, $height) + { + if (self::$blank_png === NULL) + { + // Decode the blank PNG if it has not been done already + self::$blank_png = imagecreatefromstring(base64_decode + ( + 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29'. + 'mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADqSURBVHjaYvz//z/DYAYAAcTEMMgBQAANegcCBN'. + 'CgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQ'. + 'AANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoH'. + 'AgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB'. + '3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAgAEAMpcDTTQWJVEAAAAASUVORK5CYII=' + )); + + // Set the blank PNG width and height + self::$blank_png_width = imagesx(self::$blank_png); + self::$blank_png_height = imagesy(self::$blank_png); + } + + $img = imagecreatetruecolor($width, $height); + + // Resize the blank image + imagecopyresized($img, self::$blank_png, 0, 0, 0, 0, $width, $height, self::$blank_png_width, self::$blank_png_height); + + // Prevent the alpha from being lost + imagealphablending($img, FALSE); + imagesavealpha($img, TRUE); + + return $img; + } + +} // End Image GD Driver \ No newline at end of file diff --git a/system/libraries/drivers/Image/GraphicsMagick.php b/system/libraries/drivers/Image/GraphicsMagick.php new file mode 100644 index 00000000..a8bc4d9b --- /dev/null +++ b/system/libraries/drivers/Image/GraphicsMagick.php @@ -0,0 +1,221 @@ +ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : ''; + + // Check to make sure the provided path is correct + if ( ! is_file(realpath($config['directory']).'/gm'.$this->ext)) + throw new Kohana_Exception('image.graphicsmagick.not_found', 'gm'.$this->ext); + + + // Set the installation directory + $this->dir = str_replace('\\', '/', realpath($config['directory'])).'/'; + } + + /** + * Creates a temporary image and executes the given actions. By creating a + * temporary copy of the image before manipulating it, this process is atomic. + */ + public function process($image, $actions, $dir, $file, $render = FALSE) + { + // We only need the filename + $image = $image['file']; + + // Unique temporary filename + $this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.')); + + // Copy the image to the temporary file + copy($image, $this->tmp_image); + + // Quality change is done last + $quality = (int) arr::remove('quality', $actions); + + // Use 95 for the default quality + empty($quality) and $quality = 95; + + // All calls to these will need to be escaped, so do it now + $this->cmd_image = escapeshellarg($this->tmp_image); + $this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file); + + if ($status = $this->execute($actions)) + { + // Use convert to change the image into its final version. This is + // done to allow the file type to change correctly, and to handle + // the quality conversion in the most effective way possible. + if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image)) + { + $this->errors[] = $error; + } + else + { + // Output the image directly to the browser + if ($render !== FALSE) + { + $contents = file_get_contents($this->tmp_image); + switch (substr($file, strrpos($file, '.') + 1)) + { + case 'jpg': + case 'jpeg': + header('Content-Type: image/jpeg'); + break; + case 'gif': + header('Content-Type: image/gif'); + break; + case 'png': + header('Content-Type: image/png'); + break; + } + echo $contents; + } + } + } + + // Remove the temporary image + unlink($this->tmp_image); + $this->tmp_image = ''; + + return $status; + } + + public function crop($prop) + { + // Sanitize and normalize the properties into geometry + $this->sanitize_geometry($prop); + + // Set the IM geometry based on the properties + $geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']); + + if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function flip($dir) + { + // Convert the direction into a GM command + $dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip'; + + if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function resize($prop) + { + switch ($prop['master']) + { + case Image::WIDTH: // Wx + $dim = escapeshellarg($prop['width'].'x'); + break; + case Image::HEIGHT: // xH + $dim = escapeshellarg('x'.$prop['height']); + break; + case Image::AUTO: // WxH + $dim = escapeshellarg($prop['width'].'x'.$prop['height']); + break; + case Image::NONE: // WxH! + $dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!'); + break; + } + + // Use "convert" to change the width and height + if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function rotate($amt) + { + if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function sharpen($amount) + { + // Set the sigma, radius, and amount. The amount formula allows a nice + // spread between 1 and 100 without pixelizing the image badly. + $sigma = 0.5; + $radius = $sigma * 2; + $amount = round(($amount / 80) * 3.14, 2); + + // Convert the amount to an GM command + $sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0'); + + if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function composite($properties) + { + if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' composite').' -geometry ' . escapeshellarg('+'.$properties['x'].'+'.$properties['y']).' -dissolve '.escapeshellarg($properties['transparency']).' '.escapeshellarg($properties['overlay_file']).' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + return TRUE; + } + + protected function properties() + { + return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE); + } + +} // End Image GraphicsMagick Driver \ No newline at end of file diff --git a/system/libraries/drivers/Image/ImageMagick.php b/system/libraries/drivers/Image/ImageMagick.php new file mode 100644 index 00000000..4b381fd6 --- /dev/null +++ b/system/libraries/drivers/Image/ImageMagick.php @@ -0,0 +1,222 @@ +ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : ''; + + // Check to make sure the provided path is correct + if ( ! is_file(realpath($config['directory']).'/convert'.$this->ext)) + throw new Kohana_Exception('image.imagemagick.not_found', 'convert'.$this->ext); + + // Set the installation directory + $this->dir = str_replace('\\', '/', realpath($config['directory'])).'/'; + } + + /** + * Creates a temporary image and executes the given actions. By creating a + * temporary copy of the image before manipulating it, this process is atomic. + */ + public function process($image, $actions, $dir, $file, $render = FALSE) + { + // We only need the filename + $image = $image['file']; + + // Unique temporary filename + $this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.')); + + // Copy the image to the temporary file + copy($image, $this->tmp_image); + + // Quality change is done last + $quality = (int) arr::remove('quality', $actions); + + // Use 95 for the default quality + empty($quality) and $quality = 95; + + // All calls to these will need to be escaped, so do it now + $this->cmd_image = escapeshellarg($this->tmp_image); + $this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file); + + if ($status = $this->execute($actions)) + { + // Use convert to change the image into its final version. This is + // done to allow the file type to change correctly, and to handle + // the quality conversion in the most effective way possible. + if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image)) + { + $this->errors[] = $error; + } + else + { + // Output the image directly to the browser + if ($render !== FALSE) + { + $contents = file_get_contents($this->tmp_image); + switch (substr($file, strrpos($file, '.') + 1)) + { + case 'jpg': + case 'jpeg': + header('Content-Type: image/jpeg'); + break; + case 'gif': + header('Content-Type: image/gif'); + break; + case 'png': + header('Content-Type: image/png'); + break; + } + echo $contents; + } + } + } + + // Remove the temporary image + unlink($this->tmp_image); + $this->tmp_image = ''; + + return $status; + } + + public function crop($prop) + { + // Sanitize and normalize the properties into geometry + $this->sanitize_geometry($prop); + + // Set the IM geometry based on the properties + $geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']); + + if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function flip($dir) + { + // Convert the direction into a IM command + $dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip'; + + if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function resize($prop) + { + switch ($prop['master']) + { + case Image::WIDTH: // Wx + $dim = escapeshellarg($prop['width'].'x'); + break; + case Image::HEIGHT: // xH + $dim = escapeshellarg('x'.$prop['height']); + break; + case Image::AUTO: // WxH + $dim = escapeshellarg($prop['width'].'x'.$prop['height']); + break; + case Image::NONE: // WxH! + $dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!'); + break; + } + + // Use "convert" to change the width and height + if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function rotate($amt) + { + if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function sharpen($amount) + { + // Set the sigma, radius, and amount. The amount formula allows a nice + // spread between 1 and 100 without pixelizing the image badly. + $sigma = 0.5; + $radius = $sigma * 2; + $amount = round(($amount / 80) * 3.14, 2); + + // Convert the amount to an IM command + $sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0'); + + if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + + return TRUE; + } + + public function composite($properties) + { + if ($error = exec(escapeshellcmd($this->dir.'composite'.$this->ext).' -geometry ' . escapeshellarg('+'.$properties['x'].'+'.$properties['y']).' -dissolve '.escapeshellarg($properties['transparency']).' '.escapeshellarg($properties['overlay_file']).' '.$this->cmd_image.' '.$this->cmd_image)) + { + $this->errors[] = $error; + return FALSE; + } + return TRUE; + } + + protected function properties() + { + return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE); + } + +} // End Image ImageMagick Driver \ No newline at end of file diff --git a/system/libraries/drivers/Session.php b/system/libraries/drivers/Session.php new file mode 100644 index 00000000..fb58c8d3 --- /dev/null +++ b/system/libraries/drivers/Session.php @@ -0,0 +1,70 @@ + 'apc', + * 'requests' => 10000 + * ); + * Lifetime does not need to be set as it is + * overridden by the session expiration setting. + * + * $Id: Cache.php 3769 2008-12-15 00:48:56Z zombor $ + * + * @package Core + * @author Kohana Team + * @copyright (c) 2007-2008 Kohana Team + * @license http://kohanaphp.com/license.html + */ +class Session_Cache_Driver implements Session_Driver { + + protected $cache; + protected $encrypt; + + public function __construct() + { + // Load Encrypt library + if (Kohana::config('session.encryption')) + { + $this->encrypt = new Encrypt; + } + + Kohana::log('debug', 'Session Cache Driver Initialized'); + } + + public function open($path, $name) + { + $config = Kohana::config('session.storage'); + + if (empty($config)) + { + // Load the default group + $config = Kohana::config('cache.default'); + } + elseif (is_string($config)) + { + $name = $config; + + // Test the config group name + if (($config = Kohana::config('cache.'.$config)) === NULL) + throw new Kohana_Exception('cache.undefined_group', $name); + } + + $config['lifetime'] = (Kohana::config('session.expiration') == 0) ? 86400 : Kohana::config('session.expiration'); + $this->cache = new Cache($config); + + return is_object($this->cache); + } + + public function close() + { + return TRUE; + } + + public function read($id) + { + $id = 'session_'.$id; + if ($data = $this->cache->get($id)) + { + return Kohana::config('session.encryption') ? $this->encrypt->decode($data) : $data; + } + + // Return value must be string, NOT a boolean + return ''; + } + + public function write($id, $data) + { + $id = 'session_'.$id; + $data = Kohana::config('session.encryption') ? $this->encrypt->encode($data) : $data; + + return $this->cache->set($id, $data); + } + + public function destroy($id) + { + $id = 'session_'.$id; + return $this->cache->delete($id); + } + + public function regenerate() + { + session_regenerate_id(TRUE); + + // Return new session id + return session_id(); + } + + public function gc($maxlifetime) + { + // Just return, caches are automatically cleaned up + return TRUE; + } + +} // End Session Cache Driver diff --git a/system/libraries/drivers/Session/Cookie.php b/system/libraries/drivers/Session/Cookie.php new file mode 100644 index 00000000..7b791064 --- /dev/null +++ b/system/libraries/drivers/Session/Cookie.php @@ -0,0 +1,80 @@ +cookie_name = Kohana::config('session.name').'_data'; + + if (Kohana::config('session.encryption')) + { + $this->encrypt = Encrypt::instance(); + } + + Kohana::log('debug', 'Session Cookie Driver Initialized'); + } + + public function open($path, $name) + { + return TRUE; + } + + public function close() + { + return TRUE; + } + + public function read($id) + { + $data = (string) cookie::get($this->cookie_name); + + if ($data == '') + return $data; + + return empty($this->encrypt) ? base64_decode($data) : $this->encrypt->decode($data); + } + + public function write($id, $data) + { + $data = empty($this->encrypt) ? base64_encode($data) : $this->encrypt->encode($data); + + if (strlen($data) > 4048) + { + Kohana::log('error', 'Session ('.$id.') data exceeds the 4KB limit, ignoring write.'); + return FALSE; + } + + return cookie::set($this->cookie_name, $data, Kohana::config('session.expiration')); + } + + public function destroy($id) + { + return cookie::delete($this->cookie_name); + } + + public function regenerate() + { + session_regenerate_id(TRUE); + + // Return new id + return session_id(); + } + + public function gc($maxlifetime) + { + return TRUE; + } + +} // End Session Cookie Driver Class \ No newline at end of file diff --git a/system/libraries/drivers/Session/Database.php b/system/libraries/drivers/Session/Database.php new file mode 100644 index 00000000..b4144ffb --- /dev/null +++ b/system/libraries/drivers/Session/Database.php @@ -0,0 +1,163 @@ +encrypt = Encrypt::instance(); + } + + if (is_array($config['storage'])) + { + if ( ! empty($config['storage']['group'])) + { + // Set the group name + $this->db = $config['storage']['group']; + } + + if ( ! empty($config['storage']['table'])) + { + // Set the table name + $this->table = $config['storage']['table']; + } + } + + // Load database + $this->db = Database::instance($this->db); + + Kohana::log('debug', 'Session Database Driver Initialized'); + } + + public function open($path, $name) + { + return TRUE; + } + + public function close() + { + return TRUE; + } + + public function read($id) + { + // Load the session + $query = $this->db->from($this->table)->where('session_id', $id)->limit(1)->get()->result(TRUE); + + if ($query->count() === 0) + { + // No current session + $this->session_id = NULL; + + return ''; + } + + // Set the current session id + $this->session_id = $id; + + // Load the data + $data = $query->current()->data; + + return ($this->encrypt === NULL) ? base64_decode($data) : $this->encrypt->decode($data); + } + + public function write($id, $data) + { + $data = array + ( + 'session_id' => $id, + 'last_activity' => time(), + 'data' => ($this->encrypt === NULL) ? base64_encode($data) : $this->encrypt->encode($data) + ); + + if ($this->session_id === NULL) + { + // Insert a new session + $query = $this->db->insert($this->table, $data); + } + elseif ($id === $this->session_id) + { + // Do not update the session_id + unset($data['session_id']); + + // Update the existing session + $query = $this->db->update($this->table, $data, array('session_id' => $id)); + } + else + { + // Update the session and id + $query = $this->db->update($this->table, $data, array('session_id' => $this->session_id)); + + // Set the new session id + $this->session_id = $id; + } + + return (bool) $query->count(); + } + + public function destroy($id) + { + // Delete the requested session + $this->db->delete($this->table, array('session_id' => $id)); + + // Session id is no longer valid + $this->session_id = NULL; + + return TRUE; + } + + public function regenerate() + { + // Generate a new session id + session_regenerate_id(); + + // Return new session id + return session_id(); + } + + public function gc($maxlifetime) + { + // Delete all expired sessions + $query = $this->db->delete($this->table, array('last_activity <' => time() - $maxlifetime)); + + Kohana::log('debug', 'Session garbage collected: '.$query->count().' row(s) deleted.'); + + return TRUE; + } + +} // End Session Database Driver diff --git a/system/views/kohana/template.php b/system/views/kohana/template.php new file mode 100644 index 00000000..b090fd88 --- /dev/null +++ b/system/views/kohana/template.php @@ -0,0 +1,36 @@ + + + + + + + + <?php echo html::specialchars($title) ?> + + + + + + +

      + + + + + + \ No newline at end of file diff --git a/system/views/kohana_error_disabled.php b/system/views/kohana_error_disabled.php new file mode 100644 index 00000000..cd911328 --- /dev/null +++ b/system/views/kohana_error_disabled.php @@ -0,0 +1,17 @@ + + + + + + +<?php echo $error ?> + + +
      +

      +

      +
      + + \ No newline at end of file diff --git a/system/views/kohana_error_page.php b/system/views/kohana_error_page.php new file mode 100644 index 00000000..944064cc --- /dev/null +++ b/system/views/kohana_error_page.php @@ -0,0 +1,27 @@ + + + + + + +<?php echo $error ?> + + + +
      +

      +

      + +

      + +

      + +

      + + +

      +
      + + \ No newline at end of file diff --git a/system/views/kohana_errors.css b/system/views/kohana_errors.css new file mode 100644 index 00000000..1341f57d --- /dev/null +++ b/system/views/kohana_errors.css @@ -0,0 +1,21 @@ +div#framework_error { background:#fff; border:solid 1px #ccc; font-family:sans-serif; color:#111; font-size:14px; line-height:130%; } +div#framework_error h3 { color:#fff; font-size:16px; padding:8px 6px; margin:0 0 8px; background:#f15a00; text-align:center; } +div#framework_error a { color:#228; text-decoration:none; } +div#framework_error a:hover { text-decoration:underline; } +div#framework_error strong { color:#900; } +div#framework_error p { margin:0; padding:4px 6px 10px; } +div#framework_error tt, +div#framework_error pre, +div#framework_error code { font-family:monospace; padding:2px 4px; font-size:12px; color:#333; + white-space:pre-wrap; /* CSS 2.1 */ + white-space:-moz-pre-wrap; /* For Mozilla */ + word-wrap:break-word; /* For IE5.5+ */ +} +div#framework_error tt { font-style:italic; } +div#framework_error tt:before { content:">"; color:#aaa; } +div#framework_error code tt:before { content:""; } +div#framework_error pre, +div#framework_error code { background:#eaeee5; border:solid 0 #D6D8D1; border-width:0 1px 1px 0; } +div#framework_error .block { display:block; text-align:left; } +div#framework_error .stats { padding:4px; background: #eee; border-top:solid 1px #ccc; text-align:center; font-size:10px; color:#888; } +div#framework_error .backtrace { margin:0; padding:0 6px; list-style:none; line-height:12px; } \ No newline at end of file diff --git a/system/views/kohana_profiler.php b/system/views/kohana_profiler.php new file mode 100644 index 00000000..da77a669 --- /dev/null +++ b/system/views/kohana_profiler.php @@ -0,0 +1,37 @@ + + +
      +render(); +} +?> +

      Profiler executed in s

      +
      \ No newline at end of file diff --git a/system/views/kohana_profiler_table.css b/system/views/kohana_profiler_table.css new file mode 100644 index 00000000..6e7601c9 --- /dev/null +++ b/system/views/kohana_profiler_table.css @@ -0,0 +1,53 @@ +#kohana-profiler .kp-table +{ + font-size: 1.0em; + color: #4D6171; + width: 100%; + border-collapse: collapse; + border-top: 1px solid #E5EFF8; + border-right: 1px solid #E5EFF8; + border-left: 1px solid #E5EFF8; + margin-bottom: 10px; +} +#kohana-profiler .kp-table td +{ + background-color: #FFFFFF; + border-bottom: 1px solid #E5EFF8; + padding: 3px; + vertical-align: top; +} +#kohana-profiler .kp-table .kp-title td +{ + font-weight: bold; + background-color: inherit; +} +#kohana-profiler .kp-table .kp-altrow td +{ + background-color: #F7FBFF; +} +#kohana-profiler .kp-table .kp-totalrow td +{ + background-color: #FAFAFA; + border-top: 1px solid #D2DCE5; + font-weight: bold; +} +#kohana-profiler .kp-table .kp-column +{ + width: 100px; + border-left: 1px solid #E5EFF8; + text-align: center; +} +#kohana-profiler .kp-table .kp-data, #kohana-profiler .kp-table .kp-name +{ + background-color: #FAFAFB; + vertical-align: top; +} +#kohana-profiler .kp-table .kp-name +{ + width: 200px; + border-right: 1px solid #E5EFF8; +} +#kohana-profiler .kp-table .kp-altrow .kp-data, #kohana-profiler .kp-table .kp-altrow .kp-name +{ + background-color: #F6F8FB; +} \ No newline at end of file diff --git a/system/views/kohana_profiler_table.php b/system/views/kohana_profiler_table.php new file mode 100644 index 00000000..b6b46530 --- /dev/null +++ b/system/views/kohana_profiler_table.php @@ -0,0 +1,25 @@ + + + + > + $column) + { + $class = empty($column['class']) ? '' : ' class="'.$column['class'].'"'; + $style = empty($column['style']) ? '' : ' style="'.$column['style'].'"'; + $value = $row['data'][$index]; + $value = (is_array($value) OR is_object($value)) ? '
      '.html::specialchars(print_r($value, TRUE)).'
      ' : html::specialchars($value); + echo '', $value, ''; + } + ?> + + +
      \ No newline at end of file -- cgit v1.2.3 From 4eedf804e1f26d95de6803e48c608d893c53b7d0 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Thu, 28 May 2009 07:15:00 +0800 Subject: Update all references to the core application to now point to the gallery module. This type of mass update is prone to some small bugs. Signed-off-by: Gallery Role Account --- installer/installer.php | 2 +- installer/views/install.html.php | 2 +- modules/g2_import/controllers/admin_g2_import.php | 4 +- modules/g2_import/helpers/g2_import.php | 4 +- modules/gallery/controllers/admin_dashboard.php | 2 +- modules/gallery/controllers/admin_graphics.php | 8 +- modules/gallery/controllers/admin_languages.php | 6 +- .../gallery/controllers/admin_theme_details.php | 22 +- modules/gallery/controllers/admin_themes.php | 8 +- modules/gallery/controllers/albums.php | 6 +- modules/gallery/controllers/scaffold.php | 8 +- modules/gallery/helpers/access.php | 4 +- modules/gallery/helpers/block_manager.php | 4 +- modules/gallery/helpers/core.php | 52 ---- modules/gallery/helpers/core_block.php | 100 -------- modules/gallery/helpers/core_event.php | 46 ---- modules/gallery/helpers/core_installer.php | 278 --------------------- modules/gallery/helpers/core_menu.php | 162 ------------ modules/gallery/helpers/core_search.php | 24 -- modules/gallery/helpers/core_task.php | 166 ------------ modules/gallery/helpers/core_theme.php | 137 ---------- modules/gallery/helpers/gallery.php | 52 ++++ modules/gallery/helpers/gallery_block.php | 100 ++++++++ modules/gallery/helpers/gallery_event.php | 46 ++++ modules/gallery/helpers/gallery_installer.php | 278 +++++++++++++++++++++ modules/gallery/helpers/gallery_menu.php | 162 ++++++++++++ modules/gallery/helpers/gallery_search.php | 24 ++ modules/gallery/helpers/gallery_task.php | 166 ++++++++++++ modules/gallery/helpers/gallery_theme.php | 137 ++++++++++ modules/gallery/helpers/graphics.php | 28 +-- modules/gallery/helpers/l10n_client.php | 4 +- modules/gallery/helpers/locale.php | 8 +- modules/gallery/helpers/module.php | 28 +-- modules/gallery/helpers/movie.php | 4 +- modules/gallery/helpers/theme.php | 15 +- modules/gallery/hooks/init_gallery.php | 6 +- modules/gallery/libraries/Admin_View.php | 10 +- modules/gallery/libraries/I18n.php | 2 +- modules/gallery/libraries/Theme_View.php | 20 +- modules/gallery/tests/Core_Installer_Test.php | 12 +- modules/gallery/tests/File_Structure_Test.php | 2 +- modules/gallery/tests/Movie_Helper_Test.php | 4 +- modules/gallery/tests/Photo_Helper_Test.php | 14 +- modules/gallery/tests/Photos_Controller_Test.php | 4 +- modules/gallery/tests/Var_Test.php | 32 +-- .../gallery/views/admin_advanced_settings.html.php | 2 +- modules/gallery/views/admin_block_stats.html.php | 2 +- modules/gallery/views/admin_graphics_gd.html.php | 2 +- .../views/admin_graphics_graphicsmagick.html.php | 2 +- .../views/admin_graphics_imagemagick.html.php | 2 +- modules/gallery/views/admin_languages.html.php | 2 +- modules/gallery/views/kohana_error_page.php | 5 +- modules/gallery/views/permissions_browse.html.php | 2 +- modules/gallery/views/scaffold.html.php | 2 +- .../controllers/gallery_unit_test.php | 6 +- modules/organize/controllers/organize.php | 2 +- modules/search/controllers/search.php | 2 +- modules/tag/controllers/tags.php | 2 +- themes/default/views/footer.html.php | 2 +- themes/default/views/header.html.php | 2 +- 60 files changed, 1121 insertions(+), 1119 deletions(-) delete mode 100644 modules/gallery/helpers/core.php delete mode 100644 modules/gallery/helpers/core_block.php delete mode 100644 modules/gallery/helpers/core_event.php delete mode 100644 modules/gallery/helpers/core_installer.php delete mode 100644 modules/gallery/helpers/core_menu.php delete mode 100644 modules/gallery/helpers/core_search.php delete mode 100644 modules/gallery/helpers/core_task.php delete mode 100644 modules/gallery/helpers/core_theme.php create mode 100644 modules/gallery/helpers/gallery.php create mode 100644 modules/gallery/helpers/gallery_block.php create mode 100644 modules/gallery/helpers/gallery_event.php create mode 100644 modules/gallery/helpers/gallery_installer.php create mode 100644 modules/gallery/helpers/gallery_menu.php create mode 100644 modules/gallery/helpers/gallery_search.php create mode 100644 modules/gallery/helpers/gallery_task.php create mode 100644 modules/gallery/helpers/gallery_theme.php diff --git a/installer/installer.php b/installer/installer.php index 84adc5f9..38fde1fe 100644 --- a/installer/installer.php +++ b/installer/installer.php @@ -122,7 +122,7 @@ class installer { static function create_private_key($config) { $key = md5(uniqid(mt_rand(), true)) . md5(uniqid(mt_rand(), true)); $sql = self::prepend_prefix($config["prefix"], - "INSERT INTO {vars} VALUES(NULL, 'core', 'private_key', '$key')"); + "INSERT INTO {vars} VALUES(NULL, 'gallery', 'private_key', '$key')"); if (mysql_query($sql)) { } else { throw new Exception(mysql_error()); diff --git a/installer/views/install.html.php b/installer/views/install.html.php index c3b9686d..18060219 100644 --- a/installer/views/install.html.php +++ b/installer/views/install.html.php @@ -6,7 +6,7 @@
      - +
      diff --git a/modules/g2_import/controllers/admin_g2_import.php b/modules/g2_import/controllers/admin_g2_import.php index f17660ae..25894291 100644 --- a/modules/g2_import/controllers/admin_g2_import.php +++ b/modules/g2_import/controllers/admin_g2_import.php @@ -32,8 +32,8 @@ class Admin_g2_import_Controller extends Admin_Controller { if (g2_import::is_initialized()) { $view->content->g2_stats = $g2_stats; $view->content->g2_sizes = $g2_sizes; - $view->content->thumb_size = module::get_var("core", "thumb_size"); - $view->content->resize_size = module::get_var("core", "resize_size"); + $view->content->thumb_size = module::get_var("gallery", "thumb_size"); + $view->content->resize_size = module::get_var("gallery", "resize_size"); } print $view; } diff --git a/modules/g2_import/helpers/g2_import.php b/modules/g2_import/helpers/g2_import.php index 37efcad0..dd23d7a0 100644 --- a/modules/g2_import/helpers/g2_import.php +++ b/modules/g2_import/helpers/g2_import.php @@ -481,8 +481,8 @@ class g2_import_Core { $g2_item_id = self::$current_g2_item->getId(); $derivatives = g2(GalleryCoreApi::fetchDerivativesByItemIds(array($g2_item_id))); - $target_thumb_size = module::get_var("core", "thumb_size"); - $target_resize_size = module::get_var("core", "resize_size"); + $target_thumb_size = module::get_var("gallery", "thumb_size"); + $target_resize_size = module::get_var("gallery", "resize_size"); foreach ($derivatives[$g2_item_id] as $derivative) { if ($derivative->getPostFilterOperations()) { // Let's assume for now that this is a watermark operation, which we can't handle. diff --git a/modules/gallery/controllers/admin_dashboard.php b/modules/gallery/controllers/admin_dashboard.php index d2d2f79b..a1090a6d 100644 --- a/modules/gallery/controllers/admin_dashboard.php +++ b/modules/gallery/controllers/admin_dashboard.php @@ -29,7 +29,7 @@ class Admin_Dashboard_Controller extends Admin_Controller { } public function add_block() { - $form = core_block::get_add_block_form(); + $form = gallery_block::get_add_block_form(); if ($form->validate()) { list ($module_name, $id) = explode(":", $form->add_block->id->value); $available = block_manager::get_available(); diff --git a/modules/gallery/controllers/admin_graphics.php b/modules/gallery/controllers/admin_graphics.php index 0b3014f0..7e8ef47c 100644 --- a/modules/gallery/controllers/admin_graphics.php +++ b/modules/gallery/controllers/admin_graphics.php @@ -24,7 +24,7 @@ class Admin_Graphics_Controller extends Admin_Controller { $view->content->available = ""; $tk = new ArrayObject(graphics::detect_toolkits(), ArrayObject::ARRAY_AS_PROPS); - $active = module::get_var("core", "graphics_toolkit", "none"); + $active = module::get_var("gallery", "graphics_toolkit", "none"); foreach (array("gd", "imagemagick", "graphicsmagick", "none") as $id) { if ($id == $active) { $view->content->active = new View("admin_graphics_$id.html"); @@ -43,12 +43,12 @@ class Admin_Graphics_Controller extends Admin_Controller { public function choose($toolkit) { access::verify_csrf(); - if ($toolkit != module::get_var("core", "graphics_toolkit")) { - module::set_var("core", "graphics_toolkit", $toolkit); + if ($toolkit != module::get_var("gallery", "graphics_toolkit")) { + module::set_var("gallery", "graphics_toolkit", $toolkit); $toolkit_info = graphics::detect_toolkits(); if ($toolkit == "graphicsmagick" || $toolkit == "imagemagick") { - module::set_var("core", "graphics_toolkit_path", $toolkit_info[$toolkit]); + module::set_var("gallery", "graphics_toolkit_path", $toolkit_info[$toolkit]); } site_status::clear("missing_graphics_toolkit"); diff --git a/modules/gallery/controllers/admin_languages.php b/modules/gallery/controllers/admin_languages.php index 37d335a3..1dea733c 100644 --- a/modules/gallery/controllers/admin_languages.php +++ b/modules/gallery/controllers/admin_languages.php @@ -33,7 +33,7 @@ class Admin_Languages_Controller extends Admin_Controller { public function save() { $form = $this->_languages_form(); if ($form->validate()) { - module::set_var("core", "default_locale", $form->choose_language->locale->value); + module::set_var("gallery", "default_locale", $form->choose_language->locale->value); locale::update_installed($form->choose_language->installed_locales->value); message::success(t("Settings saved")); } @@ -76,7 +76,7 @@ class Admin_Languages_Controller extends Admin_Controller { message::success(t("Your API key has been saved.")); } - log::success(t("core"), t("l10n_client API key changed.")); + log::success(t("gallery"), t("l10n_client API key changed.")); url::redirect("admin/languages"); } else { // Show the page with form errors @@ -92,7 +92,7 @@ class Admin_Languages_Controller extends Admin_Controller { ->label(t("Language settings")); $group->dropdown("locale") ->options($installed_locales) - ->selected(module::get_var("core", "default_locale")) + ->selected(module::get_var("gallery", "default_locale")) ->label(t("Default language")) ->rules('required'); diff --git a/modules/gallery/controllers/admin_theme_details.php b/modules/gallery/controllers/admin_theme_details.php index 542ec31c..fec1311b 100644 --- a/modules/gallery/controllers/admin_theme_details.php +++ b/modules/gallery/controllers/admin_theme_details.php @@ -28,32 +28,32 @@ class Admin_Theme_Details_Controller extends Admin_Controller { public function save() { $form = theme::get_edit_form_admin(); if ($form->validate()) { - module::set_var("core", "page_size", $form->edit_theme->page_size->value); + module::set_var("gallery", "page_size", $form->edit_theme->page_size->value); $thumb_size = $form->edit_theme->thumb_size->value; $thumb_dirty = false; - if (module::get_var("core", "thumb_size") != $thumb_size) { - graphics::remove_rule("core", "thumb", "resize"); + if (module::get_var("gallery", "thumb_size") != $thumb_size) { + graphics::remove_rule("gallery", "thumb", "resize"); graphics::add_rule( - "core", "thumb", "resize", + "gallery", "thumb", "resize", array("width" => $thumb_size, "height" => $thumb_size, "master" => Image::AUTO), 100); - module::set_var("core", "thumb_size", $thumb_size); + module::set_var("gallery", "thumb_size", $thumb_size); } $resize_size = $form->edit_theme->resize_size->value; $resize_dirty = false; - if (module::get_var("core", "resize_size") != $resize_size) { - graphics::remove_rule("core", "resize", "resize"); + if (module::get_var("gallery", "resize_size") != $resize_size) { + graphics::remove_rule("gallery", "resize", "resize"); graphics::add_rule( - "core", "resize", "resize", + "gallery", "resize", "resize", array("width" => $resize_size, "height" => $resize_size, "master" => Image::AUTO), 100); - module::set_var("core", "resize_size", $resize_size); + module::set_var("gallery", "resize_size", $resize_size); } - module::set_var("core", "header_text", $form->edit_theme->header_text->value); - module::set_var("core", "footer_text", $form->edit_theme->footer_text->value); + module::set_var("gallery", "header_text", $form->edit_theme->header_text->value); + module::set_var("gallery", "footer_text", $form->edit_theme->footer_text->value); message::success(t("Updated theme details")); url::redirect("admin/theme_details"); diff --git a/modules/gallery/controllers/admin_themes.php b/modules/gallery/controllers/admin_themes.php index 05c134d1..aef6c2d1 100644 --- a/modules/gallery/controllers/admin_themes.php +++ b/modules/gallery/controllers/admin_themes.php @@ -21,8 +21,8 @@ class Admin_Themes_Controller extends Admin_Controller { public function index() { $view = new Admin_View("admin.html"); $view->content = new View("admin_themes.html"); - $view->content->admin = module::get_var("core", "active_admin_theme"); - $view->content->site = module::get_var("core", "active_site_theme"); + $view->content->admin = module::get_var("gallery", "active_admin_theme"); + $view->content->site = module::get_var("gallery", "active_site_theme"); $view->content->themes = $this->_get_themes(); print $view; } @@ -64,11 +64,11 @@ class Admin_Themes_Controller extends Admin_Controller { parse_ini_file(THEMEPATH . "$theme_name/theme.info"), ArrayObject::ARRAY_AS_PROPS); if ($type == "admin" && $info->admin) { - module::set_var("core", "active_admin_theme", $theme_name); + module::set_var("gallery", "active_admin_theme", $theme_name); message::success(t("Successfully changed your admin theme to %theme_name", array("theme_name" => $info->name))); } else if ($type == "site" && $info->site) { - module::set_var("core", "active_site_theme", $theme_name); + module::set_var("gallery", "active_site_theme", $theme_name); message::success(t("Successfully changed your Gallery theme to %theme_name", array("theme_name" => $info->name))); } diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php index 5b4d5979..03a64f43 100644 --- a/modules/gallery/controllers/albums.php +++ b/modules/gallery/controllers/albums.php @@ -32,7 +32,7 @@ class Albums_Controller extends Items_Controller { } } - $page_size = module::get_var("core", "page_size", 9); + $page_size = module::get_var("gallery", "page_size", 9); $show = $this->input->get("show"); if ($show) { @@ -114,7 +114,7 @@ class Albums_Controller extends Items_Controller { } else { print json_encode( array("result" => "error", - "form" => $form->__toString() . html::script("core/js/albums_form_add.js"))); + "form" => $form->__toString() . html::script("gallery/js/albums_form_add.js"))); } } @@ -206,7 +206,7 @@ class Albums_Controller extends Items_Controller { switch ($this->input->get("type")) { case "album": print album::get_add_form($album) . - html::script("core/js/albums_form_add.js"); + html::script("gallery/js/albums_form_add.js"); break; case "photo": diff --git a/modules/gallery/controllers/scaffold.php b/modules/gallery/controllers/scaffold.php index f0063725..8ec1663a 100644 --- a/modules/gallery/controllers/scaffold.php +++ b/modules/gallery/controllers/scaffold.php @@ -94,7 +94,7 @@ class Scaffold_Controller extends Template_Controller { $type = rand(0, 10) ? "photo" : "album"; } if ($type == "album") { - $thumb_size = module::get_var("core", "thumb_size"); + $thumb_size = module::get_var("gallery", "thumb_size"); $parents[] = album::create( $parent, "rnd_" . rand(), "Rnd $i", "random album $i", $owner_id) ->save(); @@ -262,7 +262,7 @@ class Scaffold_Controller extends Template_Controller { } foreach ($to_install as $module_name) { - if ($module_name != "core") { + if ($module_name != "gallery") { require_once(DOCROOT . "modules/${module_name}/helpers/${module_name}_installer.php"); } module::install($module_name); @@ -300,7 +300,7 @@ class Scaffold_Controller extends Template_Controller { srand(0); try { - core_installer::install(true); + gallery_installer::install(true); module::load_modules(); foreach (array("user", "comment", "organize", "info", "rss", @@ -325,7 +325,7 @@ class Scaffold_Controller extends Template_Controller { $db = Database::instance(); $db->query("TRUNCATE {sessions}"); $db->query("TRUNCATE {logs}"); - $db->query("DELETE FROM {vars} WHERE `module_name` = 'core' AND `name` = '_cache'"); + $db->query("DELETE FROM {vars} WHERE `module_name` = 'gallery' AND `name` = '_cache'"); $db->update("users", array("password" => ""), array("id" => 1)); $db->update("users", array("password" => ""), array("id" => 2)); diff --git a/modules/gallery/helpers/access.php b/modules/gallery/helpers/access.php index 64ce91fa..c48f0b79 100644 --- a/modules/gallery/helpers/access.php +++ b/modules/gallery/helpers/access.php @@ -389,7 +389,7 @@ class access_Core { * @return ORM_Iterator */ private static function _get_all_groups() { - // When we build the core package, it's possible that the user module is not installed yet. + // When we build the gallery package, it's possible that the user module is not installed yet. // This is ok at packaging time, so work around it. if (module::is_active("user")) { return ORM::factory("group")->find_all(); @@ -595,7 +595,7 @@ class access_Core { } static function private_key() { - return module::get_var("core", "private_key"); + return module::get_var("gallery", "private_key"); } /** diff --git a/modules/gallery/helpers/block_manager.php b/modules/gallery/helpers/block_manager.php index 022626e5..20b641d4 100644 --- a/modules/gallery/helpers/block_manager.php +++ b/modules/gallery/helpers/block_manager.php @@ -19,11 +19,11 @@ */ class block_manager_Core { static function get_active($location) { - return unserialize(module::get_var("core", "blocks_$location", "a:0:{}")); + return unserialize(module::get_var("gallery", "blocks_$location", "a:0:{}")); } static function set_active($location, $blocks) { - module::set_var("core", "blocks_$location", serialize($blocks)); + module::set_var("gallery", "blocks_$location", serialize($blocks)); } static function add($location, $module_name, $block_id) { diff --git a/modules/gallery/helpers/core.php b/modules/gallery/helpers/core.php deleted file mode 100644 index 63f51f86..00000000 --- a/modules/gallery/helpers/core.php +++ /dev/null @@ -1,52 +0,0 @@ -admin) { - Router::$controller = "maintenance"; - Router::$controller_path = APPPATH . "controllers/maintenance.php"; - Router::$method = "index"; - } - } - - /** - * This function is called when the Gallery is fully initialized. We relay it to modules as the - * "gallery_ready" event. Any module that wants to perform an action at the start of every - * request should implement the _event::gallery_ready() handler. - */ - static function ready() { - module::event("gallery_ready"); - } - - /** - * This function is called right before the Kohana framework shuts down. We relay it to modules - * as the "gallery_shutdown" event. Any module that wants to perform an action at the start of - * every request should implement the _event::gallery_shutdown() handler. - */ - static function shutdown() { - module::event("gallery_shutdown"); - } -} \ No newline at end of file diff --git a/modules/gallery/helpers/core_block.php b/modules/gallery/helpers/core_block.php deleted file mode 100644 index 0e2e9c54..00000000 --- a/modules/gallery/helpers/core_block.php +++ /dev/null @@ -1,100 +0,0 @@ - t("Welcome to Gallery 3!"), - "photo_stream" => t("Photo Stream"), - "log_entries" => t("Log Entries"), - "stats" => t("Gallery Stats"), - "platform_info" => t("Platform Information"), - "project_news" => t("Gallery Project News")); - } - - static function get($block_id) { - $block = new Block(); - switch($block_id) { - case "welcome": - $block->css_id = "gWelcome"; - $block->title = t("Welcome to Gallery3"); - $block->content = new View("admin_block_welcome.html"); - break; - - case "photo_stream": - $block->css_id = "gPhotoStream"; - $block->title = t("Photo Stream"); - $block->content = new View("admin_block_photo_stream.html"); - $block->content->photos = - ORM::factory("item")->where("type", "photo")->orderby("created", "DESC")->find_all(10); - break; - - case "log_entries": - $block->css_id = "gLogEntries"; - $block->title = t("Log Entries"); - $block->content = new View("admin_block_log_entries.html"); - $block->content->entries = ORM::factory("log")->orderby("timestamp", "DESC")->find_all(5); - break; - - case "stats": - $block->css_id = "gStats"; - $block->title = t("Gallery Stats"); - $block->content = new View("admin_block_stats.html"); - $block->content->album_count = ORM::factory("item")->where("type", "album")->count_all(); - $block->content->photo_count = ORM::factory("item")->where("type", "photo")->count_all(); - break; - - case "platform_info": - $block->css_id = "gPlatform"; - $block->title = t("Platform Information"); - $block->content = new View("admin_block_platform.html"); - if (is_readable("/proc/loadavg")) { - $block->content->load_average = - join(" ", array_slice(split(" ", array_shift(file("/proc/loadavg"))), 0, 3)); - } else { - $block->content->load_average = t("Unavailable"); - } - break; - - case "project_news": - $block->css_id = "gProjectNews"; - $block->title = t("Gallery Project News"); - $block->content = new View("admin_block_news.html"); - $block->content->feed = feed::parse("http://gallery.menalto.com/node/feed", 3); - break; - - case "block_adder": - $block->css_id = "gBlockAdder"; - $block->title = t("Dashboard Content"); - $block->content = self::get_add_block_form(); - } - - return $block; - } - - static function get_add_block_form() { - $form = new Forge("admin/dashboard/add_block", "", "post", - array("id" => "gAddDashboardBlockForm")); - $group = $form->group("add_block")->label(t("Add Block")); - $group->dropdown("id")->label("Available Blocks")->options(block_manager::get_available()); - $group->submit("center")->value(t("Add to center")); - $group->submit("sidebar")->value(t("Add to sidebar")); - return $form; - } -} \ No newline at end of file diff --git a/modules/gallery/helpers/core_event.php b/modules/gallery/helpers/core_event.php deleted file mode 100644 index bbb53cc9..00000000 --- a/modules/gallery/helpers/core_event.php +++ /dev/null @@ -1,46 +0,0 @@ -admin && module::get_var("core", "choose_default_tookit", null)) { - graphics::choose_default_toolkit(); - module::clear_var("core", "choose_default_tookit"); - } - } -} diff --git a/modules/gallery/helpers/core_installer.php b/modules/gallery/helpers/core_installer.php deleted file mode 100644 index cffcbedb..00000000 --- a/modules/gallery/helpers/core_installer.php +++ /dev/null @@ -1,278 +0,0 @@ -query("CREATE TABLE {access_caches} ( - `id` int(9) NOT NULL auto_increment, - `item_id` int(9), - PRIMARY KEY (`id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {access_intents} ( - `id` int(9) NOT NULL auto_increment, - `item_id` int(9), - PRIMARY KEY (`id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {graphics_rules} ( - `id` int(9) NOT NULL auto_increment, - `active` BOOLEAN default 0, - `args` varchar(255) default NULL, - `module_name` varchar(64) NOT NULL, - `operation` varchar(64) NOT NULL, - `priority` int(9) NOT NULL, - `target` varchar(32) NOT NULL, - PRIMARY KEY (`id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {items} ( - `id` int(9) NOT NULL auto_increment, - `album_cover_item_id` int(9) default NULL, - `captured` int(9) default NULL, - `created` int(9) default NULL, - `description` varchar(2048) default NULL, - `height` int(9) default NULL, - `left` int(9) NOT NULL, - `level` int(9) NOT NULL, - `mime_type` varchar(64) default NULL, - `name` varchar(255) default NULL, - `owner_id` int(9) default NULL, - `parent_id` int(9) NOT NULL, - `rand_key` float default NULL, - `relative_path_cache` varchar(255) default NULL, - `resize_dirty` boolean default 1, - `resize_height` int(9) default NULL, - `resize_width` int(9) default NULL, - `right` int(9) NOT NULL, - `sort_column` varchar(64) default NULL, - `sort_order` char(4) default 'ASC', - `thumb_dirty` boolean default 1, - `thumb_height` int(9) default NULL, - `thumb_width` int(9) default NULL, - `title` varchar(255) default NULL, - `type` varchar(32) NOT NULL, - `updated` int(9) default NULL, - `view_count` int(9) default 0, - `weight` int(9) NOT NULL default 0, - `width` int(9) default NULL, - PRIMARY KEY (`id`), - KEY `parent_id` (`parent_id`), - KEY `type` (`type`), - KEY `random` (`rand_key`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {logs} ( - `id` int(9) NOT NULL auto_increment, - `category` varchar(64) default NULL, - `html` varchar(255) default NULL, - `message` text default NULL, - `referer` varchar(255) default NULL, - `severity` int(9) default 0, - `timestamp` int(9) default 0, - `url` varchar(255) default NULL, - `user_id` int(9) default 0, - PRIMARY KEY (`id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {messages} ( - `id` int(9) NOT NULL auto_increment, - `key` varchar(255) default NULL, - `severity` varchar(32) default NULL, - `value` varchar(255) default NULL, - PRIMARY KEY (`id`), - UNIQUE KEY(`key`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {modules} ( - `id` int(9) NOT NULL auto_increment, - `active` BOOLEAN default 0, - `name` varchar(64) default NULL, - `version` int(9) default NULL, - PRIMARY KEY (`id`), - UNIQUE KEY(`name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {themes} ( - `id` int(9) NOT NULL auto_increment, - `name` varchar(64) default NULL, - `version` int(9) default NULL, - PRIMARY KEY (`id`), - UNIQUE KEY(`name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {permissions} ( - `id` int(9) NOT NULL auto_increment, - `display_name` varchar(64) default NULL, - `name` varchar(64) default NULL, - PRIMARY KEY (`id`), - UNIQUE KEY(`name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {incoming_translations} ( - `id` int(9) NOT NULL auto_increment, - `key` char(32) NOT NULL, - `locale` char(10) NOT NULL, - `message` text NOT NULL, - `revision` int(9) DEFAULT NULL, - `translation` text, - PRIMARY KEY (`id`), - UNIQUE KEY(`key`, `locale`), - KEY `locale_key` (`locale`, `key`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {outgoing_translations} ( - `id` int(9) NOT NULL auto_increment, - `base_revision` int(9) DEFAULT NULL, - `key` char(32) NOT NULL, - `locale` char(10) NOT NULL, - `message` text NOT NULL, - `translation` text, - PRIMARY KEY (`id`), - UNIQUE KEY(`key`, `locale`), - KEY `locale_key` (`locale`, `key`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {sessions} ( - `session_id` varchar(127) NOT NULL, - `data` text NOT NULL, - `last_activity` int(10) UNSIGNED NOT NULL, - PRIMARY KEY (`session_id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {tasks} ( - `id` int(9) NOT NULL auto_increment, - `callback` varchar(128) default NULL, - `context` text NOT NULL, - `done` boolean default 0, - `name` varchar(128) default NULL, - `owner_id` int(9) default NULL, - `percent_complete` int(9) default 0, - `state` varchar(32) default NULL, - `status` varchar(255) default NULL, - `updated` int(9) default NULL, - PRIMARY KEY (`id`), - KEY (`owner_id`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - $db->query("CREATE TABLE {vars} ( - `id` int(9) NOT NULL auto_increment, - `module_name` varchar(64) NOT NULL, - `name` varchar(64) NOT NULL, - `value` text, - PRIMARY KEY (`id`), - UNIQUE KEY(`module_name`, `name`)) - ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - - foreach (array("albums", "logs", "modules", "resizes", "thumbs", "tmp", "uploads") as $dir) { - @mkdir(VARPATH . $dir); - } - - access::register_permission("view", "View"); - access::register_permission("view_full", "View Full Size"); - access::register_permission("edit", "Edit"); - access::register_permission("add", "Add"); - - $root = ORM::factory("item"); - $root->type = "album"; - $root->title = "Gallery"; - $root->description = ""; - $root->left = 1; - $root->right = 2; - $root->parent_id = 0; - $root->level = 1; - $root->thumb_dirty = 1; - $root->resize_dirty = 1; - $root->sort_column = "weight"; - $root->sort_order = "ASC"; - $root->save(); - access::add_item($root); - - module::set_var("core", "active_site_theme", "default"); - module::set_var("core", "active_admin_theme", "admin_default"); - module::set_var("core", "page_size", 9); - module::set_var("core", "thumb_size", 200); - module::set_var("core", "resize_size", 640); - module::set_var("core", "default_locale", "en_US"); - module::set_var("core", "image_quality", 75); - - // Add rules for generating our thumbnails and resizes - graphics::add_rule( - "core", "thumb", "resize", - array("width" => 200, "height" => 200, "master" => Image::AUTO), - 100); - graphics::add_rule( - "core", "resize", "resize", - array("width" => 640, "height" => 480, "master" => Image::AUTO), - 100); - - // Instantiate default themes (site and admin) - foreach (array("default", "admin_default") as $theme_name) { - $theme_info = new ArrayObject(parse_ini_file(THEMEPATH . $theme_name . "/theme.info"), - ArrayObject::ARRAY_AS_PROPS); - $theme = ORM::factory("theme"); - $theme->name = $theme_name; - $theme->version = $theme_info->version; - $theme->save(); - } - - block_manager::add("dashboard_sidebar", "core", "block_adder"); - block_manager::add("dashboard_sidebar", "core", "stats"); - block_manager::add("dashboard_sidebar", "core", "platform_info"); - block_manager::add("dashboard_sidebar", "core", "project_news"); - block_manager::add("dashboard_center", "core", "welcome"); - block_manager::add("dashboard_center", "core", "photo_stream"); - block_manager::add("dashboard_center", "core", "log_entries"); - - module::set_version("core", 1); - module::set_var("core", "version", "3.0 pre-beta svn"); - module::set_var("core", "choose_default_tookit", 1); - } - } - - static function uninstall() { - $db = Database::instance(); - $db->query("DROP TABLE IF EXISTS {access_caches}"); - $db->query("DROP TABLE IF EXISTS {access_intents}"); - $db->query("DROP TABLE IF EXISTS {graphics_rules}"); - $db->query("DROP TABLE IF EXISTS {items}"); - $db->query("DROP TABLE IF EXISTS {logs}"); - $db->query("DROP TABLE IF EXISTS {messages}"); - $db->query("DROP TABLE IF EXISTS {modules}"); - $db->query("DROP TABLE IF EXISTS {themes}"); - $db->query("DROP TABLE IF EXISTS {incoming_translations}"); - $db->query("DROP TABLE IF EXISTS {outgoing_translations}"); - $db->query("DROP TABLE IF EXISTS {permissions}"); - $db->query("DROP TABLE IF EXISTS {sessions}"); - $db->query("DROP TABLE IF EXISTS {tasks}"); - $db->query("DROP TABLE IF EXISTS {vars}"); - foreach (array("albums", "resizes", "thumbs", "uploads", - "modules", "logs", "database.php") as $entry) { - system("/bin/rm -rf " . VARPATH . $entry); - } - } -} diff --git a/modules/gallery/helpers/core_menu.php b/modules/gallery/helpers/core_menu.php deleted file mode 100644 index eb208560..00000000 --- a/modules/gallery/helpers/core_menu.php +++ /dev/null @@ -1,162 +0,0 @@ -admin) { - $menu->append($scaffold_menu = Menu::factory("submenu") - ->id("scaffold") - ->label("Scaffold")); - $scaffold_menu->append(Menu::factory("link") - ->id("scaffold_home") - ->label("Dashboard") - ->url(url::site("scaffold"))); - } - - $menu->append(Menu::factory("link") - ->id("home") - ->label(t("Home")) - ->url(url::site("albums/1"))); - - $item = $theme->item(); - - if (user::active()->admin || ($item && access::can("edit", $item))) { - $menu->append($options_menu = Menu::factory("submenu") - ->id("options_menu") - ->label(t("Options"))); - - if ($item && access::can("edit", $item)) { - $options_menu - ->append(Menu::factory("dialog") - ->id("edit_item") - ->label($item->is_album() ? t("Edit album") : t("Edit photo")) - ->url(url::site("form/edit/{$item->type}s/$item->id"))); - - // @todo Move album options menu to the album quick edit pane - // @todo Create resized item quick edit pane menu - if ($item->is_album()) { - $options_menu - ->append(Menu::factory("dialog") - ->id("add_item") - ->label(t("Add a photo")) - ->url(url::site("simple_uploader/app/$item->id"))) - ->append(Menu::factory("dialog") - ->id("add_album") - ->label(t("Add an album")) - ->url(url::site("form/add/albums/$item->id?type=album"))) - ->append(Menu::factory("dialog") - ->id("edit_permissions") - ->label(t("Edit permissions")) - ->url(url::site("permissions/browse/$item->id"))); - } - } - } - - if (user::active()->admin) { - $menu->append($admin_menu = Menu::factory("submenu") - ->id("admin_menu") - ->label(t("Admin"))); - self::admin($admin_menu, $theme); - foreach (module::active() as $module) { - if ($module->name == "core") { - continue; - } - $class = "{$module->name}_menu"; - if (method_exists($class, "admin")) { - call_user_func_array(array($class, "admin"), array(&$admin_menu, $theme)); - } - } - } - } - - static function album($menu, $theme) { - } - - static function photo($menu, $theme) { - if (access::can("view_full", $theme->item())) { - $menu - ->append(Menu::factory("link") - ->id("fullsize") - ->label(t("View full size")) - ->url("#") - ->css_class("gFullSizeLink")); - } - $menu - ->append(Menu::factory("link") - ->id("album") - ->label(t("Return to album")) - ->url($theme->item()->parent()->url("show={$theme->item->id}")) - ->css_id("gAlbumLink")); - } - - static function admin($menu, $theme) { - $menu - ->append(Menu::factory("link") - ->id("dashboard") - ->label(t("Dashboard")) - ->url(url::site("admin"))) - ->append(Menu::factory("submenu") - ->id("settings_menu") - ->label(t("Settings")) - ->append(Menu::factory("link") - ->id("graphics_toolkits") - ->label(t("Graphics")) - ->url(url::site("admin/graphics"))) - ->append(Menu::factory("link") - ->id("languages") - ->label(t("Languages")) - ->url(url::site("admin/languages"))) - ->append(Menu::factory("link") - ->id("l10n_mode") - ->label(Session::instance()->get("l10n_mode", false) - ? t("Stop translating") : t("Start translating")) - ->url(url::site("l10n_client/toggle_l10n_mode?csrf=" . - access::csrf_token()))) - ->append(Menu::factory("link") - ->id("advanced") - ->label("Advanced") - ->url(url::site("admin/advanced_settings")))) - ->append(Menu::factory("link") - ->id("modules") - ->label(t("Modules")) - ->url(url::site("admin/modules"))) - ->append(Menu::factory("submenu") - ->id("content_menu") - ->label(t("Content"))) - ->append(Menu::factory("submenu") - ->id("appearance_menu") - ->label(t("Appearance")) - ->append(Menu::factory("link") - ->id("themes") - ->label(t("Theme Choice")) - ->url(url::site("admin/themes"))) - ->append(Menu::factory("link") - ->id("theme_details") - ->label(t("Theme Options")) - ->url(url::site("admin/theme_details")))) - ->append(Menu::factory("link") - ->id("maintenance") - ->label(t("Maintenance")) - ->url(url::site("admin/maintenance"))) - ->append(Menu::factory("submenu") - ->id("statistics_menu") - ->label(t("Statistics")) - ->url("#")); - } -} diff --git a/modules/gallery/helpers/core_search.php b/modules/gallery/helpers/core_search.php deleted file mode 100644 index 9957a493..00000000 --- a/modules/gallery/helpers/core_search.php +++ /dev/null @@ -1,24 +0,0 @@ -description, $item->name, $item->title)); - } -} diff --git a/modules/gallery/helpers/core_task.php b/modules/gallery/helpers/core_task.php deleted file mode 100644 index e078192c..00000000 --- a/modules/gallery/helpers/core_task.php +++ /dev/null @@ -1,166 +0,0 @@ -count(); - $tasks = array(); - $tasks[] = Task_Definition::factory() - ->callback("core_task::rebuild_dirty_images") - ->name(t("Rebuild Images")) - ->description($dirty_count ? - t2("You have one out of date photo", - "You have %count out of date photos", - $dirty_count) - : t("All your photos are up to date")) - ->severity($dirty_count ? log::WARNING : log::SUCCESS); - - $tasks[] = Task_Definition::factory() - ->callback("core_task::update_l10n") - ->name(t("Update translations")) - ->description(t("Download new and updated translated strings")) - ->severity(log::SUCCESS); - - return $tasks; - } - - /** - * Task that rebuilds all dirty images. - * @param Task_Model the task - */ - static function rebuild_dirty_images($task) { - $result = graphics::find_dirty_images_query(); - $remaining = $result->count(); - $completed = $task->get("completed", 0); - - $i = 0; - foreach ($result as $row) { - $item = ORM::factory("item", $row->id); - if ($item->loaded) { - graphics::generate($item); - } - - $completed++; - $remaining--; - - if (++$i == 2) { - break; - } - } - - $task->status = t2("Updated: 1 image. Total: %total_count.", - "Updated: %count images. Total: %total_count.", - $completed, - array("total_count" => ($remaining + $completed))); - - if ($completed + $remaining > 0) { - $task->percent_complete = (int)(100 * $completed / ($completed + $remaining)); - } else { - $task->percent_complete = 100; - } - - $task->set("completed", $completed); - if ($remaining == 0) { - $task->done = true; - $task->state = "success"; - site_status::clear("graphics_dirty"); - } - } - - static function update_l10n(&$task) { - $start = microtime(true); - $dirs = $task->get("dirs"); - $files = $task->get("files"); - $cache = $task->get("cache", array()); - $i = 0; - - switch ($task->get("mode", "init")) { - case "init": // 0% - $dirs = array("core", "modules", "themes", "installer"); - $files = array(); - $task->set("mode", "find_files"); - $task->status = t("Finding files"); - break; - - case "find_files": // 0% - 10% - while (($dir = array_pop($dirs)) && microtime(true) - $start < 0.5) { - if (basename($dir) == "tests") { - continue; - } - - foreach (glob(DOCROOT . "$dir/*") as $path) { - $relative_path = str_replace(DOCROOT, "", $path); - if (is_dir($path)) { - $dirs[] = $relative_path; - } else { - $files[] = $relative_path; - } - } - } - - $task->status = t2("Finding files: found 1 file", - "Finding files: found %count files", count($files)); - - if (!$dirs) { - $task->set("mode", "scan_files"); - $task->set("total_files", count($files)); - $task->status = t("Scanning files"); - $task->percent_complete = 10; - } - break; - - case "scan_files": // 10% - 90% - while (($file = array_pop($files)) && microtime(true) - $start < 0.5) { - $file = DOCROOT . $file; - switch (pathinfo($file, PATHINFO_EXTENSION)) { - case "php": - l10n_scanner::scan_php_file($file, $cache); - break; - - case "info": - l10n_scanner::scan_info_file($file, $cache); - break; - } - } - - $total_files = $task->get("total_files"); - $task->status = t2("Scanning files: scanned 1 file", - "Scanning files: scanned %count files", $total_files - count($files)); - - $task->percent_complete = 10 + 80 * ($total_files - count($files)) / $total_files; - if (empty($files)) { - $task->set("mode", "fetch_updates"); - $task->status = t("Fetching updates"); - $task->percent_complete = 90; - } - break; - - case "fetch_updates": // 90% - 100% - l10n_client::fetch_updates(); - $task->done = true; - $task->state = "success"; - $task->status = t("Translations installed/updated"); - $task->percent_complete = 100; - } - - $task->set("files", $files); - $task->set("dirs", $dirs); - $task->set("cache", $cache); - } -} \ No newline at end of file diff --git a/modules/gallery/helpers/core_theme.php b/modules/gallery/helpers/core_theme.php deleted file mode 100644 index 28f544a1..00000000 --- a/modules/gallery/helpers/core_theme.php +++ /dev/null @@ -1,137 +0,0 @@ -get("debug")) { - $buf .= ""; - } - if (($theme->page_type == "album" || $theme->page_type == "photo") - && access::can("edit", $theme->item())) { - $buf .= ""; - $buf .= html::script("core/js/quick.js"); - } - if ($theme->page_type == "photo" && access::can("view_full", $theme->item())) { - $buf .= ""; - $buf .= html::script("core/js/fullsize.js"); - } - - if ($session->get("l10n_mode", false)) { - $buf .= ""; - $buf .= html::script("lib/jquery.cookie.js"); - $buf .= html::script("core/js/l10n_client.js"); - } - - return $buf; - } - - static function resize_top($theme, $item) { - if (access::can("edit", $item)) { - $edit_link = url::site("quick/pane/$item->id?page_type=photo"); - return "
      "; - } - } - - static function resize_bottom($theme, $item) { - if (access::can("edit", $item)) { - return "
      "; - } - } - - static function thumb_top($theme, $child) { - if (access::can("edit", $child)) { - $edit_link = url::site("quick/pane/$child->id?page_type=album"); - return "
      "; - } - } - - static function thumb_bottom($theme, $child) { - if (access::can("edit", $child)) { - return "
      "; - } - } - - static function admin_head($theme) { - $session = Session::instance(); - $buf = ""; - if ($session->get("debug")) { - $buf .= ""; - } - - if ($session->get("l10n_mode", false)) { - $buf .= ""; - $buf .= html::script("lib/jquery.cookie.js"); - $buf .= html::script("core/js/l10n_client.js"); - } - - return $buf; - } - - static function page_bottom($theme) { - $session = Session::instance(); - if ($session->get("profiler", false)) { - $profiler = new Profiler(); - $profiler->render(); - } - if ($session->get("l10n_mode", false)) { - return L10n_Client_Controller::l10n_form(); - } - - if ($session->get("after_install")) { - $session->delete("after_install"); - return new View("after_install_loader.html"); - } - } - - static function admin_page_bottom($theme) { - $session = Session::instance(); - if ($session->get("profiler", false)) { - $profiler = new Profiler(); - $profiler->render(); - } - if ($session->get("l10n_mode", false)) { - return L10n_Client_Controller::l10n_form(); - } - } - - static function credits() { - return "
    • " . - t("Powered by Gallery %version", - array("url" => "http://gallery.menalto.com", - "version" => module::get_var("core", "version"))) . - "
    • "; - } - - static function admin_credits() { - return core_theme::credits(); - } -} \ No newline at end of file diff --git a/modules/gallery/helpers/gallery.php b/modules/gallery/helpers/gallery.php new file mode 100644 index 00000000..bf33fa2b --- /dev/null +++ b/modules/gallery/helpers/gallery.php @@ -0,0 +1,52 @@ +admin) { + Router::$controller = "maintenance"; + Router::$controller_path = APPPATH . "controllers/maintenance.php"; + Router::$method = "index"; + } + } + + /** + * This function is called when the Gallery is fully initialized. We relay it to modules as the + * "gallery_ready" event. Any module that wants to perform an action at the start of every + * request should implement the _event::gallery_ready() handler. + */ + static function ready() { + module::event("gallery_ready"); + } + + /** + * This function is called right before the Kohana framework shuts down. We relay it to modules + * as the "gallery_shutdown" event. Any module that wants to perform an action at the start of + * every request should implement the _event::gallery_shutdown() handler. + */ + static function shutdown() { + module::event("gallery_shutdown"); + } +} \ No newline at end of file diff --git a/modules/gallery/helpers/gallery_block.php b/modules/gallery/helpers/gallery_block.php new file mode 100644 index 00000000..abc8a195 --- /dev/null +++ b/modules/gallery/helpers/gallery_block.php @@ -0,0 +1,100 @@ + t("Welcome to Gallery 3!"), + "photo_stream" => t("Photo Stream"), + "log_entries" => t("Log Entries"), + "stats" => t("Gallery Stats"), + "platform_info" => t("Platform Information"), + "project_news" => t("Gallery Project News")); + } + + static function get($block_id) { + $block = new Block(); + switch($block_id) { + case "welcome": + $block->css_id = "gWelcome"; + $block->title = t("Welcome to Gallery3"); + $block->content = new View("admin_block_welcome.html"); + break; + + case "photo_stream": + $block->css_id = "gPhotoStream"; + $block->title = t("Photo Stream"); + $block->content = new View("admin_block_photo_stream.html"); + $block->content->photos = + ORM::factory("item")->where("type", "photo")->orderby("created", "DESC")->find_all(10); + break; + + case "log_entries": + $block->css_id = "gLogEntries"; + $block->title = t("Log Entries"); + $block->content = new View("admin_block_log_entries.html"); + $block->content->entries = ORM::factory("log")->orderby("timestamp", "DESC")->find_all(5); + break; + + case "stats": + $block->css_id = "gStats"; + $block->title = t("Gallery Stats"); + $block->content = new View("admin_block_stats.html"); + $block->content->album_count = ORM::factory("item")->where("type", "album")->count_all(); + $block->content->photo_count = ORM::factory("item")->where("type", "photo")->count_all(); + break; + + case "platform_info": + $block->css_id = "gPlatform"; + $block->title = t("Platform Information"); + $block->content = new View("admin_block_platform.html"); + if (is_readable("/proc/loadavg")) { + $block->content->load_average = + join(" ", array_slice(split(" ", array_shift(file("/proc/loadavg"))), 0, 3)); + } else { + $block->content->load_average = t("Unavailable"); + } + break; + + case "project_news": + $block->css_id = "gProjectNews"; + $block->title = t("Gallery Project News"); + $block->content = new View("admin_block_news.html"); + $block->content->feed = feed::parse("http://gallery.menalto.com/node/feed", 3); + break; + + case "block_adder": + $block->css_id = "gBlockAdder"; + $block->title = t("Dashboard Content"); + $block->content = self::get_add_block_form(); + } + + return $block; + } + + static function get_add_block_form() { + $form = new Forge("admin/dashboard/add_block", "", "post", + array("id" => "gAddDashboardBlockForm")); + $group = $form->group("add_block")->label(t("Add Block")); + $group->dropdown("id")->label("Available Blocks")->options(block_manager::get_available()); + $group->submit("center")->value(t("Add to center")); + $group->submit("sidebar")->value(t("Add to sidebar")); + return $form; + } +} \ No newline at end of file diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php new file mode 100644 index 00000000..aa11b7c0 --- /dev/null +++ b/modules/gallery/helpers/gallery_event.php @@ -0,0 +1,46 @@ +admin && module::get_var("gallery", "choose_default_tookit", null)) { + graphics::choose_default_toolkit(); + module::clear_var("gallery", "choose_default_tookit"); + } + } +} diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php new file mode 100644 index 00000000..e3aa403f --- /dev/null +++ b/modules/gallery/helpers/gallery_installer.php @@ -0,0 +1,278 @@ +query("CREATE TABLE {access_caches} ( + `id` int(9) NOT NULL auto_increment, + `item_id` int(9), + PRIMARY KEY (`id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {access_intents} ( + `id` int(9) NOT NULL auto_increment, + `item_id` int(9), + PRIMARY KEY (`id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {graphics_rules} ( + `id` int(9) NOT NULL auto_increment, + `active` BOOLEAN default 0, + `args` varchar(255) default NULL, + `module_name` varchar(64) NOT NULL, + `operation` varchar(64) NOT NULL, + `priority` int(9) NOT NULL, + `target` varchar(32) NOT NULL, + PRIMARY KEY (`id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {items} ( + `id` int(9) NOT NULL auto_increment, + `album_cover_item_id` int(9) default NULL, + `captured` int(9) default NULL, + `created` int(9) default NULL, + `description` varchar(2048) default NULL, + `height` int(9) default NULL, + `left` int(9) NOT NULL, + `level` int(9) NOT NULL, + `mime_type` varchar(64) default NULL, + `name` varchar(255) default NULL, + `owner_id` int(9) default NULL, + `parent_id` int(9) NOT NULL, + `rand_key` float default NULL, + `relative_path_cache` varchar(255) default NULL, + `resize_dirty` boolean default 1, + `resize_height` int(9) default NULL, + `resize_width` int(9) default NULL, + `right` int(9) NOT NULL, + `sort_column` varchar(64) default NULL, + `sort_order` char(4) default 'ASC', + `thumb_dirty` boolean default 1, + `thumb_height` int(9) default NULL, + `thumb_width` int(9) default NULL, + `title` varchar(255) default NULL, + `type` varchar(32) NOT NULL, + `updated` int(9) default NULL, + `view_count` int(9) default 0, + `weight` int(9) NOT NULL default 0, + `width` int(9) default NULL, + PRIMARY KEY (`id`), + KEY `parent_id` (`parent_id`), + KEY `type` (`type`), + KEY `random` (`rand_key`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {logs} ( + `id` int(9) NOT NULL auto_increment, + `category` varchar(64) default NULL, + `html` varchar(255) default NULL, + `message` text default NULL, + `referer` varchar(255) default NULL, + `severity` int(9) default 0, + `timestamp` int(9) default 0, + `url` varchar(255) default NULL, + `user_id` int(9) default 0, + PRIMARY KEY (`id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {messages} ( + `id` int(9) NOT NULL auto_increment, + `key` varchar(255) default NULL, + `severity` varchar(32) default NULL, + `value` varchar(255) default NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`key`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {modules} ( + `id` int(9) NOT NULL auto_increment, + `active` BOOLEAN default 0, + `name` varchar(64) default NULL, + `version` int(9) default NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`name`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {themes} ( + `id` int(9) NOT NULL auto_increment, + `name` varchar(64) default NULL, + `version` int(9) default NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`name`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {permissions} ( + `id` int(9) NOT NULL auto_increment, + `display_name` varchar(64) default NULL, + `name` varchar(64) default NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`name`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {incoming_translations} ( + `id` int(9) NOT NULL auto_increment, + `key` char(32) NOT NULL, + `locale` char(10) NOT NULL, + `message` text NOT NULL, + `revision` int(9) DEFAULT NULL, + `translation` text, + PRIMARY KEY (`id`), + UNIQUE KEY(`key`, `locale`), + KEY `locale_key` (`locale`, `key`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {outgoing_translations} ( + `id` int(9) NOT NULL auto_increment, + `base_revision` int(9) DEFAULT NULL, + `key` char(32) NOT NULL, + `locale` char(10) NOT NULL, + `message` text NOT NULL, + `translation` text, + PRIMARY KEY (`id`), + UNIQUE KEY(`key`, `locale`), + KEY `locale_key` (`locale`, `key`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {sessions} ( + `session_id` varchar(127) NOT NULL, + `data` text NOT NULL, + `last_activity` int(10) UNSIGNED NOT NULL, + PRIMARY KEY (`session_id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {tasks} ( + `id` int(9) NOT NULL auto_increment, + `callback` varchar(128) default NULL, + `context` text NOT NULL, + `done` boolean default 0, + `name` varchar(128) default NULL, + `owner_id` int(9) default NULL, + `percent_complete` int(9) default 0, + `state` varchar(32) default NULL, + `status` varchar(255) default NULL, + `updated` int(9) default NULL, + PRIMARY KEY (`id`), + KEY (`owner_id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE {vars} ( + `id` int(9) NOT NULL auto_increment, + `module_name` varchar(64) NOT NULL, + `name` varchar(64) NOT NULL, + `value` text, + PRIMARY KEY (`id`), + UNIQUE KEY(`module_name`, `name`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + foreach (array("albums", "logs", "modules", "resizes", "thumbs", "tmp", "uploads") as $dir) { + @mkdir(VARPATH . $dir); + } + + access::register_permission("view", "View"); + access::register_permission("view_full", "View Full Size"); + access::register_permission("edit", "Edit"); + access::register_permission("add", "Add"); + + $root = ORM::factory("item"); + $root->type = "album"; + $root->title = "Gallery"; + $root->description = ""; + $root->left = 1; + $root->right = 2; + $root->parent_id = 0; + $root->level = 1; + $root->thumb_dirty = 1; + $root->resize_dirty = 1; + $root->sort_column = "weight"; + $root->sort_order = "ASC"; + $root->save(); + access::add_item($root); + + module::set_var("gallery", "active_site_theme", "default"); + module::set_var("gallery", "active_admin_theme", "admin_default"); + module::set_var("gallery", "page_size", 9); + module::set_var("gallery", "thumb_size", 200); + module::set_var("gallery", "resize_size", 640); + module::set_var("gallery", "default_locale", "en_US"); + module::set_var("gallery", "image_quality", 75); + + // Add rules for generating our thumbnails and resizes + graphics::add_rule( + "gallery", "thumb", "resize", + array("width" => 200, "height" => 200, "master" => Image::AUTO), + 100); + graphics::add_rule( + "gallery", "resize", "resize", + array("width" => 640, "height" => 480, "master" => Image::AUTO), + 100); + + // Instantiate default themes (site and admin) + foreach (array("default", "admin_default") as $theme_name) { + $theme_info = new ArrayObject(parse_ini_file(THEMEPATH . $theme_name . "/theme.info"), + ArrayObject::ARRAY_AS_PROPS); + $theme = ORM::factory("theme"); + $theme->name = $theme_name; + $theme->version = $theme_info->version; + $theme->save(); + } + + block_manager::add("dashboard_sidebar", "gallery", "block_adder"); + block_manager::add("dashboard_sidebar", "gallery", "stats"); + block_manager::add("dashboard_sidebar", "gallery", "platform_info"); + block_manager::add("dashboard_sidebar", "gallery", "project_news"); + block_manager::add("dashboard_center", "gallery", "welcome"); + block_manager::add("dashboard_center", "gallery", "photo_stream"); + block_manager::add("dashboard_center", "gallery", "log_entries"); + + module::set_version("gallery", 1); + module::set_var("gallery", "version", "3.0 pre-beta git"); + module::set_var("gallery", "choose_default_tookit", 1); + } + } + + static function uninstall() { + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {access_caches}"); + $db->query("DROP TABLE IF EXISTS {access_intents}"); + $db->query("DROP TABLE IF EXISTS {graphics_rules}"); + $db->query("DROP TABLE IF EXISTS {items}"); + $db->query("DROP TABLE IF EXISTS {logs}"); + $db->query("DROP TABLE IF EXISTS {messages}"); + $db->query("DROP TABLE IF EXISTS {modules}"); + $db->query("DROP TABLE IF EXISTS {themes}"); + $db->query("DROP TABLE IF EXISTS {incoming_translations}"); + $db->query("DROP TABLE IF EXISTS {outgoing_translations}"); + $db->query("DROP TABLE IF EXISTS {permissions}"); + $db->query("DROP TABLE IF EXISTS {sessions}"); + $db->query("DROP TABLE IF EXISTS {tasks}"); + $db->query("DROP TABLE IF EXISTS {vars}"); + foreach (array("albums", "resizes", "thumbs", "uploads", + "modules", "logs", "database.php") as $entry) { + system("/bin/rm -rf " . VARPATH . $entry); + } + } +} diff --git a/modules/gallery/helpers/gallery_menu.php b/modules/gallery/helpers/gallery_menu.php new file mode 100644 index 00000000..215fc420 --- /dev/null +++ b/modules/gallery/helpers/gallery_menu.php @@ -0,0 +1,162 @@ +admin) { + $menu->append($scaffold_menu = Menu::factory("submenu") + ->id("scaffold") + ->label("Scaffold")); + $scaffold_menu->append(Menu::factory("link") + ->id("scaffold_home") + ->label("Dashboard") + ->url(url::site("scaffold"))); + } + + $menu->append(Menu::factory("link") + ->id("home") + ->label(t("Home")) + ->url(url::site("albums/1"))); + + $item = $theme->item(); + + if (user::active()->admin || ($item && access::can("edit", $item))) { + $menu->append($options_menu = Menu::factory("submenu") + ->id("options_menu") + ->label(t("Options"))); + + if ($item && access::can("edit", $item)) { + $options_menu + ->append(Menu::factory("dialog") + ->id("edit_item") + ->label($item->is_album() ? t("Edit album") : t("Edit photo")) + ->url(url::site("form/edit/{$item->type}s/$item->id"))); + + // @todo Move album options menu to the album quick edit pane + // @todo Create resized item quick edit pane menu + if ($item->is_album()) { + $options_menu + ->append(Menu::factory("dialog") + ->id("add_item") + ->label(t("Add a photo")) + ->url(url::site("simple_uploader/app/$item->id"))) + ->append(Menu::factory("dialog") + ->id("add_album") + ->label(t("Add an album")) + ->url(url::site("form/add/albums/$item->id?type=album"))) + ->append(Menu::factory("dialog") + ->id("edit_permissions") + ->label(t("Edit permissions")) + ->url(url::site("permissions/browse/$item->id"))); + } + } + } + + if (user::active()->admin) { + $menu->append($admin_menu = Menu::factory("submenu") + ->id("admin_menu") + ->label(t("Admin"))); + self::admin($admin_menu, $theme); + foreach (module::active() as $module) { + if ($module->name == "gallery") { + continue; + } + $class = "{$module->name}_menu"; + if (method_exists($class, "admin")) { + call_user_func_array(array($class, "admin"), array(&$admin_menu, $theme)); + } + } + } + } + + static function album($menu, $theme) { + } + + static function photo($menu, $theme) { + if (access::can("view_full", $theme->item())) { + $menu + ->append(Menu::factory("link") + ->id("fullsize") + ->label(t("View full size")) + ->url("#") + ->css_class("gFullSizeLink")); + } + $menu + ->append(Menu::factory("link") + ->id("album") + ->label(t("Return to album")) + ->url($theme->item()->parent()->url("show={$theme->item->id}")) + ->css_id("gAlbumLink")); + } + + static function admin($menu, $theme) { + $menu + ->append(Menu::factory("link") + ->id("dashboard") + ->label(t("Dashboard")) + ->url(url::site("admin"))) + ->append(Menu::factory("submenu") + ->id("settings_menu") + ->label(t("Settings")) + ->append(Menu::factory("link") + ->id("graphics_toolkits") + ->label(t("Graphics")) + ->url(url::site("admin/graphics"))) + ->append(Menu::factory("link") + ->id("languages") + ->label(t("Languages")) + ->url(url::site("admin/languages"))) + ->append(Menu::factory("link") + ->id("l10n_mode") + ->label(Session::instance()->get("l10n_mode", false) + ? t("Stop translating") : t("Start translating")) + ->url(url::site("l10n_client/toggle_l10n_mode?csrf=" . + access::csrf_token()))) + ->append(Menu::factory("link") + ->id("advanced") + ->label("Advanced") + ->url(url::site("admin/advanced_settings")))) + ->append(Menu::factory("link") + ->id("modules") + ->label(t("Modules")) + ->url(url::site("admin/modules"))) + ->append(Menu::factory("submenu") + ->id("content_menu") + ->label(t("Content"))) + ->append(Menu::factory("submenu") + ->id("appearance_menu") + ->label(t("Appearance")) + ->append(Menu::factory("link") + ->id("themes") + ->label(t("Theme Choice")) + ->url(url::site("admin/themes"))) + ->append(Menu::factory("link") + ->id("theme_details") + ->label(t("Theme Options")) + ->url(url::site("admin/theme_details")))) + ->append(Menu::factory("link") + ->id("maintenance") + ->label(t("Maintenance")) + ->url(url::site("admin/maintenance"))) + ->append(Menu::factory("submenu") + ->id("statistics_menu") + ->label(t("Statistics")) + ->url("#")); + } +} diff --git a/modules/gallery/helpers/gallery_search.php b/modules/gallery/helpers/gallery_search.php new file mode 100644 index 00000000..2a4029d3 --- /dev/null +++ b/modules/gallery/helpers/gallery_search.php @@ -0,0 +1,24 @@ +description, $item->name, $item->title)); + } +} diff --git a/modules/gallery/helpers/gallery_task.php b/modules/gallery/helpers/gallery_task.php new file mode 100644 index 00000000..6046bfc4 --- /dev/null +++ b/modules/gallery/helpers/gallery_task.php @@ -0,0 +1,166 @@ +count(); + $tasks = array(); + $tasks[] = Task_Definition::factory() + ->callback("gallery_task::rebuild_dirty_images") + ->name(t("Rebuild Images")) + ->description($dirty_count ? + t2("You have one out of date photo", + "You have %count out of date photos", + $dirty_count) + : t("All your photos are up to date")) + ->severity($dirty_count ? log::WARNING : log::SUCCESS); + + $tasks[] = Task_Definition::factory() + ->callback("gallery_task::update_l10n") + ->name(t("Update translations")) + ->description(t("Download new and updated translated strings")) + ->severity(log::SUCCESS); + + return $tasks; + } + + /** + * Task that rebuilds all dirty images. + * @param Task_Model the task + */ + static function rebuild_dirty_images($task) { + $result = graphics::find_dirty_images_query(); + $remaining = $result->count(); + $completed = $task->get("completed", 0); + + $i = 0; + foreach ($result as $row) { + $item = ORM::factory("item", $row->id); + if ($item->loaded) { + graphics::generate($item); + } + + $completed++; + $remaining--; + + if (++$i == 2) { + break; + } + } + + $task->status = t2("Updated: 1 image. Total: %total_count.", + "Updated: %count images. Total: %total_count.", + $completed, + array("total_count" => ($remaining + $completed))); + + if ($completed + $remaining > 0) { + $task->percent_complete = (int)(100 * $completed / ($completed + $remaining)); + } else { + $task->percent_complete = 100; + } + + $task->set("completed", $completed); + if ($remaining == 0) { + $task->done = true; + $task->state = "success"; + site_status::clear("graphics_dirty"); + } + } + + static function update_l10n(&$task) { + $start = microtime(true); + $dirs = $task->get("dirs"); + $files = $task->get("files"); + $cache = $task->get("cache", array()); + $i = 0; + + switch ($task->get("mode", "init")) { + case "init": // 0% + $dirs = array("gallery", "modules", "themes", "installer"); + $files = array(); + $task->set("mode", "find_files"); + $task->status = t("Finding files"); + break; + + case "find_files": // 0% - 10% + while (($dir = array_pop($dirs)) && microtime(true) - $start < 0.5) { + if (basename($dir) == "tests") { + continue; + } + + foreach (glob(DOCROOT . "$dir/*") as $path) { + $relative_path = str_replace(DOCROOT, "", $path); + if (is_dir($path)) { + $dirs[] = $relative_path; + } else { + $files[] = $relative_path; + } + } + } + + $task->status = t2("Finding files: found 1 file", + "Finding files: found %count files", count($files)); + + if (!$dirs) { + $task->set("mode", "scan_files"); + $task->set("total_files", count($files)); + $task->status = t("Scanning files"); + $task->percent_complete = 10; + } + break; + + case "scan_files": // 10% - 90% + while (($file = array_pop($files)) && microtime(true) - $start < 0.5) { + $file = DOCROOT . $file; + switch (pathinfo($file, PATHINFO_EXTENSION)) { + case "php": + l10n_scanner::scan_php_file($file, $cache); + break; + + case "info": + l10n_scanner::scan_info_file($file, $cache); + break; + } + } + + $total_files = $task->get("total_files"); + $task->status = t2("Scanning files: scanned 1 file", + "Scanning files: scanned %count files", $total_files - count($files)); + + $task->percent_complete = 10 + 80 * ($total_files - count($files)) / $total_files; + if (empty($files)) { + $task->set("mode", "fetch_updates"); + $task->status = t("Fetching updates"); + $task->percent_complete = 90; + } + break; + + case "fetch_updates": // 90% - 100% + l10n_client::fetch_updates(); + $task->done = true; + $task->state = "success"; + $task->status = t("Translations installed/updated"); + $task->percent_complete = 100; + } + + $task->set("files", $files); + $task->set("dirs", $dirs); + $task->set("cache", $cache); + } +} \ No newline at end of file diff --git a/modules/gallery/helpers/gallery_theme.php b/modules/gallery/helpers/gallery_theme.php new file mode 100644 index 00000000..0acccb45 --- /dev/null +++ b/modules/gallery/helpers/gallery_theme.php @@ -0,0 +1,137 @@ +get("debug")) { + $buf .= ""; + } + if (($theme->page_type == "album" || $theme->page_type == "photo") + && access::can("edit", $theme->item())) { + $buf .= ""; + $buf .= html::script("gallery/js/quick.js"); + } + if ($theme->page_type == "photo" && access::can("view_full", $theme->item())) { + $buf .= ""; + $buf .= html::script("gallery/js/fullsize.js"); + } + + if ($session->get("l10n_mode", false)) { + $buf .= ""; + $buf .= html::script("lib/jquery.cookie.js"); + $buf .= html::script("gallery/js/l10n_client.js"); + } + + return $buf; + } + + static function resize_top($theme, $item) { + if (access::can("edit", $item)) { + $edit_link = url::site("quick/pane/$item->id?page_type=photo"); + return "
      "; + } + } + + static function resize_bottom($theme, $item) { + if (access::can("edit", $item)) { + return "
      "; + } + } + + static function thumb_top($theme, $child) { + if (access::can("edit", $child)) { + $edit_link = url::site("quick/pane/$child->id?page_type=album"); + return "
      "; + } + } + + static function thumb_bottom($theme, $child) { + if (access::can("edit", $child)) { + return "
      "; + } + } + + static function admin_head($theme) { + $session = Session::instance(); + $buf = ""; + if ($session->get("debug")) { + $buf .= ""; + } + + if ($session->get("l10n_mode", false)) { + $buf .= ""; + $buf .= html::script("lib/jquery.cookie.js"); + $buf .= html::script("gallery/js/l10n_client.js"); + } + + return $buf; + } + + static function page_bottom($theme) { + $session = Session::instance(); + if ($session->get("profiler", false)) { + $profiler = new Profiler(); + $profiler->render(); + } + if ($session->get("l10n_mode", false)) { + return L10n_Client_Controller::l10n_form(); + } + + if ($session->get("after_install")) { + $session->delete("after_install"); + return new View("after_install_loader.html"); + } + } + + static function admin_page_bottom($theme) { + $session = Session::instance(); + if ($session->get("profiler", false)) { + $profiler = new Profiler(); + $profiler->render(); + } + if ($session->get("l10n_mode", false)) { + return L10n_Client_Controller::l10n_form(); + } + } + + static function credits() { + return "
    • " . + t("Powered by Gallery %version", + array("url" => "http://gallery.menalto.com", + "version" => module::get_var("gallery", "version"))) . + "
    • "; + } + + static function admin_credits() { + return gallery_theme::credits(); + } +} \ No newline at end of file diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php index 805a95c0..605b9ff8 100644 --- a/modules/gallery/helpers/graphics.php +++ b/modules/gallery/helpers/graphics.php @@ -26,11 +26,11 @@ class graphics_Core { * Rules are applied to targets (thumbnails and resizes) in priority order. Rules are functions * in the graphics class. So for example, the following rule: * - * graphics::add_rule("core", "thumb", "resize", + * graphics::add_rule("gallery", "thumb", "resize", * array("width" => 200, "height" => 200, "master" => Image::AUTO), 100); * - * Specifies that "core" is adding a rule to resize thumbnails down to a max of 200px on - * the longest side. The core module adds default rules at a priority of 100. You can set + * Specifies that "gallery" is adding a rule to resize thumbnails down to a max of 200px on + * the longest side. The gallery module adds default rules at a priority of 100. You can set * higher and lower priorities to perform operations before or after this fires. * * @param string $module_name the module that added the rule @@ -194,7 +194,7 @@ class graphics_Core { } else { Image::factory($input_file) ->resize($options["width"], $options["height"], $options["master"]) - ->quality(module::get_var("core", "image_quality")) + ->quality(module::get_var("gallery", "image_quality")) ->save($output_file); } } @@ -212,7 +212,7 @@ class graphics_Core { } Image::factory($input_file) - ->quality(module::get_var("core", "image_quality")) + ->quality(module::get_var("gallery", "image_quality")) ->rotate($options["degrees"]) ->save($output_file); } @@ -262,7 +262,7 @@ class graphics_Core { Image::factory($input_file) ->composite($options["file"], $x, $y, $options["transparency"]) - ->quality(module::get_var("core", "image_quality")) + ->quality(module::get_var("gallery", "image_quality")) ->save($output_file); } @@ -302,7 +302,7 @@ class graphics_Core { $count, array("attrs" => sprintf( 'href="%s" class="gDialogLink"', - url::site("admin/maintenance/start/core_task::rebuild_dirty_images?csrf=__CSRF__")))), + url::site("admin/maintenance/start/gallery_task::rebuild_dirty_images?csrf=__CSRF__")))), "graphics_dirty"); } } @@ -332,12 +332,12 @@ class graphics_Core { $toolkits = graphics::detect_toolkits(); foreach (array("imagemagick", "graphicsmagick", "gd") as $tk) { if ($toolkits[$tk]) { - module::set_var("core", "graphics_toolkit", $tk); - module::set_var("core", "graphics_toolkit_path", $tk == "gd" ? "" : $toolkits[$tk]); + module::set_var("gallery", "graphics_toolkit", $tk); + module::set_var("gallery", "graphics_toolkit_path", $tk == "gd" ? "" : $toolkits[$tk]); break; } } - if (!module::get_var("core", "graphics_toolkit")) { + if (!module::get_var("gallery", "graphics_toolkit")) { site_status::warning( t("Graphics toolkit missing! Please choose a toolkit", array("url" => url::site("admin/graphics"))), @@ -349,7 +349,7 @@ class graphics_Core { * Choose which driver the Kohana Image library uses. */ static function init_toolkit() { - switch(module::get_var("core", "graphics_toolkit")) { + switch(module::get_var("gallery", "graphics_toolkit")) { case "gd": Kohana::config_set("image.driver", "GD"); break; @@ -357,13 +357,13 @@ class graphics_Core { case "imagemagick": Kohana::config_set("image.driver", "ImageMagick"); Kohana::config_set( - "image.params.directory", module::get_var("core", "graphics_toolkit_path")); + "image.params.directory", module::get_var("gallery", "graphics_toolkit_path")); break; case "graphicsmagick": Kohana::config_set("image.driver", "GraphicsMagick"); Kohana::config_set( - "image.params.directory", module::get_var("core", "graphics_toolkit_path")); + "image.params.directory", module::get_var("gallery", "graphics_toolkit_path")); break; } @@ -376,7 +376,7 @@ class graphics_Core { * @return boolean */ static function can($func) { - if (module::get_var("core", "graphics_toolkit") == "gd" && + if (module::get_var("gallery", "graphics_toolkit") == "gd" && $func == "rotate" && !function_exists("imagerotate")) { return false; diff --git a/modules/gallery/helpers/l10n_client.php b/modules/gallery/helpers/l10n_client.php index ec4c5429..d26739f5 100644 --- a/modules/gallery/helpers/l10n_client.php +++ b/modules/gallery/helpers/l10n_client.php @@ -35,9 +35,9 @@ class l10n_client_Core { static function api_key($api_key=null) { if ($api_key !== null) { - module::set_var("core", "l10n_client_key", $api_key); + module::set_var("gallery", "l10n_client_key", $api_key); } - return module::get_var("core", "l10n_client_key", ""); + return module::get_var("gallery", "l10n_client_key", ""); } static function server_uid($api_key=null) { diff --git a/modules/gallery/helpers/locale.php b/modules/gallery/helpers/locale.php index b707637f..2ba0f255 100644 --- a/modules/gallery/helpers/locale.php +++ b/modules/gallery/helpers/locale.php @@ -37,8 +37,8 @@ class locale_Core { static function installed() { $available = self::available(); - $default = module::get_var("core", "default_locale"); - $codes = explode("|", module::get_var("core", "installed_locales", $default)); + $default = module::get_var("gallery", "default_locale"); + $codes = explode("|", module::get_var("gallery", "installed_locales", $default)); foreach ($codes as $code) { if (isset($available->$code)) { $installed[$code] = $available[$code]; @@ -49,10 +49,10 @@ class locale_Core { static function update_installed($locales) { // Ensure that the default is included... - $default = module::get_var("core", "default_locale"); + $default = module::get_var("gallery", "default_locale"); $locales = array_merge($locales, array($default)); - module::set_var("core", "installed_locales", join("|", $locales)); + module::set_var("gallery", "installed_locales", join("|", $locales)); } // @todo Might want to add a localizable language name as well. diff --git a/modules/gallery/helpers/module.php b/modules/gallery/helpers/module.php index a48c89ed..a6cc87ac 100644 --- a/modules/gallery/helpers/module.php +++ b/modules/gallery/helpers/module.php @@ -37,7 +37,7 @@ class module_Core { $module = self::get($module_name); if (!$module->loaded) { $module->name = $module_name; - $module->active = $module_name == "core"; // only core is active by default + $module->active = $module_name == "gallery"; // only gallery is active by default } $module->version = 1; $module->save(); @@ -75,7 +75,7 @@ class module_Core { */ static function available() { $modules = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS); - foreach (array_merge(array("core/module.info"), glob(MODPATH . "*/module.info")) as $file) { + foreach (array_merge(array("gallery/module.info"), glob(MODPATH . "*/module.info")) as $file) { $module_name = basename(dirname($file)); $modules->$module_name = new ArrayObject(parse_ini_file($file), ArrayObject::ARRAY_AS_PROPS); $modules->$module_name->installed = self::is_installed($module_name); @@ -85,7 +85,7 @@ class module_Core { } // Lock certain modules - $modules->core->locked = true; + $modules->gallery->locked = true; $modules->user->locked = true; $modules->ksort(); @@ -215,10 +215,10 @@ class module_Core { if ($module->active) { self::$active[] = $module; } - if ($module->name != "core") { - $kohana_modules[] = MODPATH . $module->name; - } + $kohana_modules[] = MODPATH . $module->name; + // @todo: force 'gallery' to be at the end } + Kohana::config_set("core.modules", $kohana_modules); } @@ -252,32 +252,32 @@ class module_Core { * @return the value */ static function get_var($module_name, $name, $default_value=null) { - // We cache all vars in core._cache so that we can load all vars at once for + // We cache all vars in gallery._cache so that we can load all vars at once for // performance. if (empty(self::$var_cache)) { $row = Database::instance() ->select("value") ->from("vars") - ->where(array("module_name" => "core", "name" => "_cache")) + ->where(array("module_name" => "gallery", "name" => "_cache")) ->get() ->current(); if ($row) { self::$var_cache = unserialize($row->value); } else { - // core._cache doesn't exist. Create it now. + // gallery._cache doesn't exist. Create it now. foreach (Database::instance() ->select("module_name", "name", "value") ->from("vars") ->orderby("module_name", "name") ->get() as $row) { - if ($row->module_name == "core" && $row->name == "_cache") { + if ($row->module_name == "gallery" && $row->name == "_cache") { // This could happen if there's a race condition continue; } self::$var_cache->{$row->module_name}->{$row->name} = $row->value; } $cache = ORM::factory("var"); - $cache->module_name = "core"; + $cache->module_name = "gallery"; $cache->name = "_cache"; $cache->value = serialize(self::$var_cache); $cache->save(); @@ -309,7 +309,7 @@ class module_Core { $var->value = $value; $var->save(); - Database::instance()->delete("vars", array("module_name" => "core", "name" => "_cache")); + Database::instance()->delete("vars", array("module_name" => "gallery", "name" => "_cache")); self::$var_cache = null; } @@ -325,7 +325,7 @@ class module_Core { "WHERE `module_name` = '$module_name' " . "AND `name` = '$name'"); - Database::instance()->delete("vars", array("module_name" => "core", "name" => "_cache")); + Database::instance()->delete("vars", array("module_name" => "gallery", "name" => "_cache")); self::$var_cache = null; } @@ -343,7 +343,7 @@ class module_Core { $var->delete(); } - Database::instance()->delete("vars", array("module_name" => "core", "name" => "_cache")); + Database::instance()->delete("vars", array("module_name" => "gallery", "name" => "_cache")); self::$var_cache = null; } diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php index 3293d4ac..15225fe7 100644 --- a/modules/gallery/helpers/movie.php +++ b/modules/gallery/helpers/movie.php @@ -140,11 +140,11 @@ class movie_Core { } static function find_ffmpeg() { - if (!$ffmpeg_path = module::get_var("core", "ffmpeg_path")) { + if (!$ffmpeg_path = module::get_var("gallery", "ffmpeg_path")) { if (function_exists("exec")) { $ffmpeg_path = exec("which ffmpeg"); if ($ffmpeg_path) { - module::set_var("core", "ffmpeg_path", $ffmpeg_path); + module::set_var("gallery", "ffmpeg_path", $ffmpeg_path); } } } diff --git a/modules/gallery/helpers/theme.php b/modules/gallery/helpers/theme.php index cbe224db..af340db6 100644 --- a/modules/gallery/helpers/theme.php +++ b/modules/gallery/helpers/theme.php @@ -31,10 +31,11 @@ class theme_Core { static function load_themes() { $modules = Kohana::config("core.modules"); if (Router::$controller == "admin") { - array_unshift($modules, THEMEPATH . module::get_var("core", "active_admin_theme")); + array_unshift($modules, THEMEPATH . module::get_var("gallery", "active_admin_theme")); } else { - array_unshift($modules, THEMEPATH . module::get_var("core", "active_site_theme")); + array_unshift($modules, THEMEPATH . module::get_var("gallery", "active_site_theme")); } + Kohana::config_set("core.modules", $modules); } @@ -43,17 +44,17 @@ class theme_Core { $group = $form->group("edit_theme"); $group->input("page_size")->label(t("Items per page"))->id("gPageSize") ->rules("required|valid_digit") - ->value(module::get_var("core", "page_size")); + ->value(module::get_var("gallery", "page_size")); $group->input("thumb_size")->label(t("Thumbnail size (in pixels)"))->id("gThumbSize") ->rules("required|valid_digit") - ->value(module::get_var("core", "thumb_size")); + ->value(module::get_var("gallery", "thumb_size")); $group->input("resize_size")->label(t("Resized image size (in pixels)"))->id("gResizeSize") ->rules("required|valid_digit") - ->value(module::get_var("core", "resize_size")); + ->value(module::get_var("gallery", "resize_size")); $group->textarea("header_text")->label(t("Header text"))->id("gHeaderText") - ->value(module::get_var("core", "header_text")); + ->value(module::get_var("gallery", "header_text")); $group->textarea("footer_text")->label(t("Footer text"))->id("gFooterText") - ->value(module::get_var("core", "footer_text")); + ->value(module::get_var("gallery", "footer_text")); $group->submit("")->value(t("Save")); return $form; } diff --git a/modules/gallery/hooks/init_gallery.php b/modules/gallery/hooks/init_gallery.php index 2c36795a..5735e7dc 100644 --- a/modules/gallery/hooks/init_gallery.php +++ b/modules/gallery/hooks/init_gallery.php @@ -26,11 +26,11 @@ if (!file_exists(VARPATH . "database.php")) { Event::add("system.ready", array("I18n", "instance")); Event::add("system.ready", array("module", "load_modules")); -Event::add("system.ready", array("core", "ready")); +Event::add("system.ready", array("gallery", "ready")); Event::add("system.post_routing", array("theme", "load_themes")); Event::add("system.post_routing", array("url", "parse_url")); -Event::add("system.post_routing", array("core", "maintenance_mode")); -Event::add("system.shutdown", array("core", "shutdown")); +Event::add("system.post_routing", array("gallery", "maintenance_mode")); +Event::add("system.shutdown", array("gallery", "shutdown")); // Override the cookie if we have a session id in the URL. // @todo This should probably be an event callback diff --git a/modules/gallery/libraries/Admin_View.php b/modules/gallery/libraries/Admin_View.php index acc3f8ec..1f976871 100644 --- a/modules/gallery/libraries/Admin_View.php +++ b/modules/gallery/libraries/Admin_View.php @@ -29,15 +29,15 @@ class Admin_View_Core extends View { * @return void */ public function __construct($name) { - $theme_name = module::get_var("core", "active_site_theme"); + $theme_name = module::get_var("gallery", "active_site_theme"); if (!file_exists("themes/$theme_name")) { - module::set_var("core", "active_site_theme", "admin_default"); + module::set_var("gallery", "active_site_theme", "admin_default"); theme::load_themes(); Kohana::log("error", "Unable to locate theme '$theme_name', switching to default theme."); } parent::__construct($name); - $this->theme_name = module::get_var("core", "active_admin_theme"); + $this->theme_name = module::get_var("gallery", "active_admin_theme"); if (user::active()->admin) { $this->theme_name = Input::instance()->get("theme", $this->theme_name); } @@ -57,10 +57,10 @@ class Admin_View_Core extends View { public function admin_menu() { $menu = Menu::factory("root"); - core_menu::admin($menu, $this); + gallery_menu::admin($menu, $this); foreach (module::active() as $module) { - if ($module->name == "core") { + if ($module->name == "gallery") { continue; } $class = "{$module->name}_menu"; diff --git a/modules/gallery/libraries/I18n.php b/modules/gallery/libraries/I18n.php index c936be88..f2801169 100644 --- a/modules/gallery/libraries/I18n.php +++ b/modules/gallery/libraries/I18n.php @@ -62,7 +62,7 @@ class I18n_Core { if (self::$_instance == NULL || isset($config)) { $config = isset($config) ? $config : Kohana::config('locale'); if (empty($config['default_locale'])) { - $config['default_locale'] = module::get_var('core', 'default_locale'); + $config['default_locale'] = module::get_var('gallery', 'default_locale'); } self::$_instance = new I18n_Core($config); } diff --git a/modules/gallery/libraries/Theme_View.php b/modules/gallery/libraries/Theme_View.php index b5b97666..5fcc2943 100644 --- a/modules/gallery/libraries/Theme_View.php +++ b/modules/gallery/libraries/Theme_View.php @@ -30,15 +30,15 @@ class Theme_View_Core extends View { * @return void */ public function __construct($name, $page_type) { - $theme_name = module::get_var("core", "active_site_theme"); + $theme_name = module::get_var("gallery", "active_site_theme"); if (!file_exists("themes/$theme_name")) { - module::set_var("core", "active_site_theme", "default"); + module::set_var("gallery", "active_site_theme", "default"); theme::load_themes(); Kohana::log("error", "Unable to locate theme '$theme_name', switching to default theme."); } parent::__construct($name); - $this->theme_name = module::get_var("core", "active_site_theme"); + $this->theme_name = module::get_var("gallery", "active_site_theme"); if (user::active()->admin) { $this->theme_name = Input::instance()->get("theme", $this->theme_name); } @@ -64,7 +64,7 @@ class Theme_View_Core extends View { public function thumb_proportion() { // @TODO change the 200 to a theme supplied value when and if we come up with an // API to allow the theme to set defaults. - return module::get_var("core", "thumb_size", 200) / 200; + return module::get_var("gallery", "thumb_size", 200) / 200; } public function url($path, $absolute_url=false) { @@ -91,10 +91,10 @@ class Theme_View_Core extends View { public function site_menu() { $menu = Menu::factory("root"); if ($this->page_type != "login") { - core_menu::site($menu, $this); + gallery_menu::site($menu, $this); foreach (module::active() as $module) { - if ($module->name == "core") { + if ($module->name == "gallery") { continue; } $class = "{$module->name}_menu"; @@ -109,10 +109,10 @@ class Theme_View_Core extends View { public function album_menu() { $menu = Menu::factory("root"); - core_menu::album($menu, $this); + gallery_menu::album($menu, $this); foreach (module::active() as $module) { - if ($module->name == "core") { + if ($module->name == "gallery") { continue; } $class = "{$module->name}_menu"; @@ -126,10 +126,10 @@ class Theme_View_Core extends View { public function photo_menu() { $menu = Menu::factory("root"); - core_menu::photo($menu, $this); + gallery_menu::photo($menu, $this); foreach (module::active() as $module) { - if ($module->name == "core") { + if ($module->name == "gallery") { continue; } $class = "{$module->name}_menu"; diff --git a/modules/gallery/tests/Core_Installer_Test.php b/modules/gallery/tests/Core_Installer_Test.php index f7036286..24d24b38 100644 --- a/modules/gallery/tests/Core_Installer_Test.php +++ b/modules/gallery/tests/Core_Installer_Test.php @@ -19,21 +19,21 @@ */ /** - * This test case operates under the assumption that core_installer::install() is called by the + * This test case operates under the assumption that gallery_installer::install() is called by the * test controller before it starts. */ -class Core_Installer_Test extends Unit_Test_Case { +class Gallery_Installer_Test extends Unit_Test_Case { public function install_creates_dirs_test() { $this->assert_true(file_exists(VARPATH . "albums")); $this->assert_true(file_exists(VARPATH . "resizes")); } - public function install_registers_core_module_test() { - $core = ORM::factory("module")->where("name", "core")->find(); - $this->assert_equal("core", $core->name); + public function install_registers_gallery_module_test() { + $gallery = ORM::factory("module")->where("name", "gallery")->find(); + $this->assert_equal("gallery", $gallery->name); // This is probably too volatile to keep for long - $this->assert_equal(1, $core->version); + $this->assert_equal(1, $gallery->version); } public function install_creates_root_item_test() { diff --git a/modules/gallery/tests/File_Structure_Test.php b/modules/gallery/tests/File_Structure_Test.php index 1caa82ba..3b8c754f 100644 --- a/modules/gallery/tests/File_Structure_Test.php +++ b/modules/gallery/tests/File_Structure_Test.php @@ -218,11 +218,11 @@ class GalleryCodeFilterIterator extends FilterIterator { $path_name = $this->getInnerIterator()->getPathName(); return !( strpos($path_name, ".svn") || - strpos($path_name, "core/views/kohana_profiler.php") !== false || strpos($path_name, DOCROOT . "test") !== false || strpos($path_name, DOCROOT . "var") !== false || strpos($path_name, MODPATH . "forge") !== false || strpos($path_name, APPPATH . "views/kohana_error_page.php") !== false || + strpos($path_name, MODPATH . "gallery/views/kohana_profiler.php") !== false || strpos($path_name, MODPATH . "gallery_unit_test/views/kohana_error_page.php") !== false || strpos($path_name, MODPATH . "gallery_unit_test/views/kohana_unit_test_cli.php") !== false || strpos($path_name, MODPATH . "unit_test") !== false || diff --git a/modules/gallery/tests/Movie_Helper_Test.php b/modules/gallery/tests/Movie_Helper_Test.php index b92ef3f8..627651bb 100644 --- a/modules/gallery/tests/Movie_Helper_Test.php +++ b/modules/gallery/tests/Movie_Helper_Test.php @@ -22,7 +22,7 @@ class Movie_Helper_Test extends Unit_Test_Case { $rand = rand(); $root = ORM::factory("item", 1); try { - $movie = movie::create($root, DOCROOT . "core/tests/test.jpg", "$rand/.jpg", $rand, $rand); + $movie = movie::create($root, MODPATH . "gallery/tests/test.jpg", "$rand/.jpg", $rand, $rand); } catch (Exception $e) { // pass return; @@ -35,7 +35,7 @@ class Movie_Helper_Test extends Unit_Test_Case { $rand = rand(); $root = ORM::factory("item", 1); try { - $movie = movie::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg.", $rand, $rand); + $movie = movie::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg.", $rand, $rand); } catch (Exception $e) { $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); return; diff --git a/modules/gallery/tests/Photo_Helper_Test.php b/modules/gallery/tests/Photo_Helper_Test.php index deb11bb9..2a6693e1 100644 --- a/modules/gallery/tests/Photo_Helper_Test.php +++ b/modules/gallery/tests/Photo_Helper_Test.php @@ -21,7 +21,7 @@ class Photo_Helper_Test extends Unit_Test_Case { public function create_photo_test() { $rand = rand(); - $filename = DOCROOT . "core/tests/test.jpg"; + $filename = MODPATH . "gallery/tests/test.jpg"; $image_info = getimagesize($filename); $root = ORM::factory("item", 1); @@ -50,8 +50,8 @@ class Photo_Helper_Test extends Unit_Test_Case { public function create_conflicting_photo_test() { $rand = rand(); $root = ORM::factory("item", 1); - $photo1 = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); - $photo2 = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); + $photo1 = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); + $photo2 = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); $this->assert_true($photo1->name != $photo2->name); } @@ -68,7 +68,7 @@ class Photo_Helper_Test extends Unit_Test_Case { public function thumb_url_test() { $rand = rand(); $root = ORM::factory("item", 1); - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); + $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); $this->assert_equal("http://./var/thumbs/{$rand}.jpg", $photo->thumb_url()); } @@ -76,7 +76,7 @@ class Photo_Helper_Test extends Unit_Test_Case { $rand = rand(); $root = ORM::factory("item", 1); $album = album::create($root, $rand, $rand, $rand); - $photo = photo::create($album, DOCROOT . "core/tests/test.jpg", "$rand.jpg", $rand, $rand); + $photo = photo::create($album, MODPATH . "gallery/tests/test.jpg", "$rand.jpg", $rand, $rand); $this->assert_equal("http://./var/resizes/{$rand}/{$rand}.jpg", $photo->resize_url()); } @@ -85,7 +85,7 @@ class Photo_Helper_Test extends Unit_Test_Case { $rand = rand(); $root = ORM::factory("item", 1); try { - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand/.jpg", $rand, $rand); + $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand/.jpg", $rand, $rand); } catch (Exception $e) { // pass return; @@ -98,7 +98,7 @@ class Photo_Helper_Test extends Unit_Test_Case { $rand = rand(); $root = ORM::factory("item", 1); try { - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "$rand.jpg.", $rand, $rand); + $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "$rand.jpg.", $rand, $rand); } catch (Exception $e) { $this->assert_equal("@todo NAME_CANNOT_END_IN_PERIOD", $e->getMessage()); return; diff --git a/modules/gallery/tests/Photos_Controller_Test.php b/modules/gallery/tests/Photos_Controller_Test.php index 71319315..771cc90d 100644 --- a/modules/gallery/tests/Photos_Controller_Test.php +++ b/modules/gallery/tests/Photos_Controller_Test.php @@ -29,7 +29,7 @@ class Photos_Controller_Test extends Unit_Test_Case { public function change_photo_test() { $controller = new Photos_Controller(); $root = ORM::factory("item", 1); - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "test.jpeg", "test", "test"); + $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "test.jpeg", "test", "test"); $orig_name = $photo->name; $_POST["filename"] = "test.jpeg"; @@ -58,7 +58,7 @@ class Photos_Controller_Test extends Unit_Test_Case { public function change_photo_no_csrf_fails_test() { $controller = new Photos_Controller(); $root = ORM::factory("item", 1); - $photo = photo::create($root, DOCROOT . "core/tests/test.jpg", "test", "test", "test"); + $photo = photo::create($root, MODPATH . "gallery/tests/test.jpg", "test", "test", "test"); $_POST["name"] = "new name"; $_POST["title"] = "new title"; $_POST["description"] = "new description"; diff --git a/modules/gallery/tests/Var_Test.php b/modules/gallery/tests/Var_Test.php index 82370631..355d94a7 100644 --- a/modules/gallery/tests/Var_Test.php +++ b/modules/gallery/tests/Var_Test.php @@ -19,31 +19,31 @@ */ class Var_Test extends Unit_Test_Case { public function add_parameter_test() { - module::set_var("core", "Parameter", "original value"); - $this->assert_equal("original value", module::get_var("core", "Parameter")); + module::set_var("gallery", "Parameter", "original value"); + $this->assert_equal("original value", module::get_var("gallery", "Parameter")); - module::set_var("core", "Parameter", "updated value"); - $this->assert_equal("updated value", module::get_var("core", "Parameter")); + module::set_var("gallery", "Parameter", "updated value"); + $this->assert_equal("updated value", module::get_var("gallery", "Parameter")); } public function clear_parameter_test() { - module::set_var("core", "Parameter", "original value"); - $this->assert_equal("original value", module::get_var("core", "Parameter")); + module::set_var("gallery", "Parameter", "original value"); + $this->assert_equal("original value", module::get_var("gallery", "Parameter")); - module::clear_var("core", "Parameter"); - $this->assert_equal(null, module::get_var("core", "Parameter")); + module::clear_var("gallery", "Parameter"); + $this->assert_equal(null, module::get_var("gallery", "Parameter")); } public function incr_parameter_test() { - module::set_var("core", "Parameter", "original value"); - module::incr_var("core", "Parameter"); - $this->assert_equal("1", module::get_var("core", "Parameter")); + module::set_var("gallery", "Parameter", "original value"); + module::incr_var("gallery", "Parameter"); + $this->assert_equal("1", module::get_var("gallery", "Parameter")); - module::set_var("core", "Parameter", "2"); - module::incr_var("core", "Parameter", "9"); - $this->assert_equal("11", module::get_var("core", "Parameter")); + module::set_var("gallery", "Parameter", "2"); + module::incr_var("gallery", "Parameter", "9"); + $this->assert_equal("11", module::get_var("gallery", "Parameter")); - module::incr_var("core", "NonExistent", "9"); - $this->assert_equal(null, module::get_var("core", "NonExistent")); + module::incr_var("gallery", "NonExistent", "9"); + $this->assert_equal(null, module::get_var("gallery", "NonExistent")); } } \ No newline at end of file diff --git a/modules/gallery/views/admin_advanced_settings.html.php b/modules/gallery/views/admin_advanced_settings.html.php index 1f3825bd..9f90d671 100644 --- a/modules/gallery/views/admin_advanced_settings.html.php +++ b/modules/gallery/views/admin_advanced_settings.html.php @@ -17,7 +17,7 @@ - module_name == "core" && $var->name == "_cache") continue ?> + module_name == "gallery" && $var->name == "_cache") continue ?> module_name ?> name ?> diff --git a/modules/gallery/views/admin_block_stats.html.php b/modules/gallery/views/admin_block_stats.html.php index 2d975073..395ed71d 100644 --- a/modules/gallery/views/admin_block_stats.html.php +++ b/modules/gallery/views/admin_block_stats.html.php @@ -1,7 +1,7 @@